flowplayer
Version:
The HTML5 video player for the web
1,352 lines (1,131 loc) • 379 kB
JavaScript
/*!
Flowplayer v7.2.7 (2018-08-13) | flowplayer.com/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]);
}
}
if (innerHTML) 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 (typeof parentSelector !== 'string') {
// is an element
if (parent === parentSelector) return true;
} else {
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":36,"computed-style":37,"punycode":44}],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.splash) opts.autoplay = true;
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, bufferTimeMax might be 0
if (conf.bufferTime !== undefined) opts.bufferTime = conf.bufferTime;
if (conf.bufferTimeMax !== undefined) opts.bufferTimeMax = conf.bufferTimeMax;
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;
try {
api.__quality(quality);
} catch (e) {
// VOD / RTMP engine does not support quality
player.debug('Error changing quality in flash engine', e);
}
});
// 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":31,"./embed":2,"bean":34,"extend-object":39}],4:[function(_dereq_,module,exports){
'use strict';
var flowplayer = _dereq_('../flowplayer')
, support = flowplayer.support
, common = flowplayer.common
, bean = flowplayer.bean
, html5factory = _dereq_('./html5-factory');
function canPlay(type) {
if (typeof window.Hls === 'undefined') return false;
return /mpegurl/.test(type) && window.Hls.isSupported();
}
var engine;
engine = function(player, root) {
var Hls = window.Hls
, lastSelectedLevel
, lastSource;
function hlsjsExt(video, api, engineApi) {
var conf = flowplayer.extend({
recoverMediaError: true
}, player.conf.hlsjs, video.hlsjs);
if (player.engine.hls) player.engine.hls.destroy();
var hls = player.engine.hls = new Hls(conf);
engine.extensions.forEach(function(ext) {
ext({
hls: hls,
player: player,
root: root,
videoTag: api
});
});
hls.loadSource(video.src);
// API overriders
engineApi.resume = function() {
if (player.live && !player.dvr) api.currentTime = hls.liveSyncPosition || 0;
api.play();
};
engineApi.seek = function(seekTo) {
try {
if (player.live || player.dvr) {
api.currentTime = Math.min(
seekTo,
(hls.liveSyncPosition || api.duration - conf.livePositionOffset)
);
}
else api.currentTime = seekTo;
} catch (e) {
player.debug('Failed to seek to ', seekTo, e);
}
};
if (conf.bufferWhilePaused === false) {
player.on('pause', function() {
hls.stopLoad();
player.one('resume', function() {
hls.startLoad();
});
});
}
// Quality selection
player.on('quality', function(_ev, _api, q) {
hls.nextLevel = lastSelectedLevel = q;
});
// HLS.js error handling
var recoverMediaErrorDate
, swapAudioCodecDate;
var recover = function(isNetworkError) {
player.debug('hlsjs - recovery');
common.removeClass(root, 'is-paused');
common.addClass(root, 'is-seeking');
bean.one(api, 'seeked', function() {
if (api.paused) {
common.removeClass(root, 'is-poster');
player.poster = false;
api.play();
}
common.removeClass(root, 'is-seeking');
});
if (isNetworkError) return hls.startLoad();
var now = performance.now();
if (!recoverMediaErrorDate || now - recoverMediaErrorDate > 3000) {
recoverMediaErrorDate = performance.now();
hls.recoverMediaError();
} else if (!swapAudioCodecDate || (now - swapAudioCodecDate) > 3000) {
swapAudioCodecDate = performance.now();
hls.swapAudioCodec();
hls.recoverMediaError();
}
};
hls.on(Hls.Events.MANIFEST_PARSED, function(_, data) {
var hlsQualities = video.hlsQualities || player.conf.hlsQualities
, confQualities
, qualityLabels = {}
, levels = data.levels;
if (hlsQualities === false) return hls.attachMedia(api);
if (hlsQualities === 'drive') switch (levels.length) {
case 4:
confQualities = [1, 2, 3];
break;
case 5:
confQualities = [1, 2, 3, 4];
break;
case 6:
confQualities = [1, 3, 4, 5];
break;
case 7:
confQualities = [1, 3, 5, 6];
break;
case 8:
confQualities = [1, 3, 6, 7];
break;
default:
if (levels.length < 3 || (levels[0].height && levels[2].height && levels[0].height === levels[2].height)) {
confQualities = [];
} else {
confQualities = [1, 2];
}
break;
}
video.qualities = [{
value: -1,
label: 'Auto'
}]
if (Array.isArray(hlsQualities)) {
var confAutoQuality = hlsQualities.find(function(q) { return q === -1 || q.level && q.level === -1; });
if (!confAutoQuality) video.qualities = [];
else video.qualities[0].label = typeof confAutoQuality !== 'number' ? confAutoQuality.label : video.qualities[0].label;
confQualities = hlsQualities.map(function(q) {
if (typeof q.level !== 'undefined') qualityLabels[q.level] = q.label;
return typeof q.level !== 'undefined' ? q.level : q;
});
}
var initialLevel = -2;
video.qualities = video.qualities.concat(levels.map(function(level, i) {
if (confQualities && confQualities.indexOf(i) === -1) return false;
var label = qualityLabels[i] || (Math.min(level.width, level.height) + 'p');
if (!qualityLabels[i] && hlsQualities !== 'drive') label += ' (' + Math.round(level.bitrate / 1000) + 'k)';
if (i === lastSelectedLevel) initialLevel = i;
return {
value: i,
label: label
};
})).filter(common.identity);
var currentLevel = video.quality = initialLevel === -2 ? video.qualities[0].value || -1 : initialLevel;
if (currentLevel !== hls.currentLevel) hls.currentLevel = currentLevel;
// End quality selection
hls.attachMedia(api);
if (lastSource && video.src !== lastSource) api.play();
lastSource = video.src;
});
hls.on(Hls.Events.ERROR, function(ev, data) {
if (!data.fatal) return;
if (conf.recoverNetworkError && data.type === Hls.ErrorTypes.NETWORK_ERROR) recover(true);
else if (conf.recoverMediaError && data.type === Hls.ErrorTypes.MEDIA_ERROR) recover(false);
else {
var code = 5;
if (data.type === Hls.ErrorTypes.NETWORK_ERROR) code = 2;
if (data.type === Hls.ErrorTypes.MEDIA_ERROR) code = 3;
hls.destroy();
player.trigger('error', [player, { code: code }]);
}
});
player.one('unload', function() {
hls.destroy();
});
return {
handlers: {
error: function(e, videoTag) {
var errorCode = videoTag.error && videoTag.error.code;
if (conf.recoverMediaError && errorCode === 3 || !errorCode) {
e.preventDefault();
recover(false);
return true;
}
if (conf.recoverNetworkError && errorCode === 2) {
e.preventDefault();
recover(true);
return true;
}
}
}
};
}
return html5factory('hlsjs-lite', player, root, canPlay, hlsjsExt);
};
engine.canPlay = function(type, conf) {
if (conf.hlsjs === false || (conf.clip && conf.clip.hlsjs === false)) return false;
if (support.browser.safari && !(conf.clip && conf.clip.hlsjs || conf.hlsjs || {}).safari) return false;
return flowplayer.support.video && canPlay(type);
};
engine.engineName = 'hlsjs-lite';
engine.plugin = function(extension) {
engine.extensions.push(extension);
}
engine.extensions = [];
flowplayer.engines.push(engine);
},{"../flowplayer":31,"./html5-factory":5}],5:[function(_dereq_,module,exports){
/*eslint indent: ["error", 2]*/
/*eslint quotes: ["error", "single"]*/
var flowplayer = _dereq_('../flowplayer')
, common = flowplayer.common
, support = flowplayer.support
, bean = flowplayer.bean
, extend = flowplayer.extend;
var desktopSafari = support.browser.safari && !support.iOS;
// HTML5 --> Flowplayer event
var EVENTS = {
ended: 'finish',
pause: 'pause',
play: 'resume',
timeupdate: 'progress',
volumechange: 'volume',
ratechange: 'speed',
seeked: 'seek',
loadedmetadata: !desktopSafari ? 'ready' : 0,
canplaythrough: desktopSafari ? 'ready' : 0,
durationchange: 'ready',
error: 'error',
dataunavailable: 'error',
webkitendfullscreen: !flowplayer.support.inlineVideo && 'unload',
progress: 'buffer'
};
function html5factory(engineName, player, root, canPlay, ext) {
var api = common.findDirect('video', root)[0] || common.find('.fp-player > video', root)[0]
, conf = player.conf
, timer
, volumeLevel
, self;
return self = {
engineName: engineName,
pick: function(sources) {
var source = support.video && sources.filter(function(s) {
return canPlay(s.type);
})[0];
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]
, created = false;
if (!api) {
api = document.createElement('video');
common.prepend(container, api);
api.autoplay = !!conf.splash;
created = true;
}
common.addClass(api, 'fp-engine');
common.find('track', api).forEach(common.removeNode);
api.preload = 'none';
if (!conf.nativesubtitles) common.attr(api, 'crossorigin', false);
if (!conf.disableInline) {
api.setAttribute('webkit-playsinline', 'true');
api.setAttribute('playsinline', 'true');
}
if (!support.inlineVideo) {
common.css(api, {
position: 'absolute',
top: '-9999em'
});
}
if (support.subtitles && conf.nativesubtitles && video.subtitles && video.subtitles.length) {
common.addClass(api, 'native-subtitles');
var subtitles = video.subtitles;
var setMode = function(mode) {
var tracks = api.textTracks;
if (!tracks.length) return;
tracks[0].mode = mode;
};
if (subtitles.some(function(st) { return !common.isSameDomain(st.src); })) common.attr(api, 'crossorigin', 'anonymous');
if (typeof api.textTracks.addEventListener === 'function') api.textTracks.addEventListener('addtrack', function() {
setMode('disabled');
setMode('showing');
});
subtitles.forEach(function(st) {
api.appendChild(common.createElement('track', {
kind: 'subtitles',
srclang: st.srclang || 'en',
label: st.label || 'en',
src: st.src,
'default': st['default']
}));
});
}
// 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) {
player.on('finish.loophack', function() { player.resume(); });
}
if (typeof volumeLevel !== 'undefined') {
api.volume = volumeLevel;
}
var extra = ext(video, api, self);
if (conf.autoplay || conf.splash || video.autoplay) {
player.debug('Autoplay / Splash setup, try to start video');
api.load();
var play = function () {
try {
var p = api.play();
if (p && p.catch) {
var recoverAutoplay = function(err) {
if (err.name === 'AbortError' && err.code === 20) {
if (!created) return api.play().catch(recoverAutoplay);
else return;
}
if (!conf.mutedAutoplay) throw new Error('Unable to autoplay');
player.debug('Play errored, trying muted', err);
player.mute(true, true);
return api.play();
}
p.catch(recoverAutoplay).catch(function() {
conf.autoplay = false;
player.mute(false, true); // Restore volume as playback failed
player.trigger('stop', [player]);
});
}
} catch(e) {
player.debug('play() error thrown', e);
}
};
if (api.readyState > 0) play();
else bean.one(api, 'canplay', play);
}
self._listeners = listen(api, common.find('source', api).concat(api), video, extra) || self._listeners;
if (conf.autoplay || conf.splash || video.autoplay) return; // No preload check needed
var preloadCheck = function() {
if (!isInViewport(root)) return;
player.debug('player is in viewport, preload');
if (support.preloadMetadata) api.preload = 'metadata';
else api.load();
bean.off(document, 'scroll.preloadviewport');
};
bean.off(document, 'scroll.preloadviewport');
bean.on(document, 'scroll.preloadviewport', function() {
window.requestAnimationFrame(preloadCheck);
});
preloadCheck();
},
mute: function(flag) {
api.muted = !!flag;
player.trigger('mute', [player, flag]);
player.trigger('volume', [player, flag ? 0 : api.volume]);
},
pause: function() {
api.pause();
},
resume: function() {
api.play();
},
speed: function(val) {
api.playbackRate = val;
},
seek: function(time) {
var pausedState = api.paused || player.finished;
try {
api.currentTime = time;
if (pausedState) bean.one(api, 'seeked', function() { api.pause(); });
} catch (ignored) {}
},
volume: function(level) {
volumeLevel = level;
if (api) {
api.volume = level;
if (level) self.mute(false);
}
},
unload: function() {
bean.off(document, 'scroll.preloadviewport');
common.find('video.fp-engine', root).forEach(function (videoTag) {
if ('MediaSource' in window) {
videoTag.src = URL.createObjectURL(new MediaSource());
} else {
videoTag.src = '';
}
common.removeNode(videoTag);
});
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, extra) {
// 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() {
if (!track.activeCues.length) return;
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 (!/progress/.test(flow)) player.debug(type, '->', flow, e);
var triggerEvent = function(f) {
player.trigger(f || 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':
if (player.ready) return player.debug('Player already ready, not sending duplicate ready event');
if ((!api.duration || api.duration === Infinity) && !player.live) return player.debug('No duration and VOD setup, not sending ready event');
arg = extend(video, {
duration: api.duration < Number.MAX_VALUE ? api.duration : 0,
width: api.videoWidth,
height: api.videoHeight,
url: api.currentSrc
});
arg.seekable = arg.duration;
player.debug('Ready: ', arg);
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 === 'seek' && api.currentTime === 0) {
arg = 0;
} else if (flow == 'progress') {
return;
}
break;
case 'buffer':
arg = [];
for (var i=0; i < api.buffered.length; i++) {
arg.push({
start: api.buffered.start(i),
end: api.buffered.end(i)
});
}
if (api.buffered.length && api.buffered.end(null) === api.duration) triggerEvent('buffered');
break;
case 'speed':
arg = round(api.playbackRate);
break;
case 'volume':
arg = round(api.muted ? 0 : api.volume);
break;
case 'error':
try {
if (extra && extra.handlers && extra.handlers.error) {
var handled = extra.handlers.error(e, api);
if (handled) return;