js/VPAIDHTML5Client.js
'use strict';
var utils = require('./utils');
var unique = utils.unique('vpaidIframe');
var VPAIDAdUnit = require('./VPAIDAdUnit');
var defaultTemplate = '<!DOCTYPE html>' +
'<html lang="en">' +
'<head><meta charset="UTF-8"></head>' +
'<body style="margin:0;padding:0"><div class="ad-element"></div>' +
'<script type="text/javascript" src="{{iframeURL_JS}}"></script>' +
'<script type="text/javascript">' +
'window.parent.postMessage(\'{"event": "ready", "id": "{{iframeID}}"}\', \'{{origin}}\');' +
'</script>' +
'</body>' +
'</html>';
var AD_STOPPED = 'AdStopped';
/**
* This callback is displayed as global member. The callback use nodejs error-first callback style
* @callback NodeStyleCallback
* @param {string|null}
* @param {undefined|object}
*/
/**
* VPAIDHTML5Client
* @class
*
* @param {HTMLElement} el that will contain the iframe to load adUnit and a el to add to adUnit slot
* @param {HTMLVideoElement} video default video element to be used by adUnit
* @param {object} [templateConfig] template: html template to be used instead of the default, extraOptions: to be used when rendering the template
* @param {object} [vpaidOptions] timeout: when loading adUnit
*/
function VPAIDHTML5Client(el, video, templateConfig, vpaidOptions) {
templateConfig = templateConfig || {};
this._id = unique();
this._destroyed = false;
this._frameContainer = utils.createElementInEl(el, 'div');
this._videoEl = video;
this._vpaidOptions = vpaidOptions || {timeout: 10000};
this._templateConfig = {
template: templateConfig.template || defaultTemplate,
extraOptions: templateConfig.extraOptions || {}
};
}
/**
* destroy
*
*/
VPAIDHTML5Client.prototype.destroy = function destroy() {
if (this._destroyed) {
return;
}
this._destroyed = true;
$unloadPreviousAdUnit.call(this);
};
/**
* isDestroyed
*
* @return {boolean}
*/
VPAIDHTML5Client.prototype.isDestroyed = function isDestroyed() {
return this._destroyed;
};
/**
* loadAdUnit
*
* @param {string} adURL url of the js of the adUnit
* @param {nodeStyleCallback} callback
*/
VPAIDHTML5Client.prototype.loadAdUnit = function loadAdUnit(adURL, callback) {
if(this._onLoad){ return }
$throwIfDestroyed.call(this);
$unloadPreviousAdUnit.call(this);
var that = this;
var frame = utils.createIframeWithContent(
this._frameContainer,
this._templateConfig.template,
utils.extend({
iframeURL_JS: adURL,
iframeID: this.getID(),
origin: getOrigin()
}, this._templateConfig.extraOptions)
);
this._frame = frame;
this._onLoad = utils.callbackTimeout(
this._vpaidOptions.timeout,
onLoad.bind(this),
onTimeout.bind(this)
);
window.addEventListener('message', this._onLoad);
function onLoad (e) {
/*jshint validthis: false */
//don't clear timeout
if (e.origin !== getOrigin()) return;
var result = JSON.parse(e.data);
//don't clear timeout
if (result.id !== that.getID()) return;
var adUnit, error, createAd;
if (!that._frame.contentWindow) {
error = 'the iframe is not anymore in the DOM tree';
} else {
createAd = that._frame.contentWindow.getVPAIDAd;
error = utils.validate(typeof createAd === 'function', 'the ad didn\'t return a function to create an ad');
}
if (!error) {
var adEl = that._frame.contentWindow.document.querySelector('.ad-element');
adUnit = new VPAIDAdUnit(createAd(), adEl, that._videoEl, that._frame);
adUnit.subscribe(AD_STOPPED, $adDestroyed.bind(that));
error = utils.validate(adUnit.isValidVPAIDAd(), 'the add is not fully complaint with VPAID specification');
}
that._adUnit = adUnit;
$destroyLoadListener.call(that);
callback(error, error ? null : adUnit);
//clear timeout
return true;
}
function onTimeout() {
callback('timeout', null);
}
};
/**
* unloadAdUnit
*
*/
VPAIDHTML5Client.prototype.unloadAdUnit = function unloadAdUnit() {
$unloadPreviousAdUnit.call(this);
};
/**
* getID will return the unique id
*
* @return {string}
*/
VPAIDHTML5Client.prototype.getID = function () {
return this._id;
};
/**
* $removeEl
*
* @param {string} key
*/
function $removeEl(key) {
var el = this[key];
if (el && el.parentNode) {
el.parentNode.removeChild(el);
delete this[key];
}
}
function $adDestroyed() {
$removeAdElements.call(this);
delete this._adUnit;
}
function $unloadPreviousAdUnit() {
$removeAdElements.call(this);
$destroyAdUnit.call(this);
}
function $removeAdElements() {
$removeEl.call(this, '_frame');
$destroyLoadListener.call(this);
}
/**
* $destroyLoadListener
*
*/
function $destroyLoadListener() {
if (this._onLoad) {
window.removeEventListener('message', this._onLoad);
delete this._onLoad;
}
}
function $destroyAdUnit() {
if (this._adUnit) {
this._adUnit.stopAd();
delete this._adUnit;
}
}
/**
* $throwIfDestroyed
*
*/
function $throwIfDestroyed() {
if (this._destroyed) {
throw new Error ('VPAIDHTML5Client already destroyed!');
}
}
function getOrigin() {
if( window.location.origin ) {
return window.location.origin;
}
else {
return window.location.protocol + "//" +
window.location.hostname +
(window.location.port ? ':' + window.location.port: '');
}
}
module.exports = VPAIDHTML5Client;
window.VPAIDHTML5Client = VPAIDHTML5Client;