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;