UNPKG

vast-player

Version:

Playback VAST creatives in a web browser.

1,772 lines (1,441 loc) 157 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.VASTPlayer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ module.exports = require('./lib/VASTPlayer'); },{"./lib/VASTPlayer":5}],2:[function(require,module,exports){ 'use strict'; function proxy(event, source, target) { source.on(event, function emit(/*...args*/) { var args = [], length = arguments.length; while (length--) { args[length] = arguments[length]; } target.emit.apply(target, [event].concat(args)); }); } function init(source, target, events) { events.forEach(function(event) { if (target.listeners(event).length > 0) { proxy(event, source, target); } }); target.on('newListener', function handleNewListener(type) { if (events.indexOf(type) > -1 && target.listeners(type).length < 1) { proxy(type, source, target); } }); } function EventProxy(events) { this.events = events.slice(); this.__private__ = { target: null, source: null }; } EventProxy.prototype.from = function from(source) { this.__private__.source = source; if (this.__private__.target) { init(source, this.__private__.target, this.events); } return this; }; EventProxy.prototype.to = function to(target) { this.__private__.target = target; if (this.__private__.source) { init(this.__private__.source, target, this.events); } return this; }; module.exports = EventProxy; },{}],3:[function(require,module,exports){ 'use strict'; var VideoTracker = require('./VideoTracker'); var inherits = require('util').inherits; var EVENTS = require('./enums/HTML_MEDIA_EVENTS'); function HTMLVideoTracker(video) { var self = this; VideoTracker.call(this, Math.floor(video.duration || 0)); // call super() this.video = video; [ EVENTS.PLAYING, EVENTS.PAUSE, EVENTS.TIMEUPDATE ].forEach(function(event) { return video.addEventListener(event, function onevent() { return self.tick(); }, false); }); } inherits(HTMLVideoTracker, VideoTracker); HTMLVideoTracker.prototype._getState = function _getState() { return { playing: !this.video.paused, currentTime: this.video.currentTime }; }; module.exports = HTMLVideoTracker; },{"./VideoTracker":7,"./enums/HTML_MEDIA_EVENTS":8,"util":35}],4:[function(require,module,exports){ 'use strict'; var EVENTS = require('./enums/VPAID_EVENTS'); function identity(value) { return value; } function fire(pixels, mapper) { (pixels || []).forEach(function(src) { new Image().src = mapper(src); }); } function PixelReporter(pixels, mapper) { this.pixels = pixels.reduce(function(pixels, item) { (pixels[item.event] || (pixels[item.event] = [])).push(item.uri); return pixels; }, {}); this.__private__ = { mapper: mapper || identity }; } PixelReporter.prototype.track = function track(vpaid) { var pixels = this.pixels; var customMapper = this.__private__.mapper; var lastVolume = vpaid.adVolume; function fireType(type, mapper, predicate) { function pixelMapper(url) { return customMapper((mapper || identity)(url)); } return function firePixels() { if (!predicate || predicate()) { fire(pixels[type], pixelMapper); } }; } vpaid.on(EVENTS.AdSkipped, fireType('skip')); vpaid.on(EVENTS.AdStarted, fireType('creativeView')); vpaid.on(EVENTS.AdVolumeChange, fireType('unmute', null, function() { return lastVolume === 0 && vpaid.adVolume > 0; })); vpaid.on(EVENTS.AdVolumeChange, fireType('mute', null, function() { return lastVolume > 0 && vpaid.adVolume === 0; })); vpaid.on(EVENTS.AdImpression, fireType('impression')); vpaid.on(EVENTS.AdVideoStart, fireType('start')); vpaid.on(EVENTS.AdVideoFirstQuartile, fireType('firstQuartile')); vpaid.on(EVENTS.AdVideoMidpoint, fireType('midpoint')); vpaid.on(EVENTS.AdVideoThirdQuartile, fireType('thirdQuartile')); vpaid.on(EVENTS.AdVideoComplete, fireType('complete')); vpaid.on(EVENTS.AdClickThru, fireType('clickThrough')); vpaid.on(EVENTS.AdUserAcceptInvitation, fireType('acceptInvitationLinear')); vpaid.on(EVENTS.AdUserMinimize, fireType('collapse')); vpaid.on(EVENTS.AdUserClose, fireType('closeLinear')); vpaid.on(EVENTS.AdPaused, fireType('pause')); vpaid.on(EVENTS.AdPlaying, fireType('resume')); vpaid.on(EVENTS.AdError, fireType('error', function(pixel) { return pixel.replace(/\[ERRORCODE\]/g, 901); })); vpaid.on(EVENTS.AdVolumeChange, function updateLastVolume() { lastVolume = vpaid.adVolume; }); }; module.exports = PixelReporter; },{"./enums/VPAID_EVENTS":10}],5:[function(require,module,exports){ 'use strict'; var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; var VAST = require('vastacular').VAST; var JavaScriptVPAIDPlayer = require('./players/JavaScriptVPAID'); var FlashVPAIDPlayer = require('./players/FlashVPAID'); var HTMLVideoPlayer = require('./players/HTMLVideo'); var MIME = require('./enums/MIME'); var EVENTS = require('./enums/VPAID_EVENTS'); var EventProxy = require('./EventProxy'); var LiePromise = require('lie'); var PixelReporter = require('./PixelReporter'); function defaults(/*...objects*/) { var result = {}; var length = arguments.length; var index, object; var prop, value; for (index = 0; index < length; index++) { object = arguments[index] || {}; for (prop in object) { value = object[prop]; if (result[prop] === undefined) { result[prop] = value; } if (typeof value === 'object') { result[prop] = defaults(result[prop], value); } } } return result; } function identity(value) { return value; } function getNotReadyError() { return new Error('VASTPlayer not ready.'); } function proxy(method) { return function callMethod() { var self = this; var player = this.__private__.player; if (!this.ready) { return LiePromise.reject(getNotReadyError()); } return player[method].apply(player, arguments).then(function() { return self; }); }; } function proxyProp(property) { return { get: function get() { if (!this.ready) { throw getNotReadyError(); } return this.__private__.player[property]; }, set: function set(value) { if (!this.ready) { throw getNotReadyError(); } return (this.__private__.player[property] = value); } }; } function VASTPlayer(container, config) { var self = this; EventEmitter.call(this); // call super() this.__private__ = { container: container, config: defaults(config, { vast: { resolveWrappers: true, maxRedirects: 5 }, tracking: { mapper: identity } }), vast: null, ready: false, player: null }; this.on(EVENTS.AdClickThru, function onAdClickThru(url, id, playerHandles) { var clickThrough = url || self.vast.get('ads[0].creatives[0].videoClicks.clickThrough'); if (playerHandles && clickThrough) { window.open(clickThrough); } }); } inherits(VASTPlayer, EventEmitter); Object.defineProperties(VASTPlayer.prototype, { container: { get: function getContainer() { return this.__private__.container; } }, config: { get: function getConfig() { return this.__private__.config; } }, vast: { get: function getVast() { return this.__private__.vast; } }, ready: { get: function getReady() { return this.__private__.ready; } }, adRemainingTime: proxyProp('adRemainingTime'), adDuration: proxyProp('adDuration'), adVolume: proxyProp('adVolume') }); VASTPlayer.prototype.load = function load(uri) { var self = this; var config = this.config.vast; return VAST.fetch(uri, config).then(function loadPlayer(vast) { var config = (function() { var jsVPAIDFiles = vast.filter('ads[0].creatives[0].mediaFiles', function(mediaFile) { return ( mediaFile.type === MIME.JAVASCRIPT || mediaFile.type === 'application/x-javascript' ) && mediaFile.apiFramework === 'VPAID'; }); var swfVPAIDFiles = vast.filter('ads[0].creatives[0].mediaFiles', function(mediaFile) { return mediaFile.type === MIME.FLASH && mediaFile.apiFramework === 'VPAID'; }); var files = vast.filter('ads[0].creatives[0].mediaFiles', function() { return true; }); if (jsVPAIDFiles.length > 0) { return { player: new JavaScriptVPAIDPlayer(self.container), mediaFiles: jsVPAIDFiles }; } else if (swfVPAIDFiles.length > 0) { return { player: new FlashVPAIDPlayer(self.container, VASTPlayer.vpaidSWFLocation), mediaFiles: swfVPAIDFiles }; } return { player: new HTMLVideoPlayer(self.container), mediaFiles: files }; }()); var parameters = vast.get('ads[0].creatives[0].parameters'); var pixels = [].concat( vast.map('ads[0].impressions', function(impression) { return { event: 'impression', uri: impression.uri }; }), vast.map('ads[0].errors', function(uri) { return { event: 'error', uri: uri }; }), vast.get('ads[0].creatives[0].trackingEvents'), vast.map('ads[0].creatives[0].videoClicks.clickTrackings', function(uri) { return { event: 'clickThrough', uri: uri }; }) ); var player = config.player; var mediaFiles = config.mediaFiles; var proxy = new EventProxy(EVENTS); var reporter = new PixelReporter(pixels, self.config.tracking.mapper); proxy.from(player).to(self); self.__private__.vast = vast; self.__private__.player = player; return player.load(mediaFiles, parameters).then(function setupPixels() { reporter.track(player); }); }).then(function setReady() { self.__private__.ready = true; self.emit('ready'); return self; }).catch(function emitError(reason) { self.emit('error', reason); throw reason; }); }; VASTPlayer.prototype.startAd = proxy('startAd'); VASTPlayer.prototype.stopAd = proxy('stopAd'); VASTPlayer.prototype.pauseAd = proxy('pauseAd'); VASTPlayer.prototype.resumeAd = proxy('resumeAd'); VASTPlayer.vpaidSWFLocation = 'https://cdn.jsdelivr.net' + '/npm/vast-player@0.2.10/dist/vast-player--vpaid.swf'; module.exports = VASTPlayer; },{"./EventProxy":2,"./PixelReporter":4,"./enums/MIME":9,"./enums/VPAID_EVENTS":10,"./players/FlashVPAID":12,"./players/HTMLVideo":13,"./players/JavaScriptVPAID":14,"events":19,"lie":21,"util":35,"vastacular":36}],6:[function(require,module,exports){ 'use strict'; function VPAIDVersion(versionString) { var parts = versionString.split('.').map(parseFloat); this.string = versionString; this.major = parts[0]; this.minor = parts[1]; this.patch = parts[2]; Object.freeze(this); } VPAIDVersion.prototype.toString = function toString() { return this.string; }; module.exports = VPAIDVersion; },{}],7:[function(require,module,exports){ 'use strict'; var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; var EVENTS = require('./enums/VPAID_EVENTS'); function fire(event, tracker) { if (tracker.fired[event]) { return; } tracker.emit(event); tracker.fired[event] = true; } function VideoTracker(duration) { EventEmitter.apply(this, arguments); // call super() this.duration = duration; this.seconds = Array.apply([], new Array(duration)).map(function() { return false; }); this.fired = [ EVENTS.AdVideoStart, EVENTS.AdVideoFirstQuartile, EVENTS.AdVideoMidpoint, EVENTS.AdVideoThirdQuartile, EVENTS.AdVideoComplete ].reduce(function(fired, event) { fired[event] = false; return fired; }, {}); } inherits(VideoTracker, EventEmitter); VideoTracker.prototype.tick = function tick() { var seconds = this.seconds; var state = this._getState(); var index = Math.round(state.currentTime) - 1; var quartileIndices = [1, 2, 3, 4].map(function(quartile) { return Math.floor(this.duration / 4 * quartile); }, this); function quartileViewed(quartile) { var end = quartileIndices[quartile - 1]; return seconds.slice(0, end).every(function(second) { return second === true; }); } if (state.playing) { fire(EVENTS.AdVideoStart, this); if (index > -1) { this.seconds[index] = true; } } if (quartileViewed(1)) { fire(EVENTS.AdVideoFirstQuartile, this); } if (quartileViewed(2)) { fire(EVENTS.AdVideoMidpoint, this); } if (quartileViewed(3)) { fire(EVENTS.AdVideoThirdQuartile, this); } if (quartileViewed(4)) { fire(EVENTS.AdVideoComplete, this); } }; module.exports = VideoTracker; },{"./enums/VPAID_EVENTS":10,"events":19,"util":35}],8:[function(require,module,exports){ 'use strict'; var HTML_MEDIA_EVENTS = [ 'abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'encrypted', 'ended', 'error', 'interruptbegin', 'interruptend', 'loadeddata', 'loadedmetadata', 'loadstart', 'mozaudioavailable', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting' ]; HTML_MEDIA_EVENTS.forEach(function(event) { this[event.toUpperCase()] = event; }, HTML_MEDIA_EVENTS); Object.freeze(HTML_MEDIA_EVENTS); module.exports = HTML_MEDIA_EVENTS; },{}],9:[function(require,module,exports){ var MIME = { JAVASCRIPT: 'application/javascript', FLASH: 'application/x-shockwave-flash' }; Object.freeze(MIME); module.exports = MIME; },{}],10:[function(require,module,exports){ 'use strict'; var VPAID_EVENTS = [ 'AdLoaded', 'AdStarted', 'AdStopped', 'AdSkipped', 'AdSkippableStateChange', 'AdSizeChange', 'AdLinearChange', 'AdDurationChange', 'AdExpandedChange', 'AdRemainingTimeChange', 'AdVolumeChange', 'AdImpression', 'AdVideoStart', 'AdVideoFirstQuartile', 'AdVideoMidpoint', 'AdVideoThirdQuartile', 'AdVideoComplete', 'AdClickThru', 'AdInteraction', 'AdUserAcceptInvitation', 'AdUserMinimize', 'AdUserClose', 'AdPaused', 'AdPlaying', 'AdLog', 'AdError' ]; VPAID_EVENTS.forEach(function(event) { VPAID_EVENTS[event] = event; }); Object.freeze(VPAID_EVENTS); module.exports = VPAID_EVENTS; },{}],11:[function(require,module,exports){ 'use strict'; var win = require('./window'); var video = document.createElement('video'); var MIME = require('./enums/MIME'); exports.isDesktop = !/Android|Silk|Mobile|PlayBook/.test(win.navigator.userAgent); exports.canPlay = function canPlay(type) { var mimeTypes = win.navigator.mimeTypes; var ActiveXObject = win.ActiveXObject; switch (type) { case MIME.FLASH: try { return new ActiveXObject('ShockwaveFlash.ShockwaveFlash') ? 2 : 0; } catch (e) { return !!(mimeTypes && mimeTypes[MIME.FLASH]) ? 2 : 0; } return 0; case MIME.JAVASCRIPT: case 'application/x-javascript': return 2; default: if (video.canPlayType) { switch (video.canPlayType(type)) { case 'probably': return 2; case 'maybe': return 1; default: return 0; } } } return 0; }; Object.freeze(exports); },{"./enums/MIME":9,"./window":17}],12:[function(require,module,exports){ 'use strict'; var VPAID = require('./VPAID'); var inherits = require('util').inherits; var LiePromise = require('lie'); var uuid = require('../uuid'); var querystring = require('querystring'); var EVENTS = require('../enums/VPAID_EVENTS'); var VPAIDVersion = require('../VPAIDVersion'); function FlashVPAID(container, swfURI) { VPAID.apply(this, arguments); // call super() this.swfURI = swfURI; this.object = null; } inherits(FlashVPAID, VPAID); FlashVPAID.prototype.load = function load(mediaFiles, parameters) { var self = this; var uri = mediaFiles[0].uri; var bitrate = mediaFiles[0].bitrate; return new LiePromise(function loadCreative(resolve, reject) { var vpaid = document.createElement('object'); var eventFnName = 'vast_player__' + uuid(20); var flashvars = querystring.stringify({ vpaidURI: uri, eventCallback: eventFnName }); function cleanup(reason) { self.container.removeChild(vpaid); self.api = null; self.object = null; delete window[eventFnName]; if (reason) { reject(reason); } } vpaid.type = 'application/x-shockwave-flash'; vpaid.data = self.swfURI + '?' + flashvars; vpaid.style.display = 'block'; vpaid.style.width = '100%'; vpaid.style.height = '100%'; vpaid.style.border = 'none'; vpaid.style.opacity = '0'; vpaid.innerHTML = [ '<param name="movie" value="' + self.swfURI + '">', '<param name="flashvars" value="' + flashvars + '">', '<param name="quality" value="high">', '<param name="play" value="false">', '<param name="loop" value="false">', '<param name="wmode" value="opaque">', '<param name="scale" value="noscale">', '<param name="salign" value="lt">', '<param name="allowScriptAccess" value="always">' ].join('\n'); self.object = vpaid; window[eventFnName] = function handleVPAIDEvent(event) { switch (event.type) { case EVENTS.AdClickThru: return self.emit(event.type, event.url, event.Id, event.playerHandles); case EVENTS.AdInteraction: case EVENTS.AdLog: return self.emit(event.type, event.Id); case EVENTS.AdError: return self.emit(event.type, event.message); default: return self.emit(event.type); } }; self.once('VPAIDInterfaceReady', function initAd() { var position = vpaid.getBoundingClientRect(); var version = self.vpaidVersion = new VPAIDVersion(vpaid.handshakeVersion('2.0')); if (version.major > 2) { return reject(new Error('VPAID version ' + version + ' is not supported.')); } self.on('VPAIDInterfaceResize', function resizeAd() { var position = vpaid.getBoundingClientRect(); self.resizeAd(position.width, position.height, 'normal'); }); vpaid.initAd(position.width, position.height, 'normal', bitrate, parameters, null); }); self.once(EVENTS.AdLoaded, function handleAdLoaded() { self.api = vpaid; vpaid.style.opacity = '1'; resolve(self); }); self.once(EVENTS.AdError, function handleAdError(reason) { cleanup(new Error(reason)); }); self.once(EVENTS.AdStopped, cleanup); self.container.appendChild(vpaid); }); }; module.exports = FlashVPAID; },{"../VPAIDVersion":6,"../enums/VPAID_EVENTS":10,"../uuid":16,"./VPAID":15,"lie":21,"querystring":26,"util":35}],13:[function(require,module,exports){ 'use strict'; var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; var LiePromise = require('lie'); var canPlay = require('../environment').canPlay; var sortBy = require('sort-by'); var VPAID_EVENTS = require('../enums/VPAID_EVENTS'); var HTML_MEDIA_EVENTS = require('../enums/HTML_MEDIA_EVENTS'); var HTMLVideoTracker = require('../HTMLVideoTracker'); var EventProxy = require('../EventProxy'); function on(video, event, handler) { return video.addEventListener(event, handler, false); } function off(video, event, handler) { return video.removeEventListener(event, handler, false); } function once(video, event, handler) { return on(video, event, function onevent() { off(video, event, onevent); return handler.apply(this, arguments); }); } function method(implementation, promiseify) { function getError() { return new Error('The <video> has not been loaded.'); } return function callImplementation(/*...args*/) { if (!this.video) { if (promiseify) { return LiePromise.reject(getError()); } else { throw getError(); } } return implementation.apply(this, arguments); }; } function pickMediaFile(mediaFiles, dimensions) { var width = dimensions.width; var items = mediaFiles.map(function(mediaFile) { return { mediaFile: mediaFile, playability: canPlay(mediaFile.type) }; }).filter(function(config) { return config.playability > 0; }).sort(sortBy('-playability', '-mediaFile.bitrate')); var distances = items.map(function(item) { return Math.abs(width - item.mediaFile.width); }); var item = items[distances.indexOf(Math.min.apply(Math, distances))]; return (!item || item.playability < 1) ? null : item.mediaFile; } function HTMLVideo(container) { this.container = container; this.video = null; this.__private__ = { hasPlayed: false }; } inherits(HTMLVideo, EventEmitter); Object.defineProperties(HTMLVideo.prototype, { adRemainingTime: { get: method(function getAdRemainingTime() { return this.video.duration - this.video.currentTime; }) }, adDuration: { get: method(function getAdDuration() { return this.video.duration; }) }, adVolume: { get: method(function getAdVolume() { return this.video.volume; }), set: method(function setAdVolume(volume) { this.video.volume = volume; }) } }); HTMLVideo.prototype.load = function load(mediaFiles) { var self = this; return new LiePromise(function loadCreative(resolve, reject) { var video = document.createElement('video'); var mediaFile = pickMediaFile(mediaFiles, self.container.getBoundingClientRect()); if (!mediaFile) { return reject(new Error('There are no playable <MediaFile>s.')); } video.setAttribute('webkit-playsinline', true); video.src = mediaFile.uri; video.preload = 'auto'; video.style.display = 'block'; video.style.width = '100%'; video.style.height = '100%'; video.style.objectFit = 'contain'; once(video, HTML_MEDIA_EVENTS.LOADEDMETADATA, function onloadedmetadata() { var tracker = new HTMLVideoTracker(video); var proxy = new EventProxy(VPAID_EVENTS); proxy.from(tracker).to(self); self.video = video; resolve(self); self.emit(VPAID_EVENTS.AdLoaded); on(video, HTML_MEDIA_EVENTS.DURATIONCHANGE, function ondurationchange() { self.emit(VPAID_EVENTS.AdDurationChange); }); on(video, HTML_MEDIA_EVENTS.VOLUMECHANGE, function onvolumechange() { self.emit(VPAID_EVENTS.AdVolumeChange); }); }); once(video, HTML_MEDIA_EVENTS.ERROR, function onerror() { var error = video.error; self.emit(VPAID_EVENTS.AdError, error.message); reject(error); }); once(video, HTML_MEDIA_EVENTS.PLAYING, function onplaying() { self.__private__.hasPlayed = true; self.emit(VPAID_EVENTS.AdImpression); }); once(video, HTML_MEDIA_EVENTS.ENDED, function onended() { self.stopAd(); }); on(video, 'click', function onclick() { self.emit(VPAID_EVENTS.AdClickThru, null, null, true); }); self.container.appendChild(video); }); }; HTMLVideo.prototype.startAd = method(function startAd() { var self = this; var video = this.video; if (this.__private__.hasPlayed) { return LiePromise.reject(new Error('The ad has already been started.')); } return new LiePromise(function callPlay(resolve) { once(video, HTML_MEDIA_EVENTS.PLAYING, function onplaying() { resolve(self); self.emit(VPAID_EVENTS.AdStarted); }); return video.play(); }); }, true); HTMLVideo.prototype.stopAd = method(function stopAd() { this.container.removeChild(this.video); this.emit(VPAID_EVENTS.AdStopped); return LiePromise.resolve(this); }, true); HTMLVideo.prototype.pauseAd = method(function pauseAd() { var self = this; var video = this.video; if (this.video.paused) { return LiePromise.resolve(this); } return new LiePromise(function callPause(resolve) { once(video, HTML_MEDIA_EVENTS.PAUSE, function onpause() { resolve(self); self.emit(VPAID_EVENTS.AdPaused); }); return video.pause(); }); }, true); HTMLVideo.prototype.resumeAd = method(function resumeAd() { var self = this; var video = this.video; if (!this.__private__.hasPlayed) { return LiePromise.reject(new Error('The ad has not been started yet.')); } if (!this.video.paused) { return LiePromise.resolve(this); } return new LiePromise(function callPlay(resolve) { once(video, HTML_MEDIA_EVENTS.PLAY, function onplay() { resolve(self); self.emit(VPAID_EVENTS.AdPlaying); }); return video.play(); }); }, true); module.exports = HTMLVideo; },{"../EventProxy":2,"../HTMLVideoTracker":3,"../enums/HTML_MEDIA_EVENTS":8,"../enums/VPAID_EVENTS":10,"../environment":11,"events":19,"lie":21,"sort-by":28,"util":35}],14:[function(require,module,exports){ 'use strict'; var inherits = require('util').inherits; var VPAID = require('./VPAID'); var LiePromise = require('lie'); var EVENTS = require('../enums/VPAID_EVENTS'); var isDesktop = require('../environment').isDesktop; var VPAIDVersion = require('../VPAIDVersion'); function JavaScriptVPAID() { VPAID.apply(this, arguments); // call super() this.frame = null; } inherits(JavaScriptVPAID, VPAID); JavaScriptVPAID.prototype.load = function load(mediaFiles, parameters) { var self = this; var uri = mediaFiles[0].uri; var bitrate = mediaFiles[0].bitrate; return new LiePromise(function loadCreative(resolve, reject) { var iframe = document.createElement('iframe'); var script = document.createElement('script'); var video = document.createElement('video'); function cleanup(reason) { self.container.removeChild(iframe); self.frame = null; self.api = null; if (reason) { reject(reason); } } iframe.src = 'about:blank'; iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.display = 'block'; iframe.style.opacity = '0'; iframe.style.border = 'none'; video.setAttribute('webkit-playsinline', 'true'); video.style.display = 'block'; video.style.width = '100%'; video.style.height = '100%'; video.style.objectFit = 'contain'; self.container.appendChild(iframe); // Opening the iframe document for writing causes it to inherit its parent's location iframe.contentWindow.document.open(); iframe.contentWindow.document.close(); iframe.contentWindow.document.body.style.margin = '0'; self.frame = iframe; script.src = uri; script.onload = function onload() { var vpaid = iframe.contentWindow.getVPAIDAd(); var position = iframe.getBoundingClientRect(); var slot = iframe.contentWindow.document.body; var version = self.vpaidVersion = new VPAIDVersion(vpaid.handshakeVersion('2.0')); function resizeAd() { var position = iframe.getBoundingClientRect(); self.resizeAd(position.width, position.height, 'normal'); } if (version.major > 2) { return reject(new Error('VPAID version ' + version + ' is not supported.')); } iframe.contentWindow.addEventListener('resize', resizeAd, false); EVENTS.forEach(function subscribe(event) { return vpaid.subscribe(function handle(/*...args*/) { var args = new Array(arguments.length); var length = arguments.length; while (length--) { args[length] = arguments[length]; } return self.emit.apply(self, [event].concat(args)); }, event); }); self.once(EVENTS.AdLoaded, function onAdLoaded() { iframe.style.opacity = '1'; self.api = vpaid; resolve(self); }); self.once(EVENTS.AdError, function onAdError(reason) { cleanup(new Error(reason)); }); self.once(EVENTS.AdStopped, cleanup); vpaid.initAd( position.width, position.height, 'normal', bitrate, { AdParameters: parameters }, { slot: slot, videoSlot: video, videoSlotCanAutoPlay: isDesktop } ); }; script.onerror = function onerror() { cleanup(new Error('Failed to load MediaFile [' + uri + '].')); }; iframe.contentWindow.document.body.appendChild(video); iframe.contentWindow.document.head.appendChild(script); }); }; module.exports = JavaScriptVPAID; },{"../VPAIDVersion":6,"../enums/VPAID_EVENTS":10,"../environment":11,"./VPAID":15,"lie":21,"util":35}],15:[function(require,module,exports){ 'use strict'; var inherits = require('util').inherits; var EventEmitter = require('events').EventEmitter; var LiePromise = require('lie'); var EVENTS = require('../enums/VPAID_EVENTS'); function proxy(method, event) { return function callMethod(/*..args*/) { var args = arguments; var api = this.api; var self = this; function getError() { return new Error('Ad has not been loaded.'); } function call() { return api[method].apply(api, args); } if (!event) { if (!api) { throw getError(); } return call(); } return new LiePromise(function(resolve, reject) { if (!api) { return reject(getError()); } self.once(event, function done() { resolve(self); }); return call(); }); }; } function VPAID(container) { this.container = container; this.api = null; this.vpaidVersion = null; } inherits(VPAID, EventEmitter); Object.defineProperties(VPAID.prototype, { adLinear: { get: proxy('getAdLinear') }, adWidth: { get: proxy('getAdWidth') }, adHeight: { get: proxy('getAdHeight') }, adExpanded: { get: proxy('getAdExpanded') }, adSkippableState: { get: proxy('getAdSkippableState') }, adRemainingTime: { get: proxy('getAdRemainingTime') }, adDuration: { get: proxy('getAdDuration') }, adVolume: { get: proxy('getAdVolume'), set: proxy('setAdVolume') }, adCompanions: { get: proxy('getAdCompanions') }, adIcons: { get: proxy('getAdIcons') } }); VPAID.prototype.load = function load() { throw new Error('VPAID subclass must implement load() method.'); }; VPAID.prototype.resizeAd = proxy('resizeAd', EVENTS.AdSizeChange); VPAID.prototype.startAd = proxy('startAd', EVENTS.AdStarted); VPAID.prototype.stopAd = proxy('stopAd', EVENTS.AdStopped); VPAID.prototype.pauseAd = proxy('pauseAd', EVENTS.AdPaused); VPAID.prototype.resumeAd = proxy('resumeAd', EVENTS.AdPlaying); VPAID.prototype.expandAd = proxy('expandAd', EVENTS.AdExpandedChange); VPAID.prototype.collapseAd = proxy('collapseAd', EVENTS.AdExpandedChange); VPAID.prototype.skipAd = proxy('skipAd', EVENTS.AdSkipped); module.exports = VPAID; },{"../enums/VPAID_EVENTS":10,"events":19,"lie":21,"util":35}],16:[function(require,module,exports){ 'use strict'; var POSSIBILITIES = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var POSSIBILITIES_LENGTH = POSSIBILITIES.length; module.exports = function uuid(length) { var result = ''; while (length--) { result += POSSIBILITIES.charAt(Math.floor(Math.random() * POSSIBILITIES_LENGTH)); } return result; }; },{}],17:[function(require,module,exports){ module.exports = window; },{}],18:[function(require,module,exports){ /** * Expose `Emitter`. */ if (typeof module !== 'undefined') { module.exports = Emitter; } /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = [].slice.call(arguments, 1) , callbacks = this._callbacks['$' + event]; if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; },{}],19:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } throw TypeError('Uncaught, unspecified "error" event.'); } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; handler.apply(this, args); } } else if (isObject(handler)) { len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { var m; if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.listenerCount = function(emitter, type) { var ret; if (!emitter._events || !emitter._events[type]) ret = 0; else if (isFunction(emitter._events[type])) ret = 1; else ret = emitter._events[type].length; return ret; }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}],20:[function(require,module,exports){ (function (global){ 'use strict'; var Mutation = global.MutationObserver || global.WebKitMutationObserver; var scheduleDrain; { if (Mutation) { var called = 0; var observer = new Mutation(nextTick); var element = global.document.createTextNode(''); observer.observe(element, { characterData: true }); scheduleDrain = function () { element.data = (called = ++called % 2); }; } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { var channel = new global.MessageChannel(); channel.port1.onmessage = nextTick; scheduleDrain = function () { channel.port2.postMessage(0); }; } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { scheduleDrain = function () { // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. var scriptEl = global.document.createElement('script'); scriptEl.onreadystatechange = function () { nextTick(); scriptEl.onreadystatechange = null; scriptEl.parentNode.removeChild(scriptEl); scriptEl = null; }; global.document.documentElement.appendChild(scriptEl); }; } else { scheduleDrain = function () { setTimeout(nextTick, 0); }; } } var draining; var queue = []; //named nextTick for less confusing stack traces function nextTick() { draining = true; var i, oldQueue; var len = queue.length; while (len) { oldQueue = queue; queue = []; i = -1; while (++i < len) { oldQueue[i](); } len = queue.length; } draining = false; } module.exports = immediate; function immediate(task) { if (queue.push(task) === 1 && !draining) { scheduleDrain(); } } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],21:[function(require,module,exports){ 'use strict'; var immediate = require('immediate'); /* istanbul ignore next */ function INTERNAL() {} var handlers = {}; var REJECTED = ['REJECTED']; var FULFILLED = ['FULFILLED']; var PENDING = ['PENDING']; module.exports = Promise; function Promise(resolver) { if (typeof resolver !== 'function') { throw new TypeError('resolver must be a function'); } this.state = PENDING; this.queue = []; this.outcome = void 0; if (resolver !== INTERNAL) { safelyResolveThenable(this, resolver); } } Promise.prototype["catch"] = function (onRejected) { return this.then(null, onRejected); }; Promise.prototype.then = function (onFulfilled, onRejected) { if (typeof onFulfilled !== 'function' && this.state === FULFILLED || typeof onRejected !== 'function' && this.state === REJECTED) { return this; } var promise = new this.constructor(INTERNAL); if (this.state !== PENDING) { var resolver = this.state === FULFILLED ? onFulfilled : onRejected; unwrap(promise, resolver, this.outcome); } else { this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); } return promise; }; function QueueItem(promise, onFulfilled, onRejected) { this.promise = promise; if (typeof onFulfilled === 'function') { this.onFulfilled = onFulfilled; this.callFulfilled = this.otherCallFulfilled; } if (typeof onRejected === 'function') { this.onRejected = onRejected; this.callRejected = this.otherCallRejected; } } QueueItem.prototype.callFulfilled = function (value) { handlers.resolve(this.promise, value); }; QueueItem.prototype.otherCallFulfilled = function (value) { unwrap(this.promise, this