constructor(parentDiv, asset, isFeatured, remakeButtonsFn) {
    /** @private {!Element} */
    this.card_ = document.createElement('div');
    /** @private {!ShakaDemoAssetInfo} */
    this.asset_ = asset;
    /** @private {!Element} */
    this.actions_ = document.createElement('div');
    /** @private {!Element} */
    this.featureIconsContainer_ = document.createElement('div');
    /** @private {!Element} */
    this.progressCircle_ = document.createElement('div');
    const svgNs = 'http://www.w3.org/2000/svg';
    /** @private {!Element} */
    this.progressCircleSvg_ = document.createElementNS(svgNs, 'svg');
    /** @private {!Element} */
    this.progressCircleBack_ = document.createElementNS(svgNs, 'circle');
    /** @private {!Element} */
    this.progressCircleBar_ = document.createElementNS(svgNs, 'circle');
    /** @private {function(!shakaDemo.AssetCard)} */
    this.remakeButtonsFn_ = remakeButtonsFn;

    // Lay out the card.
    this.card_.classList.add('mdl-card-wide');
    this.card_.classList.add('mdl-card');
    this.card_.classList.add('mdl-shadow--2dp');
    this.card_.classList.add('asset-card');

    const titleDiv = document.createElement('div');
    titleDiv.classList.add('mdl-card__title');
    this.card_.appendChild(titleDiv);
    const titleText = document.createElement('h2');
    titleText.classList.add('mdl-card__title-text');
    titleText.textContent =
        isFeatured ? (asset.shortName || asset.name) : asset.name;
    titleDiv.appendChild(titleText);

    if (asset.iconUri) {
      const picture = document.createElement('picture');

      const webpSource =
        /** @type {!HTMLSourceElement} */(document.createElement('source'));
      webpSource.srcset = asset.iconUri.replace(/.png$/, '.webp');
      webpSource.type = 'image/webp';

      const pngSource =
        /** @type {!HTMLSourceElement} */(document.createElement('source'));
      pngSource.srcset = asset.iconUri;
      pngSource.type = 'image/png';

      const img =
        /** @type {!HTMLImageElement} */(document.createElement('img'));
      img.src = asset.iconUri;
      img.alt = '';  // Not necessary to understand the page

      // It can only be guaranteed that they have a webp version if they are on
      // our server.
      if (asset.iconUri.startsWith('https://storage.googleapis.com')) {
        picture.appendChild(webpSource);
      }
      picture.appendChild(pngSource);
      picture.appendChild(img);

      this.card_.appendChild(picture);
    }

    if (asset.description && isFeatured) {
      const supportingText = document.createElement('div');
      supportingText.classList.add('mdl-card__supporting-text');
      supportingText.textContent = asset.description;
      this.card_.appendChild(supportingText);
    }

    this.card_.appendChild(this.featureIconsContainer_);
    this.addFeatureIcons_(asset);

    this.actions_.classList.add('mdl-card__actions');
    this.actions_.classList.add('mdl-card--border');
    this.card_.appendChild(this.actions_);

    this.progressCircle_.classList.add('hidden');
    this.progressCircle_.classList.add('progress-circle');
    this.card_.appendChild(this.progressCircle_);
    this.progressCircleSvg_.appendChild(this.progressCircleBack_);
    this.progressCircleSvg_.appendChild(this.progressCircleBar_);
    this.progressCircle_.appendChild(this.progressCircleSvg_);
    this.progressCircleSvg_.classList.add('progress-circle-svg');
    this.progressCircleBack_.classList.add('progress-circle-back');
    this.progressCircleBar_.classList.add('progress-circle-bar');

    parentDiv.appendChild(this.card_);
    // Remake buttons AFTER appending to parent div, so that any tooltips can
    // be placed.
    this.remakeButtons();
  }
@param {!Element} parentDiv
@param {!ShakaDemoAssetInfo} asset
@param {boolean} isFeatured True if this card should use the "featured"
  style, which use the asset's short name and have descriptions.
@param {function(!shakaDemo.AssetCard)} remakeButtonsFn