<!DOCTYPE html>
<html>
<head>
<link href="http://api.140proof.com/video-js/video-js.min.css?1420673850" media="screen" rel="stylesheet" type="text/css" />
<!-- <script type="text/javascript" src="videojs.ads.js"></script> --> <!-- latest master -->
<!-- <script type="text/javascript" src="vast-client.js"></script> --> <!-- latest master -->
<script src="http://vjs.zencdn.net/4.4.3/video.js"></script> <!-- only "works" with 4.4.x, anything more recent doesn't play *any* video -->
<script type="text/javascript" src="videojs.ads-0.4.0.js"></script> <!-- commit 35bb73023383ff5863c7ae7fa26e78ba336be546, 9/27 -->
<script type="text/javascript" src="vast-client-1.3.0.js"></script> <!-- commit 3c6dbaac66f245a7bbbb64c13a44d661927c36db, 10/22 -->
<script type="text/javascript" src="videojs.vast.js"></script> <!-- latest vpaid-squashed (10/28) -->
</head>
<body>
<video id='pv'
class='video-js vjs-big-play-centered vjs-default-skin'
controls='controls'
data-setup="{}"
height='240'
poster='https://ads-production.s3.amazonaws.com/videos/466/original_Screen_Shot_2014-09-16_at_10.58.02_AM.png?1410890364'
preload='auto'
width='320'>
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
</video>
<!-- http://www.iab.net/guidelines/508676/digitalvideo/vast/vast_xml_samples -->
<script>
var player = videojs('pv');
player.ads();
// player.vast({ url : 'vast2VPAIDLinear.xml'}); // http://demo.tremorvideo.com/proddev/vast/vast2VPAIDLinear.xml, local here because CORS
player.vast({ url : 'VPAID-Brightcove.xml'}); // http://support.brightcove.com/en/video-cloud/docs/developing-vpaid-swfs
</script>
</body>
</html>
// Code goes here
/* Styles go here */
/**
* Basic Ad support plugin for video.js.
*
* Common code to support ad integrations.
*/
(function(window, document, vjs, undefined) {
"use strict";
var
/**
* Copies properties from one or more objects onto an original.
*/
extend = function(obj /*, arg1, arg2, ... */) {
var arg, i, k;
for (i=1; i<arguments.length; i++) {
arg = arguments[i];
for (k in arg) {
if (arg.hasOwnProperty(k)) {
obj[k] = arg[k];
}
}
}
return obj;
},
/**
* Add a handler for multiple listeners to an object that supports addEventListener() or on().
*
* @param {object} obj The object to which the handler will be assigned.
* @param {mixed} events A string, array of strings, or hash of string/callback pairs.
* @param {function} callback Invoked when specified events occur, if events param is not a hash.
*
* @return {object} obj The object passed in.
*/
on = function(obj, events, handler) {
var
type = Object.prototype.toString.call(events),
register = function(obj, event, handler) {
if (obj.addEventListener) {
obj.addEventListener(event, handler);
} else if (obj.on) {
obj.on(event, handler);
} else if (obj.attachEvent) {
obj.attachEvent('on' + event, handler);
} else {
throw new Error('object has no mechanism for adding event listeners');
}
},
i,
ii;
switch (type) {
case '[object String]':
register(obj, events, handler);
break;
case '[object Array]':
for (i = 0, ii = events.length; i<ii; i++) {
register(obj, events[i], handler);
}
break;
case '[object Object]':
for (i in events) {
if (events.hasOwnProperty(i)) {
register(obj, i, events[i]);
}
}
break;
default:
throw new Error('Unrecognized events parameter type: ' + type);
}
return obj;
},
/**
* Runs the callback at the next available opportunity.
* @see https://developer.mozilla.org/en-US/docs/Web/API/window.setImmediate
*/
setImmediate = function(callback) {
return (
window.setImmediate ||
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.setTimeout
)(callback, 0);
},
/**
* Clears a callback previously registered with `setImmediate`.
* @param {id} id The identifier of the callback to abort
*/
clearImmediate = function(id) {
return (window.clearImmediate ||
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.clearTimeout)(id);
},
/**
* If ads are not playing, pauses the player at the next available
* opportunity. Has no effect if ads have started. This function is necessary
* because pausing a video element while processing a `play` event on iOS can
* cause the video element to continuously toggle between playing and paused
* states.
*
* @param {object} player The video player
*/
cancelContentPlay = function(player) {
if (player.ads.cancelPlayTimeout) {
// another cancellation is already in flight, so do nothing
return;
}
player.ads.cancelPlayTimeout = setImmediate(function() {
// deregister the cancel timeout so subsequent cancels are scheduled
player.ads.cancelPlayTimeout = null;
if (!player.paused()) {
player.pause();
}
});
},
/**
* Returns an object that captures the portions of player state relevant to
* video playback. The result of this function can be passed to
* restorePlayerSnapshot with a player to return the player to the state it
* was in when this function was invoked.
* @param {object} player The videojs player object
*/
getPlayerSnapshot = function(player) {
var
tech = player.el().querySelector('.vjs-tech'),
snapshot = {
src: player.currentSrc(),
currentTime: player.currentTime(),
type: player.currentType()
};
if (tech) {
snapshot.nativePoster = tech.poster;
snapshot.style = tech.getAttribute('style');
}
return snapshot;
},
removeClass = function(element, className) {
var
classes = element.className.split(/\s+/),
i = classes.length,
newClasses = [];
while (i--) {
if (classes[i] !== className) {
newClasses.push(classes[i]);
}
}
element.className = newClasses.join(' ');
},
/**
* Attempts to modify the specified player so that its state is equivalent to
* the state of the snapshot.
* @param {object} snapshot - the player state to apply
*/
restorePlayerSnapshot = function(player, snapshot) {
var
// the playback tech
tech = player.el().querySelector('.vjs-tech'),
// the number of remaining attempts to restore the snapshot
attempts = 20,
// finish restoring the playback state
resume = function() {
player.currentTime(snapshot.currentTime);
//If this wasn't a postroll resume
if (!player.ended()) {
player.play();
}
},
// determine if the video element has loaded enough of the snapshot source
// to be ready to apply the rest of the state
tryToResume = function() {
if (tech.seekable === undefined) {
// if the tech doesn't expose the seekable time ranges, try to
// resume playback immediately
resume();
return;
}
if (tech.seekable.length > 0) {
// if some period of the video is seekable, resume playback
resume();
return;
}
// delay a bit and then check again unless we're out of attempts
if (attempts--) {
setTimeout(tryToResume, 50);
}
},
// whether the video element has been modified since the
// snapshot was taken
srcChanged;
if (snapshot.nativePoster) {
tech.poster = snapshot.nativePoster;
}
if ('style' in snapshot) {
// overwrite all css style properties to restore state precisely
tech.setAttribute('style', snapshot.style || '');
}
// Determine whether the player needs to be restored to its state
// before ad playback began. With a custom ad display or burned-in
// ads, the content player state hasn't been modified and so no
// restoration is required
if (player.src()) {
// the player was in src attribute mode before the ad and the
// src attribute has not been modified, no restoration is required
// to resume playback
srcChanged = player.src() !== snapshot.src;
} else {
// the player was configured through source element children
// and the currentSrc hasn't changed, no restoration is required
// to resume playback
srcChanged = player.currentSrc() !== snapshot.src;
}
if (srcChanged) {
// if the src changed for ad playback, reset it
player.src({ src: snapshot.src, type: snapshot.type });
// safari requires a call to `load` to pick up a changed source
player.load();
// and then resume from the snapshots time once the original src has loaded
player.one('loadedmetadata', tryToResume);
} else if (!player.ended()) {
// the src didn't change and this wasn't a postroll
// just resume playback at the current time.
player.play();
}
},
/**
* Remove the poster attribute from the video element tech, if present. When
* reusing a video element for multiple videos, the poster image will briefly
* reappear while the new source loads. Removing the attribute ahead of time
* prevents the poster from showing up between videos.
* @param {object} player The videojs player object
*/
removeNativePoster = function(player) {
var tech = player.el().querySelector('.vjs-tech');
if (tech) {
tech.removeAttribute('poster');
}
},
// ---------------------------------------------------------------------------
// Ad Framework
// ---------------------------------------------------------------------------
// default framework settings
defaults = {
// maximum amount of time in ms to wait to receive `adsready` from the ad
// implementation after play has been requested. Ad implementations are
// expected to load any dynamic libraries and make any requests to determine
// ad policies for a video during this time.
timeout: 5000,
// maximum amount of time in ms to wait for the ad implementation to start
// linear ad mode after `readyforpreroll` has fired. This is in addition to
// the standard timeout.
prerollTimeout: 100,
// when truthy, instructs the plugin to output additional information about
// plugin state to the video.js log. On most devices, the video.js log is
// the same as the developer console.
debug: false
},
adFramework = function(options) {
var
player = this,
// merge options and defaults
settings = extend({}, defaults, options || {}),
fsmHandler;
// replace the ad initializer with the ad namespace
player.ads = {
state: 'content-set',
startLinearAdMode: function() {
player.trigger('adstart');
},
endLinearAdMode: function() {
player.trigger('adend');
}
};
fsmHandler = function(event) {
// Ad Playback State Machine
var
fsm = {
'content-set': {
events: {
'adscanceled': function() {
this.state = 'content-playback';
},
'adsready': function() {
this.state = 'ads-ready';
},
'play': function() {
this.state = 'ads-ready?';
cancelContentPlay(player);
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
}
}
},
'ads-ready': {
events: {
'play': function() {
this.state = 'preroll?';
cancelContentPlay(player);
}
}
},
'preroll?': {
enter: function() {
// change class to show that we're waiting on ads
player.el().className += ' vjs-ad-loading';
// schedule an adtimeout event to fire if we waited too long
player.ads.timeout = window.setTimeout(function() {
player.trigger('adtimeout');
}, settings.prerollTimeout);
// signal to ad plugin that it's their opportunity to play a preroll
player.trigger('readyforpreroll');
},
leave: function() {
window.clearTimeout(player.ads.timeout);
clearImmediate(player.ads.cancelPlayTimeout);
player.ads.cancelPlayTimeout = null;
removeClass(player.el(), 'vjs-ad-loading');
},
events: {
'play': function() {
cancelContentPlay(player);
},
'adstart': function() {
this.state = 'ad-playback';
player.el().className += ' vjs-ad-playing';
},
'adtimeout': function() {
this.state = 'content-playback';
player.play();
}
}
},
'ads-ready?': {
enter: function() {
player.el().className += ' vjs-ad-loading';
player.ads.timeout = window.setTimeout(function() {
player.trigger('adtimeout');
}, settings.timeout);
},
leave: function() {
window.clearTimeout(player.ads.timeout);
removeClass(player.el(), 'vjs-ad-loading');
},
events: {
'play': function() {
cancelContentPlay(player);
},
'adscanceled': function() {
this.state = 'content-playback';
clearImmediate(player.ads.cancelPlayTimeout);
player.ads.cancelPlayTimeout = null;
player.play();
},
'adsready': function() {
this.state = 'preroll?';
},
'adtimeout': function() {
this.state = 'ad-timeout-playback';
}
}
},
'ad-timeout-playback': {
events: {
'adsready': function() {
if (player.paused()) {
this.state = 'ads-ready';
} else {
this.state = 'preroll?';
}
},
'contentupdate': function() {
if (player.paused()) {
this.state = 'content-set';
} else {
this.state = 'ads-ready?';
}
}
}
},
'ad-playback': {
enter: function() {
// capture current player state snapshot (playing, currentTime, src)
this.snapshot = getPlayerSnapshot(player);
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
},
leave: function() {
removeClass(player.el(), 'vjs-ad-playing');
restorePlayerSnapshot(player, this.snapshot);
},
events: {
'adend': function() {
this.state = 'content-playback';
}
}
},
'content-playback': {
events: {
'adstart': function() {
this.state = 'ad-playback';
player.el().className += ' vjs-ad-playing';
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
},
'contentupdate': function() {
if (player.paused()) {
this.state = 'content-set';
} else {
this.state = 'ads-ready?';
}
}
}
}
};
(function(state) {
var noop = function() {};
// process the current event with a noop default handler
(fsm[state].events[event.type] || noop).apply(player.ads);
// execute leave/enter callbacks if present
if (state !== player.ads.state) {
(fsm[state].leave || noop).apply(player.ads);
(fsm[player.ads.state].enter || noop).apply(player.ads);
if (settings.debug) {
videojs.log('ads', state + ' -> ' + player.ads.state);
}
}
})(player.ads.state);
};
// register for the events we're interested in
on(player, vjs.Html5.Events.concat([
// events emitted by ad plugin
'adtimeout',
'contentupdate',
// events emitted by third party ad implementors
'adsready',
'adscanceled',
'adstart', // startLinearAdMode()
'adend' // endLinearAdMode()
]), fsmHandler);
// keep track of the current content source
// if you want to change the src of the video without triggering
// the ad workflow to restart, you can update this variable before
// modifying the player's source
player.ads.contentSrc = player.currentSrc();
// implement 'contentupdate' event.
(function(){
var
// check if a new src has been set, if so, trigger contentupdate
checkSrc = function() {
var src;
if (player.ads.state !== 'ad-playback') {
src = player.currentSrc();
if (src !== player.ads.contentSrc) {
player.trigger({
type: 'contentupdate',
oldValue: player.ads.contentSrc,
newValue: src
});
player.ads.contentSrc = src;
}
}
};
// loadstart reliably indicates a new src has been set
player.on('loadstart', checkSrc);
// check immediately in case we missed the loadstart
setImmediate(checkSrc);
})();
// kick off the fsm
if (!player.paused()) {
// simulate a play event if we're autoplaying
fsmHandler({type:'play'});
}
};
// register the ad plugin framework
vjs.plugin('ads', adFramework);
})(window, document, videojs);
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.DMVAST=e()}}(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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.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(_dereq_,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
} else {
throw TypeError('Uncaught, unspecified "error" event.');
}
return false;
}
}
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;
}
},{}],2:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTAd;
VASTAd = (function() {
function VASTAd() {
this.errorURLTemplates = [];
this.impressionURLTemplates = [];
this.creatives = [];
}
return VASTAd;
})();
module.exports = VASTAd;
},{}],3:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTClient, VASTParser, VASTUtil;
VASTParser = _dereq_('./parser.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTClient = (function() {
function VASTClient() {}
VASTClient.cappingFreeLunch = 0;
VASTClient.cappingMinimumTimeInterval = 0;
VASTClient.timeout = 0;
VASTClient.get = function(url, cb) {
var now;
now = +new Date();
if (this.totalCallsTimeout < now) {
this.totalCalls = 1;
this.totalCallsTimeout = now + (60 * 60 * 1000);
} else {
this.totalCalls++;
}
if (this.cappingFreeLunch >= this.totalCalls) {
cb(null);
return;
}
if (now - this.lastSuccessfullAd < this.cappingMinimumTimeInterval) {
cb(null);
return;
}
return VASTParser.parse(url, (function(_this) {
return function(response) {
return cb(response);
};
})(this));
};
(function() {
var defineProperty, storage;
storage = VASTUtil.storage;
defineProperty = Object.defineProperty;
['lastSuccessfullAd', 'totalCalls', 'totalCallsTimeout'].forEach(function(property) {
defineProperty(VASTClient, property, {
get: function() {
return storage.getItem(property);
},
set: function(value) {
return storage.setItem(property, value);
},
configurable: false,
enumerable: true
});
});
if (VASTClient.totalCalls == null) {
VASTClient.totalCalls = 0;
}
if (VASTClient.totalCallsTimeout == null) {
VASTClient.totalCallsTimeout = 0;
}
})();
return VASTClient;
})();
module.exports = VASTClient;
},{"./parser.coffee":8,"./util.coffee":14}],4:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTCompanionAd;
VASTCompanionAd = (function() {
function VASTCompanionAd() {
this.id = null;
this.width = 0;
this.height = 0;
this.type = null;
this.staticResource = null;
this.companionClickThroughURLTemplate = null;
this.trackingEvents = {};
}
return VASTCompanionAd;
})();
module.exports = VASTCompanionAd;
},{}],5:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTCreative, VASTCreativeCompanion, VASTCreativeLinear, VASTCreativeNonLinear,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
VASTCreative = (function() {
function VASTCreative() {
this.trackingEvents = {};
}
return VASTCreative;
})();
VASTCreativeLinear = (function(_super) {
__extends(VASTCreativeLinear, _super);
function VASTCreativeLinear() {
VASTCreativeLinear.__super__.constructor.apply(this, arguments);
this.type = "linear";
this.duration = 0;
this.skipDelay = null;
this.mediaFiles = [];
this.videoClickThroughURLTemplate = null;
this.videoClickTrackingURLTemplates = [];
}
return VASTCreativeLinear;
})(VASTCreative);
VASTCreativeNonLinear = (function(_super) {
__extends(VASTCreativeNonLinear, _super);
function VASTCreativeNonLinear() {
return VASTCreativeNonLinear.__super__.constructor.apply(this, arguments);
}
return VASTCreativeNonLinear;
})(VASTCreative);
VASTCreativeCompanion = (function(_super) {
__extends(VASTCreativeCompanion, _super);
function VASTCreativeCompanion() {
this.type = "companion";
this.variations = [];
this.videoClickTrackingURLTemplates = [];
}
return VASTCreativeCompanion;
})(VASTCreative);
module.exports = {
VASTCreativeLinear: VASTCreativeLinear,
VASTCreativeNonLinear: VASTCreativeNonLinear,
VASTCreativeCompanion: VASTCreativeCompanion
};
},{}],6:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
module.exports = {
client: _dereq_('./client.coffee'),
tracker: _dereq_('./tracker.coffee'),
parser: _dereq_('./parser.coffee'),
util: _dereq_('./util.coffee')
};
},{"./client.coffee":3,"./parser.coffee":8,"./tracker.coffee":10,"./util.coffee":14}],7:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTMediaFile;
VASTMediaFile = (function() {
function VASTMediaFile() {
this.fileURL = null;
this.deliveryType = "progressive";
this.mimeType = null;
this.codec = null;
this.bitrate = 0;
this.minBitrate = 0;
this.maxBitrate = 0;
this.width = 0;
this.height = 0;
this.apiFramework = null;
}
return VASTMediaFile;
})();
module.exports = VASTMediaFile;
},{}],8:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var EventEmitter, URLHandler, VASTAd, VASTCompanionAd, VASTCreativeCompanion, VASTCreativeLinear, VASTMediaFile, VASTParser, VASTResponse, VASTUtil,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
URLHandler = _dereq_('./urlhandler.coffee');
VASTResponse = _dereq_('./response.coffee');
VASTAd = _dereq_('./ad.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTCreativeLinear = _dereq_('./creative.coffee').VASTCreativeLinear;
VASTCreativeCompanion = _dereq_('./creative.coffee').VASTCreativeCompanion;
VASTMediaFile = _dereq_('./mediafile.coffee');
VASTCompanionAd = _dereq_('./companionad.coffee');
EventEmitter = _dereq_('events').EventEmitter;
VASTParser = (function() {
var URLTemplateFilters;
function VASTParser() {}
URLTemplateFilters = [];
VASTParser.addURLTemplateFilter = function(func) {
if (typeof func === 'function') {
URLTemplateFilters.push(func);
}
};
VASTParser.removeURLTemplateFilter = function() {
return URLTemplateFilters.pop();
};
VASTParser.countURLTemplateFilters = function() {
return URLTemplateFilters.length;
};
VASTParser.clearUrlTemplateFilters = function() {
return URLTemplateFilters = [];
};
VASTParser.parse = function(url, cb) {
return this._parse(url, null, function(err, response) {
return cb(response);
});
};
VASTParser.vent = new EventEmitter();
VASTParser.track = function(templates, errorCode) {
this.vent.emit('VAST-error', errorCode);
return VASTUtil.track(templates, errorCode);
};
VASTParser.on = function(eventName, cb) {
return this.vent.on(eventName, cb);
};
VASTParser.once = function(eventName, cb) {
return this.vent.once(eventName, cb);
};
VASTParser._parse = function(url, parentURLs, cb) {
var filter, _i, _len;
for (_i = 0, _len = URLTemplateFilters.length; _i < _len; _i++) {
filter = URLTemplateFilters[_i];
url = filter(url);
}
if (parentURLs == null) {
parentURLs = [];
}
parentURLs.push(url);
return URLHandler.get(url, (function(_this) {
return function(err, xml) {
var ad, complete, loopIndex, node, response, _j, _k, _len1, _len2, _ref, _ref1;
if (err != null) {
return cb(err);
}
response = new VASTResponse();
if (!(((xml != null ? xml.documentElement : void 0) != null) && xml.documentElement.nodeName === "VAST")) {
return cb();
}
_ref = xml.documentElement.childNodes;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
node = _ref[_j];
if (node.nodeName === 'Error') {
response.errorURLTemplates.push(_this.parseNodeText(node));
}
}
_ref1 = xml.documentElement.childNodes;
for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
node = _ref1[_k];
if (node.nodeName === 'Ad') {
ad = _this.parseAdElement(node);
if (ad != null) {
response.ads.push(ad);
} else {
_this.track(response.errorURLTemplates, {
ERRORCODE: 101
});
}
}
}
complete = function(errorAlreadyRaised) {
var _l, _len3, _ref2;
if (errorAlreadyRaised == null) {
errorAlreadyRaised = false;
}
if (!response) {
return;
}
_ref2 = response.ads;
for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) {
ad = _ref2[_l];
if (ad.nextWrapperURL != null) {
return;
}
}
if (response.ads.length === 0) {
if (!errorAlreadyRaised) {
_this.track(response.errorURLTemplates, {
ERRORCODE: 303
});
}
response = null;
}
return cb(null, response);
};
loopIndex = response.ads.length;
while (loopIndex--) {
ad = response.ads[loopIndex];
if (ad.nextWrapperURL == null) {
continue;
}
(function(ad) {
var baseURL, _ref2;
if (parentURLs.length >= 10 || (_ref2 = ad.nextWrapperURL, __indexOf.call(parentURLs, _ref2) >= 0)) {
_this.track(ad.errorURLTemplates, {
ERRORCODE: 302
});
response.ads.splice(response.ads.indexOf(ad), 1);
complete();
return;
}
if (ad.nextWrapperURL.indexOf('://') === -1) {
baseURL = url.slice(0, url.lastIndexOf('/'));
ad.nextWrapperURL = "" + baseURL + "/" + ad.nextWrapperURL;
}
return _this._parse(ad.nextWrapperURL, parentURLs, function(err, wrappedResponse) {
var creative, errorAlreadyRaised, eventName, index, wrappedAd, _base, _l, _len3, _len4, _len5, _len6, _m, _n, _o, _ref3, _ref4, _ref5, _ref6;
errorAlreadyRaised = false;
if (err != null) {
_this.track(ad.errorURLTemplates, {
ERRORCODE: 301
});
response.ads.splice(response.ads.indexOf(ad), 1);
errorAlreadyRaised = true;
} else if (wrappedResponse == null) {
_this.track(ad.errorURLTemplates, {
ERRORCODE: 303
});
response.ads.splice(response.ads.indexOf(ad), 1);
errorAlreadyRaised = true;
} else {
response.errorURLTemplates = response.errorURLTemplates.concat(wrappedResponse.errorURLTemplates);
index = response.ads.indexOf(ad);
response.ads.splice(index, 1);
_ref3 = wrappedResponse.ads;
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
wrappedAd = _ref3[_l];
wrappedAd.errorURLTemplates = ad.errorURLTemplates.concat(wrappedAd.errorURLTemplates);
wrappedAd.impressionURLTemplates = ad.impressionURLTemplates.concat(wrappedAd.impressionURLTemplates);
if (ad.trackingEvents != null) {
_ref4 = wrappedAd.creatives;
for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
creative = _ref4[_m];
if (creative.type === 'linear') {
_ref5 = Object.keys(ad.trackingEvents);
for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) {
eventName = _ref5[_n];
(_base = creative.trackingEvents)[eventName] || (_base[eventName] = []);
creative.trackingEvents[eventName] = creative.trackingEvents[eventName].concat(ad.trackingEvents[eventName]);
}
}
}
}
if (ad.videoClickTrackingURLTemplates != null) {
_ref6 = wrappedAd.creatives;
for (_o = 0, _len6 = _ref6.length; _o < _len6; _o++) {
creative = _ref6[_o];
if (creative.type === 'linear') {
creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat(ad.videoClickTrackingURLTemplates);
}
}
}
response.ads.splice(index, 0, wrappedAd);
}
}
delete ad.nextWrapperURL;
return complete(errorAlreadyRaised);
});
})(ad);
}
return complete();
};
})(this));
};
VASTParser.childByName = function(node, name) {
var child, _i, _len, _ref;
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child.nodeName === name) {
return child;
}
}
};
VASTParser.childsByName = function(node, name) {
var child, childs, _i, _len, _ref;
childs = [];
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child.nodeName === name) {
childs.push(child);
}
}
return childs;
};
VASTParser.parseAdElement = function(adElement) {
var adTypeElement, _i, _len, _ref;
_ref = adElement.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
adTypeElement = _ref[_i];
if (adTypeElement.nodeName === "Wrapper") {
return this.parseWrapperElement(adTypeElement);
} else if (adTypeElement.nodeName === "InLine") {
return this.parseInLineElement(adTypeElement);
}
}
};
VASTParser.parseWrapperElement = function(wrapperElement) {
var ad, creative, wrapperCreativeElement, wrapperURLElement, _i, _len, _ref;
ad = this.parseInLineElement(wrapperElement);
wrapperURLElement = this.childByName(wrapperElement, "VASTAdTagURI");
if (wrapperURLElement != null) {
ad.nextWrapperURL = this.parseNodeText(wrapperURLElement);
}
wrapperCreativeElement = null;
_ref = ad.creatives;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
creative = _ref[_i];
if (creative.type === 'linear') {
wrapperCreativeElement = creative;
break;
}
}
if (wrapperCreativeElement != null) {
if (wrapperCreativeElement.trackingEvents != null) {
ad.trackingEvents = wrapperCreativeElement.trackingEvents;
}
if (wrapperCreativeElement.videoClickTrackingURLTemplates != null) {
ad.videoClickTrackingURLTemplates = wrapperCreativeElement.videoClickTrackingURLTemplates;
}
}
if (ad.nextWrapperURL != null) {
return ad;
}
};
VASTParser.parseInLineElement = function(inLineElement) {
var ad, creative, creativeElement, creativeTypeElement, node, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
ad = new VASTAd();
_ref = inLineElement.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
switch (node.nodeName) {
case "Error":
ad.errorURLTemplates.push(this.parseNodeText(node));
break;
case "Impression":
ad.impressionURLTemplates.push(this.parseNodeText(node));
break;
case "Creatives":
_ref1 = this.childsByName(node, "Creative");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
creativeElement = _ref1[_j];
_ref2 = creativeElement.childNodes;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
creativeTypeElement = _ref2[_k];
switch (creativeTypeElement.nodeName) {
case "Linear":
creative = this.parseCreativeLinearElement(creativeTypeElement);
if (creative) {
ad.creatives.push(creative);
}
break;
case "CompanionAds":
creative = this.parseCompanionAd(creativeTypeElement);
if (creative) {
ad.creatives.push(creative);
}
}
}
}
}
}
return ad;
};
VASTParser.parseCreativeLinearElement = function(creativeElement) {
var clickTrackingElement, creative, eventName, mediaFile, mediaFileElement, mediaFilesElement, percent, skipOffset, trackingElement, trackingEventsElement, trackingURLTemplate, videoClicksElement, _base, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4;
creative = new VASTCreativeLinear();
creative.duration = this.parseDuration(this.parseNodeText(this.childByName(creativeElement, "Duration")));
if (creative.duration === -1 && creativeElement.parentNode.parentNode.parentNode.nodeName !== 'Wrapper') {
return null;
}
skipOffset = creativeElement.getAttribute("skipoffset");
if (skipOffset == null) {
creative.skipDelay = null;
} else if (skipOffset.charAt(skipOffset.length - 1) === "%") {
percent = parseInt(skipOffset, 10);
creative.skipDelay = creative.duration * (percent / 100);
} else {
creative.skipDelay = this.parseDuration(skipOffset);
}
videoClicksElement = this.childByName(creativeElement, "VideoClicks");
if (videoClicksElement != null) {
creative.videoClickThroughURLTemplate = this.parseNodeText(this.childByName(videoClicksElement, "ClickThrough"));
_ref = this.childsByName(videoClicksElement, "ClickTracking");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
clickTrackingElement = _ref[_i];
creative.videoClickTrackingURLTemplates.push(this.parseNodeText(clickTrackingElement));
}
}
_ref1 = this.childsByName(creativeElement, "TrackingEvents");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
trackingEventsElement = _ref1[_j];
_ref2 = this.childsByName(trackingEventsElement, "Tracking");
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
trackingElement = _ref2[_k];
eventName = trackingElement.getAttribute("event");
trackingURLTemplate = this.parseNodeText(trackingElement);
if ((eventName != null) && (trackingURLTemplate != null)) {
if ((_base = creative.trackingEvents)[eventName] == null) {
_base[eventName] = [];
}
creative.trackingEvents[eventName].push(trackingURLTemplate);
}
}
}
_ref3 = this.childsByName(creativeElement, "MediaFiles");
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
mediaFilesElement = _ref3[_l];
_ref4 = this.childsByName(mediaFilesElement, "MediaFile");
for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
mediaFileElement = _ref4[_m];
mediaFile = new VASTMediaFile();
mediaFile.fileURL = this.parseNodeText(mediaFileElement);
mediaFile.deliveryType = mediaFileElement.getAttribute("delivery");
mediaFile.codec = mediaFileElement.getAttribute("codec");
mediaFile.mimeType = mediaFileElement.getAttribute("type");
mediaFile.apiFramework = mediaFileElement.getAttribute("apiFramework");
mediaFile.bitrate = parseInt(mediaFileElement.getAttribute("bitrate") || 0);
mediaFile.minBitrate = parseInt(mediaFileElement.getAttribute("minBitrate") || 0);
mediaFile.maxBitrate = parseInt(mediaFileElement.getAttribute("maxBitrate") || 0);
mediaFile.width = parseInt(mediaFileElement.getAttribute("width") || 0);
mediaFile.height = parseInt(mediaFileElement.getAttribute("height") || 0);
creative.mediaFiles.push(mediaFile);
}
}
return creative;
};
VASTParser.parseCompanionAd = function(creativeElement) {
var companionAd, companionResource, creative, eventName, staticElement, trackingElement, trackingEventsElement, trackingURLTemplate, _base, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
creative = new VASTCreativeCompanion();
_ref = this.childsByName(creativeElement, "Companion");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
companionResource = _ref[_i];
companionAd = new VASTCompanionAd();
companionAd.id = companionResource.getAttribute("id") || null;
companionAd.width = companionResource.getAttribute("width");
companionAd.height = companionResource.getAttribute("height");
_ref1 = this.childsByName(companionResource, "StaticResource");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
staticElement = _ref1[_j];
companionAd.type = staticElement.getAttribute("creativeType") || 0;
companionAd.staticResource = this.parseNodeText(staticElement);
}
_ref2 = this.childsByName(companionResource, "TrackingEvents");
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
trackingEventsElement = _ref2[_k];
_ref3 = this.childsByName(trackingEventsElement, "Tracking");
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
trackingElement = _ref3[_l];
eventName = trackingElement.getAttribute("event");
trackingURLTemplate = this.parseNodeText(trackingElement);
if ((eventName != null) && (trackingURLTemplate != null)) {
if ((_base = companionAd.trackingEvents)[eventName] == null) {
_base[eventName] = [];
}
companionAd.trackingEvents[eventName].push(trackingURLTemplate);
}
}
}
companionAd.companionClickThroughURLTemplate = this.parseNodeText(this.childByName(companionResource, "CompanionClickThrough"));
creative.variations.push(companionAd);
}
return creative;
};
VASTParser.parseDuration = function(durationString) {
var durationComponents, hours, minutes, seconds, secondsAndMS;
if (!(durationString != null)) {
return -1;
}
durationComponents = durationString.split(":");
if (durationComponents.length !== 3) {
return -1;
}
secondsAndMS = durationComponents[2].split(".");
seconds = parseInt(secondsAndMS[0]);
if (secondsAndMS.length === 2) {
seconds += parseFloat("0." + secondsAndMS[1]);
}
minutes = parseInt(durationComponents[1] * 60);
hours = parseInt(durationComponents[0] * 60 * 60);
if (isNaN(hours || isNaN(minutes || isNaN(seconds || minutes > 60 * 60 || seconds > 60)))) {
return -1;
}
return hours + minutes + seconds;
};
VASTParser.parseNodeText = function(node) {
return node && (node.textContent || node.text);
};
return VASTParser;
})();
module.exports = VASTParser;
},{"./ad.coffee":2,"./companionad.coffee":4,"./creative.coffee":5,"./mediafile.coffee":7,"./response.coffee":9,"./urlhandler.coffee":11,"./util.coffee":14,"events":1}],9:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTResponse;
VASTResponse = (function() {
function VASTResponse() {
this.ads = [];
this.errorURLTemplates = [];
}
return VASTResponse;
})();
module.exports = VASTResponse;
},{}],10:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var EventEmitter, VASTClient, VASTCreativeLinear, VASTTracker, VASTUtil,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
VASTClient = _dereq_('./client.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTCreativeLinear = _dereq_('./creative.coffee').VASTCreativeLinear;
EventEmitter = _dereq_('events').EventEmitter;
VASTTracker = (function(_super) {
__extends(VASTTracker, _super);
function VASTTracker(ad, creative) {
var eventName, events, _ref;
this.ad = ad;
this.creative = creative;
this.muted = false;
this.impressed = false;
this.skipable = false;
this.skipDelayDefault = -1;
this.trackingEvents = {};
this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close'];
_ref = creative.trackingEvents;
for (eventName in _ref) {
events = _ref[eventName];
this.trackingEvents[eventName] = events.slice(0);
}
if (creative instanceof VASTCreativeLinear) {
this.setDuration(creative.duration);
this.skipDelay = creative.skipDelay;
this.linear = true;
this.clickThroughURLTemplate = creative.videoClickThroughURLTemplate;
this.clickTrackingURLTemplates = creative.videoClickTrackingURLTemplates;
} else {
this.skipDelay = -1;
this.linear = false;
}
this.on('start', function() {
VASTClient.lastSuccessfullAd = +new Date();
});
}
VASTTracker.prototype.setDuration = function(duration) {
this.assetDuration = duration;
return this.quartiles = {
'firstQuartile': Math.round(25 * this.assetDuration) / 100,
'midpoint': Math.round(50 * this.assetDuration) / 100,
'thirdQuartile': Math.round(75 * this.assetDuration) / 100
};
};
VASTTracker.prototype.setProgress = function(progress) {
var eventName, events, percent, quartile, skipDelay, time, _i, _len, _ref;
skipDelay = this.skipDelay === null ? this.skipDelayDefault : this.skipDelay;
if (skipDelay !== -1 && !this.skipable) {
if (skipDelay > progress) {
this.emit('skip-countdown', skipDelay - progress);
} else {
this.skipable = true;
this.emit('skip-countdown', 0);
}
}
if (this.linear && this.assetDuration > 0) {
events = [];
if (progress > 0) {
events.push("start");
percent = Math.round(progress / this.assetDuration * 100);
events.push("progress-" + percent + "%");
_ref = this.quartiles;
for (quartile in _ref) {
time = _ref[quartile];
if ((time <= progress && progress <= (time + 1))) {
events.push(quartile);
}
}
}
for (_i = 0, _len = events.length; _i < _len; _i++) {
eventName = events[_i];
this.track(eventName, true);
}
if (progress < this.progress) {
this.track("rewind");
}
}
return this.progress = progress;
};
VASTTracker.prototype.setMuted = function(muted) {
if (this.muted !== muted) {
this.track(muted ? "muted" : "unmuted");
}
return this.muted = muted;
};
VASTTracker.prototype.setPaused = function(paused) {
if (this.paused !== paused) {
this.track(paused ? "pause" : "resume");
}
return this.paused = paused;
};
VASTTracker.prototype.setFullscreen = function(fullscreen) {
if (this.fullscreen !== fullscreen) {
this.track(fullscreen ? "fullscreen" : "exitFullscreen");
}
return this.fullscreen = fullscreen;
};
VASTTracker.prototype.setSkipDelay = function(duration) {
if (typeof duration === 'number') {
return this.skipDelay = duration;
}
};
VASTTracker.prototype.load = function() {
if (!this.impressed) {
this.impressed = true;
this.trackURLs(this.ad.impressionURLTemplates);
return this.track("creativeView");
}
};
VASTTracker.prototype.errorWithCode = function(errorCode) {
return this.trackURLs(this.ad.errorURLTemplates, {
ERRORCODE: errorCode
});
};
VASTTracker.prototype.complete = function() {
return this.track("complete");
};
VASTTracker.prototype.stop = function() {
return this.track(this.linear ? "closeLinear" : "close");
};
VASTTracker.prototype.skip = function() {
this.track("skip");
return this.trackingEvents = [];
};
VASTTracker.prototype.click = function() {
var clickThroughURL, variables, _ref;
if ((_ref = this.clickTrackingURLTemplates) != null ? _ref.length : void 0) {
this.trackURLs(this.clickTrackingURLTemplates);
}
if (this.clickThroughURLTemplate != null) {
if (this.linear) {
variables = {
CONTENTPLAYHEAD: this.progressFormated()
};
}
clickThroughURL = VASTUtil.resolveURLTemplates([this.clickThroughURLTemplate], variables)[0];
return this.emit("clickthrough", clickThroughURL);
}
};
VASTTracker.prototype.track = function(eventName, once) {
var idx, trackingURLTemplates;
if (once == null) {
once = false;
}
if (eventName === 'closeLinear' && ((this.trackingEvents[eventName] == null) && (this.trackingEvents['close'] != null))) {
eventName = 'close';
}
trackingURLTemplates = this.trackingEvents[eventName];
idx = this.emitAlwaysEvents.indexOf(eventName);
if (trackingURLTemplates != null) {
this.emit(eventName, '');
this.trackURLs(trackingURLTemplates);
} else if (idx !== -1) {
this.emit(eventName, '');
}
if (once === true) {
delete this.trackingEvents[eventName];
if (idx > -1) {
this.emitAlwaysEvents.splice(idx, 1);
}
}
};
VASTTracker.prototype.trackURLs = function(URLTemplates, variables) {
if (variables == null) {
variables = {};
}
if (this.linear) {
variables["CONTENTPLAYHEAD"] = this.progressFormated();
}
return VASTUtil.track(URLTemplates, variables);
};
VASTTracker.prototype.progressFormated = function() {
var h, m, ms, s, seconds;
seconds = parseInt(this.progress);
h = seconds / (60 * 60);
if (h.length < 2) {
h = "0" + h;
}
m = seconds / 60 % 60;
if (m.length < 2) {
m = "0" + m;
}
s = seconds % 60;
if (s.length < 2) {
s = "0" + m;
}
ms = parseInt((this.progress - seconds) * 100);
return "" + h + ":" + m + ":" + s + "." + ms;
};
return VASTTracker;
})(EventEmitter);
module.exports = VASTTracker;
},{"./client.coffee":3,"./creative.coffee":5,"./util.coffee":14,"events":1}],11:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var URLHandler, flash, xhr;
xhr = _dereq_('./urlhandlers/xmlhttprequest.coffee');
flash = _dereq_('./urlhandlers/flash.coffee');
URLHandler = (function() {
function URLHandler() {}
URLHandler.get = function(url, cb) {
if (typeof window === "undefined" || window === null) {
return _dereq_('./urlhandlers/' + 'node.coffee').get(url, cb);
} else if (xhr.supported()) {
return xhr.get(url, cb);
} else if (flash.supported()) {
return flash.get(url, cb);
} else {
return cb();
}
};
return URLHandler;
})();
module.exports = URLHandler;
},{"./urlhandlers/flash.coffee":12,"./urlhandlers/xmlhttprequest.coffee":13}],12:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var FlashURLHandler;
FlashURLHandler = (function() {
function FlashURLHandler() {}
FlashURLHandler.xdr = function() {
var xdr;
if (window.XDomainRequest) {
xdr = new XDomainRequest();
}
return xdr;
};
FlashURLHandler.supported = function() {
return !!this.xdr();
};
FlashURLHandler.get = function(url, cb) {
var xdr, xmlDocument;
if (xmlDocument = typeof window.ActiveXObject === "function" ? new window.ActiveXObject("Microsoft.XMLDOM") : void 0) {
xmlDocument.async = false;
} else {
return cb();
}
xdr = this.xdr();
xdr.open('GET', url);
xdr.send();
return xdr.onload = function() {
xmlDocument.loadXML(xdr.responseText);
return cb(null, xmlDocument);
};
};
return FlashURLHandler;
})();
module.exports = FlashURLHandler;
},{}],13:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var XHRURLHandler;
XHRURLHandler = (function() {
function XHRURLHandler() {}
XHRURLHandler.xhr = function() {
var xhr;
xhr = new window.XMLHttpRequest();
if ('withCredentials' in xhr) {
return xhr;
}
};
XHRURLHandler.supported = function() {
return !!this.xhr();
};
XHRURLHandler.get = function(url, cb) {
var xhr;
try {
xhr = this.xhr();
xhr.open('GET', url);
xhr.send();
return xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
return cb(null, xhr.responseXML);
}
};
} catch (_error) {
return cb();
}
};
return XHRURLHandler;
})();
module.exports = XHRURLHandler;
},{}],14:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTUtil;
VASTUtil = (function() {
function VASTUtil() {}
VASTUtil.track = function(URLTemplates, variables) {
var URL, URLs, i, _i, _len, _results;
URLs = this.resolveURLTemplates(URLTemplates, variables);
_results = [];
for (_i = 0, _len = URLs.length; _i < _len; _i++) {
URL = URLs[_i];
if (typeof window !== "undefined" && window !== null) {
i = new Image();
_results.push(i.src = URL);
} else {
}
}
return _results;
};
VASTUtil.resolveURLTemplates = function(URLTemplates, variables) {
var URLTemplate, URLs, key, macro1, macro2, resolveURL, value, _i, _len;
URLs = [];
if (variables == null) {
variables = {};
}
if (!("CACHEBUSTING" in variables)) {
variables["CACHEBUSTING"] = Math.round(Math.random() * 1.0e+10);
}
variables["random"] = variables["CACHEBUSTING"];
for (_i = 0, _len = URLTemplates.length; _i < _len; _i++) {
URLTemplate = URLTemplates[_i];
resolveURL = URLTemplate;
if (!resolveURL) {
continue;
}
for (key in variables) {
value = variables[key];
macro1 = "[" + key + "]";
macro2 = "%%" + key + "%%";
resolveURL = resolveURL.replace(macro1, value);
resolveURL = resolveURL.replace(macro2, value);
}
URLs.push(resolveURL);
}
return URLs;
};
VASTUtil.storage = (function() {
var data, isDisabled, storage, storageError;
try {
storage = typeof window !== "undefined" && window !== null ? window.localStorage || window.sessionStorage : null;
} catch (_error) {
storageError = _error;
storage = null;
}
isDisabled = function(store) {
var e, testValue;
try {
testValue = '__VASTUtil__';
store.setItem(testValue, testValue);
if (store.getItem(testValue) !== testValue) {
return true;
}
} catch (_error) {
e = _error;
return true;
}
return false;
};
if ((storage == null) || isDisabled(storage)) {
data = {};
storage = {
length: 0,
getItem: function(key) {
return data[key];
},
setItem: function(key, value) {
data[key] = value;
this.length = Object.keys(data).length;
},
removeItem: function(key) {
delete data[key];
this.length = Object.keys(data).length;
},
clear: function() {
data = {};
this.length = 0;
}
};
}
return storage;
})();
return VASTUtil;
})();
module.exports = VASTUtil;
},{}]},{},[6])
(6)
});
(function(vjs, vast, undefined) {
"use strict";
var
extend = function(obj) {
var arg, i, k;
for (i = 1; i < arguments.length; i++) {
arg = arguments[i];
for (k in arg) {
if (arg.hasOwnProperty(k)) {
obj[k] = arg[k];
}
}
}
return obj;
},
defaults = {
skip: 5, // negative disables. Ignored for VPAID as VPAID asset controls it itself
bitrate: 1000, //advised bitrate for VPAID ads
viewMode: 'normal', //view mode for VPAID ads. Possible values: normal, thumbnail, fullscreen
vpaidElement: undefined //html element used for vpaid ads
},
vastPlugin = function(options) {
var player = this;
var settings = extend({}, defaults, options || {});
var vpaidObj, vpaidListeners = {}, vpaidIFrame = null, vpaidPlayer = null, vpaidTrackInterval = -1, vpaidSeeker;
if (player.ads === undefined) {
console.log("VAST requires videojs-contrib-ads");
return;
}
// If we don't have a VAST url, just bail out.
if(settings.url === undefined) {
player.trigger('adtimeout');
return;
}
//preserve support for older video.js versions
function localize(text) {
if (player.localize) {
return player.localize(text);
} else {
return text;
}
}
// videojs-ads triggers this when src changes
player.on('contentupdate', function(){
player.vast.getContent(settings.url);
});
player.on('readyforpreroll', function() {
//in case we have something simple to show
if (player.vast.sources) {
player.vast.preroll();
} else {
player.vast.prerollVPAID();
}
});
player.vast.getContent = function(url) {
vast.client.get(url, function(response) {
if (response) {
debugger;
for (var adIdx = 0; adIdx < response.ads.length; adIdx++) {
var ad = response.ads[adIdx];
player.vast.companion = undefined;
var foundCreative = false, foundCompanion = false, foundVPAID = false;
for (var creaIdx = 0; creaIdx < ad.creatives.length; creaIdx++) {
var creative = ad.creatives[creaIdx];
if (creative.type === "linear" && !foundCreative) {
if (creative.mediaFiles.length) {
player.vastTracker = new vast.tracker(ad, creative);
var vpaidTech = player.vast.findOptimalVPAIDTech(creative.mediaFiles);
if (vpaidTech) {
foundVPAID = true;
player.vast.initVPAID(vpaidTech, function() {
player.vast.createVPAIDControls();
player.trigger('adsready');
});
} else {
player.vast.sources = player.vast.createSourceObjects(creative.mediaFiles);
if (!player.vast.sources.length) {
player.trigger('adtimeout');
return;
}
player.vast.initSimpleVAST(ad);
}
foundCreative = true;
}
} else if (creative.type === "companion" && !foundCompanion) {
//TODO is it ever used?
player.vast.companion = creative;
foundCompanion = true;
}
}
if (player.vastTracker) {
//vpaid will trigger adsready in async manner when all assets are loaded
if (!foundVPAID) {
player.trigger("adsready");
}
break;
} else {
// Inform ad server we can't find suitable media file for this ad
vast.util.track(ad.errorURLTemplates, {ERRORCODE: 403});
}
}
}
if (!player.vastTracker) {
// No pre-roll, start video
player.trigger('adtimeout');
}
});
};
player.vast.createSkipButton = function() {
var skipButton = document.createElement("div");
skipButton.className = "vast-skip-button";
if (settings.skip < 0) {
skipButton.style.display = "none";
}
player.vast.skipButton = skipButton;
player.el().appendChild(skipButton);
skipButton.onclick = function (e) {
if ((' ' + player.vast.skipButton.className + ' ').indexOf(' enabled ') >= 0) {
if (vpaidObj) {
vpaidObj.skipAd();
//will tear down after event AdSkipped is triggered
} else {
player.vastTracker.skip();
player.vast.tearDown();
}
}
if (Event.prototype.stopPropagation !== undefined) {
e.stopPropagation();
} else {
return false;
}
};
};
player.vast.getClickThrough = function () {
var clickthrough;
if (player.vastTracker.clickThroughURLTemplate) {
clickthrough = vast.util.resolveURLTemplates(
[player.vastTracker.clickThroughURLTemplate],
{
CACHEBUSTER: Math.round(Math.random() * 1.0e+10),
CONTENTPLAYHEAD: player.vastTracker.progressFormated()
}
)[0];
}
return clickthrough;
};
player.vast.preroll = function() {
player.ads.startLinearAdMode();
player.vast.showControls = player.controls();
if (player.vast.showControls ) {
player.controls(false);
}
player.autoplay(true);
// play your linear ad content
var adSources = player.vast.sources;
player.src(adSources);
var clickthrough = player.vast.getClickThrough();
var blocker = document.createElement("a");
blocker.className = "vast-blocker";
blocker.href = clickthrough || "#";
blocker.target = "_blank";
blocker.onclick = function() {
if (player.paused()) {
player.play();
return false;
}
var clicktrackers = player.vastTracker.clickTrackingURLTemplate;
if (clicktrackers) {
player.vastTracker.trackURLs([clicktrackers]);
}
player.trigger("adclick");
};
player.vast.blocker = blocker;
player.el().insertBefore(blocker, player.controlBar.el());
player.vast.createSkipButton();
player.on("timeupdate", player.vast.timeupdate);
player.one("ended", player.vast.tearDown);
};
player.vast.prerollVPAID = function() {
player.ads.startLinearAdMode();
player.vast.showControls = player.controls();
if (player.vast.showControls ) {
player.controls(false);
}
vpaidObj.startAd();
vpaidTrackInterval = setInterval(player.vast.updateSeeker, 500);
//player might be playing if video tags are different
player.pause();
};
player.vast.tearDown = function() {
if (player.vast.skipButton) {
player.vast.skipButton.parentNode.removeChild(player.vast.skipButton);
player.vast.skipButton = undefined;
}
if (player.vast.blocker) {
player.vast.blocker.parentNode.removeChild(player.vast.blocker);
player.vast.blocker = undefined;
}
player.off('timeupdate', player.vast.timeupdate);
player.off('ended', player.vast.tearDown);
if (player.vast.showControls ) {
player.controls(true);
}
if (vpaidObj) {
for (var event in vpaidListeners) {
if (!vpaidListeners.hasOwnProperty(event)) {
continue;
}
var listeners = vpaidListeners[event];
for (var i = 0; i < listeners.length; i++) {
vpaidObj.unsubscribe(listeners[i], event);
}
}
if (vpaidIFrame) {
vpaidIFrame.parentNode.removeChild(vpaidIFrame);
}
vpaidObj = null;
vpaidIFrame = null;
vpaidListeners = {};
player.vast.removeVPAIDControls();
}
if (vpaidTrackInterval != -1) {
clearInterval(vpaidTrackInterval);
vpaidTrackInterval = -1;
}
if (vpaidPlayer) {
vpaidPlayer.parentNode.removeChild(vpaidPlayer);
}
//complete in async manner. Sometimes when shutdown too soon, video does not start playback
setTimeout(function() {
player.ads.endLinearAdMode();
}, 0);
};
player.vast.enableSkipButton = function () {
if ((' ' + player.vast.skipButton.className + ' ').indexOf(' enabled ') === -1) {
player.vast.skipButton.className += " enabled";
player.vast.skipButton.innerHTML = localize("Skip");
}
};
player.vast.timeupdate = function(e) {
player.loadingSpinner.el().style.display = "none";
var timeLeft = Math.ceil(settings.skip - player.currentTime());
if(timeLeft > 0) {
var translation = localize('Skip in %num%...');
player.vast.skipButton.innerHTML = translation.replace('%num%', timeLeft);
} else {
player.vast.enableSkipButton();
}
};
player.vast.createSourceObjects = function (media_files) {
var sourcesByFormat = {}, i, j, tech;
var techOrder = player.options().techOrder;
for (i = 0, j = techOrder.length; i < j; i++) {
var techName = techOrder[i].charAt(0).toUpperCase() + techOrder[i].slice(1);
tech = window.videojs[techName];
// Check if the current tech is defined before continuing
if (!tech) {
continue;
}
// Check if the browser supports this technology
if (tech.isSupported()) {
// Loop through each source object
for (var a = 0, b = media_files.length; a < b; a++) {
var media_file = media_files[a];
var source = {type:media_file.mimeType, src:media_file.fileURL};
// Check if source can be played with this technology
if (tech.canPlaySource(source)) {
if (sourcesByFormat[techOrder[i]] === undefined) {
sourcesByFormat[techOrder[i]] = [];
}
sourcesByFormat[techOrder[i]].push({
type:media_file.mimeType,
src: media_file.fileURL,
width: media_file.width,
height: media_file.height
});
}
}
}
}
// Create sources in preferred format order
var sources = [];
for (j = 0; j < techOrder.length; j++) {
tech = techOrder[j];
if (sourcesByFormat[tech] !== undefined) {
for (i = 0; i < sourcesByFormat[tech].length; i++) {
sources.push(sourcesByFormat[tech][i]);
}
}
}
return sources;
};
//Find optimal available VPAID tech. Best match is javascript, otherwise last found will be returned
player.vast.findOptimalVPAIDTech = function(mediaFiles) {
var foundTech = null;
for (var i = 0; i < mediaFiles.length; i++) {
var mediaFile = mediaFiles[i];
if (mediaFile.apiFramework != "VPAID") {
continue;
}
if (mediaFile.mimeType == 'application/javascript') {
//bingo!
return mediaFile;
} else {
foundTech = mediaFile;
}
}
return foundTech;
};
player.vast.loadVPAIDResource = function(mediaFile, callback) {
if (mediaFile.mimeType != "application/javascript") {
throw new Error("Loading not javascript vpaid ads is not supported");
}
vpaidIFrame = document.createElement('iframe');
vpaidIFrame.style.display = 'none';
vpaidIFrame.onload = function() {
var iframeDoc = vpaidIFrame.contentDocument;
//Credos http://stackoverflow.com/a/950146/51966
// Adding the script tag to the head as suggested before
var head = iframeDoc.getElementsByTagName('head')[0];
var script = iframeDoc.createElement('script');
script.type = 'text/javascript';
script.src = mediaFile.fileURL;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = script.onload = function() {
if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
if (vpaidIFrame.contentWindow.getVPAIDAd === undefined) {
console.log("Unable to load script or script do not have getVPAIDAd method");
return;
}
callback(vpaidIFrame.contentWindow.getVPAIDAd());
}
};
head.appendChild(script);
};
document.body.appendChild(vpaidIFrame);
};
player.vast.initSimpleVAST = function(ad) {
var errorOccurred = false,
canplayFn = function() {
this.vastTracker.load();
},
timeupdateFn = function() {
if (isNaN(this.vastTracker.assetDuration)) {
this.vastTracker.assetDuration = this.duration();
}
this.vastTracker.setProgress(this.currentTime());
},
playFn = function() {
this.vastTracker.setPaused(false);
},
pauseFn = function() {
this.vastTracker.setPaused(true);
},
errorFn = function() {
// Inform ad server we couldn't play the media file for this ad
vast.util.track(ad.errorURLTemplates, {ERRORCODE: 405});
errorOccurred = true;
player.trigger('ended');
};
player.on('canplay', canplayFn);
player.on('timeupdate', timeupdateFn);
player.on('play', playFn);
player.on('pause', pauseFn);
player.on('error', errorFn);
player.one('ended', function() {
player.off('canplay', canplayFn);
player.off('timeupdate', timeupdateFn);
player.off('play', playFn);
player.off('pause', pauseFn);
player.off('error', errorFn);
if (!errorOccurred) {
this.vastTracker.complete();
}
});
};
player.vast.initVPAID = function(vpaidTech, cb) {
player.vast.loadVPAIDResource(vpaidTech, function(vpaid) {
vpaidObj = vpaid;
if (vpaid.handshakeVersion('2.0') != '2.0') {
throw new Error("Versions different to 2.0 are not supported");
}
var root = player.el();
var pref = {
videoSlotCanAutoPlay: true,
slot: root
};
if (/iphone|ipad|android/gi.test(navigator.userAgent)) {
pref.videoSlot = player.el().querySelector('.vjs-tech');
if (pref.videoSlot.tagName != 'video') { //might be using non-default source, fallback to custom video slot
pref.videoSlot = undefined;
}
}
if (!pref.videoSlot) {
vpaidPlayer = document.createElement('video');
vpaidPlayer.className = 'vast-blocker';
root.appendChild(vpaidPlayer);
pref.videoSlot = vpaidPlayer;
}
player.on('resize', function() {
vpaid.resizeAd(player.width(), player.height(), settings.viewMode);
});
player.on('fullscreenchange', function() {
if (player.isFullScreen()) {
vpaid.resizeAd(0, 0, 'fullscreen');
} else {
vpaid.resizeAd(player.width, player.width, settings.viewMode);
}
});
function setTrackerDuration() {
if (vpaidObj.getAdDuration) {
var duration = vpaidObj.getAdDuration();
if (duration > 0) {
player.vastTracker.setDuration(duration);
}
}
}
player.vast.onVPAID('AdError', function() {
player.vast.tearDown();
});
player.vast.oneVPAID('AdLoaded', function() {
if (cb) {
cb(vpaid);
}
setTrackerDuration();
});
player.vast.oneVPAID('AdStopped', function() {
player.vast.tearDown();
});
player.vast.onVPAID('AdDurationChange', function() {
setTrackerDuration();
});
player.vast.onVPAID('AdRemainingTimeChange', function() {
setTrackerDuration();
});
player.vast.oneVPAID('AdSkipped', function() {
player.vastTracker.skip();
player.vast.tearDown();
});
player.vast.oneVPAID('AdStarted', function() {
player.ads.startLinearAdMode();
player.vastTracker.load();
});
player.vast.onVPAID('AdVolumeChange', function() {
player.vastTracker.setMuted(vpaidObj.getAdVolume() === 0);
player.setVolume(vpaidObj.getAdVolume());
});
player.vast.onVPAID('AdImpression', function() {
//TODO
});
player.vast.onVPAID('AdVideoStart', function() {
player.vastTracker.setProgress(0);
});
player.vast.onVPAID('AdVideoFirstQuartile', function() {
var emulatedFirstQuartile = Math.round(25 * vpaidObj.getAdDuration()) / 100;
player.vastTracker.setProgress(emulatedFirstQuartile);
});
player.vast.onVPAID('AdVideoMidpoint', function() {
var emulatedMidpoint = Math.round(50 * vpaidObj.getAdDuration()) / 100;
player.vastTracker.setProgress(emulatedMidpoint);
});
player.vast.onVPAID('AdVideoThirdQuartile', function() {
var emulatedThirdQuartile = Math.round(75 * vpaidObj.getAdDuration()) / 100;
player.vastTracker.setProgress(emulatedThirdQuartile);
});
player.vast.onVPAID('AdVideoComplete', function() {
player.vastTracker.setProgress(vpaidObj.getAdDuration());
});
player.vast.onVPAID('AdClickThru', function(url, id, playerHandles) {
player.vastTracker.click();
if (playerHandles) {
if (!url) {
url = player.vast.getClickThrough();
}
//TODO open url
}
});
player.vast.onVPAID('AdUserAcceptInvitation', function() {
//TODO implement in vast client
player.vastTracker.acceptInvitation();
});
player.vast.onVPAID('AdUserClose', function() {
player.vastTracker.close();
});
player.vast.onVPAID('AdPaused', function() {
player.vastTracker.setPaused(true);
});
player.vast.onVPAID('AdPlaying', function() {
player.vastTracker.setPaused(false);
});
player.vast.onVPAID('AdSkippableStateChange', function() {
if (vpaidObj.getAdSkippableState()) {
player.vast.createSkipButton();
player.vast.enableSkipButton();
} else if (player.vast.skipButton) {
player.vast.skipButton.parentNode.removeChild(player.vast.skipButton);
}
});
//TODO add creativeData
vpaid.initAd(player.width(), player.height(), settings.viewMode, settings.bitrate, {}, pref);
});
};
player.vast.createVPAIDControls = function() {
vpaidSeeker = document.createElement('div');
vpaidSeeker.className = 'vast-ad-control';
vpaidSeeker.innerHTML = '<span class="vast-advertisement">' + localize('Advertisement') + ' <span class="vast-ad-left"></span></span><div class="vast-progress-holder"><div class="vjs-play-progress"></div></div>';
player.el().appendChild(vpaidSeeker, player.el().childNodes[0]);
};
player.vast.removeVPAIDControls = function() {
if (vpaidSeeker) {
vpaidSeeker.parentNode.removeChild(vpaidSeeker);
}
};
player.vast.updateSeeker = function() {
if (!vpaidObj && vpaidTrackInterval != -1) { //might be it was shutdown earlier than first seek could appear. Silently remove itself
clearInterval(vpaidTrackInterval);
vpaidTrackInterval = -1;
return;
}
var remaining = vpaidObj.getAdRemainingTime();
if (remaining < 0) {
return;
}
var total = vpaidObj.getAdDuration();
if (total < 0) {
return;
}
var progress = vpaidSeeker.querySelector('.vjs-play-progress');
progress.style.width = ((total - remaining) / total * 100) + '%';
//taken from videojs-iva
var remainingMinutes = Math.floor(remaining / 60);
var remainingSeconds = Math.floor(remaining % 60);
if (remainingSeconds.toString().length < 2) {
remainingSeconds = '0' + remainingSeconds;
}
var remains = remainingMinutes + ':' + remainingSeconds;
progress.innerHTML = '<span class="vjs-control-text">' + remains + '</span>';
vpaidSeeker.querySelector('.vast-ad-left').innerHTML = remains;
};
player.vast.onVPAID = function(event, func) {
if (vpaidListeners[event] === undefined) {
vpaidListeners[event] = [];
}
vpaidListeners[event].push(func);
vpaidObj.subscribe(func, event);
};
player.vast.offVPAID = function(event, func) {
vpaidObj.unsubscribe(func, event);
if (vpaidListeners[event]) {
var listeners = vpaidListeners[event],
index = -1;
if (!Array.prototype.indexOf) {
for (var i = 0; i < listeners.length; i++) {
if (listeners[i] == func) {
index = i;
break;
}
}
} else {
index = listeners.indexOf(func);
}
if (index != -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
delete vpaidListeners[event];
}
}
};
player.vast.oneVPAID = function(event, func) {
var wrapper = function() {
player.vast.offVPAID(event, wrapper);
func();
};
player.vast.onVPAID(event, wrapper);
};
// make an ads request immediately so we're ready when the viewer
// hits "play"
if (player.currentSrc()) {
player.vast.getContent(settings.url);
}
};
vjs.plugin('vast', vastPlugin);
}(window.videojs, window.DMVAST));
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.DMVAST=e()}}(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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.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(_dereq_,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
} else {
throw TypeError('Uncaught, unspecified "error" event.');
}
return false;
}
}
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;
}
},{}],2:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTAd;
VASTAd = (function() {
function VASTAd() {
this.errorURLTemplates = [];
this.impressionURLTemplates = [];
this.creatives = [];
}
return VASTAd;
})();
module.exports = VASTAd;
},{}],3:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTClient, VASTParser, VASTUtil;
VASTParser = _dereq_('./parser.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTClient = (function() {
function VASTClient() {}
VASTClient.cappingFreeLunch = 0;
VASTClient.cappingMinimumTimeInterval = 0;
VASTClient.timeout = 0;
VASTClient.get = function(url, cb) {
var now;
now = +new Date();
if (this.totalCallsTimeout < now) {
this.totalCalls = 1;
this.totalCallsTimeout = now + (60 * 60 * 1000);
} else {
this.totalCalls++;
}
if (this.cappingFreeLunch >= this.totalCalls) {
cb(null);
return;
}
if (now - this.lastSuccessfullAd < this.cappingMinimumTimeInterval) {
cb(null);
return;
}
return VASTParser.parse(url, (function(_this) {
return function(response) {
return cb(response);
};
})(this));
};
(function() {
var defineProperty, storage;
storage = VASTUtil.storage;
defineProperty = Object.defineProperty;
['lastSuccessfullAd', 'totalCalls', 'totalCallsTimeout'].forEach(function(property) {
defineProperty(VASTClient, property, {
get: function() {
return storage.getItem(property);
},
set: function(value) {
return storage.setItem(property, value);
},
configurable: false,
enumerable: true
});
});
if (VASTClient.totalCalls == null) {
VASTClient.totalCalls = 0;
}
if (VASTClient.totalCallsTimeout == null) {
VASTClient.totalCallsTimeout = 0;
}
})();
return VASTClient;
})();
module.exports = VASTClient;
},{"./parser.coffee":8,"./util.coffee":14}],4:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTCompanionAd;
VASTCompanionAd = (function() {
function VASTCompanionAd() {
this.id = null;
this.width = 0;
this.height = 0;
this.type = null;
this.staticResource = null;
this.companionClickThroughURLTemplate = null;
this.trackingEvents = {};
}
return VASTCompanionAd;
})();
module.exports = VASTCompanionAd;
},{}],5:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTCreative, VASTCreativeCompanion, VASTCreativeLinear, VASTCreativeNonLinear,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
VASTCreative = (function() {
function VASTCreative() {
this.trackingEvents = {};
}
return VASTCreative;
})();
VASTCreativeLinear = (function(_super) {
__extends(VASTCreativeLinear, _super);
function VASTCreativeLinear() {
VASTCreativeLinear.__super__.constructor.apply(this, arguments);
this.type = "linear";
this.duration = 0;
this.skipDelay = null;
this.mediaFiles = [];
this.videoClickThroughURLTemplate = null;
this.videoClickTrackingURLTemplates = [];
}
return VASTCreativeLinear;
})(VASTCreative);
VASTCreativeNonLinear = (function(_super) {
__extends(VASTCreativeNonLinear, _super);
function VASTCreativeNonLinear() {
return VASTCreativeNonLinear.__super__.constructor.apply(this, arguments);
}
return VASTCreativeNonLinear;
})(VASTCreative);
VASTCreativeCompanion = (function(_super) {
__extends(VASTCreativeCompanion, _super);
function VASTCreativeCompanion() {
this.type = "companion";
this.variations = [];
this.videoClickTrackingURLTemplates = [];
}
return VASTCreativeCompanion;
})(VASTCreative);
module.exports = {
VASTCreativeLinear: VASTCreativeLinear,
VASTCreativeNonLinear: VASTCreativeNonLinear,
VASTCreativeCompanion: VASTCreativeCompanion
};
},{}],6:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
module.exports = {
client: _dereq_('./client.coffee'),
tracker: _dereq_('./tracker.coffee'),
parser: _dereq_('./parser.coffee'),
util: _dereq_('./util.coffee')
};
},{"./client.coffee":3,"./parser.coffee":8,"./tracker.coffee":10,"./util.coffee":14}],7:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTMediaFile;
VASTMediaFile = (function() {
function VASTMediaFile() {
this.fileURL = null;
this.deliveryType = "progressive";
this.mimeType = null;
this.codec = null;
this.bitrate = 0;
this.minBitrate = 0;
this.maxBitrate = 0;
this.width = 0;
this.height = 0;
this.apiFramework = null;
}
return VASTMediaFile;
})();
module.exports = VASTMediaFile;
},{}],8:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var URLHandler, VASTAd, VASTCompanionAd, VASTCreativeCompanion, VASTCreativeLinear, VASTMediaFile, VASTParser, VASTResponse, VASTUtil,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
URLHandler = _dereq_('./urlhandler.coffee');
VASTResponse = _dereq_('./response.coffee');
VASTAd = _dereq_('./ad.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTCreativeLinear = _dereq_('./creative.coffee').VASTCreativeLinear;
VASTCreativeCompanion = _dereq_('./creative.coffee').VASTCreativeCompanion;
VASTMediaFile = _dereq_('./mediafile.coffee');
VASTCompanionAd = _dereq_('./companionad.coffee');
VASTParser = (function() {
var URLTemplateFilters;
function VASTParser() {}
URLTemplateFilters = [];
VASTParser.addURLTemplateFilter = function(func) {
if (typeof func === 'function') {
URLTemplateFilters.push(func);
}
};
VASTParser.removeURLTemplateFilter = function() {
return URLTemplateFilters.pop();
};
VASTParser.countURLTemplateFilters = function() {
return URLTemplateFilters.length;
};
VASTParser.clearUrlTemplateFilters = function() {
return URLTemplateFilters = [];
};
VASTParser.parse = function(url, cb) {
return this._parse(url, null, function(err, response) {
return cb(response);
});
};
VASTParser._parse = function(url, parentURLs, cb) {
var filter, _i, _len;
for (_i = 0, _len = URLTemplateFilters.length; _i < _len; _i++) {
filter = URLTemplateFilters[_i];
url = filter(url);
}
if (parentURLs == null) {
parentURLs = [];
}
parentURLs.push(url);
return URLHandler.get(url, (function(_this) {
return function(err, xml) {
var ad, complete, loopIndex, node, response, _j, _k, _len1, _len2, _ref, _ref1;
if (err != null) {
return cb(err);
}
response = new VASTResponse();
if (!(((xml != null ? xml.documentElement : void 0) != null) && xml.documentElement.nodeName === "VAST")) {
return cb();
}
_ref = xml.documentElement.childNodes;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
node = _ref[_j];
if (node.nodeName === 'Error') {
response.errorURLTemplates.push(_this.parseNodeText(node));
}
}
_ref1 = xml.documentElement.childNodes;
for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
node = _ref1[_k];
if (node.nodeName === 'Ad') {
ad = _this.parseAdElement(node);
if (ad != null) {
response.ads.push(ad);
} else {
VASTUtil.track(response.errorURLTemplates, {
ERRORCODE: 101
});
}
}
}
complete = function() {
var _l, _len3, _ref2;
if (!response) {
return;
}
_ref2 = response.ads;
for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) {
ad = _ref2[_l];
if (ad.nextWrapperURL != null) {
return;
}
}
if (response.ads.length === 0) {
VASTUtil.track(response.errorURLTemplates, {
ERRORCODE: 303
});
response = null;
}
return cb(null, response);
};
loopIndex = response.ads.length;
while (loopIndex--) {
ad = response.ads[loopIndex];
if (ad.nextWrapperURL == null) {
continue;
}
(function(ad) {
var baseURL, _ref2;
if (parentURLs.length >= 10 || (_ref2 = ad.nextWrapperURL, __indexOf.call(parentURLs, _ref2) >= 0)) {
VASTUtil.track(ad.errorURLTemplates, {
ERRORCODE: 302
});
response.ads.splice(response.ads.indexOf(ad), 1);
complete();
return;
}
if (ad.nextWrapperURL.indexOf('://') === -1) {
baseURL = url.slice(0, url.lastIndexOf('/'));
ad.nextWrapperURL = "" + baseURL + "/" + ad.nextWrapperURL;
}
return _this._parse(ad.nextWrapperURL, parentURLs, function(err, wrappedResponse) {
var creative, eventName, index, wrappedAd, _base, _l, _len3, _len4, _len5, _len6, _m, _n, _o, _ref3, _ref4, _ref5, _ref6;
if (err != null) {
VASTUtil.track(ad.errorURLTemplates, {
ERRORCODE: 301
});
response.ads.splice(response.ads.indexOf(ad), 1);
} else if (wrappedResponse == null) {
VASTUtil.track(ad.errorURLTemplates, {
ERRORCODE: 303
});
response.ads.splice(response.ads.indexOf(ad), 1);
} else {
response.errorURLTemplates = response.errorURLTemplates.concat(wrappedResponse.errorURLTemplates);
index = response.ads.indexOf(ad);
response.ads.splice(index, 1);
_ref3 = wrappedResponse.ads;
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
wrappedAd = _ref3[_l];
wrappedAd.errorURLTemplates = ad.errorURLTemplates.concat(wrappedAd.errorURLTemplates);
wrappedAd.impressionURLTemplates = ad.impressionURLTemplates.concat(wrappedAd.impressionURLTemplates);
if (ad.trackingEvents != null) {
_ref4 = wrappedAd.creatives;
for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
creative = _ref4[_m];
if (creative.type === 'linear') {
_ref5 = Object.keys(ad.trackingEvents);
for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) {
eventName = _ref5[_n];
(_base = creative.trackingEvents)[eventName] || (_base[eventName] = []);
creative.trackingEvents[eventName] = creative.trackingEvents[eventName].concat(ad.trackingEvents[eventName]);
}
}
}
}
if (ad.videoClickTrackingURLTemplates != null) {
_ref6 = wrappedAd.creatives;
for (_o = 0, _len6 = _ref6.length; _o < _len6; _o++) {
creative = _ref6[_o];
if (creative.type === 'linear') {
creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat(ad.videoClickTrackingURLTemplates);
}
}
}
response.ads.splice(index, 0, wrappedAd);
}
}
delete ad.nextWrapperURL;
return complete();
});
})(ad);
}
return complete();
};
})(this));
};
VASTParser.childByName = function(node, name) {
var child, _i, _len, _ref;
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child.nodeName === name) {
return child;
}
}
};
VASTParser.childsByName = function(node, name) {
var child, childs, _i, _len, _ref;
childs = [];
_ref = node.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child.nodeName === name) {
childs.push(child);
}
}
return childs;
};
VASTParser.parseAdElement = function(adElement) {
var adTypeElement, _i, _len, _ref;
_ref = adElement.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
adTypeElement = _ref[_i];
if (adTypeElement.nodeName === "Wrapper") {
return this.parseWrapperElement(adTypeElement);
} else if (adTypeElement.nodeName === "InLine") {
return this.parseInLineElement(adTypeElement);
}
}
};
VASTParser.parseWrapperElement = function(wrapperElement) {
var ad, creative, wrapperCreativeElement, wrapperURLElement, _i, _len, _ref;
ad = this.parseInLineElement(wrapperElement);
wrapperURLElement = this.childByName(wrapperElement, "VASTAdTagURI");
if (wrapperURLElement != null) {
ad.nextWrapperURL = this.parseNodeText(wrapperURLElement);
}
wrapperCreativeElement = null;
_ref = ad.creatives;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
creative = _ref[_i];
if (creative.type === 'linear') {
wrapperCreativeElement = creative;
break;
}
}
if (wrapperCreativeElement != null) {
if (wrapperCreativeElement.trackingEvents != null) {
ad.trackingEvents = wrapperCreativeElement.trackingEvents;
}
if (wrapperCreativeElement.videoClickTrackingURLTemplates != null) {
ad.videoClickTrackingURLTemplates = wrapperCreativeElement.videoClickTrackingURLTemplates;
}
}
if (ad.nextWrapperURL != null) {
return ad;
}
};
VASTParser.parseInLineElement = function(inLineElement) {
var ad, creative, creativeElement, creativeTypeElement, node, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
ad = new VASTAd();
_ref = inLineElement.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
switch (node.nodeName) {
case "Error":
ad.errorURLTemplates.push(this.parseNodeText(node));
break;
case "Impression":
ad.impressionURLTemplates.push(this.parseNodeText(node));
break;
case "Creatives":
_ref1 = this.childsByName(node, "Creative");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
creativeElement = _ref1[_j];
_ref2 = creativeElement.childNodes;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
creativeTypeElement = _ref2[_k];
switch (creativeTypeElement.nodeName) {
case "Linear":
creative = this.parseCreativeLinearElement(creativeTypeElement);
if (creative) {
ad.creatives.push(creative);
}
break;
case "CompanionAds":
creative = this.parseCompanionAd(creativeTypeElement);
if (creative) {
ad.creatives.push(creative);
}
}
}
}
}
}
return ad;
};
VASTParser.parseCreativeLinearElement = function(creativeElement) {
var clickTrackingElement, creative, eventName, mediaFile, mediaFileElement, mediaFilesElement, percent, skipOffset, trackingElement, trackingEventsElement, trackingURLTemplate, videoClicksElement, _base, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4;
creative = new VASTCreativeLinear();
creative.duration = this.parseDuration(this.parseNodeText(this.childByName(creativeElement, "Duration")));
if (creative.duration === -1 && creativeElement.parentNode.parentNode.parentNode.nodeName !== 'Wrapper') {
return null;
}
skipOffset = creativeElement.getAttribute("skipoffset");
if (skipOffset == null) {
creative.skipDelay = null;
} else if (skipOffset.charAt(skipOffset.length - 1) === "%") {
percent = parseInt(skipOffset, 10);
creative.skipDelay = creative.duration * (percent / 100);
} else {
creative.skipDelay = this.parseDuration(skipOffset);
}
videoClicksElement = this.childByName(creativeElement, "VideoClicks");
if (videoClicksElement != null) {
creative.videoClickThroughURLTemplate = this.parseNodeText(this.childByName(videoClicksElement, "ClickThrough"));
_ref = this.childsByName(videoClicksElement, "ClickTracking");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
clickTrackingElement = _ref[_i];
creative.videoClickTrackingURLTemplates.push(this.parseNodeText(clickTrackingElement));
}
}
_ref1 = this.childsByName(creativeElement, "TrackingEvents");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
trackingEventsElement = _ref1[_j];
_ref2 = this.childsByName(trackingEventsElement, "Tracking");
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
trackingElement = _ref2[_k];
eventName = trackingElement.getAttribute("event");
trackingURLTemplate = this.parseNodeText(trackingElement);
if ((eventName != null) && (trackingURLTemplate != null)) {
if ((_base = creative.trackingEvents)[eventName] == null) {
_base[eventName] = [];
}
creative.trackingEvents[eventName].push(trackingURLTemplate);
}
}
}
_ref3 = this.childsByName(creativeElement, "MediaFiles");
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
mediaFilesElement = _ref3[_l];
_ref4 = this.childsByName(mediaFilesElement, "MediaFile");
for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
mediaFileElement = _ref4[_m];
mediaFile = new VASTMediaFile();
mediaFile.fileURL = this.parseNodeText(mediaFileElement);
mediaFile.deliveryType = mediaFileElement.getAttribute("delivery");
mediaFile.codec = mediaFileElement.getAttribute("codec");
mediaFile.mimeType = mediaFileElement.getAttribute("type");
mediaFile.apiFramework = mediaFileElement.getAttribute("apiFramework");
mediaFile.bitrate = parseInt(mediaFileElement.getAttribute("bitrate") || 0);
mediaFile.minBitrate = parseInt(mediaFileElement.getAttribute("minBitrate") || 0);
mediaFile.maxBitrate = parseInt(mediaFileElement.getAttribute("maxBitrate") || 0);
mediaFile.width = parseInt(mediaFileElement.getAttribute("width") || 0);
mediaFile.height = parseInt(mediaFileElement.getAttribute("height") || 0);
creative.mediaFiles.push(mediaFile);
}
}
return creative;
};
VASTParser.parseCompanionAd = function(creativeElement) {
var companionAd, companionResource, creative, eventName, staticElement, trackingElement, trackingEventsElement, trackingURLTemplate, _base, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
creative = new VASTCreativeCompanion();
_ref = this.childsByName(creativeElement, "Companion");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
companionResource = _ref[_i];
companionAd = new VASTCompanionAd();
companionAd.id = companionResource.getAttribute("id") || null;
companionAd.width = companionResource.getAttribute("width");
companionAd.height = companionResource.getAttribute("height");
_ref1 = this.childsByName(companionResource, "StaticResource");
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
staticElement = _ref1[_j];
companionAd.type = staticElement.getAttribute("creativeType") || 0;
companionAd.staticResource = this.parseNodeText(staticElement);
}
_ref2 = this.childsByName(companionResource, "TrackingEvents");
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
trackingEventsElement = _ref2[_k];
_ref3 = this.childsByName(trackingEventsElement, "Tracking");
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
trackingElement = _ref3[_l];
eventName = trackingElement.getAttribute("event");
trackingURLTemplate = this.parseNodeText(trackingElement);
if ((eventName != null) && (trackingURLTemplate != null)) {
if ((_base = companionAd.trackingEvents)[eventName] == null) {
_base[eventName] = [];
}
companionAd.trackingEvents[eventName].push(trackingURLTemplate);
}
}
}
companionAd.companionClickThroughURLTemplate = this.parseNodeText(this.childByName(companionResource, "CompanionClickThrough"));
creative.variations.push(companionAd);
}
return creative;
};
VASTParser.parseDuration = function(durationString) {
var durationComponents, hours, minutes, seconds, secondsAndMS;
if (!(durationString != null)) {
return -1;
}
durationComponents = durationString.split(":");
if (durationComponents.length !== 3) {
return -1;
}
secondsAndMS = durationComponents[2].split(".");
seconds = parseInt(secondsAndMS[0]);
if (secondsAndMS.length === 2) {
seconds += parseFloat("0." + secondsAndMS[1]);
}
minutes = parseInt(durationComponents[1] * 60);
hours = parseInt(durationComponents[0] * 60 * 60);
if (isNaN(hours || isNaN(minutes || isNaN(seconds || minutes > 60 * 60 || seconds > 60)))) {
return -1;
}
return hours + minutes + seconds;
};
VASTParser.parseNodeText = function(node) {
return node && (node.textContent || node.text);
};
return VASTParser;
})();
module.exports = VASTParser;
},{"./ad.coffee":2,"./companionad.coffee":4,"./creative.coffee":5,"./mediafile.coffee":7,"./response.coffee":9,"./urlhandler.coffee":11,"./util.coffee":14}],9:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTResponse;
VASTResponse = (function() {
function VASTResponse() {
this.ads = [];
this.errorURLTemplates = [];
}
return VASTResponse;
})();
module.exports = VASTResponse;
},{}],10:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var EventEmitter, VASTClient, VASTCreativeLinear, VASTTracker, VASTUtil,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
VASTClient = _dereq_('./client.coffee');
VASTUtil = _dereq_('./util.coffee');
VASTCreativeLinear = _dereq_('./creative.coffee').VASTCreativeLinear;
EventEmitter = _dereq_('events').EventEmitter;
VASTTracker = (function(_super) {
__extends(VASTTracker, _super);
function VASTTracker(ad, creative) {
var eventName, events, _ref;
this.ad = ad;
this.creative = creative;
this.muted = false;
this.impressed = false;
this.skipable = false;
this.skipDelayDefault = -1;
this.trackingEvents = {};
this.emitAlwaysEvents = ['creativeView', 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'resume', 'pause', 'rewind', 'skip', 'closeLinear', 'close'];
_ref = creative.trackingEvents;
for (eventName in _ref) {
events = _ref[eventName];
this.trackingEvents[eventName] = events.slice(0);
}
if (creative instanceof VASTCreativeLinear) {
this.setDuration(creative.duration);
this.skipDelay = creative.skipDelay;
this.linear = true;
this.clickThroughURLTemplate = creative.videoClickThroughURLTemplate;
this.clickTrackingURLTemplates = creative.videoClickTrackingURLTemplates;
} else {
this.skipDelay = -1;
this.linear = false;
}
this.on('start', function() {
VASTClient.lastSuccessfullAd = +new Date();
});
}
VASTTracker.prototype.setDuration = function(duration) {
this.assetDuration = duration;
return this.quartiles = {
'firstQuartile': Math.round(25 * this.assetDuration) / 100,
'midpoint': Math.round(50 * this.assetDuration) / 100,
'thirdQuartile': Math.round(75 * this.assetDuration) / 100
};
};
VASTTracker.prototype.setProgress = function(progress) {
var eventName, events, percent, quartile, skipDelay, time, _i, _len, _ref;
skipDelay = this.skipDelay === null ? this.skipDelayDefault : this.skipDelay;
if (skipDelay !== -1 && !this.skipable) {
if (skipDelay > progress) {
this.emit('skip-countdown', skipDelay - progress);
} else {
this.skipable = true;
this.emit('skip-countdown', 0);
}
}
if (this.linear && this.assetDuration > 0) {
events = [];
if (progress > 0) {
events.push("start");
percent = Math.round(progress / this.assetDuration * 100);
events.push("progress-" + percent + "%");
_ref = this.quartiles;
for (quartile in _ref) {
time = _ref[quartile];
if ((time <= progress && progress <= (time + 1))) {
events.push(quartile);
}
}
}
for (_i = 0, _len = events.length; _i < _len; _i++) {
eventName = events[_i];
this.track(eventName, true);
}
if (progress < this.progress) {
this.track("rewind");
}
}
return this.progress = progress;
};
VASTTracker.prototype.setMuted = function(muted) {
if (this.muted !== muted) {
this.track(muted ? "muted" : "unmuted");
}
return this.muted = muted;
};
VASTTracker.prototype.setPaused = function(paused) {
if (this.paused !== paused) {
this.track(paused ? "pause" : "resume");
}
return this.paused = paused;
};
VASTTracker.prototype.setFullscreen = function(fullscreen) {
if (this.fullscreen !== fullscreen) {
this.track(fullscreen ? "fullscreen" : "exitFullscreen");
}
return this.fullscreen = fullscreen;
};
VASTTracker.prototype.setSkipDelay = function(duration) {
if (typeof duration === 'number') {
return this.skipDelay = duration;
}
};
VASTTracker.prototype.load = function() {
if (!this.impressed) {
this.impressed = true;
this.trackURLs(this.ad.impressionURLTemplates);
return this.track("creativeView");
}
};
VASTTracker.prototype.errorWithCode = function(errorCode) {
return this.trackURLs(this.ad.errorURLTemplates, {
ERRORCODE: errorCode
});
};
VASTTracker.prototype.complete = function() {
return this.track("complete");
};
VASTTracker.prototype.stop = function() {
return this.track(this.linear ? "closeLinear" : "close");
};
VASTTracker.prototype.skip = function() {
this.track("skip");
return this.trackingEvents = [];
};
VASTTracker.prototype.click = function() {
var clickThroughURL, variables, _ref;
if ((_ref = this.clickTrackingURLTemplates) != null ? _ref.length : void 0) {
this.trackURLs(this.clickTrackingURLTemplates);
}
if (this.clickThroughURLTemplate != null) {
if (this.linear) {
variables = {
CONTENTPLAYHEAD: this.progressFormated()
};
}
clickThroughURL = VASTUtil.resolveURLTemplates([this.clickThroughURLTemplate], variables)[0];
return this.emit("clickthrough", clickThroughURL);
}
};
VASTTracker.prototype.track = function(eventName, once) {
var idx, trackingURLTemplates;
if (once == null) {
once = false;
}
if (eventName === 'closeLinear' && ((this.trackingEvents[eventName] == null) && (this.trackingEvents['close'] != null))) {
eventName = 'close';
}
trackingURLTemplates = this.trackingEvents[eventName];
idx = this.emitAlwaysEvents.indexOf(eventName);
if (trackingURLTemplates != null) {
this.emit(eventName, '');
this.trackURLs(trackingURLTemplates);
} else if (idx !== -1) {
this.emit(eventName, '');
}
if (once === true) {
delete this.trackingEvents[eventName];
if (idx > -1) {
this.emitAlwaysEvents.splice(idx, 1);
}
}
};
VASTTracker.prototype.trackURLs = function(URLTemplates, variables) {
if (variables == null) {
variables = {};
}
if (this.linear) {
variables["CONTENTPLAYHEAD"] = this.progressFormated();
}
return VASTUtil.track(URLTemplates, variables);
};
VASTTracker.prototype.progressFormated = function() {
var h, m, ms, s, seconds;
seconds = parseInt(this.progress);
h = seconds / (60 * 60);
if (h.length < 2) {
h = "0" + h;
}
m = seconds / 60 % 60;
if (m.length < 2) {
m = "0" + m;
}
s = seconds % 60;
if (s.length < 2) {
s = "0" + m;
}
ms = parseInt((this.progress - seconds) * 100);
return "" + h + ":" + m + ":" + s + "." + ms;
};
return VASTTracker;
})(EventEmitter);
module.exports = VASTTracker;
},{"./client.coffee":3,"./creative.coffee":5,"./util.coffee":14,"events":1}],11:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var URLHandler, flash, xhr;
xhr = _dereq_('./urlhandlers/xmlhttprequest.coffee');
flash = _dereq_('./urlhandlers/flash.coffee');
URLHandler = (function() {
function URLHandler() {}
URLHandler.get = function(url, cb) {
if (typeof window === "undefined" || window === null) {
return _dereq_('./urlhandlers/' + 'node.coffee').get(url, cb);
} else if (xhr.supported()) {
return xhr.get(url, cb);
} else if (flash.supported()) {
return flash.get(url, cb);
} else {
return cb();
}
};
return URLHandler;
})();
module.exports = URLHandler;
},{"./urlhandlers/flash.coffee":12,"./urlhandlers/xmlhttprequest.coffee":13}],12:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var FlashURLHandler;
FlashURLHandler = (function() {
function FlashURLHandler() {}
FlashURLHandler.xdr = function() {
var xdr;
if (window.XDomainRequest) {
xdr = new XDomainRequest();
}
return xdr;
};
FlashURLHandler.supported = function() {
return !!this.xdr();
};
FlashURLHandler.get = function(url, cb) {
var xdr, xmlDocument;
if (xmlDocument = typeof window.ActiveXObject === "function" ? new window.ActiveXObject("Microsoft.XMLDOM") : void 0) {
xmlDocument.async = false;
} else {
return cb();
}
xdr = this.xdr();
xdr.open('GET', url);
xdr.send();
return xdr.onload = function() {
xmlDocument.loadXML(xdr.responseText);
return cb(null, xmlDocument);
};
};
return FlashURLHandler;
})();
module.exports = FlashURLHandler;
},{}],13:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var XHRURLHandler;
XHRURLHandler = (function() {
function XHRURLHandler() {}
XHRURLHandler.xhr = function() {
var xhr;
xhr = new window.XMLHttpRequest();
if ('withCredentials' in xhr) {
return xhr;
}
};
XHRURLHandler.supported = function() {
return !!this.xhr();
};
XHRURLHandler.get = function(url, cb) {
var xhr;
xhr = this.xhr();
xhr.open('GET', url);
xhr.send();
return xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
return cb(null, xhr.responseXML);
}
};
};
return XHRURLHandler;
})();
module.exports = XHRURLHandler;
},{}],14:[function(_dereq_,module,exports){
// Generated by CoffeeScript 1.7.1
var VASTUtil;
VASTUtil = (function() {
function VASTUtil() {}
VASTUtil.track = function(URLTemplates, variables) {
var URL, URLs, i, _i, _len, _results;
URLs = this.resolveURLTemplates(URLTemplates, variables);
_results = [];
for (_i = 0, _len = URLs.length; _i < _len; _i++) {
URL = URLs[_i];
if (typeof window !== "undefined" && window !== null) {
i = new Image();
_results.push(i.src = URL);
} else {
}
}
return _results;
};
VASTUtil.resolveURLTemplates = function(URLTemplates, variables) {
var URLTemplate, URLs, key, macro1, macro2, resolveURL, value, _i, _len;
URLs = [];
if (variables == null) {
variables = {};
}
if (!("CACHEBUSTING" in variables)) {
variables["CACHEBUSTING"] = Math.round(Math.random() * 1.0e+10);
}
variables["random"] = variables["CACHEBUSTING"];
for (_i = 0, _len = URLTemplates.length; _i < _len; _i++) {
URLTemplate = URLTemplates[_i];
resolveURL = URLTemplate;
if (!resolveURL) {
continue;
}
for (key in variables) {
value = variables[key];
macro1 = "[" + key + "]";
macro2 = "%%" + key + "%%";
resolveURL = resolveURL.replace(macro1, value);
resolveURL = resolveURL.replace(macro2, value);
}
URLs.push(resolveURL);
}
return URLs;
};
VASTUtil.storage = (function() {
var data, isDisabled, storage, storageError;
try {
storage = typeof window !== "undefined" && window !== null ? window.localStorage || window.sessionStorage : null;
} catch (_error) {
storageError = _error;
storage = null;
}
isDisabled = function(store) {
var e, testValue;
try {
testValue = '__VASTUtil__';
store.setItem(testValue, testValue);
if (store.getItem(testValue) !== testValue) {
return true;
}
} catch (_error) {
e = _error;
return true;
}
return false;
};
if ((storage == null) || isDisabled(storage)) {
data = {};
storage = {
length: 0,
getItem: function(key) {
return data[key];
},
setItem: function(key, value) {
data[key] = value;
this.length = Object.keys(data).length;
},
removeItem: function(key) {
delete data[key];
this.length = Object.keys(data).length;
},
clear: function() {
data = {};
this.length = 0;
}
};
}
return storage;
})();
return VASTUtil;
})();
module.exports = VASTUtil;
},{}]},{},[6])
(6)
});
/**
* Basic Ad support plugin for video.js.
*
* Common code to support ad integrations.
*/
(function(window, document, vjs, undefined) {
"use strict";
var
/**
* Copies properties from one or more objects onto an original.
*/
extend = function(obj /*, arg1, arg2, ... */) {
var arg, i, k;
for (i=1; i<arguments.length; i++) {
arg = arguments[i];
for (k in arg) {
if (arg.hasOwnProperty(k)) {
obj[k] = arg[k];
}
}
}
return obj;
},
/**
* Add a handler for multiple listeners to an object that supports addEventListener() or on().
*
* @param {object} obj The object to which the handler will be assigned.
* @param {mixed} events A string, array of strings, or hash of string/callback pairs.
* @param {function} callback Invoked when specified events occur, if events param is not a hash.
*
* @return {object} obj The object passed in.
*/
on = function(obj, events, handler) {
var
type = Object.prototype.toString.call(events),
register = function(obj, event, handler) {
if (obj.addEventListener) {
obj.addEventListener(event, handler);
} else if (obj.on) {
obj.on(event, handler);
} else if (obj.attachEvent) {
obj.attachEvent('on' + event, handler);
} else {
throw new Error('object has no mechanism for adding event listeners');
}
},
i,
ii;
switch (type) {
case '[object String]':
register(obj, events, handler);
break;
case '[object Array]':
for (i = 0, ii = events.length; i<ii; i++) {
register(obj, events[i], handler);
}
break;
case '[object Object]':
for (i in events) {
if (events.hasOwnProperty(i)) {
register(obj, i, events[i]);
}
}
break;
default:
throw new Error('Unrecognized events parameter type: ' + type);
}
return obj;
},
/**
* Runs the callback at the next available opportunity.
* @see https://developer.mozilla.org/en-US/docs/Web/API/window.setImmediate
*/
setImmediate = function(callback) {
return (
window.setImmediate ||
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.setTimeout
)(callback, 0);
},
/**
* Clears a callback previously registered with `setImmediate`.
* @param {id} id The identifier of the callback to abort
*/
clearImmediate = function(id) {
return (window.clearImmediate ||
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.clearTimeout)(id);
},
/**
* If ads are not playing, pauses the player at the next available
* opportunity. Has no effect if ads have started. This function is necessary
* because pausing a video element while processing a `play` event on iOS can
* cause the video element to continuously toggle between playing and paused
* states.
*
* @param {object} player The video player
*/
cancelContentPlay = function(player) {
if (player.ads.cancelPlayTimeout) {
// another cancellation is already in flight, so do nothing
return;
}
player.ads.cancelPlayTimeout = setImmediate(function() {
// deregister the cancel timeout so subsequent cancels are scheduled
player.ads.cancelPlayTimeout = null;
if (!player.paused()) {
player.pause();
}
});
},
/**
* Returns an object that captures the portions of player state relevant to
* video playback. The result of this function can be passed to
* restorePlayerSnapshot with a player to return the player to the state it
* was in when this function was invoked.
* @param {object} player The videojs player object
*/
getPlayerSnapshot = function(player) {
var
tech = player.el().querySelector('.vjs-tech'),
snapshot = {
src: player.currentSrc(),
currentTime: player.currentTime(),
type: player.currentType()
};
if (tech) {
snapshot.nativePoster = tech.poster;
}
return snapshot;
},
removeClass = function(element, className) {
var
classes = element.className.split(/\s+/),
i = classes.length,
newClasses = [];
while (i--) {
if (classes[i] !== className) {
newClasses.push(classes[i]);
}
}
element.className = newClasses.join(' ');
},
/**
* Attempts to modify the specified player so that its state is equivalent to
* the state of the snapshot.
* @param {object} snapshot - the player state to apply
*/
restorePlayerSnapshot = function(player, snapshot) {
var
// the playback tech
tech = player.el().querySelector('.vjs-tech'),
// the number of remaining attempts to restore the snapshot
attempts = 20,
// finish restoring the playback state
resume = function() {
player.currentTime(snapshot.currentTime);
//If this wasn't a postroll resume
if (!player.ended()) {
player.play();
}
},
// determine if the video element has loaded enough of the snapshot source
// to be ready to apply the rest of the state
tryToResume = function() {
if (tech.seekable === undefined) {
// if the tech doesn't expose the seekable time ranges, try to
// resume playback immediately
resume();
return;
}
if (tech.seekable.length > 0) {
// if some period of the video is seekable, resume playback
resume();
return;
}
// delay a bit and then check again unless we're out of attempts
if (attempts--) {
setTimeout(tryToResume, 50);
}
},
// whether the video element has been modified since the
// snapshot was taken
srcChanged;
if (snapshot.nativePoster) {
tech.poster = snapshot.nativePoster;
}
// Determine whether the player needs to be restored to its state
// before ad playback began. With a custom ad display or burned-in
// ads, the content player state hasn't been modified and so no
// restoration is required
if (player.src()) {
// the player was in src attribute mode before the ad and the
// src attribute has not been modified, no restoration is required
// to resume playback
srcChanged = player.src() !== snapshot.src;
} else {
// the player was configured through source element children
// and the currentSrc hasn't changed, no restoration is required
// to resume playback
srcChanged = player.currentSrc() !== snapshot.src;
}
if (srcChanged) {
// if the src changed for ad playback, reset it
player.src({ src: snapshot.src, type: snapshot.type });
// safari requires a call to `load` to pick up a changed source
player.load();
// and then resume from the snapshots time once the original src has loaded
player.one('loadedmetadata', tryToResume);
} else if (!player.ended()) {
// the src didn't change and this wasn't a postroll
// just resume playback at the current time.
player.play();
}
},
/**
* Remove the poster attribute from the video element tech, if present. When
* reusing a video element for multiple videos, the poster image will briefly
* reappear while the new source loads. Removing the attribute ahead of time
* prevents the poster from showing up between videos.
* @param {object} player The videojs player object
*/
removeNativePoster = function(player) {
var tech = player.el().querySelector('.vjs-tech');
if (tech) {
tech.removeAttribute('poster');
}
},
// ---------------------------------------------------------------------------
// Ad Framework
// ---------------------------------------------------------------------------
// default framework settings
defaults = {
// maximum amount of time in ms to wait to receive `adsready` from the ad
// implementation after play has been requested. Ad implementations are
// expected to load any dynamic libraries and make any requests to determine
// ad policies for a video during this time.
timeout: 5000,
// maximum amount of time in ms to wait for the ad implementation to start
// linear ad mode after `readyforpreroll` has fired. This is in addition to
// the standard timeout.
prerollTimeout: 100,
// when truthy, instructs the plugin to output additional information about
// plugin state to the video.js log. On most devices, the video.js log is
// the same as the developer console.
debug: false
},
adFramework = function(options) {
var
player = this,
// merge options and defaults
settings = extend({}, defaults, options || {}),
fsmHandler;
// replace the ad initializer with the ad namespace
player.ads = {
state: 'content-set',
startLinearAdMode: function() {
player.trigger('adstart');
},
endLinearAdMode: function() {
player.trigger('adend');
}
};
fsmHandler = function(event) {
// Ad Playback State Machine
var
fsm = {
'content-set': {
events: {
'adsready': function() {
this.state = 'ads-ready';
},
'play': function() {
this.state = 'ads-ready?';
cancelContentPlay(player);
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
}
}
},
'ads-ready': {
events: {
'play': function() {
this.state = 'preroll?';
cancelContentPlay(player);
}
}
},
'preroll?': {
enter: function() {
// change class to show that we're waiting on ads
player.el().className += ' vjs-ad-loading';
// schedule an adtimeout event to fire if we waited too long
player.ads.timeout = window.setTimeout(function() {
player.trigger('adtimeout');
}, settings.prerollTimeout);
// signal to ad plugin that it's their opportunity to play a preroll
player.trigger('readyforpreroll');
},
leave: function() {
window.clearTimeout(player.ads.timeout);
clearImmediate(player.ads.cancelPlayTimeout);
player.ads.cancelPlayTimeout = null;
removeClass(player.el(), 'vjs-ad-loading');
},
events: {
'play': function() {
cancelContentPlay(player);
},
'adstart': function() {
this.state = 'ad-playback';
player.el().className += ' vjs-ad-playing';
},
'adtimeout': function() {
this.state = 'content-playback';
player.play();
}
}
},
'ads-ready?': {
enter: function() {
player.el().className += ' vjs-ad-loading';
player.ads.timeout = window.setTimeout(function() {
player.trigger('adtimeout');
}, settings.timeout);
},
leave: function() {
window.clearTimeout(player.ads.timeout);
removeClass(player.el(), 'vjs-ad-loading');
},
events: {
'play': function() {
cancelContentPlay(player);
},
'adsready': function() {
this.state = 'preroll?';
},
'adtimeout': function() {
this.state = 'ad-timeout-playback';
}
}
},
'ad-timeout-playback': {
events: {
'adsready': function() {
if (player.paused()) {
this.state = 'ads-ready';
} else {
this.state = 'preroll?';
}
},
'contentupdate': function() {
if (player.paused()) {
this.state = 'content-set';
} else {
this.state = 'ads-ready?';
}
}
}
},
'ad-playback': {
enter: function() {
// capture current player state snapshot (playing, currentTime, src)
this.snapshot = getPlayerSnapshot(player);
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
},
leave: function() {
removeClass(player.el(), 'vjs-ad-playing');
restorePlayerSnapshot(player, this.snapshot);
},
events: {
'adend': function() {
this.state = 'content-playback';
}
}
},
'content-playback': {
events: {
'adstart': function() {
this.state = 'ad-playback';
player.el().className += ' vjs-ad-playing';
// remove the poster so it doesn't flash between videos
removeNativePoster(player);
},
'contentupdate': function() {
if (player.paused()) {
this.state = 'content-set';
} else {
this.state = 'ads-ready?';
}
}
}
}
};
(function(state) {
var noop = function() {};
// process the current event with a noop default handler
(fsm[state].events[event.type] || noop).apply(player.ads);
// execute leave/enter callbacks if present
if (state !== player.ads.state) {
(fsm[state].leave || noop).apply(player.ads);
(fsm[player.ads.state].enter || noop).apply(player.ads);
if (settings.debug) {
videojs.log('ads', state + ' -> ' + player.ads.state);
}
}
})(player.ads.state);
};
// register for the events we're interested in
on(player, vjs.Html5.Events.concat([
// events emitted by ad plugin
'adtimeout',
'contentupdate',
// events emitted by third party ad implementors
'adsready',
'adstart', // startLinearAdMode()
'adend' // endLinearAdMode()
]), fsmHandler);
// keep track of the current content source
// if you want to change the src of the video without triggering
// the ad workflow to restart, you can update this variable before
// modifying the player's source
player.ads.contentSrc = player.currentSrc();
// implement 'contentupdate' event.
(function(){
var
// check if a new src has been set, if so, trigger contentupdate
checkSrc = function() {
var src;
if (player.ads.state !== 'ad-playback') {
src = player.currentSrc();
if (src !== player.ads.contentSrc) {
player.trigger({
type: 'contentupdate',
oldValue: player.ads.contentSrc,
newValue: src
});
player.ads.contentSrc = src;
}
}
};
// loadstart reliably indicates a new src has been set
player.on('loadstart', checkSrc);
// check immediately in case we missed the loadstart
setImmediate(checkSrc);
})();
// kick off the fsm
if (!player.paused()) {
// simulate a play event if we're autoplaying
fsmHandler({type:'play'});
}
};
// register the ad plugin framework
vjs.plugin('ads', adFramework);
})(window, document, videojs);
<VAST version="2.0">
<Ad id="preroll-1">
<InLine>
<AdSystem>2.0</AdSystem>
<AdTitle>5773100</AdTitle>
<Creatives>
<Creative>
<Linear>
<Duration>00:00:01</Duration>
<MediaFiles>
<MediaFile height="270" width="370" type="application/x-shockwave-flash">
<![CDATA[
http://static.scanscout.com/ads/vpaidad3.swf?adData=http%3A//app.scanscout.com/ssframework/adStreamJSController.xml%3Fa%3Dgetadscheduleforcontent%26PI%3D567%26scheduleVersion%3Dnull%26HI%3D567|preroll|7496075541100999745%26AI%3D0
]]>
</MediaFile>
</MediaFiles>
</Linear>
</Creative>
<Creative>
<CompanionAds>
<Companion height="250" width="300" id="573242">
<HTMLResource>
<![CDATA[
<A onClick="var i= new Image(1,1); i.src='http://app.scanscout.com/ssframework/log/log.png?a=logitemaction&RI=573242&CbC=1&CbF=true&EC=0&RC=0&SmC=2&CbM=1.0E-5&VI=44cfc3b2382300cb751ba129fe51f46a&admode=preroll&PRI=7496075541100999745&RprC=5&ADsn=20&VcaI=192,197&RrC=1&VgI=44cfc3b2382300cb751ba129fe51f46a&AVI=142&Ust=ma&Uctry=us&CI=1247549&AC=4&PI=567&Udma=506&ADI=5773100&VclF=true';" HREF="http://vaseline.com" target="_blank"> <IMG SRC="http://media.scanscout.com/ads/vaseline300x250Companion.jpg" BORDER=0 WIDTH=300 HEIGHT=250 ALT="Click Here"> </A> <img src="http://app.scanscout.com/ssframework/log/log.png?a=logitemaction&RI=573242&CbC=1&CbF=true&EC=1&RC=0&SmC=2&CbM=1.0E-5&VI=44cfc3b2382300cb751ba129fe51f46a&admode=preroll&PRI=7496075541100999745&RprC=5&ADsn=20&VcaI=192,197&RrC=1&VgI=44cfc3b2382300cb751ba129fe51f46a&AVI=142&Ust=ma&Uctry=us&CI=1247549&AC=4&PI=567&Udma=506&ADI=5773100&VclF=true" height="1" width="1">
]]>
</HTMLResource>
</Companion>
</CompanionAds>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>
<?xml version="1.0" encoding="UTF-8"?>
<VAST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vast_2.0.1-creativeView.xsd"
version="2.0">
<Ad id="overlay-1">
<InLine>
<AdSystem>2.0</AdSystem>
<AdTitle>VPAID Example</AdTitle>
<!-- <Impression><![CDATA[http://impression_url_goes_here]]</Impression> -->
<Creatives>
<Creative>
<NonLinearAds>
<NonLinear apiFramework="VPAID" id="overlay-1" width="480" height="200" >
<StaticResource creativeType="application/x-shockwave-flash">
<![CDATA[http://admin.brightcove.com/vpaid/Example.swf]]></StaticResource>
</NonLinear>
</NonLinearAds>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>