Manual Reference Source Test

src/modules/identity-window.js

import Logger from './logger';
import {addIFrameModal, addIFrameModalStyle, iFrameWrapperId} from '../templates/iframe-modal.js';

const _STORAGE_KEY = 'nbc-identity-window';

/**
 * Window pop-up handling
 */
class IdentityWindow {
  /**
   * @param {Object} props
   */
  constructor(props = {}) {
    this._pingTimer = null;
    this._popUpTimer = null;
    this._isInit = false;
    this._logger = new Logger('Identity-Window');
    this._logger.enabled = props.debug;
    this._config = null;

    if (props.targetOrigin) this.setTarget(props.targetOrigin);
    if (props.onMessage) this._onMessageCallback = props.onMessage;
    if (props.onClose) this._onCloseCallback = props.onClose;
    if (props.config) this._config = props.config;

    this.onMessage = this.onMessage.bind(this);
    this.onClose = this.onClose.bind(this);
  }

  /**
   * @type {Boolean}
   */
  get isInit() {
    return this._isInit;
  }

  /**
   * Initialize an event listener to listen to message from the pop-up
   */
  init() {
    this._logger.log('Popup controller initialized');

    // Check for cached target origin
    let stored = localStorage.getItem(_STORAGE_KEY);

    if (stored) {
      stored = JSON.parse(stored);

      if (stored.target) this.setTarget(stored.target);
    }

    window.addEventListener('message', this.onMessage, false);
    this._isInit = true;
  }

  /**
   * Initialize target window
   */
  targetInit() {
    this.init();
    if (window.opener || window.parent) this._messageSource = window.opener || window.parent;
    this.pingPong();
  }

  /**
   * Remove the pop-up controller
   */
  remove() {
    this._isInit = false;
    window.removeEventListener('message', this.onMessage, false);
  }

  /**
   * Send messages to the pop-up
   *
   * @param {Object} message
   */
  postMessage(message) {
    if (!this._messageSource) {
      if (!window.opener && !window.parent) return;
      this._messageSource = window.opener || window.parent;
    }

    this._logger.log('Message sent:', message, this._targetOrigin);
    this._messageSource.postMessage({source: 'identity', message: JSON.stringify(message)}, this._targetOrigin);
  }

  /**
   * Set the source of the pop-up messages
   *
   * @param {*} source
   */
  setSource(source) {
    this._logger.log('Source set:', source);
    this._messageSource = source;
  }

  /**
   * Set the target of the pop-up messages
   *
   * @param {*} target
   */
  setTarget(target) {
    this._logger.log('Target set:', target);
    this._targetOrigin = target;
    localStorage.setItem(_STORAGE_KEY, JSON.stringify({target}));
  }

  /**
   * Check if pop-up window is open
   *
   * @return {boolean}
   */
  isPopUpClosed() {
    return !this._popUpWindow || this._popUpWindow.closed || typeof this._popUpWindow.closed=='undefined';
  }

  /**
   * Open windowed popup in the center of the screen
   *
   * @param {string} url - window href
   * @param {string} title - window title
   * @param {number} w - window width
   * @param {number} h - window height
   */
  open(url, title, w, h) {
    const left = (screen.width - w) / 2;
    const top = (screen.height - h) / 2;
    this._popUpWindow = window.open(
      url,
      title,
      'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left,
    );

    if (this.isPopUpClosed()) {
      this._setState('pop-up-blocked');
    } else {
      // Puts focus on the new window
      if (window.focus) this._popUpWindow.focus();
      this.setSource(this._popUpWindow);

      this.startPopUpTimer();
    }
  }

  /**
   * Open windowed iframe in body of app
   *
   * @param {string} url - iframe source
   * @param {boolean} isManageProfile - apply manage profile style to modal
   */
  openIframe(url, isManageProfile = false) {
    const themeConfig = (this._config && this._config.theme) || {};
    this.loadIFrameStyling().then(() => {
      const iframe = addIFrameModal(url, themeConfig, this.closeIframe, isManageProfile);
      if (!iframe) return;
      this.setSource(iframe.contentWindow);
    });
  }

  /**
   * Close windowed iframe
   */
  closeIframe() {
    document.body.style.overflow = '';
    const iframe = document.getElementById(iFrameWrapperId);
    if (iframe) iframe.remove();
  }

  /**
   * Subscribe to check if pop-up has closed
   */
  startPopUpTimer() {
    if (this._popUpTimer) return;

    this._popUpTimer = window.setInterval(() => {
      if (this.isPopUpClosed()) {
        this.onClose();
      }
    }, 1000);
  }

  /**
   * Send ping messages to host window to make sure windows stay connected
   */
  pingPong() {
    if (this._pingTimer) return;
    this._pingTimer = window.setInterval(() => {
      this.postMessage('ping');
    }, 2000);
  }

  /**
   * Receive ping message and connect the 2 windows if necessary
   */
  handlePing() {
    if (this._popUpWindow && this._popUpTimer) return;
    this._popUpWindow = this._messageSource;
    this.startPopUpTimer();
  }

  /**
   * Receive message from window
   *
   * @param {Object} event
   */
  onMessage(event) {
    if (event.origin !== this._targetOrigin || !event.data || event.data.source !== 'identity') return;

    // this._logger.log('Message event received:', event);
    this._messageSource = event.source;

    if (!event.data.message) return;

    const data = JSON.parse(event.data.message);
    // this._logger.log('Message data parsed:', data);
    if (!data) return;

    if (data === 'closing') this.onClose(true);
    if (data === 'ping') this.handlePing();
    if (this._onMessageCallback) this._onMessageCallback(data);
  }

  /**
   * Pop-up close callback
   *
   * @param {Boolean} fromWindow
   * @return {Function}
   */
  onClose(fromWindow) {
    window.clearInterval(this._popUpTimer);
    this._popUpTimer = null;
    window.clearInterval(this._pingTimer);
    this._pingTimer = null;

    if (!this._onCloseCallback) return;
    return this._onCloseCallback(fromWindow);
  }

  /**
   * Load iFrame styling on demand
   *
   * @return {Promise}
   */
  loadIFrameStyling() {
    return new Promise((resolve) => {
      const fontElementId = 'identity-iframe-google-font';

      if (!document.getElementById(fontElementId)) {
        const fontElement = document.createElement('link');
        fontElement.id = fontElementId;
        fontElement.href = 'https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap';
        fontElement.rel = 'stylesheet';

        document.head.appendChild(fontElement);
      }

      const themeConfig = (this._config && this._config.theme) || {};
      addIFrameModalStyle(themeConfig).then(resolve);
    });
  }
}

export default IdentityWindow;