src/scripts/ads/vpaid/VPAIDAdUnitWrapper.js
'use strict';
var VASTError = require('../vast/VASTError');
var utilities = require('../../utils/utilityFunctions');
function VPAIDAdUnitWrapper(vpaidAdUnit, opts) {
if (!(this instanceof VPAIDAdUnitWrapper)) {
return new VPAIDAdUnitWrapper(vpaidAdUnit, opts);
}
sanityCheck(vpaidAdUnit, opts);
this.options = utilities.extend({}, opts);
this._adUnit = vpaidAdUnit;
/*** Local Functions ***/
function sanityCheck(adUnit, opts) {
if (!adUnit || !VPAIDAdUnitWrapper.checkVPAIDInterface(adUnit)) {
throw new VASTError('on VPAIDAdUnitWrapper, the passed VPAID adUnit does not fully implement the VPAID interface');
}
if (!utilities.isObject(opts)) {
throw new VASTError("on VPAIDAdUnitWrapper, expected options hash but got '" + opts + "'");
}
if (!("responseTimeout" in opts) || !utilities.isNumber(opts.responseTimeout) ){
throw new VASTError("on VPAIDAdUnitWrapper, expected responseTimeout in options");
}
}
}
VPAIDAdUnitWrapper.checkVPAIDInterface = function checkVPAIDInterface(VPAIDAdUnit) {
//NOTE: skipAd is not part of the method list because it only appears in VPAID 2.0 and we support VPAID 1.0
var VPAIDInterfaceMethods = [
'handshakeVersion', 'initAd', 'startAd', 'stopAd', 'resizeAd', 'pauseAd', 'expandAd', 'collapseAd'
];
for (var i = 0, len = VPAIDInterfaceMethods.length; i < len; i++) {
if (!VPAIDAdUnit || !utilities.isFunction(VPAIDAdUnit[VPAIDInterfaceMethods[i]])) {
return false;
}
}
return canSubscribeToEvents(VPAIDAdUnit) && canUnsubscribeFromEvents(VPAIDAdUnit);
/*** Local Functions ***/
function canSubscribeToEvents(adUnit) {
return utilities.isFunction(adUnit.subscribe) || utilities.isFunction(adUnit.addEventListener) || utilities.isFunction(adUnit.on);
}
function canUnsubscribeFromEvents(adUnit) {
return utilities.isFunction(adUnit.unsubscribe) || utilities.isFunction(adUnit.removeEventListener) || utilities.isFunction(adUnit.off);
}
};
VPAIDAdUnitWrapper.prototype.adUnitAsyncCall = function () {
var args = utilities.arrayLikeObjToArray(arguments);
var method = args.shift();
var cb = args.pop();
var timeoutId;
sanityCheck(method, cb, this._adUnit);
args.push(wrapCallback());
this._adUnit[method].apply(this._adUnit, args);
timeoutId = setTimeout(function () {
timeoutId = null;
cb(new VASTError("on VPAIDAdUnitWrapper, timeout while waiting for a response on call '" + method + "'"));
cb = utilities.noop;
}, this.options.responseTimeout);
/*** Local functions ***/
function sanityCheck(method, cb, adUnit) {
if (!utilities.isString(method) || !utilities.isFunction(adUnit[method])) {
throw new VASTError("on VPAIDAdUnitWrapper.adUnitAsyncCall, invalid method name");
}
if (!utilities.isFunction(cb)) {
throw new VASTError("on VPAIDAdUnitWrapper.adUnitAsyncCall, missing callback");
}
}
function wrapCallback() {
return function () {
if (timeoutId) {
clearTimeout(timeoutId);
}
cb.apply(this, arguments);
};
}
};
VPAIDAdUnitWrapper.prototype.on = function (evtName, handler) {
var addEventListener = this._adUnit.addEventListener || this._adUnit.subscribe || this._adUnit.on;
addEventListener.call(this._adUnit, evtName, handler);
};
VPAIDAdUnitWrapper.prototype.off = function (evtName, handler) {
var removeEventListener = this._adUnit.removeEventListener || this._adUnit.unsubscribe || this._adUnit.off;
removeEventListener.call(this._adUnit, evtName, handler);
};
VPAIDAdUnitWrapper.prototype.waitForEvent = function (evtName, cb, context) {
var timeoutId;
sanityCheck(evtName, cb);
context = context || null;
this.on(evtName, responseListener);
timeoutId = setTimeout(function () {
cb(new VASTError("on VPAIDAdUnitWrapper.waitForEvent, timeout while waiting for event '" + evtName + "'"));
timeoutId = null;
cb = utilities.noop;
}, this.options.responseTimeout);
/*** Local functions ***/
function sanityCheck(evtName, cb) {
if (!utilities.isString(evtName)) {
throw new VASTError("on VPAIDAdUnitWrapper.waitForEvent, missing evt name");
}
if (!utilities.isFunction(cb)) {
throw new VASTError("on VPAIDAdUnitWrapper.waitForEvent, missing callback");
}
}
function responseListener() {
var args = utilities.arrayLikeObjToArray(arguments);
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
args.unshift(null);
cb.apply(context, args);
}
};
// VPAID METHODS
VPAIDAdUnitWrapper.prototype.handshakeVersion = function (version, cb) {
this.adUnitAsyncCall('handshakeVersion', version, cb);
};
/* jshint maxparams:6 */
VPAIDAdUnitWrapper.prototype.initAd = function (width, height, viewMode, desiredBitrate, adUnitData, cb) {
this.waitForEvent('AdLoaded', cb);
this._adUnit.initAd(width, height, viewMode, desiredBitrate, adUnitData);
};
VPAIDAdUnitWrapper.prototype.resizeAd = function (width, height, viewMode, cb) {
// NOTE: AdSizeChange event is only supported on VPAID 2.0 so for the moment we are not going to use it
// and will assume that everything is fine after the async call
this.adUnitAsyncCall('resizeAd', width, height, viewMode, cb);
};
VPAIDAdUnitWrapper.prototype.startAd = function (cb) {
this.waitForEvent('AdStarted', cb);
this._adUnit.startAd();
};
VPAIDAdUnitWrapper.prototype.stopAd = function (cb) {
this.waitForEvent('AdStopped', cb);
this._adUnit.stopAd();
};
VPAIDAdUnitWrapper.prototype.pauseAd = function (cb) {
this.waitForEvent('AdPaused', cb);
this._adUnit.pauseAd();
};
VPAIDAdUnitWrapper.prototype.resumeAd = function (cb) {
this.waitForEvent('AdPlaying', cb);
this._adUnit.resumeAd();
};
VPAIDAdUnitWrapper.prototype.expandAd = function (cb) {
this.waitForEvent('AdExpandedChange', cb);
this._adUnit.expandAd();
};
VPAIDAdUnitWrapper.prototype.collapseAd = function (cb) {
this.waitForEvent('AdExpandedChange', cb);
this._adUnit.collapseAd();
};
VPAIDAdUnitWrapper.prototype.skipAd = function (cb) {
this.waitForEvent('AdSkipped', cb);
this._adUnit.skipAd();
};
//VPAID property getters
[
'adLinear',
'adWidth',
'adHeight',
'adExpanded',
'adSkippableState',
'adRemainingTime',
'adDuration',
'adVolume',
'adCompanions',
'adIcons'
].forEach(function (property) {
var getterName = 'get' + utilities.capitalize(property);
VPAIDAdUnitWrapper.prototype[getterName] = function (cb) {
this.adUnitAsyncCall(getterName, cb);
};
});
//VPAID property setters
VPAIDAdUnitWrapper.prototype.setAdVolume = function(volume, cb){
this.adUnitAsyncCall('setAdVolume',volume, cb);
};
module.exports = VPAIDAdUnitWrapper;