flowplayer
Version:
The HTML5 video player for the web
1,350 lines (1,107 loc) • 368 kB
JavaScript
/*!
Flowplayer v7.0.4 (2017-04-28) | flowplayer.org/license
*/
/*! (C) WebReflection Mit Style License */
(function(e){function g(e,t,n,r){for(var i,s=n.slice(),o=w(t,e),u=0,a=s.length;u<a;u++){handler=s[u],typeof handler=="object"&&typeof handler.handleEvent=="function"?handler.handleEvent(o):handler.call(e,o);if(o.stoppedImmediatePropagation)break}return i=!o.stoppedPropagation,r&&i&&e.parentNode?e.parentNode.dispatchEvent(o):!o.defaultPrevented}function y(e,t){return{configurable:!0,get:e,set:t}}function b(e,t,n){var r=f(t||e,n);u(e,"textContent",y(function(){return r.get.call(this)},function(e){r.set.call(this,e)}))}function w(e,t){return e.currentTarget=t,e.eventPhase=e.target===e.currentTarget?2:3,e}function E(e,t){var n=e.length;while(n--&&e[n]!==t);return n}function S(){if(this.tagName==="BR")return"\n";var e=this.firstChild,t=[];while(e)e.nodeType!==8&&e.nodeType!==7&&t.push(e.textContent),e=e.nextSibling;return t.join("")}function x(e){return e.nodeType!==9&&document.documentElement.contains(e)}function T(e){!n&&d.test(document.readyState)&&(n=!n,document.detachEvent(r,T),e=document.createEvent("Event"),e.initEvent(i,!0,!0),document.dispatchEvent(e))}function N(e){var t;while(t=this.lastChild)this.removeChild(t);e!=null&&this.appendChild(document.createTextNode(e))}function C(t,n){return n||(n=e.event),n.target||(n.target=n.srcElement||n.fromElement||document),n.timeStamp||(n.timeStamp=(new Date).getTime()),n}if(document.createEvent)return;var t=!0,n=!1,r="onreadystatechange",i="DOMContentLoaded",s="__IE8__"+Math.random(),o=e.Object,u=o.defineProperty||function(e,t,n){e[t]=n.value},a=o.defineProperties||function(t,n){for(var r in n)if(l.call(n,r))try{u(t,r,n[r])}catch(i){e.console&&console.log(r+" failed on object:",t,i.message)}},f=o.getOwnPropertyDescriptor,l=o.prototype.hasOwnProperty,c=e.Element.prototype,h=e.Text.prototype,p=/^[a-z]+$/,d=/loaded|complete/,v={},m=document.createElement("div");b(e.HTMLCommentElement.prototype,c,"nodeValue"),b(e.HTMLScriptElement.prototype,null,"text"),b(h,null,"nodeValue"),b(e.HTMLTitleElement.prototype,null,"text"),u(e.HTMLStyleElement.prototype,"textContent",function(e){return y(function(){return e.get.call(this.styleSheet)},function(t){e.set.call(this.styleSheet,t)})}(f(e.CSSStyleSheet.prototype,"cssText"))),a(c,{textContent:{get:S,set:N},firstElementChild:{get:function(){for(var e=this.childNodes||[],t=0,n=e.length;t<n;t++)if(e[t].nodeType==1)return e[t]}},lastElementChild:{get:function(){for(var e=this.childNodes||[],t=e.length;t--;)if(e[t].nodeType==1)return e[t]}},previousElementSibling:{get:function(){var e=this.previousSibling;while(e&&e.nodeType!=1)e=e.previousSibling;return e}},nextElementSibling:{get:function(){var e=this.nextSibling;while(e&&e.nodeType!=1)e=e.nextSibling;return e}},childElementCount:{get:function(){for(var e=0,t=this.childNodes||[],n=t.length;n--;e+=t[n].nodeType==1);return e}},addEventListener:{value:function(e,t,n){var r=this,i="on"+e,o=r[s]||u(r,s,{value:{}})[s],a=o[i]||(o[i]={}),f=a.h||(a.h=[]),c;if(!l.call(a,"w")){a.w=function(e){return e[s]||g(r,C(r,e),f,!1)};if(!l.call(v,i))if(p.test(e))try{c=document.createEventObject(),c[s]=!0,r.nodeType!=9&&r.parentNode==null&&m.appendChild(r),r.fireEvent(i,c),v[i]=!0}catch(c){v[i]=!1;while(m.hasChildNodes())m.removeChild(m.firstChild)}else v[i]=!1;(a.n=v[i])&&r.attachEvent(i,a.w)}E(f,t)<0&&f[n?"unshift":"push"](t)}},dispatchEvent:{value:function(e){var t=this,n="on"+e.type,r=t[s],i=r&&r[n],o=!!i,u;return e.target||(e.target=t),o?i.n?t.fireEvent(n,e):g(t,e,i.h,!0):(u=t.parentNode)?u.dispatchEvent(e):!0,!e.defaultPrevented}},removeEventListener:{value:function(e,t,n){var r=this,i="on"+e,o=r[s],u=o&&o[i],a=u&&u.h,f=a?E(a,t):-1;-1<f&&a.splice(f,1)}}}),a(h,{addEventListener:{value:c.addEventListener},dispatchEvent:{value:c.dispatchEvent},removeEventListener:{value:c.removeEventListener}}),a(e.XMLHttpRequest.prototype,{addEventListener:{value:function(e,t,n){var r=this,i="on"+e,o=r[s]||u(r,s,{value:{}})[s],a=o[i]||(o[i]={}),f=a.h||(a.h=[]);E(f,t)<0&&(r[i]||(r[i]=function(){var t=document.createEvent("Event");t.initEvent(e,!0,!0),r.dispatchEvent(t)}),f[n?"unshift":"push"](t))}},dispatchEvent:{value:function(e){var t=this,n="on"+e.type,r=t[s],i=r&&r[n],o=!!i;return o&&(i.n?t.fireEvent(n,e):g(t,e,i.h,!0))}},removeEventListener:{value:c.removeEventListener}}),a(e.Event.prototype,{bubbles:{value:!0,writable:!0},cancelable:{value:!0,writable:!0},preventDefault:{value:function(){this.cancelable&&(this.defaultPrevented=!0,this.returnValue=!1)}},stopPropagation:{value:function(){this.stoppedPropagation=!0,this.cancelBubble=!0}},stopImmediatePropagation:{value:function(){this.stoppedImmediatePropagation=!0,this.stopPropagation()}},initEvent:{value:function(e,t,n){this.type=e,this.bubbles=!!t,this.cancelable=!!n,this.bubbles||this.stopPropagation()}}}),a(e.HTMLDocument.prototype,{textContent:{get:function(){return this.nodeType===11?S.call(this):null},set:function(e){this.nodeType===11&&N.call(this,e)}},addEventListener:{value:function(n,s,o){var u=this;c.addEventListener.call(u,n,s,o),t&&n===i&&!d.test(u.readyState)&&(t=!1,u.attachEvent(r,T),e==top&&function a(e){try{u.documentElement.doScroll("left"),T()}catch(t){setTimeout(a,50)}}())}},dispatchEvent:{value:c.dispatchEvent},removeEventListener:{value:c.removeEventListener},createEvent:{value:function(e){var t;if(e!=="Event")throw new Error("unsupported "+e);return t=document.createEventObject(),t.timeStamp=(new Date).getTime(),t}}}),a(e.Window.prototype,{getComputedStyle:{value:function(){function i(e){this._=e}function s(){}var e=/^(?:[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|))(?!px)[a-z%]+$/,t=/^(top|right|bottom|left)$/,n=/\-([a-z])/g,r=function(e,t){return t.toUpperCase()};return i.prototype.getPropertyValue=function(i){var s=this._,o=s.style,u=s.currentStyle,a=s.runtimeStyle,f,l,c;return i=(i==="float"?"style-float":i).replace(n,r),f=u?u[i]:o[i],e.test(f)&&!t.test(i)&&(l=o.left,c=a&&a.left,c&&(a.left=u.left),o.left=i==="fontSize"?"1em":f,f=o.pixelLeft+"px",o.left=l,c&&(a.left=c)),f==null?f:f+""||"auto"},s.prototype.getPropertyValue=function(){return null},function(e,t){return t?new s(e):new i(e)}}()},addEventListener:{value:function(t,n,r){var i=e,o="on"+t,u;i[o]||(i[o]=function(e){return g(i,C(i,e),u,!1)}),u=i[o][s]||(i[o][s]=[]),E(u,n)<0&&u[r?"unshift":"push"](n)}},dispatchEvent:{value:function(t){var n=e["on"+t.type];return n?n.call(e,t)!==!1&&!t.defaultPrevented:!0}},removeEventListener:{value:function(t,n,r){var i="on"+t,u=(e[i]||o)[s],a=u?E(u,n):-1;-1<a&&u.splice(a,1)}}})})(this);
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.flowplayer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
'use strict';
var common = module.exports = {},
ClassList = _dereq_('class-list'),
$ = window.jQuery,
punycode = _dereq_('punycode'),
computedStyle = _dereq_('computed-style');
common.noop = function() {};
common.identity = function(i) { return i; };
common.removeNode = function(el) {
if (!el || !el.parentNode) return;
el.parentNode.removeChild(el);
};
common.find = function(query, ctx) {
if ($) return $(query, ctx).toArray();
ctx = ctx || document;
return Array.prototype.map.call(ctx.querySelectorAll(query), function(el) { return el; });
};
common.text = function(el, txt) {
el[('innerText' in el) ? 'innerText' : 'textContent'] = txt;
};
common.findDirect = function(query, ctx) {
return common.find(query, ctx).filter(function(node) {
return node.parentNode === ctx;
});
};
common.hasClass = function(el, kls) {
if (typeof el.className !== 'string') return false;
return ClassList(el).contains(kls);
};
common.isSameDomain = function(url) {
var w = window.location,
a = common.createElement('a', { href: url });
return w.hostname === a.hostname &&
w.protocol === a.protocol &&
w.port === a.port;
};
common.css = function(el, property, value) {
if (typeof property === 'object') {
return Object.keys(property).forEach(function(key) {
common.css(el, key, property[key]);
});
}
if (typeof value !== 'undefined') {
if (value === '') return el ? el.style.removeProperty(property) : undefined;
return el ? el.style.setProperty(property, value) : undefined;
}
return el ? computedStyle(el, property) : undefined;
};
common.createElement = function(tag, attributes, innerHTML) {
try {
var el = document.createElement(tag);
for (var key in attributes) {
if (!attributes.hasOwnProperty(key)) continue;
if (key === 'css') {
common.css(el, attributes[key]);
} else {
common.attr(el, key, attributes[key]);
}
}
el.innerHTML = innerHTML || '';
return el;
} catch (e) {
if (!$) throw e;
return $('<' + tag + '>' + innerHTML + '</' + tag + '>').attr(attributes)[0];
}
};
common.toggleClass = function(el, cls, flag) {
if (!el) return;
var classes = ClassList(el);
if (typeof flag === 'undefined') classes.toggle(cls);
else if (flag) classes.add(cls);
else if (!flag) classes.remove(cls);
};
common.addClass = function(el, cls) {
return common.toggleClass(el, cls, true);
};
common.removeClass = function(el, cls) {
return common.toggleClass(el, cls, false);
};
common.append = function(par, child) {
par.appendChild(child);
return par;
};
common.appendTo = function(child, par) {
common.append(par, child);
return child;
};
common.prepend = function(par, child) {
par.insertBefore(child, par.firstChild);
};
// Inserts `el` after `child` that is child of `par`
common.insertAfter = function(par, child, el) {
if (child == common.lastChild(par)) par.appendChild(el);
var childIndex = Array.prototype.indexOf.call(par.children, child);
par.insertBefore(el, par.children[childIndex + 1]);
};
common.html = function(elms, val) {
elms = elms.length ? elms : [elms];
elms.forEach(function(elm) {
elm.innerHTML = val;
});
};
common.attr = function(el, key, val) {
if (key === 'class') key = 'className';
if (common.hasOwnOrPrototypeProperty(el, key)) {
try {
el[key] = val;
} catch (e) { // Most likely IE not letting set property
if ($) {
$(el).attr(key, val);
} else {
throw e;
}
}
} else {
if (val === false) {
el.removeAttribute(key);
} else {
el.setAttribute(key, val);
}
}
return el;
};
common.prop = function(el, key, val) {
if (typeof val === 'undefined') {
return el && el[key];
}
el[key] = val;
};
common.offset = function(el) {
var ret = el.getBoundingClientRect();
if (el.offsetWidth / el.offsetHeight > el.clientWidth / el.clientHeight) { // https://github.com/flowplayer/flowplayer/issues/757
ret = {
left: ret.left * 100,
right: ret.right * 100,
top: ret.top * 100,
bottom: ret.bottom * 100,
width: ret.width * 100,
height: ret.height * 100
};
}
return ret;
};
common.width = function(el, val) {
/*jshint -W093 */
if (val) return el.style.width = (''+val).replace(/px$/, '') + 'px';
var ret = common.offset(el).width;
return typeof ret === 'undefined' ? el.offsetWidth : ret;
};
common.height = function(el, val) {
/*jshint -W093 */
if (val) return el.style.height = (''+val).replace(/px$/, '') + 'px';
var ret = common.offset(el).height;
return typeof ret === 'undefined' ? el.offsetHeight : ret;
};
common.lastChild = function(el) {
return el.children[el.children.length - 1];
};
common.hasParent = function(el, parentSelector) {
var parent = el.parentElement;
while (parent) {
if (common.matches(parent, parentSelector)) return true;
parent = parent.parentElement;
}
return false;
};
common.createAbsoluteUrl = function(url) {
return common.createElement('a', {href: url}).href; // This won't work on IE7
};
common.xhrGet = function(url, successCb, errorCb) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
if (this.status >= 400) return errorCb();
successCb(this.responseText);
};
xhr.open('get', url, true);
xhr.send();
};
common.pick = function(obj, props) {
var ret = {};
props.forEach(function(prop) {
if (obj.hasOwnProperty(prop)) ret[prop] = obj[prop];
});
return ret;
};
common.hostname = function(host) {
return punycode.toUnicode(host || window.location.hostname);
};
//Hacks
common.browser = {
webkit: 'WebkitAppearance' in document.documentElement.style
};
common.getPrototype = function(el) {
/* jshint proto:true */
if (!Object.getPrototypeOf) return el.__proto__;
return Object.getPrototypeOf(el);
};
common.hasOwnOrPrototypeProperty = function(obj, prop) {
var o = obj;
while (o) {
if (Object.prototype.hasOwnProperty.call(o, prop)) return true;
o = common.getPrototype(o);
}
return false;
};
// Polyfill for Element.matches
// adapted from https://developer.mozilla.org/en/docs/Web/API/Element/matches
common.matches = function(elem, selector) {
var proto = Element.prototype,
fn = proto.matches ||
proto.matchesSelector ||
proto.mozMatchesSelector ||
proto.msMatchesSelector ||
proto.oMatchesSelector ||
proto.webkitMatchesSelector ||
function (selector) {
var element = this,
matches = (element.document || element.ownerDocument).querySelectorAll(selector),
i = 0;
while (matches[i] && matches[i] !== element) {
i++;
}
return matches[i] ? true : false;
};
return fn.call(elem, selector);
};
// Polyfill for CSSStyleDeclaration
// from https://github.com/shawnbot/aight
(function(CSSSDProto) {
function getAttribute(property) {
return property.replace(/-[a-z]/g, function(bit) {
return bit[1].toUpperCase();
});
}
// patch CSSStyleDeclaration.prototype using IE8's methods
if (typeof CSSSDProto.setAttribute !== "undefined") {
CSSSDProto.setProperty = function(property, value) {
return this.setAttribute(getAttribute(property), String(value) /*, important */ );
};
CSSSDProto.getPropertyValue = function(property) {
return this.getAttribute(getAttribute(property)) || null;
};
CSSSDProto.removeProperty = function(property) {
var value = this.getPropertyValue(property);
this.removeAttribute(getAttribute(property));
return value;
};
}
})(window.CSSStyleDeclaration.prototype);
},{"class-list":33,"computed-style":34,"punycode":41}],2:[function(_dereq_,module,exports){
'use strict';
var common = _dereq_('../common');
// movie required in opts
module.exports = function embed(swf, flashvars, wmode, bgColor) {
wmode = wmode || "opaque";
var id = "obj" + ("" + Math.random()).slice(2, 15),
tag = '<object class="fp-engine" id="' + id+ '" name="' + id + '" ',
msie = navigator.userAgent.indexOf('MSIE') > -1;
tag += msie ? 'classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">' :
' data="' + swf + '" type="application/x-shockwave-flash">';
var opts = {
width: "100%",
height: "100%",
allowscriptaccess: "always",
wmode: wmode,
quality: "high",
flashvars: "",
// https://github.com/flowplayer/flowplayer/issues/13#issuecomment-9369919
movie: swf + (msie ? "?" + id : ""),
name: id
};
if (wmode !== 'transparent') opts.bgcolor = bgColor || '#333333';
// flashvars
Object.keys(flashvars).forEach(function(key) {
opts.flashvars += key + "=" + flashvars[key] + "&";
});
// parameters
Object.keys(opts).forEach(function(key) {
tag += '<param name="' + key + '" value="'+ opts[key] +'"/>';
});
tag += "</object>";
var el = common.createElement('div', {}, tag);
return common.find('object', el);
};
// Flash is buggy allover
if (window.attachEvent) {
window.attachEvent("onbeforeunload", function() {
window.__flash_savedUnloadHandler = window.__flash_unloadHandler = function() {};
});
}
},{"../common":1}],3:[function(_dereq_,module,exports){
'use strict';
var flowplayer = _dereq_('../flowplayer'),
common = _dereq_('../common'),
embed = _dereq_('./embed'),
extend = _dereq_('extend-object'),
bean = _dereq_('bean'),
engineImpl;
engineImpl = function flashEngine(player, root) {
var conf = player.conf,
loadVideo,
callbackId,
api;
var suspended, timeouts = [];
var engine = {
engineName: engineImpl.engineName,
pick: function(sources) {
var source = extend({}, (function() {
if (flowplayer.support.flashVideo) {
var selectedSource;
for (var i = 0, source; i < sources.length; i++) {
source = sources[i];
if (/mp4|flv|flash/i.test(source.type)) selectedSource = source;
if (player.conf.swfHls && /mpegurl/i.test(source.type)) selectedSource = source;
if (selectedSource && !/mp4/i.test(selectedSource.type)) return selectedSource;
// Did not find any source or source was video/mp4, let's try find more
}
return selectedSource; // Accept the fact we don't have anything or just an MP4
}
})());
if (!source) return;
if (source.src && !isAbsolute(source.src) && !player.conf.rtmp && !source.rtmp) source.src = common.createAbsoluteUrl(source.src);
return source;
},
suspendEngine: function() {
suspended = true;
},
resumeEngine: function() {
suspended = false;
},
load: function(video) {
loadVideo = video;
timeouts.forEach(function(t) { clearTimeout(t); });
function escapeURL(url) {
return url.replace(/&/g, '%26').replace(/&/g, '%26').replace(/=/g, '%3D');
}
var html5Tag = common.findDirect('video', root)[0] || common.find('.fp-player > video', root)[0],
url = video.src,
is_absolute = isAbsolute(url);
var removeTag = function() {
common.removeNode(html5Tag);
};
var hasSupportedSource = function(sources) {
return sources.some(function(src) {
return !!html5Tag.canPlayType(src.type);
});
};
if (flowplayer.support.video &&
common.prop(html5Tag, 'autoplay') &&
hasSupportedSource(video.sources)) bean.one(html5Tag, 'timeupdate', removeTag);
else removeTag();
// convert to absolute
var rtmp = video.rtmp || conf.rtmp;
if (!is_absolute && !rtmp) url = common.createAbsoluteUrl(url);
if (api && isHLS(video) && api.data !== common.createAbsoluteUrl(conf.swfHls)) engine.unload();
if (api) {
['live', 'preload', 'loop'].forEach(function(prop) {
if (!video.hasOwnProperty(prop)) return;
api.__set(prop, video[prop]);
});
Object.keys(video.flashls || {}).forEach(function(key) {
api.__set('hls_' + key, video.flashls[key]);
});
var providerChangeNeeded = false;
if (!is_absolute && rtmp) api.__set('rtmp', rtmp.url || rtmp);
else {
var oldRtmp = api.__get('rtmp');
providerChangeNeeded = !!oldRtmp;
api.__set('rtmp', null);
}
api.__play(url, providerChangeNeeded || video.rtmp && video.rtmp !== conf.rtmp);
} else {
callbackId = "fpCallback" + ("" + Math.random()).slice(3, 15);
url = escapeURL(url);
var opts = {
hostname: conf.embedded ? common.hostname(conf.hostname) : common.hostname(location.hostname),
url: url,
callback: callbackId
};
if (root.getAttribute('data-origin')) {
opts.origin = root.getAttribute('data-origin');
}
// optional conf
['proxy', 'key', 'autoplay', 'preload', 'subscribe', 'live', 'loop', 'debug', 'splash', 'poster', 'rtmpt'].forEach(function(key) {
if (conf.hasOwnProperty(key)) opts[key] = conf[key];
if (video.hasOwnProperty(key)) opts[key] = video[key];
if ((conf.rtmp || {}).hasOwnProperty(key)) opts[key] = (conf.rtmp || {})[key];
if ((video.rtmp || {}).hasOwnProperty(key)) opts[key] = (video.rtmp || {})[key];
});
if (conf.rtmp) opts.rtmp = conf.rtmp.url || conf.rtmp;
if (video.rtmp) opts.rtmp = video.rtmp.url || video.rtmp;
Object.keys(video.flashls || {}).forEach(function(key) {
var val = video.flashls[key];
opts['hls_' + key] = val;
});
var hlsQualities = typeof video.hlsQualities !== 'undefined' ? video.hlsQualities : conf.hlsQualities;
if (typeof hlsQualities !== 'undefined') opts.hlsQualities = hlsQualities ? encodeURIComponent(JSON.stringify(hlsQualities)) : hlsQualities;
// bufferTime might be 0
if (conf.bufferTime !== undefined) opts.bufferTime = conf.bufferTime;
if (is_absolute) delete opts.rtmp;
// issues #376
if (opts.rtmp) {
opts.rtmp = escapeURL(opts.rtmp);
}
// issues #733, 906
var bgColor = conf.bgcolor || common.css(root, 'background-color') ||'', bg;
if (bgColor.indexOf('rgb') === 0) {
bg = toHex(bgColor);
} else if (bgColor.indexOf('#') === 0) {
bg = toLongHex(bgColor);
}
// issues #387
opts.initialVolume = player.volumeLevel;
var swfUrl = isHLS(video) ? conf.swfHls : conf.swf;
api = embed(swfUrl, opts, conf.wmode, bg)[0];
var container = common.find('.fp-player', root)[0];
common.prepend(container, api);
player.off('quality.flashengine').on('quality.flashengine', function(ev, _api, quality) {
var hlsQualities =
typeof player.video.hlsQualities !== 'undefined' ?
player.video.hlsQualities :
player.conf.hlsQualities;
if (!hlsQualities) return;
api.__quality(quality);
});
// throw error if no loading occurs
setTimeout(function() {
try {
if (!api.PercentLoaded()) {
return player.trigger("error", [player, { code: 7, url: conf.swf }]);
}
} catch (e) {}
}, 5000);
// detect disabled flash
//
timeouts.push(setTimeout(function() {
if (typeof api.PercentLoaded === 'undefined') {
player.trigger('flashdisabled', [player]);
}
}, 15000));
timeouts.push(setTimeout(function() {
if (typeof api.PercentLoaded === 'undefined') {
player.trigger('flashdisabled', [player, false]);
}
}, 500));
player.off('resume.flashhack').on('resume.flashhack', function() {
var timer = setTimeout(function() {
var currentTime = api.__status().time;
var timer2 = setTimeout(function() {
if (player.playing && !player.loading && api.__status().time === currentTime) {
player.trigger('flashdisabled', [player]);
}
}, 400);
timeouts.push(timer2);
player.one('seek.flashhack pause.flashhack load.flashack', function() { clearTimeout(timer2); });
}, 800);
timeouts.push(timer);
player.one('progress', function() {
clearTimeout(timer);
});
});
api.pollInterval = setInterval(function () {
if (!api || suspended) return;
var status = api.__status ? api.__status() : null;
if (!status) return;
if (player.conf.live || player.live || video.live) {
video.seekOffset = status.seekOffset;
video.duration = status.duration + status.seekOffset;
}
if (player.playing && status.time && status.time !== player.video.time) player.trigger("progress", [player, status.time]);
video.buffer = status.buffer / video.bytes * video.duration;
player.trigger("buffer", [player, video.buffer]);
if (!video.buffered && status.time > 0) {
video.buffered = true;
player.trigger("buffered", [player]);
}
}, 250);
// listen
window[callbackId] = function(type, arg) {
var video = loadVideo;
if (conf.debug) {
if (type.indexOf('debug') === 0 && arg && arg.length) {
console.log.apply(console, ['-- ' + type].concat(arg));
}
else console.log("--", type, arg);
}
var event = {
type: type
};
switch (type) {
// RTMP sends a lot of finish events in vain
// case "finish": if (conf.rtmp) return;
case "ready": arg = extend(video, arg); break;
case "click": event.flash = true; break;
case "keydown": event.which = arg; break;
case "seek": video.time = arg; break;
case "status":
player.trigger("progress", [player, arg.time]);
if (arg.buffer < video.bytes && !video.buffered) {
video.buffer = arg.buffer / video.bytes * video.duration;
player.trigger("buffer", video.buffer);
} else if (!video.buffered) {
video.buffered = true;
player.trigger("buffered");
}
break;
case "metadata":
var str = atob(arg);
arg = {
key: str.substr(10, 4),
data: str.substr(21)
}
break;
}
if (type === 'click' || type === 'keydown') {
event.target = root;
bean.fire(root, type, [event]);
}
else if (type != 'buffered' && type !== 'unload') {
// add some delay so that player is truly ready after an event
setTimeout(function() { player.trigger(event, [player, arg]); }, 1);
} else if (type === 'unload') {
player.trigger(event, [player, arg]);
}
};
}
},
// not supported yet
speed: common.noop,
unload: function() {
if (api && api.__unload) api.__unload();
try {
if (callbackId && window[callbackId])delete window[callbackId];
} catch (e) {}
common.find("object", root).forEach(common.removeNode);
api = 0;
player.off('.flashengine');
player.off('.flashhack');
clearInterval(api.pollInterval);
timeouts.forEach(function(t) { clearTimeout(t); });
}
};
['pause','resume','seek','volume'].forEach(function(name) {
engine[name] = function(arg) {
try {
if (player.ready) {
if (arg === undefined) {
api["__" + name]();
} else {
api["__" + name](arg);
}
}
} catch (e) {
if (typeof api["__" + name] === 'undefined') { //flash lost it's methods
return player.trigger('flashdisabled', [player]);
}
throw e;
}
};
});
function toHex(bg) {
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
bg = bg.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (!bg) return;
return '#' + hex(bg[1]) + hex(bg[2]) + hex(bg[3]);
}
function toLongHex(bg) {
if (bg.length === 7) return bg;
var a = bg.split('').slice(1);
return '#' + a.map(function(i) {
return i + i;
}).join('');
}
function isHLS(video) {
return /application\/x-mpegurl/i.test(video.type);
}
return engine;
};
engineImpl.engineName = 'flash';
engineImpl.canPlay = function(type, conf) {
return flowplayer.support.flashVideo && /video\/(mp4|flash|flv)/i.test(type) || flowplayer.support.flashVideo && conf.swfHls && /mpegurl/i.test(type);
};
flowplayer.engines.push(engineImpl);
function isAbsolute(url) {
return /^https?:/.test(url);
}
},{"../common":1,"../flowplayer":28,"./embed":2,"bean":31,"extend-object":36}],4:[function(_dereq_,module,exports){
'use strict';
var flowplayer = _dereq_('../flowplayer'),
bean = _dereq_('bean'),
extend = _dereq_('extend-object'),
common = _dereq_('../common');
var VIDEO = document.createElement('video');
// HTML5 --> Flowplayer event
var EVENTS = {
// fired
ended: 'finish',
pause: 'pause',
play: 'resume',
//progress: 'buffer',
timeupdate: 'progress',
volumechange: 'volume',
ratechange: 'speed',
//seeking: 'beforeseek',
seeked: 'seek',
// abort: 'resume',
// not fired
loadeddata: 'ready',
// loadedmetadata: 0,
// canplay: 0,
// error events
// load: 0,
// emptied: 0,
// empty: 0,
error: 'error',
dataunavailable: 'error',
webkitendfullscreen: !flowplayer.support.inlineVideo && 'unload'
};
function round(val, per) {
per = per || 100;
return Math.round(val * per) / per;
}
function getType(type) {
return /mpegurl/i.test(type) ? "application/x-mpegurl" : type;
}
function canPlay(type) {
if (!/^(video|application)/i.test(type))
type = getType(type);
return !!VIDEO.canPlayType(type).replace("no", '');
}
function findFromSourcesByType(sources, type) {
var arr = sources.filter(function(s) {
return s.type === type;
});
return arr.length ? arr[0] : null;
}
var videoTagCache;
var createVideoTag = function(video, autoplay, preload, useCache, inline, subtitles) {
if (typeof autoplay === 'undefined') autoplay = true;
if (typeof preload === 'undefined') preload = 'none';
if (typeof useCache === 'undefined') useCache = true;
if (typeof inline === 'undefined') inline = true;
if (useCache && videoTagCache) {
videoTagCache.type = getType(video.type);
videoTagCache.src = video.src;
common.find('track', videoTagCache).forEach(common.removeNode);
videoTagCache.removeAttribute('crossorigin');
return videoTagCache;
}
var el = document.createElement('video');
el.src = video.src;
el.type = getType(video.type);
var className = 'fp-engine ';
if (subtitles && subtitles.length) className += 'native-subtitles';
el.className = className;
if (flowplayer.support.autoplay) el.autoplay = autoplay ? 'autoplay' : false;
if (flowplayer.support.dataload) el.preload = preload;
if (inline) {
el.setAttribute('webkit-playsinline', 'true');
el.setAttribute('playsinline', 'true');
}
if (subtitles && subtitles.length) {
var setMode = function(mode) {
var tracks = el.textTracks;
if (!tracks.length) return;
tracks[0].mode = mode;
};
if (subtitles.some(function(st) { return !common.isSameDomain(st.src); })) common.attr(el, 'crossorigin', 'anonymous');
if (typeof el.textTracks.addEventListener === 'function') el.textTracks.addEventListener('addtrack', function() {
setMode('disabled');
setMode('showing');
});
subtitles.forEach(function(st) {
el.appendChild(common.createElement('track', {
kind: 'subtitles',
srclang: st.srclang || 'en',
label: st.label || 'en',
src: st.src,
'default': st['default']
}));
});
}
if (useCache) videoTagCache = el;
return el;
};
var engine;
engine = function(player, root) {
var api = common.findDirect('video', root)[0] || common.find('.fp-player > video', root)[0],
support = flowplayer.support,
conf = player.conf,
self,
timer,
lastBuffer,
volumeLevel;
/*jshint -W093 */
return self = {
engineName: engine.engineName,
pick: function(sources) {
var source = (function() {
if (support.video) {
if (conf.videoTypePreference) {
var mp4source = findFromSourcesByType(sources, conf.videoTypePreference);
if (mp4source) return mp4source;
}
for (var i = 0; i < sources.length; i++) {
if (canPlay(sources[i].type)) return sources[i];
}
}
})();
if (!source) return;
if (typeof source.src === 'string') source.src = common.createAbsoluteUrl(source.src);
return source;
},
load: function(video) {
var container = common.find('.fp-player', root)[0], reload = false, created = false;
if (conf.splash && !api) {
api = createVideoTag(
video,
undefined,
undefined,
undefined,
!conf.disableInline,
flowplayer.support.subtitles && conf.nativesubtitles && video.subtitles
);
common.prepend(container, api);
created = true;
} else if (!api) {
api = createVideoTag(video, !!video.autoplay || !!conf.autoplay, conf.clip.preload || true, false);
common.prepend(container, api);
created = true;
} else {
common.addClass(api, 'fp-engine');
common.find('source,track', api).forEach(common.removeNode);
if (!player.conf.nativesubtitles) common.attr(api, 'crossorigin', false);
reload = api.src === video.src;
}
if (!support.inlineVideo) {
common.css(api, {
position: 'absolute',
top: '-9999em'
});
}
//TODO subtitles support
// IE does not fire delegated timeupdate events
bean.off(api, 'timeupdate', common.noop);
bean.on(api, 'timeupdate', common.noop);
common.prop(api, 'loop', false);
player.off('.loophack');
if (video.loop || conf.loop) {
if (/mpegurl/i.test(video.type)) {
player.on('finish.loophack', function() { player.resume(); });
}
else common.prop(api, 'loop', true);
}
if (typeof volumeLevel !== 'undefined') {
api.volume = volumeLevel;
}
if (player.video.src && video.src != player.video.src || video.index) common.attr(api, 'autoplay', 'autoplay');
api.src = video.src;
api.type = video.type;
self._listeners = listen(api, common.find("source", api).concat(api), video) || self._listeners;
if (reload || (created && !conf.splash)) api.load();
if (support.iOS.iPad && support.iOS.chrome) api.load();
if (api.paused && (video.autoplay || conf.autoplay || conf.splash)) api.play();
},
pause: function() {
api.pause();
},
resume: function() {
api.play();
},
speed: function(val) {
api.playbackRate = val;
},
seek: function(time) {
try {
var pausedState = player.paused;
api.currentTime = time;
if (pausedState) api.pause();
} catch (ignored) {}
},
volume: function(level) {
volumeLevel = level;
if (api) {
api.volume = level;
}
},
unload: function() {
common.find('video.fp-engine', root).forEach(function (videoTag) {
common.attr(videoTag, 'src', '');
common.removeNode(videoTag);
});
if (!support.cachedVideoTag) videoTagCache = null;
timer = clearInterval(timer);
var instanceId = root.getAttribute('data-flowplayer-instance-id');
delete api.listeners[instanceId];
api = 0;
if (self._listeners) Object.keys(self._listeners).forEach(function(typ) {
self._listeners[typ].forEach(function(l) {
root.removeEventListener(typ, l, true);
});
});
}
};
function listen(api, sources, video) {
// listen only once
var instanceId = root.getAttribute('data-flowplayer-instance-id');
if (api.listeners && api.listeners.hasOwnProperty(instanceId)) {
api.listeners[instanceId] = video;
return;
}
(api.listeners || (api.listeners = {}))[instanceId] = video;
bean.on(sources, 'error', function(e) {
try {
if (canPlay(e.target.getAttribute('type'))) {
player.trigger("error", [player, { code: 4, video: extend(video, {src: api.src, url: api.src}) }]);
}
} catch (er) {
// Most likely: https://bugzilla.mozilla.org/show_bug.cgi?id=208427
}
});
player.on('shutdown', function() {
bean.off(sources);
bean.off(api, '.dvrhack');
player.off('.loophack');
});
var eventListeners = {};
//Special event handling for HLS metadata events
var listenMetadata = function(track) {
if (track.kind !== 'metadata') return;
track.mode = 'hidden';
track.addEventListener('cuechange', function() {
player.trigger('metadata', [player, track.activeCues[0].value]);
}, false);
};
if (api && api.textTracks && api.textTracks.length) Array.prototype.forEach.call(api.textTracks, listenMetadata);
if (api && api.textTracks && typeof api.textTracks.addEventListener === 'function') api.textTracks.addEventListener('addtrack', function(tev) {
listenMetadata(tev.track);
}, false);
if (player.conf.dvr || player.dvr || video.dvr) {
bean.on(api, 'progress.dvrhack', function() {
if (!api.seekable.length) return;
player.video.duration = api.seekable.end(null);
player.video.seekOffset = api.seekable.start(null);
player.trigger('dvrwindow', [player, {
start: api.seekable.start(null),
end: api.seekable.end(null)
}]);
if (api.currentTime >= api.seekable.start(null)) return;
api.currentTime = api.seekable.start(null);
});
}
Object.keys(EVENTS).forEach(function(type) {
var flow = EVENTS[type];
if (type === 'webkitendfullscreen' && player.conf.disableInline) flow = 'unload';
if (!flow) return;
var l = function(e) {
video = api.listeners[instanceId];
if (!e.target || !common.hasClass(e.target, 'fp-engine')) return;
if (conf.debug && !/progress/.test(flow)) console.log(type, "->", flow, e);
var triggerEvent = function() {
player.trigger(flow, [player, arg]);
};
// no events if player not ready
if (!player.ready && !/ready|error/.test(flow) || !flow || !common.find('video', root).length) {
if (flow === 'resume') player.one('ready', function() { setTimeout(function() { triggerEvent(); }) });
return;
}
var arg;
if (flow === 'unload') { //Call player unload
player.unload();
return;
}
switch (flow) {
case "ready":
arg = extend(video, {
duration: api.duration < Number.MAX_VALUE ? api.duration : 0,
width: api.videoWidth,
height: api.videoHeight,
url: api.currentSrc,
src: api.currentSrc
});
try {
arg.seekable = /mpegurl/i.test(video ? (video.type || '') : '') && api.duration || api.seekable && api.seekable.end(null) || player.live;
} catch (ignored) {}
// buffer
timer = timer || setInterval(function() {
try {
arg.buffer = api.buffered.end(null);
} catch (ignored) {}
if (arg.buffer) {
if (round(arg.buffer, 1000) < round(arg.duration, 1000) && !arg.buffered && arg.buffer !== lastBuffer) {
player.trigger("buffer", [player, arg.buffer]);
lastBuffer = arg.buffer;
} else if (!arg.buffered && arg.buffer !== lastBuffer) {
arg.buffered = true;
player.trigger("buffer", [player, arg.buffer]).trigger("buffered", e);
lastBuffer = arg.buffer;
clearInterval(timer);
timer = 0;
}
}
}, 250);
if (!player.live && !arg.duration && !support.hlsDuration && type === "loadeddata") {
var durationChanged = function() {
arg.duration = api.duration;
try {
arg.seekable = api.seekable && api.seekable.end(null);
} catch (ignored) {}
triggerEvent();
api.removeEventListener('durationchange', durationChanged);
common.toggleClass(root, 'is-live', false);
};
api.addEventListener('durationchange', durationChanged);
// Ugly hack to handle broken Android devices
var timeUpdated = function() {
if (!player.ready && !api.duration) { // No duration even though the video already plays
arg.duration = 0;
common.addClass(root, 'is-live'); // Make UI believe it's live
triggerEvent();
}
api.removeEventListener('timeupdate', timeUpdated);
};
api.addEventListener('timeupdate', timeUpdated);
return;
}
break;
case "progress": case "seek":
if (api.currentTime > 0 || player.live) {
arg = Math.max(api.currentTime, 0);
} else if (flow == 'progress') {
return;
}
break;
case "speed":
arg = round(api.playbackRate);
break;
case "volume":
arg = round(api.volume);
break;
case "error":
try {
arg = (e.srcElement || e.originalTarget).error;
arg.video = extend(video, {src: api.src, url: api.src});
} catch (er) {
// Most likely https://bugzilla.mozilla.org/show_bug.cgi?id=208427
return;
}
}
triggerEvent();
};
root.addEventListener(type, l, true);
if (!eventListeners[type]) eventListeners[type] = [];
eventListeners[type].push(l);
});
return eventListeners;
}
};
engine.canPlay = function(type) {
return flowplayer.support.video && canPlay(type);
};
engine.engineName = 'html5';
flowplayer.engines.push(engine);
},{"../common":1,"../flowplayer":28,"bean":31,"extend-object":36}],5:[function(_dereq_,module,exports){
'use strict';
var flowplayer = _dereq_('../flowplayer')
, common = _dereq_('../common')
, bean = _dereq_('bean');
flowplayer(function(api, root) {
api.on('ready', function() {
var el = common.find('video.fp-engine', root)[0];
if (!el) return;
el.setAttribute('x-webkit-airplay', 'allow');
if (!window.WebKitPlaybackTargetAvailabilityEvent) return;
el.addEventListener('webkitplaybacktargetavailabilitychanged', function(ev) {
if (ev.availability !== 'available') return;
var btnContainer = common.find('.fp-header', root)[0];
common.find('.fp-airplay', btnContainer).forEach(common.removeNode);
var trigger = common.createElement('a', { 'class': 'fp-airplay fp-icon', title: 'Play on AirPlay device'})
btnContainer.insertBefore(trigger, common.find('.fp-fullscreen', btnContainer)[0]);
});
el.addEventListener('webkitcurrentplaybacktargetiswirelesschanged', function() {
var trigger = common.find('.fp-airplay', root)[0];
if (!trigger) return;
common.toggleClass(trigger, 'fp-active', el.webkitCurrentPlaybackTargetIsWireless);
});
});
bean.on(root, 'click', '.fp-airplay', function(ev) {
ev.preventDefault();
var video = common.find('video.fp-engine', root)[0];
video.webkitShowPlaybackTargetPicker();
});
});
},{"../common":1,"../flowplayer":28,"bean":31}],6:[function(_dereq_,module,exports){
'use strict';
/* global _gat */
var flowplayer = _dereq_('../flowplayer'),
TYPE_RE = _dereq_('./resolve').TYPE_RE,
scriptjs = _dereq_('scriptjs'),
bean = _dereq_('bean');
flowplayer(function(player, root) {
var id = player.conf.analytics, time = 0, last = 0, timer;
if (id) {
// load Analytics script if needed
if (typeof _gat == 'undefined') scriptjs("//google-analytics.com/ga.js");
var getTracker = function() {
var tracker = _gat._getTracker(id);
tracker._setAllowLinker(true);
return tracker;
};
var track = function track(e, api, video) {
video = video || player.video;
if (time && typeof _gat != 'undefined') {
var tracker = getTracker();
// http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html
tracker._trackEvent(
"Video / Seconds played",
player.engine.engineName + "/" + video.type,
video.title || root.getAttribute("title") || video.src.split("/").slice(-1)[0].replace(TYPE_RE, ''),
Math.round(time / 1000)
);
time = 0;
if (timer) {
clearTimeout(timer);
timer = null;
}
}
};
player.bind("load unload", track).bind("progress", function() {
if (!player.seeking) {
time += last ? (+new Date() - last) : 0;
last = +new Date();
}
if (!timer) {
timer = setTimeout(function() {
timer = null;
var tracker = getTracker();
tracker._trackEvent('Flowplayer heartbeat', 'Heartbeat', '', 0, true);
}, 10*60*1000); // heartbeat every 10 minutes
}
}).bind("pause", function() {
last = 0;
});
player.bind('shutdown', function() {
bean.off(window, 'unload', track);
});
bean.on(window, 'unload', track);
}
});
},{"../flowplayer":28,"./resolve":19,"bean":31,"scriptjs":42}],7:[function(_dereq_,module,exports){
/* global chrome */
/* eslint-disable no-console */
'use strict';
var flowplayer = _dereq_('../flowplayer')
, common = _dereq_('../common')
, bean = _dereq_('bean')
, scriptjs = _dereq_('scriptjs');
flowplayer(function(api, root) {
if (api.conf.chromecast === false) return;
scriptjs('https://www.gstatic.com/cv/js/sender/v1/cast_sender.js');
window['__onGCastApiAvailable'] = function(loaded) {
if (!loaded) return;
initialize();
};
var conf = api.conf.chromecast || {}
, session
, timer
, trigger;
function initialize() {
var applicationId, sessionRequest, apiConfig;
applicationId = conf.applicationId || chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID;
sessionRequest = new chrome.cast.SessionRequest(applicationId);
apiConfig = new chrome.cast.ApiConfig(
sessionRequest,
sessionListener,
receiverListener
);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
}
function sessionListener() {
console.log('sessionListener');
}
function receiverListener(ev) {
if (ev !== chrome.cast.ReceiverAvailability.AVAILABLE) return;
createUIElements();
}
function onInitSuccess() {
/* noop */
}
function onError() {
console.log('onError');
}
function createUIElements() {
var btnContainer = common.find('.fp-header', root)[0];
common.find('.fp-chromecast', btnContainer).forEach(common.removeNode);
common.find('.fp-chromecast-engine', root).forEach(common.removeNode);
trigger = common.createElement('a', { 'class': 'fp-chromecast fp-icon', title: 'Play on Cast device'})
btnContainer.insertBefore(trigger, common.find('.