adContainer/VideoAdContainer.js

/* eslint-disable promise/prefer-await-to-then */
import loadScript from './helpers/loadScript';
import createAdVideoElement from './helpers/createAdVideoElement';
import createAdContainer from './helpers/createAdContainer';
import createIframe from './helpers/createIframe';
import getContentDocument from './helpers/getContentDocument';
import unique from './helpers/unique';

const nextId = unique('videoAdContainer');
const hidden = Symbol('hidden');

/**
 * @class
 * @alias VideoAdContainer
 * @description This class provides everything necessary to contain and create a video ad within a given placeholder Element.
 */
class VideoAdContainer {
  [hidden] = {
    destroyed: false,
    iframe: null,
    readyPromise: null
  };

  /**
   * Creates a VideoAdContainer.
   *
   * @param {HTMLDivElement} placeholder - DIV that will contain the ad.
   * @param {HTMLVideoElement} [videoElement] - optional videoElement that will be used to play the ad.
   */
  constructor (placeholder, videoElement = null) {
    if (!(placeholder instanceof Element)) {
      throw new TypeError('placeholder is not an Element');
    }

    this[hidden].id = nextId();
    this.element = createAdContainer();
    this.executionContext = null;

    this.isOriginalVideoElement = Boolean(videoElement);

    if (videoElement) {
      this.videoElement = videoElement;
    } else {
      this.videoElement = createAdVideoElement();
      this.element.appendChild(this.videoElement);
    }

    placeholder.appendChild(this.element);
  }

  /**
   * Adds the passed script to the ad container.
   *
   * @param {string} src - script source uri.
   * @param {Object} options - Options map.
   * @param {string} options.type - Defaults to 'text/javascript'.
   * @param {boolean} options.async - if "true" the "async" attribute is added to the new script. Defaults to false.
   * @param {boolean} options.defer - if "true" the "defer" attribute is added to the new script. Defaults to true.
   */
  async addScript (src, options = {}) {
    if (this.isDestroyed()) {
      throw new Error('VideoAdContainer has been destroyed');
    }

    if (!this[hidden].iframe) {
      this[hidden].iframe = await createIframe(this.element, this[hidden].id);
      this.executionContext = this[hidden].iframe.contentWindow;
    }

    const placeholder = getContentDocument(this[hidden].iframe).body;

    return loadScript(src, {
      placeholder,
      ...options
    });
  }

  /**
   * Destroys the VideoAdContainer.
   */
  destroy () {
    this.element.parentNode.removeChild(this.element);
    this[hidden].destroyed = true;
  }

  /**
   * Checks if the container is destroyed.
   *
   * @returns {boolean} - true if the container is destroyed and false otherwise.
   */
  isDestroyed () {
    return this[hidden].destroyed;
  }
}

export default VideoAdContainer;