js/jsFlashBridge.js
'use strict';
let unique = require('./utils').unique;
let isPositiveInt = require('./utils').isPositiveInt;
let stringEndsWith = require('./utils').stringEndsWith;
let SingleValueRegistry = require('./registry').SingleValueRegistry;
let MultipleValuesRegistry = require('./registry').MultipleValuesRegistry;
const registry = require('./jsFlashBridgeRegistry');
const VPAID_FLASH_HANDLER = 'vpaid_video_flash_handler';
const ERROR = 'AdError';
export class JSFlashBridge {
constructor (el, flashURL, flashID, width, height, loadHandShake) {
this._el = el;
this._flashID = flashID;
this._flashURL = flashURL;
this._width = width;
this._height = height;
this._handlers = new MultipleValuesRegistry();
this._callbacks = new SingleValueRegistry();
this._uniqueMethodIdentifier = unique(this._flashID);
this._ready = false;
this._handShakeHandler = loadHandShake;
registry.addInstance(this._flashID, this);
}
on(eventName, callback) {
this._handlers.add(eventName, callback);
}
off(eventName, callback) {
return this._handlers.remove(eventName, callback);
}
offEvent(eventName) {
return this._handlers.removeByKey(eventName);
}
offAll() {
return this._handlers.removeAll();
}
callFlashMethod(methodName, args = [], callback = undefined) {
var callbackID = '';
// if no callback, some methods the return is void so they don't need callback
if (callback) {
callbackID = `${this._uniqueMethodIdentifier()}_${methodName}`;
this._callbacks.add(callbackID, callback);
}
try {
//methods are created by ExternalInterface.addCallback in as3 code, if for some reason it failed
//this code will throw an error
this._el[methodName]([callbackID].concat(args));
} catch (e) {
if (callback) {
$asyncCallback.call(this, callbackID, e);
} else {
//if there isn't any callback to return error use error event handler
this._trigger(ERROR, e);
}
}
}
removeCallback(callback) {
return this._callbacks.removeByValue(callback);
}
removeCallbackByMethodName(suffix) {
this._callbacks.filterKeys((key) => {
return stringEndsWith(key, suffix);
}).forEach((key) => {
this._callbacks.remove(key);
});
}
removeAllCallbacks() {
return this._callbacks.removeAll();
}
_trigger(eventName, event) {
this._handlers.get(eventName).forEach((callback) => {
//clickThru has to be sync, if not will be block by the popupblocker
if (eventName === 'AdClickThru') {
callback(event);
} else {
setTimeout(() => {
if (this._handlers.get(eventName).length > 0) {
callback(event);
}
}, 0);
}
});
}
_callCallback(methodName, callbackID, err, result) {
let callback = this._callbacks.get(callbackID);
//not all methods callback's are mandatory
//but if there exist an error, fire the error event
if (!callback) {
if (err && callbackID === '') {
this.trigger(ERROR, err);
}
return;
}
$asyncCallback.call(this, callbackID, err, result);
}
_handShake(err, data) {
this._ready = true;
if (this._handShakeHandler) {
this._handShakeHandler(err, data);
delete this._handShakeHandler;
}
}
//methods like properties specific to this implementation of VPAID
getSize() {
return {width: this._width, height: this._height};
}
setSize(newWidth, newHeight) {
this._width = isPositiveInt(newWidth, this._width);
this._height = isPositiveInt(newHeight, this._height);
this._el.setAttribute('width', this._width);
this._el.setAttribute('height', this._height);
}
getWidth() {
return this._width;
}
setWidth(newWidth) {
this.setSize(newWidth, this._height);
}
getHeight() {
return this._height;
}
setHeight(newHeight) {
this.setSize(this._width, newHeight);
}
getFlashID() {
return this._flashID;
}
getFlashURL() {
return this._flashURL;
}
isReady() {
return this._ready;
}
destroy() {
this.offAll();
this.removeAllCallbacks();
registry.removeInstanceByID(this._flashID);
if (this._el.parentElement) {
this._el.parentElement.removeChild(this._el);
}
}
}
function $asyncCallback(callbackID, err, result) {
setTimeout(() => {
let callback = this._callbacks.get(callbackID);
if (callback) {
this._callbacks.remove(callbackID);
callback(err, result);
}
}, 0);
}
Object.defineProperty(JSFlashBridge, 'VPAID_FLASH_HANDLER', {
writable: false,
configurable: false,
value: VPAID_FLASH_HANDLER
});
/**
* External interface handler
*
* @param {string} flashID identifier of the flash who call this
* @param {string} typeID what type of message is, can be 'event' or 'callback'
* @param {string} typeName if the typeID is a event the typeName will be the eventName, if is a callback the typeID is the methodName that is related this callback
* @param {string} callbackID only applies when the typeID is 'callback', identifier of the callback to call
* @param {object} error error object
* @param {object} data
*/
window[VPAID_FLASH_HANDLER] = (flashID, typeID, typeName, callbackID, error, data) => {
let instance = registry.getInstanceByID(flashID);
if (!instance) return;
if (typeName === 'handShake') {
instance._handShake(error, data);
} else {
if (typeID !== 'event') {
instance._callCallback(typeName, callbackID, error, data);
} else {
instance._trigger(typeName, data);
}
}
};