plyr/dist/plyr.polyfilled.js
2018-10-24 23:04:18 +11:00

10394 lines
330 KiB
JavaScript

typeof navigator === "object" && (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define('Plyr', factory) :
(global.Plyr = factory());
}(this, (function () { 'use strict';
// Polyfill for creating CustomEvents on IE9/10/11
// code pulled from:
// https://github.com/d4tocchini/customevent-polyfill
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent#Polyfill
(function() {
if (typeof window === 'undefined') {
return;
}
try {
var ce = new window.CustomEvent('test', { cancelable: true });
ce.preventDefault();
if (ce.defaultPrevented !== true) {
// IE has problems with .preventDefault() on custom events
// http://stackoverflow.com/questions/23349191
throw new Error('Could not prevent default');
}
} catch (e) {
var CustomEvent = function(event, params) {
var evt, origPrevent;
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
evt = document.createEvent('CustomEvent');
evt.initCustomEvent(
event,
params.bubbles,
params.cancelable,
params.detail
);
origPrevent = evt.preventDefault;
evt.preventDefault = function() {
origPrevent.call(this);
try {
Object.defineProperty(this, 'defaultPrevented', {
get: function() {
return true;
}
});
} catch (e) {
this.defaultPrevented = true;
}
};
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent; // expose definition to window
}
})();
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
(function(global) {
/**
* Polyfill URLSearchParams
*
* Inspired from : https://github.com/WebReflection/url-search-params/blob/master/src/url-search-params.js
*/
var checkIfIteratorIsSupported = function() {
try {
return !!Symbol.iterator;
} catch (error) {
return false;
}
};
var iteratorSupported = checkIfIteratorIsSupported();
var createIterator = function(items) {
var iterator = {
next: function() {
var value = items.shift();
return { done: value === void 0, value: value };
}
};
if (iteratorSupported) {
iterator[Symbol.iterator] = function() {
return iterator;
};
}
return iterator;
};
/**
* Search param name and values should be encoded according to https://url.spec.whatwg.org/#urlencoded-serializing
* encodeURIComponent() produces the same result except encoding spaces as `%20` instead of `+`.
*/
var serializeParam = function(value) {
return encodeURIComponent(value).replace(/%20/g, '+');
};
var deserializeParam = function(value) {
return decodeURIComponent(value).replace(/\+/g, ' ');
};
var polyfillURLSearchParams = function() {
var URLSearchParams = function(searchString) {
Object.defineProperty(this, '_entries', { writable: true, value: {} });
if (typeof searchString === 'string') {
if (searchString !== '') {
this._fromString(searchString);
}
} else if (searchString instanceof URLSearchParams) {
var _this = this;
searchString.forEach(function(value, name) {
_this.append(name, value);
});
}
};
var proto = URLSearchParams.prototype;
proto.append = function(name, value) {
if (name in this._entries) {
this._entries[name].push(value.toString());
} else {
this._entries[name] = [value.toString()];
}
};
proto.delete = function(name) {
delete this._entries[name];
};
proto.get = function(name) {
return (name in this._entries) ? this._entries[name][0] : null;
};
proto.getAll = function(name) {
return (name in this._entries) ? this._entries[name].slice(0) : [];
};
proto.has = function(name) {
return (name in this._entries);
};
proto.set = function(name, value) {
this._entries[name] = [value.toString()];
};
proto.forEach = function(callback, thisArg) {
var entries;
for (var name in this._entries) {
if (this._entries.hasOwnProperty(name)) {
entries = this._entries[name];
for (var i = 0; i < entries.length; i++) {
callback.call(thisArg, entries[i], name, this);
}
}
}
};
proto.keys = function() {
var items = [];
this.forEach(function(value, name) {
items.push(name);
});
return createIterator(items);
};
proto.values = function() {
var items = [];
this.forEach(function(value) {
items.push(value);
});
return createIterator(items);
};
proto.entries = function() {
var items = [];
this.forEach(function(value, name) {
items.push([name, value]);
});
return createIterator(items);
};
if (iteratorSupported) {
proto[Symbol.iterator] = proto.entries;
}
proto.toString = function() {
var searchArray = [];
this.forEach(function(value, name) {
searchArray.push(serializeParam(name) + '=' + serializeParam(value));
});
return searchArray.join('&');
};
Object.defineProperty(proto, '_fromString', {
enumerable: false,
configurable: false,
writable: false,
value: function(searchString) {
this._entries = {};
searchString = searchString.replace(/^\?/, '');
var attributes = searchString.split('&');
var attribute;
for (var i = 0; i < attributes.length; i++) {
attribute = attributes[i].split('=');
this.append(
deserializeParam(attribute[0]),
(attribute.length > 1) ? deserializeParam(attribute[1]) : ''
);
}
}
});
global.URLSearchParams = URLSearchParams;
};
if (!('URLSearchParams' in global) || (new URLSearchParams('?a=1').toString() !== 'a=1')) {
polyfillURLSearchParams();
}
if (typeof URLSearchParams.prototype.sort !== 'function') {
URLSearchParams.prototype.sort = function() {
var _this = this;
var items = [];
this.forEach(function(value, name) {
items.push([name, value]);
if (!_this._entries) {
_this.delete(name);
}
});
items.sort(function(a, b) {
if (a[0] < b[0]) {
return -1;
} else if (a[0] > b[0]) {
return +1;
} else {
return 0;
}
});
if (_this._entries) { // force reset because IE keeps keys index
_this._entries = {};
}
for (var i = 0; i < items.length; i++) {
this.append(items[i][0], items[i][1]);
}
};
}
// HTMLAnchorElement
})(
(typeof commonjsGlobal !== 'undefined') ? commonjsGlobal
: ((typeof window !== 'undefined') ? window
: ((typeof self !== 'undefined') ? self : commonjsGlobal))
);
(function(global) {
/**
* Polyfill URL
*
* Inspired from : https://github.com/arv/DOM-URL-Polyfill/blob/master/src/url.js
*/
var checkIfURLIsSupported = function() {
try {
var u = new URL('b', 'http://a');
u.pathname = 'c%20d';
return (u.href === 'http://a/c%20d') && u.searchParams;
} catch (e) {
return false;
}
};
var polyfillURL = function() {
var _URL = global.URL;
var URL = function(url, base) {
if (typeof url !== 'string') url = String(url);
// Only create another document if the base is different from current location.
var doc = document, baseElement;
if (base && (global.location === void 0 || base !== global.location.href)) {
doc = document.implementation.createHTMLDocument('');
baseElement = doc.createElement('base');
baseElement.href = base;
doc.head.appendChild(baseElement);
try {
if (baseElement.href.indexOf(base) !== 0) throw new Error(baseElement.href);
} catch (err) {
throw new Error('URL unable to set base ' + base + ' due to ' + err);
}
}
var anchorElement = doc.createElement('a');
anchorElement.href = url;
if (baseElement) {
doc.body.appendChild(anchorElement);
anchorElement.href = anchorElement.href; // force href to refresh
}
if (anchorElement.protocol === ':' || !/:/.test(anchorElement.href)) {
throw new TypeError('Invalid URL');
}
Object.defineProperty(this, '_anchorElement', {
value: anchorElement
});
// create a linked searchParams which reflect its changes on URL
var searchParams = new URLSearchParams(this.search);
var enableSearchUpdate = true;
var enableSearchParamsUpdate = true;
var _this = this;
['append', 'delete', 'set'].forEach(function(methodName) {
var method = searchParams[methodName];
searchParams[methodName] = function() {
method.apply(searchParams, arguments);
if (enableSearchUpdate) {
enableSearchParamsUpdate = false;
_this.search = searchParams.toString();
enableSearchParamsUpdate = true;
}
};
});
Object.defineProperty(this, 'searchParams', {
value: searchParams,
enumerable: true
});
var search = void 0;
Object.defineProperty(this, '_updateSearchParams', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
if (this.search !== search) {
search = this.search;
if (enableSearchParamsUpdate) {
enableSearchUpdate = false;
this.searchParams._fromString(this.search);
enableSearchUpdate = true;
}
}
}
});
};
var proto = URL.prototype;
var linkURLWithAnchorAttribute = function(attributeName) {
Object.defineProperty(proto, attributeName, {
get: function() {
return this._anchorElement[attributeName];
},
set: function(value) {
this._anchorElement[attributeName] = value;
},
enumerable: true
});
};
['hash', 'host', 'hostname', 'port', 'protocol']
.forEach(function(attributeName) {
linkURLWithAnchorAttribute(attributeName);
});
Object.defineProperty(proto, 'search', {
get: function() {
return this._anchorElement['search'];
},
set: function(value) {
this._anchorElement['search'] = value;
this._updateSearchParams();
},
enumerable: true
});
Object.defineProperties(proto, {
'toString': {
get: function() {
var _this = this;
return function() {
return _this.href;
};
}
},
'href': {
get: function() {
return this._anchorElement.href.replace(/\?$/, '');
},
set: function(value) {
this._anchorElement.href = value;
this._updateSearchParams();
},
enumerable: true
},
'pathname': {
get: function() {
return this._anchorElement.pathname.replace(/(^\/?)/, '/');
},
set: function(value) {
this._anchorElement.pathname = value;
},
enumerable: true
},
'origin': {
get: function() {
// get expected port from protocol
var expectedPort = { 'http:': 80, 'https:': 443, 'ftp:': 21 }[this._anchorElement.protocol];
// add port to origin if, expected port is different than actual port
// and it is not empty f.e http://foo:8080
// 8080 != 80 && 8080 != ''
var addPortToOrigin = this._anchorElement.port != expectedPort &&
this._anchorElement.port !== '';
return this._anchorElement.protocol +
'//' +
this._anchorElement.hostname +
(addPortToOrigin ? (':' + this._anchorElement.port) : '');
},
enumerable: true
},
'password': { // TODO
get: function() {
return '';
},
set: function(value) {
},
enumerable: true
},
'username': { // TODO
get: function() {
return '';
},
set: function(value) {
},
enumerable: true
},
});
URL.createObjectURL = function(blob) {
return _URL.createObjectURL.apply(_URL, arguments);
};
URL.revokeObjectURL = function(url) {
return _URL.revokeObjectURL.apply(_URL, arguments);
};
global.URL = URL;
};
if (!checkIfURLIsSupported()) {
polyfillURL();
}
if ((global.location !== void 0) && !('origin' in global.location)) {
var getOrigin = function() {
return global.location.protocol + '//' + global.location.hostname + (global.location.port ? (':' + global.location.port) : '');
};
try {
Object.defineProperty(global.location, 'origin', {
get: getOrigin,
enumerable: true
});
} catch (e) {
setInterval(function() {
global.location.origin = getOrigin();
}, 100);
}
}
})(
(typeof commonjsGlobal !== 'undefined') ? commonjsGlobal
: ((typeof window !== 'undefined') ? window
: ((typeof self !== 'undefined') ? self : commonjsGlobal))
);
var _aFunction = function (it) {
if (typeof it != 'function') throw TypeError(it + ' is not a function!');
return it;
};
// optional / simple context binding
var _ctx = function (fn, that, length) {
_aFunction(fn);
if (that === undefined) return fn;
switch (length) {
case 1: return function (a) {
return fn.call(that, a);
};
case 2: return function (a, b) {
return fn.call(that, a, b);
};
case 3: return function (a, b, c) {
return fn.call(that, a, b, c);
};
}
return function (/* ...args */) {
return fn.apply(that, arguments);
};
};
var _global = createCommonjsModule(function (module) {
// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
var global = module.exports = typeof window != 'undefined' && window.Math == Math
? window : typeof self != 'undefined' && self.Math == Math ? self
// eslint-disable-next-line no-new-func
: Function('return this')();
if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef
});
var _core = createCommonjsModule(function (module) {
var core = module.exports = { version: '2.5.7' };
if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
});
var _core_1 = _core.version;
var _isObject = function (it) {
return typeof it === 'object' ? it !== null : typeof it === 'function';
};
var _anObject = function (it) {
if (!_isObject(it)) throw TypeError(it + ' is not an object!');
return it;
};
var _fails = function (exec) {
try {
return !!exec();
} catch (e) {
return true;
}
};
// Thank's IE8 for his funny defineProperty
var _descriptors = !_fails(function () {
return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
});
var document$1 = _global.document;
// typeof document.createElement is 'object' in old IE
var is = _isObject(document$1) && _isObject(document$1.createElement);
var _domCreate = function (it) {
return is ? document$1.createElement(it) : {};
};
var _ie8DomDefine = !_descriptors && !_fails(function () {
return Object.defineProperty(_domCreate('div'), 'a', { get: function () { return 7; } }).a != 7;
});
// 7.1.1 ToPrimitive(input [, PreferredType])
// instead of the ES6 spec version, we didn't implement @@toPrimitive case
// and the second argument - flag - preferred type is a string
var _toPrimitive = function (it, S) {
if (!_isObject(it)) return it;
var fn, val;
if (S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) return val;
if (typeof (fn = it.valueOf) == 'function' && !_isObject(val = fn.call(it))) return val;
if (!S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) return val;
throw TypeError("Can't convert object to primitive value");
};
var dP = Object.defineProperty;
var f = _descriptors ? Object.defineProperty : function defineProperty(O, P, Attributes) {
_anObject(O);
P = _toPrimitive(P, true);
_anObject(Attributes);
if (_ie8DomDefine) try {
return dP(O, P, Attributes);
} catch (e) { /* empty */ }
if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!');
if ('value' in Attributes) O[P] = Attributes.value;
return O;
};
var _objectDp = {
f: f
};
var _propertyDesc = function (bitmap, value) {
return {
enumerable: !(bitmap & 1),
configurable: !(bitmap & 2),
writable: !(bitmap & 4),
value: value
};
};
var _hide = _descriptors ? function (object, key, value) {
return _objectDp.f(object, key, _propertyDesc(1, value));
} : function (object, key, value) {
object[key] = value;
return object;
};
var hasOwnProperty = {}.hasOwnProperty;
var _has = function (it, key) {
return hasOwnProperty.call(it, key);
};
var id = 0;
var px = Math.random();
var _uid = function (key) {
return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));
};
var _redefine = createCommonjsModule(function (module) {
var SRC = _uid('src');
var TO_STRING = 'toString';
var $toString = Function[TO_STRING];
var TPL = ('' + $toString).split(TO_STRING);
_core.inspectSource = function (it) {
return $toString.call(it);
};
(module.exports = function (O, key, val, safe) {
var isFunction = typeof val == 'function';
if (isFunction) _has(val, 'name') || _hide(val, 'name', key);
if (O[key] === val) return;
if (isFunction) _has(val, SRC) || _hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key)));
if (O === _global) {
O[key] = val;
} else if (!safe) {
delete O[key];
_hide(O, key, val);
} else if (O[key]) {
O[key] = val;
} else {
_hide(O, key, val);
}
// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
})(Function.prototype, TO_STRING, function toString() {
return typeof this == 'function' && this[SRC] || $toString.call(this);
});
});
var PROTOTYPE = 'prototype';
var $export = function (type, name, source) {
var IS_FORCED = type & $export.F;
var IS_GLOBAL = type & $export.G;
var IS_STATIC = type & $export.S;
var IS_PROTO = type & $export.P;
var IS_BIND = type & $export.B;
var target = IS_GLOBAL ? _global : IS_STATIC ? _global[name] || (_global[name] = {}) : (_global[name] || {})[PROTOTYPE];
var exports = IS_GLOBAL ? _core : _core[name] || (_core[name] = {});
var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {});
var key, own, out, exp;
if (IS_GLOBAL) source = name;
for (key in source) {
// contains in native
own = !IS_FORCED && target && target[key] !== undefined;
// export native or passed
out = (own ? target : source)[key];
// bind timers to global for call from export context
exp = IS_BIND && own ? _ctx(out, _global) : IS_PROTO && typeof out == 'function' ? _ctx(Function.call, out) : out;
// extend global
if (target) _redefine(target, key, out, type & $export.U);
// export
if (exports[key] != out) _hide(exports, key, exp);
if (IS_PROTO && expProto[key] != out) expProto[key] = out;
}
};
_global.core = _core;
// type bitmap
$export.F = 1; // forced
$export.G = 2; // global
$export.S = 4; // static
$export.P = 8; // proto
$export.B = 16; // bind
$export.W = 32; // wrap
$export.U = 64; // safe
$export.R = 128; // real proto method for `library`
var _export = $export;
// 7.2.1 RequireObjectCoercible(argument)
var _defined = function (it) {
if (it == undefined) throw TypeError("Can't call method on " + it);
return it;
};
// 7.1.13 ToObject(argument)
var _toObject = function (it) {
return Object(_defined(it));
};
// call something on iterator step with safe closing on error
var _iterCall = function (iterator, fn, value, entries) {
try {
return entries ? fn(_anObject(value)[0], value[1]) : fn(value);
// 7.4.6 IteratorClose(iterator, completion)
} catch (e) {
var ret = iterator['return'];
if (ret !== undefined) _anObject(ret.call(iterator));
throw e;
}
};
var _iterators = {};
var _library = false;
var _shared = createCommonjsModule(function (module) {
var SHARED = '__core-js_shared__';
var store = _global[SHARED] || (_global[SHARED] = {});
(module.exports = function (key, value) {
return store[key] || (store[key] = value !== undefined ? value : {});
})('versions', []).push({
version: _core.version,
mode: 'global',
copyright: '© 2018 Denis Pushkarev (zloirock.ru)'
});
});
var _wks = createCommonjsModule(function (module) {
var store = _shared('wks');
var Symbol = _global.Symbol;
var USE_SYMBOL = typeof Symbol == 'function';
var $exports = module.exports = function (name) {
return store[name] || (store[name] =
USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : _uid)('Symbol.' + name));
};
$exports.store = store;
});
// check on default Array iterator
var ITERATOR = _wks('iterator');
var ArrayProto = Array.prototype;
var _isArrayIter = function (it) {
return it !== undefined && (_iterators.Array === it || ArrayProto[ITERATOR] === it);
};
// 7.1.4 ToInteger
var ceil = Math.ceil;
var floor = Math.floor;
var _toInteger = function (it) {
return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
};
// 7.1.15 ToLength
var min = Math.min;
var _toLength = function (it) {
return it > 0 ? min(_toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991
};
var _createProperty = function (object, index, value) {
if (index in object) _objectDp.f(object, index, _propertyDesc(0, value));
else object[index] = value;
};
var toString = {}.toString;
var _cof = function (it) {
return toString.call(it).slice(8, -1);
};
// getting tag from 19.1.3.6 Object.prototype.toString()
var TAG = _wks('toStringTag');
// ES3 wrong here
var ARG = _cof(function () { return arguments; }()) == 'Arguments';
// fallback for IE11 Script Access Denied error
var tryGet = function (it, key) {
try {
return it[key];
} catch (e) { /* empty */ }
};
var _classof = function (it) {
var O, T, B;
return it === undefined ? 'Undefined' : it === null ? 'Null'
// @@toStringTag case
: typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T
// builtinTag case
: ARG ? _cof(O)
// ES3 arguments fallback
: (B = _cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;
};
var ITERATOR$1 = _wks('iterator');
var core_getIteratorMethod = _core.getIteratorMethod = function (it) {
if (it != undefined) return it[ITERATOR$1]
|| it['@@iterator']
|| _iterators[_classof(it)];
};
var ITERATOR$2 = _wks('iterator');
var SAFE_CLOSING = false;
try {
var riter = [7][ITERATOR$2]();
riter['return'] = function () { SAFE_CLOSING = true; };
} catch (e) { /* empty */ }
var _iterDetect = function (exec, skipClosing) {
if (!skipClosing && !SAFE_CLOSING) return false;
var safe = false;
try {
var arr = [7];
var iter = arr[ITERATOR$2]();
iter.next = function () { return { done: safe = true }; };
arr[ITERATOR$2] = function () { return iter; };
exec(arr);
} catch (e) { /* empty */ }
return safe;
};
_export(_export.S + _export.F * !_iterDetect(function (iter) { }), 'Array', {
// 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)
from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
var O = _toObject(arrayLike);
var C = typeof this == 'function' ? this : Array;
var aLen = arguments.length;
var mapfn = aLen > 1 ? arguments[1] : undefined;
var mapping = mapfn !== undefined;
var index = 0;
var iterFn = core_getIteratorMethod(O);
var length, result, step, iterator;
if (mapping) mapfn = _ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2);
// if object isn't iterable or it's array with default iterator - use simple case
if (iterFn != undefined && !(C == Array && _isArrayIter(iterFn))) {
for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) {
_createProperty(result, index, mapping ? _iterCall(iterator, mapfn, [step.value, index], true) : step.value);
}
} else {
length = _toLength(O.length);
for (result = new C(length); length > index; index++) {
_createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]);
}
}
result.length = index;
return result;
}
});
// fallback for non-array-like ES3 and non-enumerable old V8 strings
// eslint-disable-next-line no-prototype-builtins
var _iobject = Object('z').propertyIsEnumerable(0) ? Object : function (it) {
return _cof(it) == 'String' ? it.split('') : Object(it);
};
// 7.2.2 IsArray(argument)
var _isArray = Array.isArray || function isArray(arg) {
return _cof(arg) == 'Array';
};
var SPECIES = _wks('species');
var _arraySpeciesConstructor = function (original) {
var C;
if (_isArray(original)) {
C = original.constructor;
// cross-realm fallback
if (typeof C == 'function' && (C === Array || _isArray(C.prototype))) C = undefined;
if (_isObject(C)) {
C = C[SPECIES];
if (C === null) C = undefined;
}
} return C === undefined ? Array : C;
};
// 9.4.2.3 ArraySpeciesCreate(originalArray, length)
var _arraySpeciesCreate = function (original, length) {
return new (_arraySpeciesConstructor(original))(length);
};
// 0 -> Array#forEach
// 1 -> Array#map
// 2 -> Array#filter
// 3 -> Array#some
// 4 -> Array#every
// 5 -> Array#find
// 6 -> Array#findIndex
var _arrayMethods = function (TYPE, $create) {
var IS_MAP = TYPE == 1;
var IS_FILTER = TYPE == 2;
var IS_SOME = TYPE == 3;
var IS_EVERY = TYPE == 4;
var IS_FIND_INDEX = TYPE == 6;
var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
var create = $create || _arraySpeciesCreate;
return function ($this, callbackfn, that) {
var O = _toObject($this);
var self = _iobject(O);
var f = _ctx(callbackfn, that, 3);
var length = _toLength(self.length);
var index = 0;
var result = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;
var val, res;
for (;length > index; index++) if (NO_HOLES || index in self) {
val = self[index];
res = f(val, index, O);
if (TYPE) {
if (IS_MAP) result[index] = res; // map
else if (res) switch (TYPE) {
case 3: return true; // some
case 5: return val; // find
case 6: return index; // findIndex
case 2: result.push(val); // filter
} else if (IS_EVERY) return false; // every
}
}
return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : result;
};
};
// 22.1.3.31 Array.prototype[@@unscopables]
var UNSCOPABLES = _wks('unscopables');
var ArrayProto$1 = Array.prototype;
if (ArrayProto$1[UNSCOPABLES] == undefined) _hide(ArrayProto$1, UNSCOPABLES, {});
var _addToUnscopables = function (key) {
ArrayProto$1[UNSCOPABLES][key] = true;
};
// 22.1.3.8 Array.prototype.find(predicate, thisArg = undefined)
var $find = _arrayMethods(5);
var KEY = 'find';
var forced = true;
// Shouldn't skip holes
if (KEY in []) Array(1)[KEY](function () { forced = false; });
_export(_export.P + _export.F * forced, 'Array', {
find: function find(callbackfn /* , that = undefined */) {
return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
}
});
_addToUnscopables(KEY);
var f$1 = {}.propertyIsEnumerable;
var _objectPie = {
f: f$1
};
// to indexed object, toObject with fallback for non-array-like ES3 strings
var _toIobject = function (it) {
return _iobject(_defined(it));
};
var gOPD = Object.getOwnPropertyDescriptor;
var f$2 = _descriptors ? gOPD : function getOwnPropertyDescriptor(O, P) {
O = _toIobject(O);
P = _toPrimitive(P, true);
if (_ie8DomDefine) try {
return gOPD(O, P);
} catch (e) { /* empty */ }
if (_has(O, P)) return _propertyDesc(!_objectPie.f.call(O, P), O[P]);
};
var _objectGopd = {
f: f$2
};
// Works with __proto__ only. Old v8 can't work with null proto objects.
/* eslint-disable no-proto */
var check = function (O, proto) {
_anObject(O);
if (!_isObject(proto) && proto !== null) throw TypeError(proto + ": can't set as prototype!");
};
var _setProto = {
set: Object.setPrototypeOf || ('__proto__' in {} ? // eslint-disable-line
function (test, buggy, set) {
try {
set = _ctx(Function.call, _objectGopd.f(Object.prototype, '__proto__').set, 2);
set(test, []);
buggy = !(test instanceof Array);
} catch (e) { buggy = true; }
return function setPrototypeOf(O, proto) {
check(O, proto);
if (buggy) O.__proto__ = proto;
else set(O, proto);
return O;
};
}({}, false) : undefined),
check: check
};
var setPrototypeOf = _setProto.set;
var _inheritIfRequired = function (that, target, C) {
var S = target.constructor;
var P;
if (S !== C && typeof S == 'function' && (P = S.prototype) !== C.prototype && _isObject(P) && setPrototypeOf) {
setPrototypeOf(that, P);
} return that;
};
var max = Math.max;
var min$1 = Math.min;
var _toAbsoluteIndex = function (index, length) {
index = _toInteger(index);
return index < 0 ? max(index + length, 0) : min$1(index, length);
};
// false -> Array#indexOf
// true -> Array#includes
var _arrayIncludes = function (IS_INCLUDES) {
return function ($this, el, fromIndex) {
var O = _toIobject($this);
var length = _toLength(O.length);
var index = _toAbsoluteIndex(fromIndex, length);
var value;
// Array#includes uses SameValueZero equality algorithm
// eslint-disable-next-line no-self-compare
if (IS_INCLUDES && el != el) while (length > index) {
value = O[index++];
// eslint-disable-next-line no-self-compare
if (value != value) return true;
// Array#indexOf ignores holes, Array#includes - not
} else for (;length > index; index++) if (IS_INCLUDES || index in O) {
if (O[index] === el) return IS_INCLUDES || index || 0;
} return !IS_INCLUDES && -1;
};
};
var shared = _shared('keys');
var _sharedKey = function (key) {
return shared[key] || (shared[key] = _uid(key));
};
var arrayIndexOf = _arrayIncludes(false);
var IE_PROTO = _sharedKey('IE_PROTO');
var _objectKeysInternal = function (object, names) {
var O = _toIobject(object);
var i = 0;
var result = [];
var key;
for (key in O) if (key != IE_PROTO) _has(O, key) && result.push(key);
// Don't enum bug & hidden keys
while (names.length > i) if (_has(O, key = names[i++])) {
~arrayIndexOf(result, key) || result.push(key);
}
return result;
};
// IE 8- don't enum bug keys
var _enumBugKeys = (
'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'
).split(',');
// 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O)
var hiddenKeys = _enumBugKeys.concat('length', 'prototype');
var f$3 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
return _objectKeysInternal(O, hiddenKeys);
};
var _objectGopn = {
f: f$3
};
var _stringWs = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
'\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
var space = '[' + _stringWs + ']';
var non = '\u200b\u0085';
var ltrim = RegExp('^' + space + space + '*');
var rtrim = RegExp(space + space + '*$');
var exporter = function (KEY, exec, ALIAS) {
var exp = {};
var FORCE = _fails(function () {
return !!_stringWs[KEY]() || non[KEY]() != non;
});
var fn = exp[KEY] = FORCE ? exec(trim) : _stringWs[KEY];
if (ALIAS) exp[ALIAS] = fn;
_export(_export.P + _export.F * FORCE, 'String', exp);
};
// 1 -> String#trimLeft
// 2 -> String#trimRight
// 3 -> String#trim
var trim = exporter.trim = function (string, TYPE) {
string = String(_defined(string));
if (TYPE & 1) string = string.replace(ltrim, '');
if (TYPE & 2) string = string.replace(rtrim, '');
return string;
};
var _stringTrim = exporter;
// 19.1.2.14 / 15.2.3.14 Object.keys(O)
var _objectKeys = Object.keys || function keys(O) {
return _objectKeysInternal(O, _enumBugKeys);
};
var _objectDps = _descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
_anObject(O);
var keys = _objectKeys(Properties);
var length = keys.length;
var i = 0;
var P;
while (length > i) _objectDp.f(O, P = keys[i++], Properties[P]);
return O;
};
var document$2 = _global.document;
var _html = document$2 && document$2.documentElement;
// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
var IE_PROTO$1 = _sharedKey('IE_PROTO');
var Empty = function () { /* empty */ };
var PROTOTYPE$1 = 'prototype';
// Create object with fake `null` prototype: use iframe Object with cleared prototype
var createDict = function () {
// Thrash, waste and sodomy: IE GC bug
var iframe = _domCreate('iframe');
var i = _enumBugKeys.length;
var lt = '<';
var gt = '>';
var iframeDocument;
iframe.style.display = 'none';
_html.appendChild(iframe);
iframe.src = 'javascript:'; // eslint-disable-line no-script-url
// createDict = iframe.contentWindow.Object;
// html.removeChild(iframe);
iframeDocument = iframe.contentWindow.document;
iframeDocument.open();
iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);
iframeDocument.close();
createDict = iframeDocument.F;
while (i--) delete createDict[PROTOTYPE$1][_enumBugKeys[i]];
return createDict();
};
var _objectCreate = Object.create || function create(O, Properties) {
var result;
if (O !== null) {
Empty[PROTOTYPE$1] = _anObject(O);
result = new Empty();
Empty[PROTOTYPE$1] = null;
// add "__proto__" for Object.getPrototypeOf polyfill
result[IE_PROTO$1] = O;
} else result = createDict();
return Properties === undefined ? result : _objectDps(result, Properties);
};
var gOPN = _objectGopn.f;
var gOPD$1 = _objectGopd.f;
var dP$1 = _objectDp.f;
var $trim = _stringTrim.trim;
var NUMBER = 'Number';
var $Number = _global[NUMBER];
var Base = $Number;
var proto = $Number.prototype;
// Opera ~12 has broken Object#toString
var BROKEN_COF = _cof(_objectCreate(proto)) == NUMBER;
var TRIM = 'trim' in String.prototype;
// 7.1.3 ToNumber(argument)
var toNumber = function (argument) {
var it = _toPrimitive(argument, false);
if (typeof it == 'string' && it.length > 2) {
it = TRIM ? it.trim() : $trim(it, 3);
var first = it.charCodeAt(0);
var third, radix, maxCode;
if (first === 43 || first === 45) {
third = it.charCodeAt(2);
if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
} else if (first === 48) {
switch (it.charCodeAt(1)) {
case 66: case 98: radix = 2; maxCode = 49; break; // fast equal /^0b[01]+$/i
case 79: case 111: radix = 8; maxCode = 55; break; // fast equal /^0o[0-7]+$/i
default: return +it;
}
for (var digits = it.slice(2), i = 0, l = digits.length, code; i < l; i++) {
code = digits.charCodeAt(i);
// parseInt parses a string to a first unavailable symbol
// but ToNumber should return NaN if a string contains unavailable symbols
if (code < 48 || code > maxCode) return NaN;
} return parseInt(digits, radix);
}
} return +it;
};
if (!$Number(' 0o1') || !$Number('0b1') || $Number('+0x1')) {
$Number = function Number(value) {
var it = arguments.length < 1 ? 0 : value;
var that = this;
return that instanceof $Number
// check on 1..constructor(foo) case
&& (BROKEN_COF ? _fails(function () { proto.valueOf.call(that); }) : _cof(that) != NUMBER)
? _inheritIfRequired(new Base(toNumber(it)), that, $Number) : toNumber(it);
};
for (var keys = _descriptors ? gOPN(Base) : (
// ES3:
'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
// ES6 (in case, if modules with ES6 Number statics required before):
'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger'
).split(','), j = 0, key; keys.length > j; j++) {
if (_has(Base, key = keys[j]) && !_has($Number, key)) {
dP$1($Number, key, gOPD$1(Base, key));
}
}
$Number.prototype = proto;
proto.constructor = $Number;
_redefine(_global, NUMBER, $Number);
}
// most Object methods by ES6 should accept primitives
var _objectSap = function (KEY, exec) {
var fn = (_core.Object || {})[KEY] || Object[KEY];
var exp = {};
exp[KEY] = exec(fn);
_export(_export.S + _export.F * _fails(function () { fn(1); }), 'Object', exp);
};
// 19.1.2.14 Object.keys(O)
_objectSap('keys', function () {
return function keys(it) {
return _objectKeys(_toObject(it));
};
});
// 7.2.8 IsRegExp(argument)
var MATCH = _wks('match');
var _isRegexp = function (it) {
var isRegExp;
return _isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : _cof(it) == 'RegExp');
};
// helper for String#{startsWith, endsWith, includes}
var _stringContext = function (that, searchString, NAME) {
if (_isRegexp(searchString)) throw TypeError('String#' + NAME + " doesn't accept regex!");
return String(_defined(that));
};
var MATCH$1 = _wks('match');
var _failsIsRegexp = function (KEY) {
var re = /./;
try {
'/./'[KEY](re);
} catch (e) {
try {
re[MATCH$1] = false;
return !'/./'[KEY](re);
} catch (f) { /* empty */ }
} return true;
};
var INCLUDES = 'includes';
_export(_export.P + _export.F * _failsIsRegexp(INCLUDES), 'String', {
includes: function includes(searchString /* , position = 0 */) {
return !!~_stringContext(this, searchString, INCLUDES)
.indexOf(searchString, arguments.length > 1 ? arguments[1] : undefined);
}
});
// https://github.com/tc39/Array.prototype.includes
var $includes = _arrayIncludes(true);
_export(_export.P, 'Array', {
includes: function includes(el /* , fromIndex = 0 */) {
return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
}
});
_addToUnscopables('includes');
var _fixReWks = function (KEY, length, exec) {
var SYMBOL = _wks(KEY);
var fns = exec(_defined, SYMBOL, ''[KEY]);
var strfn = fns[0];
var rxfn = fns[1];
if (_fails(function () {
var O = {};
O[SYMBOL] = function () { return 7; };
return ''[KEY](O) != 7;
})) {
_redefine(String.prototype, KEY, strfn);
_hide(RegExp.prototype, SYMBOL, length == 2
// 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
// 21.2.5.11 RegExp.prototype[@@split](string, limit)
? function (string, arg) { return rxfn.call(string, this, arg); }
// 21.2.5.6 RegExp.prototype[@@match](string)
// 21.2.5.9 RegExp.prototype[@@search](string)
: function (string) { return rxfn.call(string, this); }
);
}
};
// @@search logic
_fixReWks('search', 1, function (defined, SEARCH, $search) {
// 21.1.3.15 String.prototype.search(regexp)
return [function search(regexp) {
var O = defined(this);
var fn = regexp == undefined ? undefined : regexp[SEARCH];
return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
}, $search];
});
// 21.2.5.3 get RegExp.prototype.flags
var _flags = function () {
var that = _anObject(this);
var result = '';
if (that.global) result += 'g';
if (that.ignoreCase) result += 'i';
if (that.multiline) result += 'm';
if (that.unicode) result += 'u';
if (that.sticky) result += 'y';
return result;
};
// 21.2.5.3 get RegExp.prototype.flags()
if (_descriptors && /./g.flags != 'g') _objectDp.f(RegExp.prototype, 'flags', {
configurable: true,
get: _flags
});
var TO_STRING = 'toString';
var $toString = /./[TO_STRING];
var define = function (fn) {
_redefine(RegExp.prototype, TO_STRING, fn, true);
};
// 21.2.5.14 RegExp.prototype.toString()
if (_fails(function () { return $toString.call({ source: 'a', flags: 'b' }) != '/a/b'; })) {
define(function toString() {
var R = _anObject(this);
return '/'.concat(R.source, '/',
'flags' in R ? R.flags : !_descriptors && R instanceof RegExp ? _flags.call(R) : undefined);
});
// FF44- RegExp#toString has a wrong name
} else if ($toString.name != TO_STRING) {
define(function toString() {
return $toString.call(this);
});
}
var _iterStep = function (done, value) {
return { value: value, done: !!done };
};
var def = _objectDp.f;
var TAG$1 = _wks('toStringTag');
var _setToStringTag = function (it, tag, stat) {
if (it && !_has(it = stat ? it : it.prototype, TAG$1)) def(it, TAG$1, { configurable: true, value: tag });
};
var IteratorPrototype = {};
// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
_hide(IteratorPrototype, _wks('iterator'), function () { return this; });
var _iterCreate = function (Constructor, NAME, next) {
Constructor.prototype = _objectCreate(IteratorPrototype, { next: _propertyDesc(1, next) });
_setToStringTag(Constructor, NAME + ' Iterator');
};
// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)
var IE_PROTO$2 = _sharedKey('IE_PROTO');
var ObjectProto = Object.prototype;
var _objectGpo = Object.getPrototypeOf || function (O) {
O = _toObject(O);
if (_has(O, IE_PROTO$2)) return O[IE_PROTO$2];
if (typeof O.constructor == 'function' && O instanceof O.constructor) {
return O.constructor.prototype;
} return O instanceof Object ? ObjectProto : null;
};
var ITERATOR$3 = _wks('iterator');
var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`
var FF_ITERATOR = '@@iterator';
var KEYS = 'keys';
var VALUES = 'values';
var returnThis = function () { return this; };
var _iterDefine = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {
_iterCreate(Constructor, NAME, next);
var getMethod = function (kind) {
if (!BUGGY && kind in proto) return proto[kind];
switch (kind) {
case KEYS: return function keys() { return new Constructor(this, kind); };
case VALUES: return function values() { return new Constructor(this, kind); };
} return function entries() { return new Constructor(this, kind); };
};
var TAG = NAME + ' Iterator';
var DEF_VALUES = DEFAULT == VALUES;
var VALUES_BUG = false;
var proto = Base.prototype;
var $native = proto[ITERATOR$3] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
var $default = $native || getMethod(DEFAULT);
var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
var methods, key, IteratorPrototype;
// Fix native
if ($anyNative) {
IteratorPrototype = _objectGpo($anyNative.call(new Base()));
if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {
// Set @@toStringTag to native iterators
_setToStringTag(IteratorPrototype, TAG, true);
// fix for some old engines
if (typeof IteratorPrototype[ITERATOR$3] != 'function') _hide(IteratorPrototype, ITERATOR$3, returnThis);
}
}
// fix Array#{values, @@iterator}.name in V8 / FF
if (DEF_VALUES && $native && $native.name !== VALUES) {
VALUES_BUG = true;
$default = function values() { return $native.call(this); };
}
// Define iterator
if (BUGGY || VALUES_BUG || !proto[ITERATOR$3]) {
_hide(proto, ITERATOR$3, $default);
}
// Plug for library
_iterators[NAME] = $default;
_iterators[TAG] = returnThis;
if (DEFAULT) {
methods = {
values: DEF_VALUES ? $default : getMethod(VALUES),
keys: IS_SET ? $default : getMethod(KEYS),
entries: $entries
};
if (FORCED) for (key in methods) {
if (!(key in proto)) _redefine(proto, key, methods[key]);
} else _export(_export.P + _export.F * (BUGGY || VALUES_BUG), NAME, methods);
}
return methods;
};
// 22.1.3.4 Array.prototype.entries()
// 22.1.3.13 Array.prototype.keys()
// 22.1.3.29 Array.prototype.values()
// 22.1.3.30 Array.prototype[@@iterator]()
var es6_array_iterator = _iterDefine(Array, 'Array', function (iterated, kind) {
this._t = _toIobject(iterated); // target
this._i = 0; // next index
this._k = kind; // kind
// 22.1.5.2.1 %ArrayIteratorPrototype%.next()
}, function () {
var O = this._t;
var kind = this._k;
var index = this._i++;
if (!O || index >= O.length) {
this._t = undefined;
return _iterStep(1);
}
if (kind == 'keys') return _iterStep(0, index);
if (kind == 'values') return _iterStep(0, O[index]);
return _iterStep(0, [index, O[index]]);
}, 'values');
// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)
_iterators.Arguments = _iterators.Array;
_addToUnscopables('keys');
_addToUnscopables('values');
_addToUnscopables('entries');
var ITERATOR$4 = _wks('iterator');
var TO_STRING_TAG = _wks('toStringTag');
var ArrayValues = _iterators.Array;
var DOMIterables = {
CSSRuleList: true, // TODO: Not spec compliant, should be false.
CSSStyleDeclaration: false,
CSSValueList: false,
ClientRectList: false,
DOMRectList: false,
DOMStringList: false,
DOMTokenList: true,
DataTransferItemList: false,
FileList: false,
HTMLAllCollection: false,
HTMLCollection: false,
HTMLFormElement: false,
HTMLSelectElement: false,
MediaList: true, // TODO: Not spec compliant, should be false.
MimeTypeArray: false,
NamedNodeMap: false,
NodeList: true,
PaintRequestList: false,
Plugin: false,
PluginArray: false,
SVGLengthList: false,
SVGNumberList: false,
SVGPathSegList: false,
SVGPointList: false,
SVGStringList: false,
SVGTransformList: false,
SourceBufferList: false,
StyleSheetList: true, // TODO: Not spec compliant, should be false.
TextTrackCueList: false,
TextTrackList: false,
TouchList: false
};
for (var collections = _objectKeys(DOMIterables), i = 0; i < collections.length; i++) {
var NAME = collections[i];
var explicit = DOMIterables[NAME];
var Collection = _global[NAME];
var proto$1 = Collection && Collection.prototype;
var key$1;
if (proto$1) {
if (!proto$1[ITERATOR$4]) _hide(proto$1, ITERATOR$4, ArrayValues);
if (!proto$1[TO_STRING_TAG]) _hide(proto$1, TO_STRING_TAG, NAME);
_iterators[NAME] = ArrayValues;
if (explicit) for (key$1 in es6_array_iterator) if (!proto$1[key$1]) _redefine(proto$1, key$1, es6_array_iterator[key$1], true);
}
}
// true -> String#at
// false -> String#codePointAt
var _stringAt = function (TO_STRING) {
return function (that, pos) {
var s = String(_defined(that));
var i = _toInteger(pos);
var l = s.length;
var a, b;
if (i < 0 || i >= l) return TO_STRING ? '' : undefined;
a = s.charCodeAt(i);
return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
? TO_STRING ? s.charAt(i) : a
: TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
};
};
var $at = _stringAt(true);
// 21.1.3.27 String.prototype[@@iterator]()
_iterDefine(String, 'String', function (iterated) {
this._t = String(iterated); // target
this._i = 0; // next index
// 21.1.5.2.1 %StringIteratorPrototype%.next()
}, function () {
var O = this._t;
var index = this._i;
var point;
if (index >= O.length) return { value: undefined, done: true };
point = $at(O, index);
this._i += point.length;
return { value: point, done: false };
});
var _meta = createCommonjsModule(function (module) {
var META = _uid('meta');
var setDesc = _objectDp.f;
var id = 0;
var isExtensible = Object.isExtensible || function () {
return true;
};
var FREEZE = !_fails(function () {
return isExtensible(Object.preventExtensions({}));
});
var setMeta = function (it) {
setDesc(it, META, { value: {
i: 'O' + ++id, // object ID
w: {} // weak collections IDs
} });
};
var fastKey = function (it, create) {
// return primitive with prefix
if (!_isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
if (!_has(it, META)) {
// can't set metadata to uncaught frozen object
if (!isExtensible(it)) return 'F';
// not necessary to add metadata
if (!create) return 'E';
// add missing metadata
setMeta(it);
// return object ID
} return it[META].i;
};
var getWeak = function (it, create) {
if (!_has(it, META)) {
// can't set metadata to uncaught frozen object
if (!isExtensible(it)) return true;
// not necessary to add metadata
if (!create) return false;
// add missing metadata
setMeta(it);
// return hash weak collections IDs
} return it[META].w;
};
// add metadata on freeze-family methods calling
var onFreeze = function (it) {
if (FREEZE && meta.NEED && isExtensible(it) && !_has(it, META)) setMeta(it);
return it;
};
var meta = module.exports = {
KEY: META,
NEED: false,
fastKey: fastKey,
getWeak: getWeak,
onFreeze: onFreeze
};
});
var _meta_1 = _meta.KEY;
var _meta_2 = _meta.NEED;
var _meta_3 = _meta.fastKey;
var _meta_4 = _meta.getWeak;
var _meta_5 = _meta.onFreeze;
var f$4 = Object.getOwnPropertySymbols;
var _objectGops = {
f: f$4
};
// 19.1.2.1 Object.assign(target, source, ...)
var $assign = Object.assign;
// should work with symbols and should have deterministic property order (V8 bug)
var _objectAssign = !$assign || _fails(function () {
var A = {};
var B = {};
// eslint-disable-next-line no-undef
var S = Symbol();
var K = 'abcdefghijklmnopqrst';
A[S] = 7;
K.split('').forEach(function (k) { B[k] = k; });
return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K;
}) ? function assign(target, source) { // eslint-disable-line no-unused-vars
var T = _toObject(target);
var aLen = arguments.length;
var index = 1;
var getSymbols = _objectGops.f;
var isEnum = _objectPie.f;
while (aLen > index) {
var S = _iobject(arguments[index++]);
var keys = getSymbols ? _objectKeys(S).concat(getSymbols(S)) : _objectKeys(S);
var length = keys.length;
var j = 0;
var key;
while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key];
} return T;
} : $assign;
var _redefineAll = function (target, src, safe) {
for (var key in src) _redefine(target, key, src[key], safe);
return target;
};
var _anInstance = function (it, Constructor, name, forbiddenField) {
if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) {
throw TypeError(name + ': incorrect invocation!');
} return it;
};
var _forOf = createCommonjsModule(function (module) {
var BREAK = {};
var RETURN = {};
var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) {
var iterFn = ITERATOR ? function () { return iterable; } : core_getIteratorMethod(iterable);
var f = _ctx(fn, that, entries ? 2 : 1);
var index = 0;
var length, step, iterator, result;
if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!');
// fast case for arrays with default iterator
if (_isArrayIter(iterFn)) for (length = _toLength(iterable.length); length > index; index++) {
result = entries ? f(_anObject(step = iterable[index])[0], step[1]) : f(iterable[index]);
if (result === BREAK || result === RETURN) return result;
} else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) {
result = _iterCall(iterator, f, step.value, entries);
if (result === BREAK || result === RETURN) return result;
}
};
exports.BREAK = BREAK;
exports.RETURN = RETURN;
});
var _validateCollection = function (it, TYPE) {
if (!_isObject(it) || it._t !== TYPE) throw TypeError('Incompatible receiver, ' + TYPE + ' required!');
return it;
};
var getWeak = _meta.getWeak;
var arrayFind = _arrayMethods(5);
var arrayFindIndex = _arrayMethods(6);
var id$1 = 0;
// fallback for uncaught frozen keys
var uncaughtFrozenStore = function (that) {
return that._l || (that._l = new UncaughtFrozenStore());
};
var UncaughtFrozenStore = function () {
this.a = [];
};
var findUncaughtFrozen = function (store, key) {
return arrayFind(store.a, function (it) {
return it[0] === key;
});
};
UncaughtFrozenStore.prototype = {
get: function (key) {
var entry = findUncaughtFrozen(this, key);
if (entry) return entry[1];
},
has: function (key) {
return !!findUncaughtFrozen(this, key);
},
set: function (key, value) {
var entry = findUncaughtFrozen(this, key);
if (entry) entry[1] = value;
else this.a.push([key, value]);
},
'delete': function (key) {
var index = arrayFindIndex(this.a, function (it) {
return it[0] === key;
});
if (~index) this.a.splice(index, 1);
return !!~index;
}
};
var _collectionWeak = {
getConstructor: function (wrapper, NAME, IS_MAP, ADDER) {
var C = wrapper(function (that, iterable) {
_anInstance(that, C, NAME, '_i');
that._t = NAME; // collection type
that._i = id$1++; // collection id
that._l = undefined; // leak store for uncaught frozen objects
if (iterable != undefined) _forOf(iterable, IS_MAP, that[ADDER], that);
});
_redefineAll(C.prototype, {
// 23.3.3.2 WeakMap.prototype.delete(key)
// 23.4.3.3 WeakSet.prototype.delete(value)
'delete': function (key) {
if (!_isObject(key)) return false;
var data = getWeak(key);
if (data === true) return uncaughtFrozenStore(_validateCollection(this, NAME))['delete'](key);
return data && _has(data, this._i) && delete data[this._i];
},
// 23.3.3.4 WeakMap.prototype.has(key)
// 23.4.3.4 WeakSet.prototype.has(value)
has: function has(key) {
if (!_isObject(key)) return false;
var data = getWeak(key);
if (data === true) return uncaughtFrozenStore(_validateCollection(this, NAME)).has(key);
return data && _has(data, this._i);
}
});
return C;
},
def: function (that, key, value) {
var data = getWeak(_anObject(key), true);
if (data === true) uncaughtFrozenStore(that).set(key, value);
else data[that._i] = value;
return that;
},
ufstore: uncaughtFrozenStore
};
var _collection = function (NAME, wrapper, methods, common, IS_MAP, IS_WEAK) {
var Base = _global[NAME];
var C = Base;
var ADDER = IS_MAP ? 'set' : 'add';
var proto = C && C.prototype;
var O = {};
var fixMethod = function (KEY) {
var fn = proto[KEY];
_redefine(proto, KEY,
KEY == 'delete' ? function (a) {
return IS_WEAK && !_isObject(a) ? false : fn.call(this, a === 0 ? 0 : a);
} : KEY == 'has' ? function has(a) {
return IS_WEAK && !_isObject(a) ? false : fn.call(this, a === 0 ? 0 : a);
} : KEY == 'get' ? function get(a) {
return IS_WEAK && !_isObject(a) ? undefined : fn.call(this, a === 0 ? 0 : a);
} : KEY == 'add' ? function add(a) { fn.call(this, a === 0 ? 0 : a); return this; }
: function set(a, b) { fn.call(this, a === 0 ? 0 : a, b); return this; }
);
};
if (typeof C != 'function' || !(IS_WEAK || proto.forEach && !_fails(function () {
new C().entries().next();
}))) {
// create collection constructor
C = common.getConstructor(wrapper, NAME, IS_MAP, ADDER);
_redefineAll(C.prototype, methods);
_meta.NEED = true;
} else {
var instance = new C();
// early implementations not supports chaining
var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
// V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
var THROWS_ON_PRIMITIVES = _fails(function () { instance.has(1); });
// most early implementations doesn't supports iterables, most modern - not close it correctly
var ACCEPT_ITERABLES = _iterDetect(function (iter) { new C(iter); }); // eslint-disable-line no-new
// for early implementations -0 and +0 not the same
var BUGGY_ZERO = !IS_WEAK && _fails(function () {
// V8 ~ Chromium 42- fails only with 5+ elements
var $instance = new C();
var index = 5;
while (index--) $instance[ADDER](index, index);
return !$instance.has(-0);
});
if (!ACCEPT_ITERABLES) {
C = wrapper(function (target, iterable) {
_anInstance(target, C, NAME);
var that = _inheritIfRequired(new Base(), target, C);
if (iterable != undefined) _forOf(iterable, IS_MAP, that[ADDER], that);
return that;
});
C.prototype = proto;
proto.constructor = C;
}
if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
fixMethod('delete');
fixMethod('has');
IS_MAP && fixMethod('get');
}
if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
// weak collections should not contains .clear method
if (IS_WEAK && proto.clear) delete proto.clear;
}
_setToStringTag(C, NAME);
O[NAME] = C;
_export(_export.G + _export.W + _export.F * (C != Base), O);
if (!IS_WEAK) common.setStrong(C, NAME, IS_MAP);
return C;
};
var es6_weakMap = createCommonjsModule(function (module) {
var each = _arrayMethods(0);
var WEAK_MAP = 'WeakMap';
var getWeak = _meta.getWeak;
var isExtensible = Object.isExtensible;
var uncaughtFrozenStore = _collectionWeak.ufstore;
var tmp = {};
var InternalMap;
var wrapper = function (get) {
return function WeakMap() {
return get(this, arguments.length > 0 ? arguments[0] : undefined);
};
};
var methods = {
// 23.3.3.3 WeakMap.prototype.get(key)
get: function get(key) {
if (_isObject(key)) {
var data = getWeak(key);
if (data === true) return uncaughtFrozenStore(_validateCollection(this, WEAK_MAP)).get(key);
return data ? data[this._i] : undefined;
}
},
// 23.3.3.5 WeakMap.prototype.set(key, value)
set: function set(key, value) {
return _collectionWeak.def(_validateCollection(this, WEAK_MAP), key, value);
}
};
// 23.3 WeakMap Objects
var $WeakMap = module.exports = _collection(WEAK_MAP, wrapper, methods, _collectionWeak, true, true);
// IE11 WeakMap frozen keys fix
if (_fails(function () { return new $WeakMap().set((Object.freeze || Object)(tmp), 7).get(tmp) != 7; })) {
InternalMap = _collectionWeak.getConstructor(wrapper, WEAK_MAP);
_objectAssign(InternalMap.prototype, methods);
_meta.NEED = true;
each(['delete', 'has', 'get', 'set'], function (key) {
var proto = $WeakMap.prototype;
var method = proto[key];
_redefine(proto, key, function (a, b) {
// store frozen objects on internal weakmap shim
if (_isObject(a) && !isExtensible(a)) {
if (!this._f) this._f = new InternalMap();
var result = this._f[key](a, b);
return key == 'set' ? this : result;
// store all the rest on native weakmap
} return method.call(this, a, b);
});
});
}
});
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
// 19.1.3.1 Object.assign(target, source)
_export(_export.S + _export.F, 'Object', { assign: _objectAssign });
// @@split logic
_fixReWks('split', 2, function (defined, SPLIT, $split) {
var isRegExp = _isRegexp;
var _split = $split;
var $push = [].push;
var $SPLIT = 'split';
var LENGTH = 'length';
var LAST_INDEX = 'lastIndex';
if (
'abbc'[$SPLIT](/(b)*/)[1] == 'c' ||
'test'[$SPLIT](/(?:)/, -1)[LENGTH] != 4 ||
'ab'[$SPLIT](/(?:ab)*/)[LENGTH] != 2 ||
'.'[$SPLIT](/(.?)(.?)/)[LENGTH] != 4 ||
'.'[$SPLIT](/()()/)[LENGTH] > 1 ||
''[$SPLIT](/.?/)[LENGTH]
) {
var NPCG = /()??/.exec('')[1] === undefined; // nonparticipating capturing group
// based on es5-shim implementation, need to rework it
$split = function (separator, limit) {
var string = String(this);
if (separator === undefined && limit === 0) return [];
// If `separator` is not a regex, use native split
if (!isRegExp(separator)) return _split.call(string, separator, limit);
var output = [];
var flags = (separator.ignoreCase ? 'i' : '') +
(separator.multiline ? 'm' : '') +
(separator.unicode ? 'u' : '') +
(separator.sticky ? 'y' : '');
var lastLastIndex = 0;
var splitLimit = limit === undefined ? 4294967295 : limit >>> 0;
// Make `global` and avoid `lastIndex` issues by working with a copy
var separatorCopy = new RegExp(separator.source, flags + 'g');
var separator2, match, lastIndex, lastLength, i;
// Doesn't need flags gy, but they don't hurt
if (!NPCG) separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\s)', flags);
while (match = separatorCopy.exec(string)) {
// `separatorCopy.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0][LENGTH];
if (lastIndex > lastLastIndex) {
output.push(string.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for NPCG
// eslint-disable-next-line no-loop-func
if (!NPCG && match[LENGTH] > 1) match[0].replace(separator2, function () {
for (i = 1; i < arguments[LENGTH] - 2; i++) if (arguments[i] === undefined) match[i] = undefined;
});
if (match[LENGTH] > 1 && match.index < string[LENGTH]) $push.apply(output, match.slice(1));
lastLength = match[0][LENGTH];
lastLastIndex = lastIndex;
if (output[LENGTH] >= splitLimit) break;
}
if (separatorCopy[LAST_INDEX] === match.index) separatorCopy[LAST_INDEX]++; // Avoid an infinite loop
}
if (lastLastIndex === string[LENGTH]) {
if (lastLength || !separatorCopy.test('')) output.push('');
} else output.push(string.slice(lastLastIndex));
return output[LENGTH] > splitLimit ? output.slice(0, splitLimit) : output;
};
// Chakra, V8
} else if ('0'[$SPLIT](undefined, 0)[LENGTH]) {
$split = function (separator, limit) {
return separator === undefined && limit === 0 ? [] : _split.call(this, separator, limit);
};
}
// 21.1.3.17 String.prototype.split(separator, limit)
return [function split(separator, limit) {
var O = defined(this);
var fn = separator == undefined ? undefined : separator[SPLIT];
return fn !== undefined ? fn.call(separator, O, limit) : $split.call(String(O), separator, limit);
}, $split];
});
var isEnum = _objectPie.f;
var _objectToArray = function (isEntries) {
return function (it) {
var O = _toIobject(it);
var keys = _objectKeys(O);
var length = keys.length;
var i = 0;
var result = [];
var key;
while (length > i) if (isEnum.call(O, key = keys[i++])) {
result.push(isEntries ? [key, O[key]] : O[key]);
} return result;
};
};
// https://github.com/tc39/proposal-object-values-entries
var $entries = _objectToArray(true);
_export(_export.S, 'Object', {
entries: function entries(it) {
return $entries(it);
}
});
// https://github.com/tc39/proposal-object-values-entries
var $values = _objectToArray(false);
_export(_export.S, 'Object', {
values: function values(it) {
return $values(it);
}
});
// @@replace logic
_fixReWks('replace', 2, function (defined, REPLACE, $replace) {
// 21.1.3.14 String.prototype.replace(searchValue, replaceValue)
return [function replace(searchValue, replaceValue) {
var O = defined(this);
var fn = searchValue == undefined ? undefined : searchValue[REPLACE];
return fn !== undefined
? fn.call(searchValue, O, replaceValue)
: $replace.call(String(O), searchValue, replaceValue);
}, $replace];
});
// 7.3.20 SpeciesConstructor(O, defaultConstructor)
var SPECIES$1 = _wks('species');
var _speciesConstructor = function (O, D) {
var C = _anObject(O).constructor;
var S;
return C === undefined || (S = _anObject(C)[SPECIES$1]) == undefined ? D : _aFunction(S);
};
// fast apply, http://jsperf.lnkit.com/fast-apply/5
var _invoke = function (fn, args, that) {
var un = that === undefined;
switch (args.length) {
case 0: return un ? fn()
: fn.call(that);
case 1: return un ? fn(args[0])
: fn.call(that, args[0]);
case 2: return un ? fn(args[0], args[1])
: fn.call(that, args[0], args[1]);
case 3: return un ? fn(args[0], args[1], args[2])
: fn.call(that, args[0], args[1], args[2]);
case 4: return un ? fn(args[0], args[1], args[2], args[3])
: fn.call(that, args[0], args[1], args[2], args[3]);
} return fn.apply(that, args);
};
var process = _global.process;
var setTask = _global.setImmediate;
var clearTask = _global.clearImmediate;
var MessageChannel = _global.MessageChannel;
var Dispatch = _global.Dispatch;
var counter = 0;
var queue = {};
var ONREADYSTATECHANGE = 'onreadystatechange';
var defer, channel, port;
var run = function () {
var id = +this;
// eslint-disable-next-line no-prototype-builtins
if (queue.hasOwnProperty(id)) {
var fn = queue[id];
delete queue[id];
fn();
}
};
var listener = function (event) {
run.call(event.data);
};
// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
if (!setTask || !clearTask) {
setTask = function setImmediate(fn) {
var args = [];
var i = 1;
while (arguments.length > i) args.push(arguments[i++]);
queue[++counter] = function () {
// eslint-disable-next-line no-new-func
_invoke(typeof fn == 'function' ? fn : Function(fn), args);
};
defer(counter);
return counter;
};
clearTask = function clearImmediate(id) {
delete queue[id];
};
// Node.js 0.8-
if (_cof(process) == 'process') {
defer = function (id) {
process.nextTick(_ctx(run, id, 1));
};
// Sphere (JS game engine) Dispatch API
} else if (Dispatch && Dispatch.now) {
defer = function (id) {
Dispatch.now(_ctx(run, id, 1));
};
// Browsers with MessageChannel, includes WebWorkers
} else if (MessageChannel) {
channel = new MessageChannel();
port = channel.port2;
channel.port1.onmessage = listener;
defer = _ctx(port.postMessage, port, 1);
// Browsers with postMessage, skip WebWorkers
// IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
} else if (_global.addEventListener && typeof postMessage == 'function' && !_global.importScripts) {
defer = function (id) {
_global.postMessage(id + '', '*');
};
_global.addEventListener('message', listener, false);
// IE8-
} else if (ONREADYSTATECHANGE in _domCreate('script')) {
defer = function (id) {
_html.appendChild(_domCreate('script'))[ONREADYSTATECHANGE] = function () {
_html.removeChild(this);
run.call(id);
};
};
// Rest old browsers
} else {
defer = function (id) {
setTimeout(_ctx(run, id, 1), 0);
};
}
}
var _task = {
set: setTask,
clear: clearTask
};
var macrotask = _task.set;
var Observer = _global.MutationObserver || _global.WebKitMutationObserver;
var process$1 = _global.process;
var Promise$1 = _global.Promise;
var isNode = _cof(process$1) == 'process';
var _microtask = function () {
var head, last, notify;
var flush = function () {
var parent, fn;
if (isNode && (parent = process$1.domain)) parent.exit();
while (head) {
fn = head.fn;
head = head.next;
try {
fn();
} catch (e) {
if (head) notify();
else last = undefined;
throw e;
}
} last = undefined;
if (parent) parent.enter();
};
// Node.js
if (isNode) {
notify = function () {
process$1.nextTick(flush);
};
// browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339
} else if (Observer && !(_global.navigator && _global.navigator.standalone)) {
var toggle = true;
var node = document.createTextNode('');
new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new
notify = function () {
node.data = toggle = !toggle;
};
// environments with maybe non-completely correct, but existent Promise
} else if (Promise$1 && Promise$1.resolve) {
// Promise.resolve without an argument throws an error in LG WebOS 2
var promise = Promise$1.resolve(undefined);
notify = function () {
promise.then(flush);
};
// for other environments - macrotask based on:
// - setImmediate
// - MessageChannel
// - window.postMessag
// - onreadystatechange
// - setTimeout
} else {
notify = function () {
// strange IE + webpack dev server bug - use .call(global)
macrotask.call(_global, flush);
};
}
return function (fn) {
var task = { fn: fn, next: undefined };
if (last) last.next = task;
if (!head) {
head = task;
notify();
} last = task;
};
};
// 25.4.1.5 NewPromiseCapability(C)
function PromiseCapability(C) {
var resolve, reject;
this.promise = new C(function ($$resolve, $$reject) {
if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
resolve = $$resolve;
reject = $$reject;
});
this.resolve = _aFunction(resolve);
this.reject = _aFunction(reject);
}
var f$5 = function (C) {
return new PromiseCapability(C);
};
var _newPromiseCapability = {
f: f$5
};
var _perform = function (exec) {
try {
return { e: false, v: exec() };
} catch (e) {
return { e: true, v: e };
}
};
var navigator$1 = _global.navigator;
var _userAgent = navigator$1 && navigator$1.userAgent || '';
var _promiseResolve = function (C, x) {
_anObject(C);
if (_isObject(x) && x.constructor === C) return x;
var promiseCapability = _newPromiseCapability.f(C);
var resolve = promiseCapability.resolve;
resolve(x);
return promiseCapability.promise;
};
var SPECIES$2 = _wks('species');
var _setSpecies = function (KEY) {
var C = _global[KEY];
if (_descriptors && C && !C[SPECIES$2]) _objectDp.f(C, SPECIES$2, {
configurable: true,
get: function () { return this; }
});
};
var task = _task.set;
var microtask = _microtask();
var PROMISE = 'Promise';
var TypeError$1 = _global.TypeError;
var process$2 = _global.process;
var versions = process$2 && process$2.versions;
var v8 = versions && versions.v8 || '';
var $Promise = _global[PROMISE];
var isNode$1 = _classof(process$2) == 'process';
var empty = function () { /* empty */ };
var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper;
var newPromiseCapability = newGenericPromiseCapability = _newPromiseCapability.f;
var USE_NATIVE = !!function () {
try {
// correct subclassing with @@species support
var promise = $Promise.resolve(1);
var FakePromise = (promise.constructor = {})[_wks('species')] = function (exec) {
exec(empty, empty);
};
// unhandled rejections tracking support, NodeJS Promise without it fails @@species test
return (isNode$1 || typeof PromiseRejectionEvent == 'function')
&& promise.then(empty) instanceof FakePromise
// v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
// https://bugs.chromium.org/p/chromium/issues/detail?id=830565
// we can't detect it synchronously, so just check versions
&& v8.indexOf('6.6') !== 0
&& _userAgent.indexOf('Chrome/66') === -1;
} catch (e) { /* empty */ }
}();
// helpers
var isThenable = function (it) {
var then;
return _isObject(it) && typeof (then = it.then) == 'function' ? then : false;
};
var notify = function (promise, isReject) {
if (promise._n) return;
promise._n = true;
var chain = promise._c;
microtask(function () {
var value = promise._v;
var ok = promise._s == 1;
var i = 0;
var run = function (reaction) {
var handler = ok ? reaction.ok : reaction.fail;
var resolve = reaction.resolve;
var reject = reaction.reject;
var domain = reaction.domain;
var result, then, exited;
try {
if (handler) {
if (!ok) {
if (promise._h == 2) onHandleUnhandled(promise);
promise._h = 1;
}
if (handler === true) result = value;
else {
if (domain) domain.enter();
result = handler(value); // may throw
if (domain) {
domain.exit();
exited = true;
}
}
if (result === reaction.promise) {
reject(TypeError$1('Promise-chain cycle'));
} else if (then = isThenable(result)) {
then.call(result, resolve, reject);
} else resolve(result);
} else reject(value);
} catch (e) {
if (domain && !exited) domain.exit();
reject(e);
}
};
while (chain.length > i) run(chain[i++]); // variable length - can't use forEach
promise._c = [];
promise._n = false;
if (isReject && !promise._h) onUnhandled(promise);
});
};
var onUnhandled = function (promise) {
task.call(_global, function () {
var value = promise._v;
var unhandled = isUnhandled(promise);
var result, handler, console;
if (unhandled) {
result = _perform(function () {
if (isNode$1) {
process$2.emit('unhandledRejection', value, promise);
} else if (handler = _global.onunhandledrejection) {
handler({ promise: promise, reason: value });
} else if ((console = _global.console) && console.error) {
console.error('Unhandled promise rejection', value);
}
});
// Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
promise._h = isNode$1 || isUnhandled(promise) ? 2 : 1;
} promise._a = undefined;
if (unhandled && result.e) throw result.v;
});
};
var isUnhandled = function (promise) {
return promise._h !== 1 && (promise._a || promise._c).length === 0;
};
var onHandleUnhandled = function (promise) {
task.call(_global, function () {
var handler;
if (isNode$1) {
process$2.emit('rejectionHandled', promise);
} else if (handler = _global.onrejectionhandled) {
handler({ promise: promise, reason: promise._v });
}
});
};
var $reject = function (value) {
var promise = this;
if (promise._d) return;
promise._d = true;
promise = promise._w || promise; // unwrap
promise._v = value;
promise._s = 2;
if (!promise._a) promise._a = promise._c.slice();
notify(promise, true);
};
var $resolve = function (value) {
var promise = this;
var then;
if (promise._d) return;
promise._d = true;
promise = promise._w || promise; // unwrap
try {
if (promise === value) throw TypeError$1("Promise can't be resolved itself");
if (then = isThenable(value)) {
microtask(function () {
var wrapper = { _w: promise, _d: false }; // wrap
try {
then.call(value, _ctx($resolve, wrapper, 1), _ctx($reject, wrapper, 1));
} catch (e) {
$reject.call(wrapper, e);
}
});
} else {
promise._v = value;
promise._s = 1;
notify(promise, false);
}
} catch (e) {
$reject.call({ _w: promise, _d: false }, e); // wrap
}
};
// constructor polyfill
if (!USE_NATIVE) {
// 25.4.3.1 Promise(executor)
$Promise = function Promise(executor) {
_anInstance(this, $Promise, PROMISE, '_h');
_aFunction(executor);
Internal.call(this);
try {
executor(_ctx($resolve, this, 1), _ctx($reject, this, 1));
} catch (err) {
$reject.call(this, err);
}
};
// eslint-disable-next-line no-unused-vars
Internal = function Promise(executor) {
this._c = []; // <- awaiting reactions
this._a = undefined; // <- checked in isUnhandled reactions
this._s = 0; // <- state
this._d = false; // <- done
this._v = undefined; // <- value
this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled
this._n = false; // <- notify
};
Internal.prototype = _redefineAll($Promise.prototype, {
// 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)
then: function then(onFulfilled, onRejected) {
var reaction = newPromiseCapability(_speciesConstructor(this, $Promise));
reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
reaction.fail = typeof onRejected == 'function' && onRejected;
reaction.domain = isNode$1 ? process$2.domain : undefined;
this._c.push(reaction);
if (this._a) this._a.push(reaction);
if (this._s) notify(this, false);
return reaction.promise;
},
// 25.4.5.1 Promise.prototype.catch(onRejected)
'catch': function (onRejected) {
return this.then(undefined, onRejected);
}
});
OwnPromiseCapability = function () {
var promise = new Internal();
this.promise = promise;
this.resolve = _ctx($resolve, promise, 1);
this.reject = _ctx($reject, promise, 1);
};
_newPromiseCapability.f = newPromiseCapability = function (C) {
return C === $Promise || C === Wrapper
? new OwnPromiseCapability(C)
: newGenericPromiseCapability(C);
};
}
_export(_export.G + _export.W + _export.F * !USE_NATIVE, { Promise: $Promise });
_setToStringTag($Promise, PROMISE);
_setSpecies(PROMISE);
Wrapper = _core[PROMISE];
// statics
_export(_export.S + _export.F * !USE_NATIVE, PROMISE, {
// 25.4.4.5 Promise.reject(r)
reject: function reject(r) {
var capability = newPromiseCapability(this);
var $$reject = capability.reject;
$$reject(r);
return capability.promise;
}
});
_export(_export.S + _export.F * (!USE_NATIVE), PROMISE, {
// 25.4.4.6 Promise.resolve(x)
resolve: function resolve(x) {
return _promiseResolve(_library && this === Wrapper ? $Promise : this, x);
}
});
_export(_export.S + _export.F * !(USE_NATIVE && _iterDetect(function (iter) {
$Promise.all(iter)['catch'](empty);
})), PROMISE, {
// 25.4.4.1 Promise.all(iterable)
all: function all(iterable) {
var C = this;
var capability = newPromiseCapability(C);
var resolve = capability.resolve;
var reject = capability.reject;
var result = _perform(function () {
var values = [];
var index = 0;
var remaining = 1;
_forOf(iterable, false, function (promise) {
var $index = index++;
var alreadyCalled = false;
values.push(undefined);
remaining++;
C.resolve(promise).then(function (value) {
if (alreadyCalled) return;
alreadyCalled = true;
values[$index] = value;
--remaining || resolve(values);
}, reject);
});
--remaining || resolve(values);
});
if (result.e) reject(result.v);
return capability.promise;
},
// 25.4.4.4 Promise.race(iterable)
race: function race(iterable) {
var C = this;
var capability = newPromiseCapability(C);
var reject = capability.reject;
var result = _perform(function () {
_forOf(iterable, false, function (promise) {
C.resolve(promise).then(capability.resolve, reject);
});
});
if (result.e) reject(result.v);
return capability.promise;
}
});
var STARTS_WITH = 'startsWith';
var $startsWith = ''[STARTS_WITH];
_export(_export.P + _export.F * _failsIsRegexp(STARTS_WITH), 'String', {
startsWith: function startsWith(searchString /* , position = 0 */) {
var that = _stringContext(this, searchString, STARTS_WITH);
var index = _toLength(Math.min(arguments.length > 1 ? arguments[1] : undefined, that.length));
var search = String(searchString);
return $startsWith
? $startsWith.call(that, search, index)
: that.slice(index, index + search.length) === search;
}
});
// 20.1.2.4 Number.isNaN(number)
_export(_export.S, 'Number', {
isNaN: function isNaN(number) {
// eslint-disable-next-line no-self-compare
return number != number;
}
});
// ==========================================================================
// Type checking utils
// ==========================================================================
var getConstructor = function getConstructor(input) {
return input !== null && typeof input !== 'undefined' ? input.constructor : null;
};
var instanceOf = function instanceOf(input, constructor) {
return Boolean(input && constructor && input instanceof constructor);
};
var isNullOrUndefined = function isNullOrUndefined(input) {
return input === null || typeof input === 'undefined';
};
var isObject = function isObject(input) {
return getConstructor(input) === Object;
};
var isNumber = function isNumber(input) {
return getConstructor(input) === Number && !Number.isNaN(input);
};
var isString = function isString(input) {
return getConstructor(input) === String;
};
var isBoolean = function isBoolean(input) {
return getConstructor(input) === Boolean;
};
var isFunction = function isFunction(input) {
return getConstructor(input) === Function;
};
var isArray = function isArray(input) {
return Array.isArray(input);
};
var isWeakMap = function isWeakMap(input) {
return instanceOf(input, WeakMap);
};
var isNodeList = function isNodeList(input) {
return instanceOf(input, NodeList);
};
var isElement = function isElement(input) {
return instanceOf(input, Element);
};
var isTextNode = function isTextNode(input) {
return getConstructor(input) === Text;
};
var isEvent = function isEvent(input) {
return instanceOf(input, Event);
};
var isKeyboardEvent = function isKeyboardEvent(input) {
return instanceOf(input, KeyboardEvent);
};
var isCue = function isCue(input) {
return instanceOf(input, window.TextTrackCue) || instanceOf(input, window.VTTCue);
};
var isTrack = function isTrack(input) {
return instanceOf(input, TextTrack) || !isNullOrUndefined(input) && isString(input.kind);
};
var isEmpty = function isEmpty(input) {
return isNullOrUndefined(input) || (isString(input) || isArray(input) || isNodeList(input)) && !input.length || isObject(input) && !Object.keys(input).length;
};
var isUrl = function isUrl(input) {
// Accept a URL object
if (instanceOf(input, window.URL)) {
return true;
} // Must be string from here
if (!isString(input)) {
return false;
} // Add the protocol if required
var string = input;
if (!input.startsWith('http://') || !input.startsWith('https://')) {
string = "http://".concat(input);
}
try {
return !isEmpty(new URL(string).hostname);
} catch (e) {
return false;
}
};
var is$1 = {
nullOrUndefined: isNullOrUndefined,
object: isObject,
number: isNumber,
string: isString,
boolean: isBoolean,
function: isFunction,
array: isArray,
weakMap: isWeakMap,
nodeList: isNodeList,
element: isElement,
textNode: isTextNode,
event: isEvent,
keyboardEvent: isKeyboardEvent,
cue: isCue,
track: isTrack,
url: isUrl,
empty: isEmpty
};
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g
var supportsPassiveListeners = function () {
// Test via a getter in the options object to see if the passive property is accessed
var supported = false;
try {
var options = Object.defineProperty({}, 'passive', {
get: function get() {
supported = true;
return null;
}
});
window.addEventListener('test', null, options);
window.removeEventListener('test', null, options);
} catch (e) {// Do nothing
}
return supported;
}(); // Toggle event listener
function toggleListener(element, event, callback) {
var _this = this;
var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
// Bail if no element, event, or callback
if (!element || !('addEventListener' in element) || is$1.empty(event) || !is$1.function(callback)) {
return;
} // Allow multiple events
var events = event.split(' '); // Build options
// Default to just the capture boolean for browsers with no passive listener support
var options = capture; // If passive events listeners are supported
if (supportsPassiveListeners) {
options = {
// Whether the listener can be passive (i.e. default never prevented)
passive: passive,
// Whether the listener is a capturing listener or not
capture: capture
};
} // If a single node is passed, bind the event listener
events.forEach(function (type) {
if (_this && _this.eventListeners && toggle) {
// Cache event listener
_this.eventListeners.push({
element: element,
type: type,
callback: callback,
options: options
});
}
element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
});
} // Bind event handler
function on(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments.length > 2 ? arguments[2] : undefined;
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
toggleListener.call(this, element, events, callback, true, passive, capture);
} // Unbind event handler
function off(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments.length > 2 ? arguments[2] : undefined;
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
toggleListener.call(this, element, events, callback, false, passive, capture);
} // Bind once-only event handler
function once(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments.length > 2 ? arguments[2] : undefined;
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
function onceCallback() {
off(element, events, onceCallback, passive, capture);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
callback.apply(this, args);
}
toggleListener.call(this, element, events, onceCallback, true, passive, capture);
} // Trigger event
function triggerEvent(element) {
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var bubbles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var detail = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
// Bail if no element
if (!is$1.element(element) || is$1.empty(type)) {
return;
} // Create and dispatch the event
var event = new CustomEvent(type, {
bubbles: bubbles,
detail: Object.assign({}, detail, {
plyr: this
})
}); // Dispatch the event
element.dispatchEvent(event);
} // Unbind all cached event listeners
function unbindListeners() {
if (this && this.eventListeners) {
this.eventListeners.forEach(function (item) {
var element = item.element,
type = item.type,
callback = item.callback,
options = item.options;
element.removeEventListener(type, callback, options);
});
this.eventListeners = [];
}
} // Run method when / if player is ready
function ready() {
var _this2 = this;
return new Promise(function (resolve) {
return _this2.ready ? setTimeout(resolve, 0) : on.call(_this2, _this2.elements.container, 'ready', resolve);
}).then(function () {});
}
function wrap(elements, wrapper) {
// Convert `elements` to an array, if necessary.
var targets = elements.length ? elements : [elements]; // Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
Array.from(targets).reverse().forEach(function (element, index) {
var child = index > 0 ? wrapper.cloneNode(true) : wrapper; // Cache the current parent and sibling.
var parent = element.parentNode;
var sibling = element.nextSibling; // Wrap the element (is automatically removed from its current
// parent).
child.appendChild(element); // If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
// append it to the parent.
if (sibling) {
parent.insertBefore(child, sibling);
} else {
parent.appendChild(child);
}
});
} // Set attributes
function setAttributes(element, attributes) {
if (!is$1.element(element) || is$1.empty(attributes)) {
return;
} // Assume null and undefined attributes should be left out,
// Setting them would otherwise convert them to "null" and "undefined"
Object.entries(attributes).filter(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
value = _ref2[1];
return !is$1.nullOrUndefined(value);
}).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
key = _ref4[0],
value = _ref4[1];
return element.setAttribute(key, value);
});
} // Create a DocumentFragment
function createElement(type, attributes, text) {
// Create a new <element>
var element = document.createElement(type); // Set all passed attributes
if (is$1.object(attributes)) {
setAttributes(element, attributes);
} // Add text node
if (is$1.string(text)) {
element.innerText = text;
} // Return built element
return element;
} // Inaert an element after another
function insertAfter(element, target) {
if (!is$1.element(element) || !is$1.element(target)) {
return;
}
target.parentNode.insertBefore(element, target.nextSibling);
} // Insert a DocumentFragment
function insertElement(type, parent, attributes, text) {
if (!is$1.element(parent)) {
return;
}
parent.appendChild(createElement(type, attributes, text));
} // Remove element(s)
function removeElement(element) {
if (is$1.nodeList(element) || is$1.array(element)) {
Array.from(element).forEach(removeElement);
return;
}
if (!is$1.element(element) || !is$1.element(element.parentNode)) {
return;
}
element.parentNode.removeChild(element);
} // Remove all child elements
function emptyElement(element) {
if (!is$1.element(element)) {
return;
}
var length = element.childNodes.length;
while (length > 0) {
element.removeChild(element.lastChild);
length -= 1;
}
} // Replace element
function replaceElement(newChild, oldChild) {
if (!is$1.element(oldChild) || !is$1.element(oldChild.parentNode) || !is$1.element(newChild)) {
return null;
}
oldChild.parentNode.replaceChild(newChild, oldChild);
return newChild;
} // Get an attribute object from a string selector
function getAttributesFromSelector(sel, existingAttributes) {
// For example:
// '.test' to { class: 'test' }
// '#test' to { id: 'test' }
// '[data-test="test"]' to { 'data-test': 'test' }
if (!is$1.string(sel) || is$1.empty(sel)) {
return {};
}
var attributes = {};
var existing = existingAttributes;
sel.split(',').forEach(function (s) {
// Remove whitespace
var selector = s.trim();
var className = selector.replace('.', '');
var stripped = selector.replace(/[[\]]/g, ''); // Get the parts and value
var parts = stripped.split('=');
var key = parts[0];
var value = parts.length > 1 ? parts[1].replace(/["']/g, '') : ''; // Get the first character
var start = selector.charAt(0);
switch (start) {
case '.':
// Add to existing classname
if (is$1.object(existing) && is$1.string(existing.class)) {
existing.class += " ".concat(className);
}
attributes.class = className;
break;
case '#':
// ID selector
attributes.id = selector.replace('#', '');
break;
case '[':
// Attribute selector
attributes[key] = value;
break;
default:
break;
}
});
return attributes;
} // Toggle hidden
function toggleHidden(element, hidden) {
if (!is$1.element(element)) {
return;
}
var hide = hidden;
if (!is$1.boolean(hide)) {
hide = !element.hidden;
}
if (hide) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
} // Mirror Element.classList.toggle, with IE compatibility for "force" argument
function toggleClass(element, className, force) {
if (is$1.nodeList(element)) {
return Array.from(element).map(function (e) {
return toggleClass(e, className, force);
});
}
if (is$1.element(element)) {
var method = 'toggle';
if (typeof force !== 'undefined') {
method = force ? 'add' : 'remove';
}
element.classList[method](className);
return element.classList.contains(className);
}
return false;
} // Has class name
function hasClass(element, className) {
return is$1.element(element) && element.classList.contains(className);
} // Element matches selector
function matches(element, selector) {
var prototype = {
Element: Element
};
function match() {
return Array.from(document.querySelectorAll(selector)).includes(this);
}
var matches = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return matches.call(element, selector);
} // Find all elements
function getElements(selector) {
return this.elements.container.querySelectorAll(selector);
} // Find a single element
function getElement(selector) {
return this.elements.container.querySelector(selector);
} // Trap focus inside container
function trapFocus() {
var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
var toggle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!is$1.element(element)) {
return;
}
var focusable = getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');
var first = focusable[0];
var last = focusable[focusable.length - 1];
var trap = function trap(event) {
// Bail if not tab key or not fullscreen
if (event.key !== 'Tab' || event.keyCode !== 9) {
return;
} // Get the current focused element
var focused = document.activeElement;
if (focused === last && !event.shiftKey) {
// Move focus to first element that can be tabbed if Shift isn't used
first.focus();
event.preventDefault();
} else if (focused === first && event.shiftKey) {
// Move focus to last element that can be tabbed if Shift is used
last.focus();
event.preventDefault();
}
};
toggleListener.call(this, this.elements.container, 'keydown', trap, toggle, false);
} // Set focus and tab focus class
function setFocus() {
var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!is$1.element(element)) {
return;
} // Set regular focus
element.focus(); // If we want to mimic keyboard focus via tab
if (tabFocus) {
toggleClass(element, this.config.classNames.tabFocus);
}
}
var transitionEndEvent = function () {
var element = document.createElement('span');
var events = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
};
var type = Object.keys(events).find(function (event) {
return element.style[event] !== undefined;
});
return is$1.string(type) ? events[type] : false;
}(); // Force repaint of element
function repaint(element) {
setTimeout(function () {
try {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {// Do nothing
}
}, 0);
}
// ==========================================================================
// Browser sniffing
// Unfortunately, due to mixed support, UA sniffing is required
// ==========================================================================
var browser = {
isIE:
/* @cc_on!@ */
!!document.documentMode,
isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent),
isIPhone: /(iPhone|iPod)/gi.test(navigator.platform),
isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform)
};
var defaultCodecs = {
'audio/ogg': 'vorbis',
'audio/wav': '1',
'video/webm': 'vp8, vorbis',
'video/mp4': 'avc1.42E01E, mp4a.40.2',
'video/ogg': 'theora'
}; // Check for feature support
var support = {
// Basic support
audio: 'canPlayType' in document.createElement('audio'),
video: 'canPlayType' in document.createElement('video'),
// Check for support
// Basic functionality vs full UI
check: function check(type, provider, playsinline) {
var canPlayInline = browser.isIPhone && playsinline && support.playsinline;
var api = support[type] || provider !== 'html5';
var ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline);
return {
api: api,
ui: ui
};
},
// Picture-in-picture support
// Safari only currently
pip: function () {
return !browser.isIPhone && is$1.function(createElement('video').webkitSetPresentationMode);
}(),
// Airplay support
// Safari only currently
airplay: is$1.function(window.WebKitPlaybackTargetAvailabilityEvent),
// Inline playback support
// https://webkit.org/blog/6784/new-video-policies-for-ios/
playsinline: 'playsInline' in document.createElement('video'),
// Check for mime type support against a player instance
// Credits: http://diveintohtml5.info/everything.html
// Related: http://www.leanbackplayer.com/test/h5mt.html
mime: function mime(inputType) {
var _inputType$split = inputType.split('/'),
_inputType$split2 = _slicedToArray(_inputType$split, 1),
mediaType = _inputType$split2[0];
if (!this.isHTML5 || mediaType !== this.type) {
return false;
}
var type;
if (inputType && inputType.includes('codecs=')) {
// Use input directly
type = inputType;
} else if (inputType === 'audio/mpeg') {
// Skip codec
type = 'audio/mpeg;';
} else if (inputType in defaultCodecs) {
// Use codec
type = "".concat(inputType, "; codecs=\"").concat(defaultCodecs[inputType], "\"");
}
try {
return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
} catch (err) {
return false;
}
},
// Check for textTracks support
textTracks: 'textTracks' in document.createElement('video'),
// <input type="range"> Sliders
rangeInput: function () {
var range = document.createElement('input');
range.type = 'range';
return range.type === 'range';
}(),
// Touch
// NOTE: Remember a device can be mouse + touch enabled so we check on first touch event
touch: 'ontouchstart' in document.documentElement,
// Detect transitions support
transitions: transitionEndEvent !== false,
// Reduced motion iOS & MacOS setting
// https://webkit.org/blog/7551/responsive-design-for-motion/
reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches
};
var html5 = {
getSources: function getSources() {
var _this = this;
if (!this.isHTML5) {
return [];
}
var sources = Array.from(this.media.querySelectorAll('source')); // Filter out unsupported sources
return sources.filter(function (source) {
return support.mime.call(_this, source.getAttribute('type'));
});
},
// Get quality levels
getQualityOptions: function getQualityOptions() {
// Get sizes from <source> elements
return html5.getSources.call(this).map(function (source) {
return Number(source.getAttribute('size'));
}).filter(Boolean);
},
extend: function extend() {
if (!this.isHTML5) {
return;
}
var player = this; // Quality
Object.defineProperty(player.media, 'quality', {
get: function get() {
// Get sources
var sources = html5.getSources.call(player);
var source = sources.find(function (source) {
return source.getAttribute('src') === player.source;
}); // Return size, if match is found
return source && Number(source.getAttribute('size'));
},
set: function set(input) {
// Get sources
var sources = html5.getSources.call(player); // Get first match for requested size
var source = sources.find(function (source) {
return Number(source.getAttribute('size')) === input;
}); // No matching source found
if (!source) {
return;
} // Get current state
var _player$media = player.media,
currentTime = _player$media.currentTime,
paused = _player$media.paused,
preload = _player$media.preload,
readyState = _player$media.readyState; // Set new source
player.media.src = source.getAttribute('src'); // Prevent loading if preload="none" and the current source isn't loaded (#1044)
if (preload !== 'none' || readyState) {
// Restore time
player.once('loadedmetadata', function () {
player.currentTime = currentTime; // Resume playing
if (!paused) {
player.play();
}
}); // Load new source
player.media.load();
} // Trigger change event
triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: input
}); // Save to storage
player.storage.set({
quality: input
});
}
});
},
// Cancel current network requests
// See https://github.com/sampotts/plyr/issues/174
cancelRequests: function cancelRequests() {
if (!this.isHTML5) {
return;
} // Remove child sources
removeElement(html5.getSources.call(this)); // Set blank video src attribute
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
// Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection
this.media.setAttribute('src', this.config.blankVideo); // Load the new empty source
// This will cancel existing requests
// See https://github.com/sampotts/plyr/issues/174
this.media.load(); // Debugging
this.debug.log('Cancelled network requests');
}
};
// ==========================================================================
function dedupe(array) {
if (!is$1.array(array)) {
return array;
}
return array.filter(function (item, index) {
return array.indexOf(item) === index;
});
} // Get the closest value in an array
function closest(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
return array.reduce(function (prev, curr) {
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
});
}
function cloneDeep(object) {
return JSON.parse(JSON.stringify(object));
} // Get a nested value in an object
function getDeep(object, path) {
return path.split('.').reduce(function (obj, key) {
return obj && obj[key];
}, object);
} // Deep extend destination object with N more objects
function extend() {
var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
sources[_key - 1] = arguments[_key];
}
if (!sources.length) {
return target;
}
var source = sources.shift();
if (!is$1.object(source)) {
return target;
}
Object.keys(source).forEach(function (key) {
if (is$1.object(source[key])) {
if (!Object.keys(target).includes(key)) {
Object.assign(target, _defineProperty({}, key, {}));
}
extend(target[key], source[key]);
} else {
Object.assign(target, _defineProperty({}, key, source[key]));
}
});
return extend.apply(void 0, [target].concat(sources));
}
var dP$2 = _objectDp.f;
var gOPN$1 = _objectGopn.f;
var $RegExp = _global.RegExp;
var Base$1 = $RegExp;
var proto$2 = $RegExp.prototype;
var re1 = /a/g;
var re2 = /a/g;
// "new" creates a new object, old webkit buggy here
var CORRECT_NEW = new $RegExp(re1) !== re1;
if (_descriptors && (!CORRECT_NEW || _fails(function () {
re2[_wks('match')] = false;
// RegExp constructor can alter flags and IsRegExp works correct with @@match
return $RegExp(re1) != re1 || $RegExp(re2) == re2 || $RegExp(re1, 'i') != '/a/i';
}))) {
$RegExp = function RegExp(p, f) {
var tiRE = this instanceof $RegExp;
var piRE = _isRegexp(p);
var fiU = f === undefined;
return !tiRE && piRE && p.constructor === $RegExp && fiU ? p
: _inheritIfRequired(CORRECT_NEW
? new Base$1(piRE && !fiU ? p.source : p, f)
: Base$1((piRE = p instanceof $RegExp) ? p.source : p, piRE && fiU ? _flags.call(p) : f)
, tiRE ? this : proto$2, $RegExp);
};
var proxy = function (key) {
key in $RegExp || dP$2($RegExp, key, {
configurable: true,
get: function () { return Base$1[key]; },
set: function (it) { Base$1[key] = it; }
});
};
for (var keys$1 = gOPN$1(Base$1), i$1 = 0; keys$1.length > i$1;) proxy(keys$1[i$1++]);
proto$2.constructor = $RegExp;
$RegExp.prototype = proto$2;
_redefine(_global, 'RegExp', $RegExp);
}
_setSpecies('RegExp');
function generateId(prefix) {
return "".concat(prefix, "-").concat(Math.floor(Math.random() * 10000));
} // Format string
function format(input) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if (is$1.empty(input)) {
return input;
}
return input.toString().replace(/{(\d+)}/g, function (match, i) {
return args[i].toString();
});
} // Get percentage
function getPercentage(current, max) {
if (current === 0 || max === 0 || Number.isNaN(current) || Number.isNaN(max)) {
return 0;
}
return (current / max * 100).toFixed(2);
} // Replace all occurances of a string in a string
function replaceAll() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var find = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
} // Convert to title case
function toTitleCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return input.toString().replace(/\w\S*/g, function (text) {
return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();
});
} // Convert string to pascalCase
function toPascalCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString(); // Convert kebab case
string = replaceAll(string, '-', ' '); // Convert snake case
string = replaceAll(string, '_', ' '); // Convert to title case
string = toTitleCase(string); // Convert to pascal case
return replaceAll(string, ' ', '');
} // Convert string to pascalCase
function toCamelCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString(); // Convert to pascal case
string = toPascalCase(string); // Convert first character to lowercase
return string.charAt(0).toLowerCase() + string.slice(1);
} // Remove HTML from a string
function stripHTML(source) {
var fragment = document.createDocumentFragment();
var element = document.createElement('div');
fragment.appendChild(element);
element.innerHTML = source;
return fragment.firstChild.innerText;
} // Like outerHTML, but also works for DocumentFragment
function getHTML(element) {
var wrapper = document.createElement('div');
wrapper.appendChild(element);
return wrapper.innerHTML;
}
var resources = {
pip: 'PIP',
airplay: 'AirPlay',
html5: 'HTML5',
vimeo: 'Vimeo',
youtube: 'YouTube'
};
var i18n = {
get: function get() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (is$1.empty(key) || is$1.empty(config)) {
return '';
}
var string = getDeep(config.i18n, key);
if (is$1.empty(string)) {
if (Object.keys(resources).includes(key)) {
return resources[key];
}
return '';
}
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = replaceAll(string, key, value);
});
return string;
}
};
var Storage =
/*#__PURE__*/
function () {
function Storage(player) {
_classCallCheck(this, Storage);
this.enabled = player.config.storage.enabled;
this.key = player.config.storage.key;
} // Check for actual support (see if we can use it)
_createClass(Storage, [{
key: "get",
value: function get(key) {
if (!Storage.supported || !this.enabled) {
return null;
}
var store = window.localStorage.getItem(this.key);
if (is$1.empty(store)) {
return null;
}
var json = JSON.parse(store);
return is$1.string(key) && key.length ? json[key] : json;
}
}, {
key: "set",
value: function set(object) {
// Bail if we don't have localStorage support or it's disabled
if (!Storage.supported || !this.enabled) {
return;
} // Can only store objectst
if (!is$1.object(object)) {
return;
} // Get current storage
var storage = this.get(); // Default to empty object
if (is$1.empty(storage)) {
storage = {};
} // Update the working copy of the values
extend(storage, object); // Update storage
window.localStorage.setItem(this.key, JSON.stringify(storage));
}
}], [{
key: "supported",
get: function get() {
try {
if (!('localStorage' in window)) {
return false;
}
var test = '___test'; // Try to use it (it might be disabled, e.g. user is in private mode)
// see: https://github.com/sampotts/plyr/issues/131
window.localStorage.setItem(test, test);
window.localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
}]);
return Storage;
}();
// ==========================================================================
// Fetch wrapper
// Using XHR to avoid issues with older browsers
// ==========================================================================
function fetch(url) {
var responseType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'text';
return new Promise(function (resolve, reject) {
try {
var request = new XMLHttpRequest(); // Check for CORS support
if (!('withCredentials' in request)) {
return;
}
request.addEventListener('load', function () {
if (responseType === 'text') {
try {
resolve(JSON.parse(request.responseText));
} catch (e) {
resolve(request.responseText);
}
} else {
resolve(request.response);
}
});
request.addEventListener('error', function () {
throw new Error(request.status);
});
request.open('GET', url, true); // Set the required response type
request.responseType = responseType;
request.send();
} catch (e) {
reject(e);
}
});
}
// ==========================================================================
function loadSprite(url, id) {
if (!is$1.string(url)) {
return;
}
var prefix = 'cache';
var hasId = is$1.string(id);
var isCached = false;
var exists = function exists() {
return document.getElementById(id) !== null;
};
var update = function update(container, data) {
container.innerHTML = data; // Check again incase of race condition
if (hasId && exists()) {
return;
} // Inject the SVG to the body
document.body.insertAdjacentElement('afterbegin', container);
}; // Only load once if ID set
if (!hasId || !exists()) {
var useStorage = Storage.supported; // Create container
var container = document.createElement('div');
container.setAttribute('hidden', '');
if (hasId) {
container.setAttribute('id', id);
} // Check in cache
if (useStorage) {
var cached = window.localStorage.getItem("".concat(prefix, "-").concat(id));
isCached = cached !== null;
if (isCached) {
var data = JSON.parse(cached);
update(container, data.content);
}
} // Get the sprite
fetch(url).then(function (result) {
if (is$1.empty(result)) {
return;
}
if (useStorage) {
window.localStorage.setItem("".concat(prefix, "-").concat(id), JSON.stringify({
content: result
}));
}
update(container, result);
}).catch(function () {});
}
}
// ==========================================================================
var getHours = function getHours(value) {
return parseInt(value / 60 / 60 % 60, 10);
};
var getMinutes = function getMinutes(value) {
return parseInt(value / 60 % 60, 10);
};
var getSeconds = function getSeconds(value) {
return parseInt(value % 60, 10);
}; // Format time to UI friendly string
function formatTime() {
var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var displayHours = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var inverted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
// Bail if the value isn't a number
if (!is$1.number(time)) {
return formatTime(null, displayHours, inverted);
} // Format time component to add leading zero
var format = function format(value) {
return "0".concat(value).slice(-2);
}; // Breakdown to hours, mins, secs
var hours = getHours(time);
var mins = getMinutes(time);
var secs = getSeconds(time); // Do we need to display hours?
if (displayHours || hours > 0) {
hours = "".concat(hours, ":");
} else {
hours = '';
} // Render
return "".concat(inverted && time > 0 ? '-' : '').concat(hours).concat(format(mins), ":").concat(format(secs));
}
var controls = {
// Get icon URL
getIconUrl: function getIconUrl() {
var url = new URL(this.config.iconUrl, window.location);
var cors = url.host !== window.location.host || browser.isIE && !window.svg4everybody;
return {
url: this.config.iconUrl,
cors: cors
};
},
// Find the UI controls
findElements: function findElements() {
try {
this.elements.controls = getElement.call(this, this.config.selectors.controls.wrapper); // Buttons
this.elements.buttons = {
play: getElements.call(this, this.config.selectors.buttons.play),
pause: getElement.call(this, this.config.selectors.buttons.pause),
restart: getElement.call(this, this.config.selectors.buttons.restart),
rewind: getElement.call(this, this.config.selectors.buttons.rewind),
fastForward: getElement.call(this, this.config.selectors.buttons.fastForward),
mute: getElement.call(this, this.config.selectors.buttons.mute),
pip: getElement.call(this, this.config.selectors.buttons.pip),
airplay: getElement.call(this, this.config.selectors.buttons.airplay),
settings: getElement.call(this, this.config.selectors.buttons.settings),
captions: getElement.call(this, this.config.selectors.buttons.captions),
fullscreen: getElement.call(this, this.config.selectors.buttons.fullscreen)
}; // Progress
this.elements.progress = getElement.call(this, this.config.selectors.progress); // Inputs
this.elements.inputs = {
seek: getElement.call(this, this.config.selectors.inputs.seek),
volume: getElement.call(this, this.config.selectors.inputs.volume)
}; // Display
this.elements.display = {
buffer: getElement.call(this, this.config.selectors.display.buffer),
currentTime: getElement.call(this, this.config.selectors.display.currentTime),
duration: getElement.call(this, this.config.selectors.display.duration)
}; // Seek tooltip
if (is$1.element(this.elements.progress)) {
this.elements.display.seekTooltip = this.elements.progress.querySelector(".".concat(this.config.classNames.tooltip));
}
return true;
} catch (error) {
// Log it
this.debug.warn('It looks like there is a problem with your custom controls HTML', error); // Restore native video controls
this.toggleNativeControls(true);
return false;
}
},
// Create <svg> icon
createIcon: function createIcon(type, attributes) {
var namespace = 'http://www.w3.org/2000/svg';
var iconUrl = controls.getIconUrl.call(this);
var iconPath = "".concat(!iconUrl.cors ? iconUrl.url : '', "#").concat(this.config.iconPrefix); // Create <svg>
var icon = document.createElementNS(namespace, 'svg');
setAttributes(icon, extend(attributes, {
role: 'presentation',
focusable: 'false'
})); // Create the <use> to reference sprite
var use = document.createElementNS(namespace, 'use');
var path = "".concat(iconPath, "-").concat(type); // Set `href` attributes
// https://github.com/sampotts/plyr/issues/460
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
if ('href' in use) {
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
} // Always set the older attribute even though it's "deprecated" (it'll be around for ages)
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path); // Add <use> to <svg>
icon.appendChild(use);
return icon;
},
// Create hidden text label
createLabel: function createLabel(key) {
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var text = i18n.get(key, this.config);
var attributes = Object.assign({}, attr, {
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
});
return createElement('span', attributes, text);
},
// Create a badge
createBadge: function createBadge(text) {
if (is$1.empty(text)) {
return null;
}
var badge = createElement('span', {
class: this.config.classNames.menu.value
});
badge.appendChild(createElement('span', {
class: this.config.classNames.menu.badge
}, text));
return badge;
},
// Create a <button>
createButton: function createButton(buttonType, attr) {
var attributes = Object.assign({}, attr);
var type = toCamelCase(buttonType);
var props = {
element: 'button',
toggle: false,
label: null,
icon: null,
labelPressed: null,
iconPressed: null
};
['element', 'icon', 'label'].forEach(function (key) {
if (Object.keys(attributes).includes(key)) {
props[key] = attributes[key];
delete attributes[key];
}
}); // Default to 'button' type to prevent form submission
if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
attributes.type = 'button';
} // Set class name
if (Object.keys(attributes).includes('class')) {
if (!attributes.class.includes(this.config.classNames.control)) {
attributes.class += " ".concat(this.config.classNames.control);
}
} else {
attributes.class = this.config.classNames.control;
} // Large play button
switch (buttonType) {
case 'play':
props.toggle = true;
props.label = 'play';
props.labelPressed = 'pause';
props.icon = 'play';
props.iconPressed = 'pause';
break;
case 'mute':
props.toggle = true;
props.label = 'mute';
props.labelPressed = 'unmute';
props.icon = 'volume';
props.iconPressed = 'muted';
break;
case 'captions':
props.toggle = true;
props.label = 'enableCaptions';
props.labelPressed = 'disableCaptions';
props.icon = 'captions-off';
props.iconPressed = 'captions-on';
break;
case 'fullscreen':
props.toggle = true;
props.label = 'enterFullscreen';
props.labelPressed = 'exitFullscreen';
props.icon = 'enter-fullscreen';
props.iconPressed = 'exit-fullscreen';
break;
case 'play-large':
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
type = 'play';
props.label = 'play';
props.icon = 'play';
break;
default:
if (is$1.empty(props.label)) {
props.label = type;
}
if (is$1.empty(props.icon)) {
props.icon = buttonType;
}
}
var button = createElement(props.element); // Setup toggle icon and labels
if (props.toggle) {
// Icon
button.appendChild(controls.createIcon.call(this, props.iconPressed, {
class: 'icon--pressed'
}));
button.appendChild(controls.createIcon.call(this, props.icon, {
class: 'icon--not-pressed'
})); // Label/Tooltip
button.appendChild(controls.createLabel.call(this, props.labelPressed, {
class: 'label--pressed'
}));
button.appendChild(controls.createLabel.call(this, props.label, {
class: 'label--not-pressed'
}));
} else {
button.appendChild(controls.createIcon.call(this, props.icon));
button.appendChild(controls.createLabel.call(this, props.label));
} // Merge and set attributes
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
setAttributes(button, attributes); // We have multiple play buttons
if (type === 'play') {
if (!is$1.array(this.elements.buttons[type])) {
this.elements.buttons[type] = [];
}
this.elements.buttons[type].push(button);
} else {
this.elements.buttons[type] = button;
}
return button;
},
// Create an <input type='range'>
createRange: function createRange(type, attributes) {
// Seek input
var input = createElement('input', extend(getAttributesFromSelector(this.config.selectors.inputs[type]), {
type: 'range',
min: 0,
max: 100,
step: 0.01,
value: 0,
autocomplete: 'off',
// A11y fixes for https://github.com/sampotts/plyr/issues/905
role: 'slider',
'aria-label': i18n.get(type, this.config),
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuenow': 0
}, attributes));
this.elements.inputs[type] = input; // Set the fill for webkit now
controls.updateRangeFill.call(this, input);
return input;
},
// Create a <progress>
createProgress: function createProgress(type, attributes) {
var progress = createElement('progress', extend(getAttributesFromSelector(this.config.selectors.display[type]), {
min: 0,
max: 100,
value: 0,
role: 'presentation',
'aria-hidden': true
}, attributes)); // Create the label inside
if (type !== 'volume') {
progress.appendChild(createElement('span', null, '0'));
var suffixKey = {
played: 'played',
buffer: 'buffered'
}[type];
var suffix = suffixKey ? i18n.get(suffixKey, this.config) : '';
progress.innerText = "% ".concat(suffix.toLowerCase());
}
this.elements.display[type] = progress;
return progress;
},
// Create time display
createTime: function createTime(type) {
var attributes = getAttributesFromSelector(this.config.selectors.display[type]);
var container = createElement('div', extend(attributes, {
class: "".concat(this.config.classNames.display.time, " ").concat(attributes.class ? attributes.class : '').trim(),
'aria-label': i18n.get(type, this.config)
}), '00:00'); // Reference for updates
this.elements.display[type] = container;
return container;
},
// Bind keyboard shortcuts for a menu item
// We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220143
bindMenuItemShortcuts: function bindMenuItemShortcuts(menuItem, type) {
var _this = this;
// Navigate through menus via arrow keys and space
on(menuItem, 'keydown keyup', function (event) {
// We only care about space and ⬆️ ⬇️️ ➡️
if (![32, 38, 39, 40].includes(event.which)) {
return;
} // Prevent play / seek
event.preventDefault();
event.stopPropagation(); // We're just here to prevent the keydown bubbling
if (event.type === 'keydown') {
return;
}
var isRadioButton = matches(menuItem, '[role="menuitemradio"]'); // Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) {
controls.showMenuPanel.call(_this, type, true);
} else {
var target;
if (event.which !== 32) {
if (event.which === 40 || isRadioButton && event.which === 39) {
target = menuItem.nextElementSibling;
if (!is$1.element(target)) {
target = menuItem.parentNode.firstElementChild;
}
} else {
target = menuItem.previousElementSibling;
if (!is$1.element(target)) {
target = menuItem.parentNode.lastElementChild;
}
}
setFocus.call(_this, target, true);
}
}
}, false); // Enter will fire a `click` event but we still need to manage focus
// So we bind to keyup which fires after and set focus here
on(menuItem, 'keyup', function (event) {
if (event.which !== 13) {
return;
}
controls.focusFirstMenuItem.call(_this, null, true);
});
},
// Create a settings menu item
createMenuItem: function createMenuItem(_ref) {
var _this2 = this;
var value = _ref.value,
list = _ref.list,
type = _ref.type,
title = _ref.title,
_ref$badge = _ref.badge,
badge = _ref$badge === void 0 ? null : _ref$badge,
_ref$checked = _ref.checked,
checked = _ref$checked === void 0 ? false : _ref$checked;
var attributes = getAttributesFromSelector(this.config.selectors.inputs[type]);
var menuItem = createElement('button', extend(attributes, {
type: 'button',
role: 'menuitemradio',
class: "".concat(this.config.classNames.control, " ").concat(attributes.class ? attributes.class : '').trim(),
'aria-checked': checked,
value: value
}));
var flex = createElement('span'); // We have to set as HTML incase of special characters
flex.innerHTML = title;
if (is$1.element(badge)) {
flex.appendChild(badge);
}
menuItem.appendChild(flex); // Replicate radio button behaviour
Object.defineProperty(menuItem, 'checked', {
enumerable: true,
get: function get() {
return menuItem.getAttribute('aria-checked') === 'true';
},
set: function set(checked) {
// Ensure exclusivity
if (checked) {
Array.from(menuItem.parentNode.children).filter(function (node) {
return matches(node, '[role="menuitemradio"]');
}).forEach(function (node) {
return node.setAttribute('aria-checked', 'false');
});
}
menuItem.setAttribute('aria-checked', checked ? 'true' : 'false');
}
});
this.listeners.bind(menuItem, 'click keyup', function (event) {
if (is$1.keyboardEvent(event) && event.which !== 32) {
return;
}
event.preventDefault();
event.stopPropagation();
menuItem.checked = true;
switch (type) {
case 'language':
_this2.currentTrack = Number(value);
break;
case 'quality':
_this2.quality = value;
break;
case 'speed':
_this2.speed = parseFloat(value);
break;
default:
break;
}
controls.showMenuPanel.call(_this2, 'home', is$1.keyboardEvent(event));
}, type, false);
controls.bindMenuItemShortcuts.call(this, menuItem, type);
list.appendChild(menuItem);
},
// Format a time for display
formatTime: function formatTime$$1() {
var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var inverted = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
// Bail if the value isn't a number
if (!is$1.number(time)) {
return time;
} // Always display hours if duration is over an hour
var forceHours = getHours(this.duration) > 0;
return formatTime(time, forceHours, inverted);
},
// Update the displayed time
updateTimeDisplay: function updateTimeDisplay() {
var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var inverted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
// Bail if there's no element to display or the value isn't a number
if (!is$1.element(target) || !is$1.number(time)) {
return;
} // eslint-disable-next-line no-param-reassign
target.innerText = controls.formatTime(time, inverted);
},
// Update volume UI and storage
updateVolume: function updateVolume() {
if (!this.supported.ui) {
return;
} // Update range
if (is$1.element(this.elements.inputs.volume)) {
controls.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);
} // Update mute state
if (is$1.element(this.elements.buttons.mute)) {
this.elements.buttons.mute.pressed = this.muted || this.volume === 0;
}
},
// Update seek value and lower fill
setRange: function setRange(target) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
if (!is$1.element(target)) {
return;
} // eslint-disable-next-line
target.value = value; // Webkit range fill
controls.updateRangeFill.call(this, target);
},
// Update <progress> elements
updateProgress: function updateProgress(event) {
var _this3 = this;
if (!this.supported.ui || !is$1.event(event)) {
return;
}
var value = 0;
var setProgress = function setProgress(target, input) {
var value = is$1.number(input) ? input : 0;
var progress = is$1.element(target) ? target : _this3.elements.display.buffer; // Update value and label
if (is$1.element(progress)) {
progress.value = value; // Update text label inside
var label = progress.getElementsByTagName('span')[0];
if (is$1.element(label)) {
label.childNodes[0].nodeValue = value;
}
}
};
if (event) {
switch (event.type) {
// Video playing
case 'timeupdate':
case 'seeking':
case 'seeked':
value = getPercentage(this.currentTime, this.duration); // Set seek range value only if it's a 'natural' time event
if (event.type === 'timeupdate') {
controls.setRange.call(this, this.elements.inputs.seek, value);
}
break;
// Check buffer status
case 'playing':
case 'progress':
setProgress(this.elements.display.buffer, this.buffered * 100);
break;
default:
break;
}
}
},
// Webkit polyfill for lower fill range
updateRangeFill: function updateRangeFill(target) {
// Get range from event if event passed
var range = is$1.event(target) ? target.target : target; // Needs to be a valid <input type='range'>
if (!is$1.element(range) || range.getAttribute('type') !== 'range') {
return;
} // Set aria values for https://github.com/sampotts/plyr/issues/905
if (matches(range, this.config.selectors.inputs.seek)) {
range.setAttribute('aria-valuenow', this.currentTime);
var currentTime = controls.formatTime(this.currentTime);
var duration = controls.formatTime(this.duration);
var format$$1 = i18n.get('seekLabel', this.config);
range.setAttribute('aria-valuetext', format$$1.replace('{currentTime}', currentTime).replace('{duration}', duration));
} else if (matches(range, this.config.selectors.inputs.volume)) {
var percent = range.value * 100;
range.setAttribute('aria-valuenow', percent);
range.setAttribute('aria-valuetext', "".concat(percent.toFixed(1), "%"));
} else {
range.setAttribute('aria-valuenow', range.value);
} // WebKit only
if (!browser.isWebkit) {
return;
} // Set CSS custom property
range.style.setProperty('--value', "".concat(range.value / range.max * 100, "%"));
},
// Update hover tooltip for seeking
updateSeekTooltip: function updateSeekTooltip(event) {
var _this4 = this;
// Bail if setting not true
if (!this.config.tooltips.seek || !is$1.element(this.elements.inputs.seek) || !is$1.element(this.elements.display.seekTooltip) || this.duration === 0) {
return;
} // Calculate percentage
var percent = 0;
var clientRect = this.elements.progress.getBoundingClientRect();
var visible = "".concat(this.config.classNames.tooltip, "--visible");
var toggle = function toggle(_toggle) {
toggleClass(_this4.elements.display.seekTooltip, visible, _toggle);
}; // Hide on touch
if (this.touch) {
toggle(false);
return;
} // Determine percentage, if already visible
if (is$1.event(event)) {
percent = 100 / clientRect.width * (event.pageX - clientRect.left);
} else if (hasClass(this.elements.display.seekTooltip, visible)) {
percent = parseFloat(this.elements.display.seekTooltip.style.left, 10);
} else {
return;
} // Set bounds
if (percent < 0) {
percent = 0;
} else if (percent > 100) {
percent = 100;
} // Display the time a click would seek to
controls.updateTimeDisplay.call(this, this.elements.display.seekTooltip, this.duration / 100 * percent); // Set position
this.elements.display.seekTooltip.style.left = "".concat(percent, "%"); // Show/hide the tooltip
// If the event is a moues in/out and percentage is inside bounds
if (is$1.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) {
toggle(event.type === 'mouseenter');
}
},
// Handle time change event
timeUpdate: function timeUpdate(event) {
// Only invert if only one time element is displayed and used for both duration and currentTime
var invert = !is$1.element(this.elements.display.duration) && this.config.invertTime; // Duration
controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert); // Ignore updates while seeking
if (event && event.type === 'timeupdate' && this.media.seeking) {
return;
} // Playing progress
controls.updateProgress.call(this, event);
},
// Show the duration on metadataloaded or durationchange events
durationUpdate: function durationUpdate() {
// Bail if no UI or durationchange event triggered after playing/seek when invertTime is false
if (!this.supported.ui || !this.config.invertTime && this.currentTime) {
return;
} // If duration is the 2**32 (shaka), Infinity (HLS), DASH-IF (Number.MAX_SAFE_INTEGER || Number.MAX_VALUE) indicating live we hide the currentTime and progressbar.
// https://github.com/video-dev/hls.js/blob/5820d29d3c4c8a46e8b75f1e3afa3e68c1a9a2db/src/controller/buffer-controller.js#L415
// https://github.com/google/shaka-player/blob/4d889054631f4e1cf0fbd80ddd2b71887c02e232/lib/media/streaming_engine.js#L1062
// https://github.com/Dash-Industry-Forum/dash.js/blob/69859f51b969645b234666800d4cb596d89c602d/src/dash/models/DashManifestModel.js#L338
if (this.duration >= Math.pow(2, 32)) {
toggleHidden(this.elements.display.currentTime, true);
toggleHidden(this.elements.progress, true);
return;
} // Update ARIA values
if (is$1.element(this.elements.inputs.seek)) {
this.elements.inputs.seek.setAttribute('aria-valuemax', this.duration);
} // If there's a spot to display duration
var hasDuration = is$1.element(this.elements.display.duration); // If there's only one time display, display duration there
if (!hasDuration && this.config.displayDuration && this.paused) {
controls.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);
} // If there's a duration element, update content
if (hasDuration) {
controls.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);
} // Update the tooltip (if visible)
controls.updateSeekTooltip.call(this);
},
// Hide/show a tab
toggleMenuButton: function toggleMenuButton(setting, toggle) {
toggleHidden(this.elements.settings.buttons[setting], !toggle);
},
// Update the selected setting
updateSetting: function updateSetting(setting, container, input) {
var pane = this.elements.settings.panels[setting];
var value = null;
var list = container;
if (setting === 'captions') {
value = this.currentTrack;
} else {
value = !is$1.empty(input) ? input : this[setting]; // Get default
if (is$1.empty(value)) {
value = this.config[setting].default;
} // Unsupported value
if (!is$1.empty(this.options[setting]) && !this.options[setting].includes(value)) {
this.debug.warn("Unsupported value of '".concat(value, "' for ").concat(setting));
return;
} // Disabled value
if (!this.config[setting].options.includes(value)) {
this.debug.warn("Disabled value of '".concat(value, "' for ").concat(setting));
return;
}
} // Get the list if we need to
if (!is$1.element(list)) {
list = pane && pane.querySelector('[role="menu"]');
} // If there's no list it means it's not been rendered...
if (!is$1.element(list)) {
return;
} // Update the label
var label = this.elements.settings.buttons[setting].querySelector(".".concat(this.config.classNames.menu.value));
label.innerHTML = controls.getLabel.call(this, setting, value); // Find the radio option and check it
var target = list && list.querySelector("[value=\"".concat(value, "\"]"));
if (is$1.element(target)) {
target.checked = true;
}
},
// Translate a value into a nice label
getLabel: function getLabel(setting, value) {
switch (setting) {
case 'speed':
return value === 1 ? i18n.get('normal', this.config) : "".concat(value, "&times;");
case 'quality':
if (is$1.number(value)) {
var label = i18n.get("qualityLabel.".concat(value), this.config);
if (!label.length) {
return "".concat(value, "p");
}
return label;
}
return toTitleCase(value);
case 'captions':
return captions.getLabel.call(this);
default:
return null;
}
},
// Set the quality menu
setQualityMenu: function setQualityMenu(options) {
var _this5 = this;
// Menu required
if (!is$1.element(this.elements.settings.panels.quality)) {
return;
}
var type = 'quality';
var list = this.elements.settings.panels.quality.querySelector('[role="menu"]'); // Set options if passed and filter based on uniqueness and config
if (is$1.array(options)) {
this.options.quality = dedupe(options).filter(function (quality) {
return _this5.config.quality.options.includes(quality);
});
} // Toggle the pane and tab
var toggle = !is$1.empty(this.options.quality) && this.options.quality.length > 1;
controls.toggleMenuButton.call(this, type, toggle); // Empty the menu
emptyElement(list); // Check if we need to toggle the parent
controls.checkMenu.call(this); // If we're hiding, nothing more to do
if (!toggle) {
return;
} // Get the badge HTML for HD, 4K etc
var getBadge = function getBadge(quality) {
var label = i18n.get("qualityBadge.".concat(quality), _this5.config);
if (!label.length) {
return null;
}
return controls.createBadge.call(_this5, label);
}; // Sort options by the config and then render options
this.options.quality.sort(function (a, b) {
var sorting = _this5.config.quality.options;
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
}).forEach(function (quality) {
controls.createMenuItem.call(_this5, {
value: quality,
list: list,
type: type,
title: controls.getLabel.call(_this5, 'quality', quality),
badge: getBadge(quality)
});
});
controls.updateSetting.call(this, type, list);
},
// Set the looping options
/* setLoopMenu() {
// Menu required
if (!is.element(this.elements.settings.panels.loop)) {
return;
}
const options = ['start', 'end', 'all', 'reset'];
const list = this.elements.settings.panels.loop.querySelector('[role="menu"]');
// Show the pane and tab
toggleHidden(this.elements.settings.buttons.loop, false);
toggleHidden(this.elements.settings.panels.loop, false);
// Toggle the pane and tab
const toggle = !is.empty(this.loop.options);
controls.toggleMenuButton.call(this, 'loop', toggle);
// Empty the menu
emptyElement(list);
options.forEach(option => {
const item = createElement('li');
const button = createElement(
'button',
extend(getAttributesFromSelector(this.config.selectors.buttons.loop), {
type: 'button',
class: this.config.classNames.control,
'data-plyr-loop-action': option,
}),
i18n.get(option, this.config)
);
if (['start', 'end'].includes(option)) {
const badge = controls.createBadge.call(this, '00:00');
button.appendChild(badge);
}
item.appendChild(button);
list.appendChild(item);
});
}, */
// Get current selected caption language
// TODO: rework this to user the getter in the API?
// Set a list of available captions languages
setCaptionsMenu: function setCaptionsMenu() {
var _this6 = this;
// Menu required
if (!is$1.element(this.elements.settings.panels.captions)) {
return;
} // TODO: Captions or language? Currently it's mixed
var type = 'captions';
var list = this.elements.settings.panels.captions.querySelector('[role="menu"]');
var tracks = captions.getTracks.call(this);
var toggle = Boolean(tracks.length); // Toggle the pane and tab
controls.toggleMenuButton.call(this, type, toggle); // Empty the menu
emptyElement(list); // Check if we need to toggle the parent
controls.checkMenu.call(this); // If there's no captions, bail
if (!toggle) {
return;
} // Generate options data
var options = tracks.map(function (track, value) {
return {
value: value,
checked: _this6.captions.toggled && _this6.currentTrack === value,
title: captions.getLabel.call(_this6, track),
badge: track.language && controls.createBadge.call(_this6, track.language.toUpperCase()),
list: list,
type: 'language'
};
}); // Add the "Disabled" option to turn off captions
options.unshift({
value: -1,
checked: !this.captions.toggled,
title: i18n.get('disabled', this.config),
list: list,
type: 'language'
}); // Generate options
options.forEach(controls.createMenuItem.bind(this));
controls.updateSetting.call(this, type, list);
},
// Set a list of available captions languages
setSpeedMenu: function setSpeedMenu(options) {
var _this7 = this;
// Menu required
if (!is$1.element(this.elements.settings.panels.speed)) {
return;
}
var type = 'speed';
var list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); // Set the speed options
if (is$1.array(options)) {
this.options.speed = options;
} else if (this.isHTML5 || this.isVimeo) {
this.options.speed = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
} // Set options if passed and filter based on config
this.options.speed = this.options.speed.filter(function (speed) {
return _this7.config.speed.options.includes(speed);
}); // Toggle the pane and tab
var toggle = !is$1.empty(this.options.speed) && this.options.speed.length > 1;
controls.toggleMenuButton.call(this, type, toggle); // Empty the menu
emptyElement(list); // Check if we need to toggle the parent
controls.checkMenu.call(this); // If we're hiding, nothing more to do
if (!toggle) {
return;
} // Create items
this.options.speed.forEach(function (speed) {
controls.createMenuItem.call(_this7, {
value: speed,
list: list,
type: type,
title: controls.getLabel.call(_this7, 'speed', speed)
});
});
controls.updateSetting.call(this, type, list);
},
// Check if we need to hide/show the settings menu
checkMenu: function checkMenu() {
var buttons = this.elements.settings.buttons;
var visible = !is$1.empty(buttons) && Object.values(buttons).some(function (button) {
return !button.hidden;
});
toggleHidden(this.elements.settings.menu, !visible);
},
// Focus the first menu item in a given (or visible) menu
focusFirstMenuItem: function focusFirstMenuItem(pane) {
var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (this.elements.settings.popup.hidden) {
return;
}
var target = pane;
if (!is$1.element(target)) {
target = Object.values(this.elements.settings.panels).find(function (pane) {
return !pane.hidden;
});
}
var firstItem = target.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, tabFocus);
},
// Show/hide menu
toggleMenu: function toggleMenu(input) {
var popup = this.elements.settings.popup;
var button = this.elements.buttons.settings; // Menu and button are required
if (!is$1.element(popup) || !is$1.element(button)) {
return;
} // True toggle by default
var hidden = popup.hidden;
var show = hidden;
if (is$1.boolean(input)) {
show = input;
} else if (is$1.keyboardEvent(input) && input.which === 27) {
show = false;
} else if (is$1.event(input)) {
var isMenuItem = popup.contains(input.target); // If the click was inside the menu or if the click
// wasn't the button or menu item and we're trying to
// show the menu (a doc click shouldn't show the menu)
if (isMenuItem || !isMenuItem && input.target !== button && show) {
return;
}
} // Set button attributes
button.setAttribute('aria-expanded', show); // Show the actual popup
toggleHidden(popup, !show); // Add class hook
toggleClass(this.elements.container, this.config.classNames.menu.open, show); // Focus the first item if key interaction
if (show && is$1.keyboardEvent(input)) {
controls.focusFirstMenuItem.call(this, null, true);
} else if (!show && !hidden) {
// If closing, re-focus the button
setFocus.call(this, button, is$1.keyboardEvent(input));
}
},
// Get the natural size of a menu panel
getMenuSize: function getMenuSize(tab) {
var clone = tab.cloneNode(true);
clone.style.position = 'absolute';
clone.style.opacity = 0;
clone.removeAttribute('hidden'); // Append to parent so we get the "real" size
tab.parentNode.appendChild(clone); // Get the sizes before we remove
var width = clone.scrollWidth;
var height = clone.scrollHeight; // Remove from the DOM
removeElement(clone);
return {
width: width,
height: height
};
},
// Show a panel in the menu
showMenuPanel: function showMenuPanel() {
var _this8 = this;
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var target = document.getElementById("plyr-settings-".concat(this.id, "-").concat(type)); // Nothing to show, bail
if (!is$1.element(target)) {
return;
} // Hide all other panels
var container = target.parentNode;
var current = Array.from(container.children).find(function (node) {
return !node.hidden;
}); // If we can do fancy animations, we'll animate the height/width
if (support.transitions && !support.reducedMotion) {
// Set the current width as a base
container.style.width = "".concat(current.scrollWidth, "px");
container.style.height = "".concat(current.scrollHeight, "px"); // Get potential sizes
var size = controls.getMenuSize.call(this, target); // Restore auto height/width
var restore = function restore(event) {
// We're only bothered about height and width on the container
if (event.target !== container || !['width', 'height'].includes(event.propertyName)) {
return;
} // Revert back to auto
container.style.width = '';
container.style.height = ''; // Only listen once
off.call(_this8, container, transitionEndEvent, restore);
}; // Listen for the transition finishing and restore auto height/width
on.call(this, container, transitionEndEvent, restore); // Set dimensions to target
container.style.width = "".concat(size.width, "px");
container.style.height = "".concat(size.height, "px");
} // Set attributes on current tab
toggleHidden(current, true); // Set attributes on target
toggleHidden(target, false); // Focus the first item
controls.focusFirstMenuItem.call(this, target, tabFocus);
},
// Set the download link
setDownloadLink: function setDownloadLink() {
var button = this.elements.buttons.download; // Bail if no button
if (!is$1.element(button)) {
return;
} // Set download link
button.setAttribute('href', this.download);
},
// Build the default HTML
// TODO: Set order based on order in the config.controls array?
create: function create(data) {
var _this9 = this;
// Create the container
var container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper)); // Restart button
if (this.config.controls.includes('restart')) {
container.appendChild(controls.createButton.call(this, 'restart'));
} // Rewind button
if (this.config.controls.includes('rewind')) {
container.appendChild(controls.createButton.call(this, 'rewind'));
} // Play/Pause button
if (this.config.controls.includes('play')) {
container.appendChild(controls.createButton.call(this, 'play'));
} // Fast forward button
if (this.config.controls.includes('fast-forward')) {
container.appendChild(controls.createButton.call(this, 'fast-forward'));
} // Progress
if (this.config.controls.includes('progress')) {
var progress = createElement('div', getAttributesFromSelector(this.config.selectors.progress)); // Seek range slider
progress.appendChild(controls.createRange.call(this, 'seek', {
id: "plyr-seek-".concat(data.id)
})); // Buffer progress
progress.appendChild(controls.createProgress.call(this, 'buffer')); // TODO: Add loop display indicator
// Seek tooltip
if (this.config.tooltips.seek) {
var tooltip = createElement('span', {
class: this.config.classNames.tooltip
}, '00:00');
progress.appendChild(tooltip);
this.elements.display.seekTooltip = tooltip;
}
this.elements.progress = progress;
container.appendChild(this.elements.progress);
} // Media current time display
if (this.config.controls.includes('current-time')) {
container.appendChild(controls.createTime.call(this, 'currentTime'));
} // Media duration display
if (this.config.controls.includes('duration')) {
container.appendChild(controls.createTime.call(this, 'duration'));
} // Volume controls
if (this.config.controls.includes('mute') || this.config.controls.includes('volume')) {
var volume = createElement('div', {
class: 'plyr__volume'
}); // Toggle mute button
if (this.config.controls.includes('mute')) {
volume.appendChild(controls.createButton.call(this, 'mute'));
} // Volume range control
if (this.config.controls.includes('volume')) {
// Set the attributes
var attributes = {
max: 1,
step: 0.05,
value: this.config.volume
}; // Create the volume range slider
volume.appendChild(controls.createRange.call(this, 'volume', extend(attributes, {
id: "plyr-volume-".concat(data.id)
})));
this.elements.volume = volume;
}
container.appendChild(volume);
} // Toggle captions button
if (this.config.controls.includes('captions')) {
container.appendChild(controls.createButton.call(this, 'captions'));
} // Settings button / menu
if (this.config.controls.includes('settings') && !is$1.empty(this.config.settings)) {
var control = createElement('div', {
class: 'plyr__menu',
hidden: ''
});
control.appendChild(controls.createButton.call(this, 'settings', {
'aria-haspopup': true,
'aria-controls': "plyr-settings-".concat(data.id),
'aria-expanded': false
}));
var popup = createElement('div', {
class: 'plyr__menu__container',
id: "plyr-settings-".concat(data.id),
hidden: ''
});
var inner = createElement('div');
var home = createElement('div', {
id: "plyr-settings-".concat(data.id, "-home")
}); // Create the menu
var menu = createElement('div', {
role: 'menu'
});
home.appendChild(menu);
inner.appendChild(home);
this.elements.settings.panels.home = home; // Build the menu items
this.config.settings.forEach(function (type) {
// TODO: bundle this with the createMenuItem helper and bindings
var menuItem = createElement('button', extend(getAttributesFromSelector(_this9.config.selectors.buttons.settings), {
type: 'button',
class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--forward"),
role: 'menuitem',
'aria-haspopup': true,
hidden: ''
})); // Bind menu shortcuts for keyboard users
controls.bindMenuItemShortcuts.call(_this9, menuItem, type); // Show menu on click
on(menuItem, 'click', function () {
controls.showMenuPanel.call(_this9, type, false);
});
var flex = createElement('span', null, i18n.get(type, _this9.config));
var value = createElement('span', {
class: _this9.config.classNames.menu.value
}); // Speed contains HTML entities
value.innerHTML = data[type];
flex.appendChild(value);
menuItem.appendChild(flex);
menu.appendChild(menuItem); // Build the panes
var pane = createElement('div', {
id: "plyr-settings-".concat(data.id, "-").concat(type),
hidden: ''
}); // Back button
var backButton = createElement('button', {
type: 'button',
class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--back")
}); // Visible label
backButton.appendChild(createElement('span', {
'aria-hidden': true
}, i18n.get(type, _this9.config))); // Screen reader label
backButton.appendChild(createElement('span', {
class: _this9.config.classNames.hidden
}, i18n.get('menuBack', _this9.config))); // Go back via keyboard
on(pane, 'keydown', function (event) {
// We only care about <-
if (event.which !== 37) {
return;
} // Prevent seek
event.preventDefault();
event.stopPropagation(); // Show the respective menu
controls.showMenuPanel.call(_this9, 'home', true);
}, false); // Go back via button click
on(backButton, 'click', function () {
controls.showMenuPanel.call(_this9, 'home', false);
}); // Add to pane
pane.appendChild(backButton); // Menu
pane.appendChild(createElement('div', {
role: 'menu'
}));
inner.appendChild(pane);
_this9.elements.settings.buttons[type] = menuItem;
_this9.elements.settings.panels[type] = pane;
});
popup.appendChild(inner);
control.appendChild(popup);
container.appendChild(control);
this.elements.settings.popup = popup;
this.elements.settings.menu = control;
} // Picture in picture button
if (this.config.controls.includes('pip') && support.pip) {
container.appendChild(controls.createButton.call(this, 'pip'));
} // Airplay button
if (this.config.controls.includes('airplay') && support.airplay) {
container.appendChild(controls.createButton.call(this, 'airplay'));
} // Download button
if (this.config.controls.includes('download')) {
var _attributes = {
element: 'a',
href: this.download,
target: '_blank'
};
var download = this.config.urls.download;
if (!is$1.url(download) && this.isEmbed) {
extend(_attributes, {
icon: "logo-".concat(this.provider),
label: this.provider
});
}
container.appendChild(controls.createButton.call(this, 'download', _attributes));
} // Toggle fullscreen button
if (this.config.controls.includes('fullscreen')) {
container.appendChild(controls.createButton.call(this, 'fullscreen'));
} // Larger overlaid play button
if (this.config.controls.includes('play-large')) {
this.elements.container.appendChild(controls.createButton.call(this, 'play-large'));
}
this.elements.controls = container; // Set available quality levels
if (this.isHTML5) {
controls.setQualityMenu.call(this, html5.getQualityOptions.call(this));
}
controls.setSpeedMenu.call(this);
return container;
},
// Insert controls
inject: function inject() {
var _this10 = this;
// Sprite
if (this.config.loadSprite) {
var icon = controls.getIconUrl.call(this); // Only load external sprite using AJAX
if (icon.cors) {
loadSprite(icon.url, 'sprite-plyr');
}
} // Create a unique ID
this.id = Math.floor(Math.random() * 10000); // Null by default
var container = null;
this.elements.controls = null; // Set template properties
var props = {
id: this.id,
seektime: this.config.seekTime,
title: this.config.title
};
var update = true; // If function, run it and use output
if (is$1.function(this.config.controls)) {
this.config.controls = this.config.controls.call(this.props);
} // Convert falsy controls to empty array (primarily for empty strings)
if (!this.config.controls) {
this.config.controls = [];
}
if (is$1.element(this.config.controls) || is$1.string(this.config.controls)) {
// HTMLElement or Non-empty string passed as the option
container = this.config.controls;
} else {
// Create controls
container = controls.create.call(this, {
id: this.id,
seektime: this.config.seekTime,
speed: this.speed,
quality: this.quality,
captions: captions.getLabel.call(this) // TODO: Looping
// loop: 'None',
});
update = false;
} // Replace props with their value
var replace = function replace(input) {
var result = input;
Object.entries(props).forEach(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 2),
key = _ref3[0],
value = _ref3[1];
result = replaceAll(result, "{".concat(key, "}"), value);
});
return result;
}; // Update markup
if (update) {
if (is$1.string(this.config.controls)) {
container = replace(container);
} else if (is$1.element(container)) {
container.innerHTML = replace(container.innerHTML);
}
} // Controls container
var target; // Inject to custom location
if (is$1.string(this.config.selectors.controls.container)) {
target = document.querySelector(this.config.selectors.controls.container);
} // Inject into the container by default
if (!is$1.element(target)) {
target = this.elements.container;
} // Inject controls HTML (needs to be before captions, hence "afterbegin")
var insertMethod = is$1.element(container) ? 'insertAdjacentElement' : 'insertAdjacentHTML';
target[insertMethod]('afterbegin', container); // Find the elements if need be
if (!is$1.element(this.elements.controls)) {
controls.findElements.call(this);
} // Add pressed property to buttons
if (!is$1.empty(this.elements.buttons)) {
var addProperty = function addProperty(button) {
var className = _this10.config.classNames.controlPressed;
Object.defineProperty(button, 'pressed', {
enumerable: true,
get: function get() {
return hasClass(button, className);
},
set: function set() {
var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
toggleClass(button, className, pressed);
}
});
}; // Toggle classname when pressed property is set
Object.values(this.elements.buttons).filter(Boolean).forEach(function (button) {
if (is$1.array(button) || is$1.nodeList(button)) {
Array.from(button).filter(Boolean).forEach(addProperty);
} else {
addProperty(button);
}
});
} // Edge sometimes doesn't finish the paint so force a redraw
if (window.navigator.userAgent.includes('Edge')) {
repaint(target);
} // Setup tooltips
if (this.config.tooltips.controls) {
var _this$config = this.config,
classNames = _this$config.classNames,
selectors = _this$config.selectors;
var selector = "".concat(selectors.controls.wrapper, " ").concat(selectors.labels, " .").concat(classNames.hidden);
var labels = getElements.call(this, selector);
Array.from(labels).forEach(function (label) {
toggleClass(label, _this10.config.classNames.hidden, false);
toggleClass(label, _this10.config.classNames.tooltip, true);
});
}
}
};
/**
* Parse a string to a URL object
* @param {string} input - the URL to be parsed
* @param {boolean} safe - failsafe parsing
*/
function parseUrl(input) {
var safe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var url = input;
if (safe) {
var parser = document.createElement('a');
parser.href = url;
url = parser.href;
}
try {
return new URL(url);
} catch (e) {
return null;
}
} // Convert object to URLSearchParams
function buildUrlParams(input) {
var params = new URLSearchParams();
if (is$1.object(input)) {
Object.entries(input).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
params.set(key, value);
});
}
return params;
}
var captions = {
// Setup captions
setup: function setup() {
// Requires UI support
if (!this.supported.ui) {
return;
} // Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || this.isHTML5 && !support.textTracks) {
// Clear menu and hide
if (is$1.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
return;
} // Inject the container
if (!is$1.element(this.elements.captions)) {
this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions));
insertAfter(this.elements.captions, this.elements.wrapper);
} // Fix IE captions if CORS is used
// Fetch captions and inject as blobs instead (data URIs not supported!)
if (browser.isIE && window.URL) {
var elements = this.media.querySelectorAll('track');
Array.from(elements).forEach(function (track) {
var src = track.getAttribute('src');
var url = parseUrl(src);
if (url !== null && url.hostname !== window.location.href.hostname && ['http:', 'https:'].includes(url.protocol)) {
fetch(src, 'blob').then(function (blob) {
track.setAttribute('src', window.URL.createObjectURL(blob));
}).catch(function () {
removeElement(track);
});
}
});
} // Get and set initial data
// The "preferred" options are not realized unless / until the wanted language has a match
// * languages: Array of user's browser languages.
// * language: The language preferred by user settings or config
// * active: The state preferred by user settings or config
// * toggled: The real captions state
var browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
var languages = dedupe(browserLanguages.map(function (language) {
return language.split('-')[0];
}));
var language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase(); // Use first browser language when language is 'auto'
if (language === 'auto') {
var _languages = _slicedToArray(languages, 1);
language = _languages[0];
}
var active = this.storage.get('captions');
if (!is$1.boolean(active)) {
active = this.config.captions.active;
}
Object.assign(this.captions, {
toggled: false,
active: active,
language: language,
languages: languages
}); // Watch changes to textTracks and update captions menu
if (this.isHTML5) {
var trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
on.call(this, this.media.textTracks, trackEvents, captions.update.bind(this));
} // Update available languages in list next tick (the event must not be triggered before the listeners)
setTimeout(captions.update.bind(this), 0);
},
// Update available language options in settings based on tracks
update: function update() {
var _this = this;
var tracks = captions.getTracks.call(this, true); // Get the wanted language
var _this$captions = this.captions,
active = _this$captions.active,
language = _this$captions.language,
meta = _this$captions.meta,
currentTrackNode = _this$captions.currentTrackNode;
var languageExists = Boolean(tracks.find(function (track) {
return track.language === language;
})); // Handle tracks (add event listener and "pseudo"-default)
if (this.isHTML5 && this.isVideo) {
tracks.filter(function (track) {
return !meta.get(track);
}).forEach(function (track) {
_this.debug.log('Track added', track); // Attempt to store if the original dom element was "default"
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
track.mode = 'hidden'; // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
});
});
} // Update language first time it matches, or if the previous matching track was removed
if (languageExists && this.language !== language || !tracks.includes(currentTrackNode)) {
captions.setLanguage.call(this, language);
captions.toggle.call(this, active && languageExists);
} // Enable or disable captions based on track length
toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is$1.empty(tracks)); // Update available languages in list
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
if (!this.supported.ui) {
return;
}
var toggled = this.captions.toggled; // Current state
var activeClass = this.config.classNames.captions.active; // Get the next state
// If the method is called without parameter, toggle based on current value
var active = is$1.nullOrUndefined(input) ? !toggled : input; // Update state and trigger event
if (active !== toggled) {
// When passive, don't override user preferences
if (!passive) {
this.captions.active = active;
this.storage.set({
captions: active
});
} // Force language if the call isn't passive and there is no matching language to toggle to
if (!this.language && active && !passive) {
var tracks = captions.getTracks.call(this);
var track = captions.findTrack.call(this, [this.captions.language].concat(_toConsumableArray(this.captions.languages)), true); // Override user preferences to avoid switching languages if a matching track is added
this.captions.language = track.language; // Set caption, but don't store in localStorage as user preference
captions.set.call(this, tracks.indexOf(track));
return;
} // Toggle button if it's enabled
if (this.elements.buttons.captions) {
this.elements.buttons.captions.pressed = active;
} // Add class hook
toggleClass(this.elements.container, activeClass, active);
this.captions.toggled = active; // Update settings menu
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
set: function set(index) {
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var tracks = captions.getTracks.call(this); // Disable captions if setting to -1
if (index === -1) {
captions.toggle.call(this, false, passive);
return;
}
if (!is$1.number(index)) {
this.debug.warn('Invalid caption argument', index);
return;
}
if (!(index in tracks)) {
this.debug.warn('Track not found', index);
return;
}
if (this.captions.currentTrack !== index) {
this.captions.currentTrack = index;
var track = tracks[index];
var _ref = track || {},
language = _ref.language; // Store reference to node for invalidation on remove
this.captions.currentTrackNode = track; // Update settings menu
controls.updateSetting.call(this, 'captions'); // When passive, don't override user preferences
if (!passive) {
this.captions.language = language;
this.storage.set({
language: language
});
} // Handle Vimeo captions
if (this.isVimeo) {
this.embed.enableTextTrack(language);
} // Trigger event
triggerEvent.call(this, this.media, 'languagechange');
} // Show captions
captions.toggle.call(this, true, passive);
if (this.isHTML5 && this.isVideo) {
// If we change the active track while a cue is already displayed we need to update it
captions.updateCues.call(this);
}
},
// Set captions by language
// Used internally for the language setter with the passive option forced to false
setLanguage: function setLanguage(input) {
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (!is$1.string(input)) {
this.debug.warn('Invalid language argument', input);
return;
} // Normalize
var language = input.toLowerCase();
this.captions.language = language; // Set currentTrack
var tracks = captions.getTracks.call(this);
var track = captions.findTrack.call(this, [language]);
captions.set.call(this, tracks.indexOf(track), passive);
},
// Get current valid caption tracks
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
var tracks = Array.from((this.media || {}).textTracks || []); // For HTML5, use cache instead of current tracks when it exists (if captions.update is false)
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
return sortIsDefault(b) - sortIsDefault(a);
});
var track;
languages.every(function (language) {
track = sorted.find(function (track) {
return track.language === language;
});
return !track; // Break iteration if there is a match
}); // If no match is found but is required, get first
return track || (force ? sorted[0] : undefined);
},
// Get the current track
getCurrentTrack: function getCurrentTrack() {
return captions.getTracks.call(this)[this.currentTrack];
},
// Get UI label for track
getLabel: function getLabel(track) {
var currentTrack = track;
if (!is$1.track(currentTrack) && support.textTracks && this.captions.toggled) {
currentTrack = captions.getCurrentTrack.call(this);
}
if (is$1.track(currentTrack)) {
if (!is$1.empty(currentTrack.label)) {
return currentTrack.label;
}
if (!is$1.empty(currentTrack.language)) {
return track.language.toUpperCase();
}
return i18n.get('enabled', this.config);
}
return i18n.get('disabled', this.config);
},
// Update captions using current track's active cues
// Also optional array argument in case there isn't any track (ex: vimeo)
updateCues: function updateCues(input) {
// Requires UI
if (!this.supported.ui) {
return;
}
if (!is$1.element(this.elements.captions)) {
this.debug.warn('No captions element to render to');
return;
} // Only accept array or empty input
if (!is$1.nullOrUndefined(input) && !Array.isArray(input)) {
this.debug.warn('updateCues: Invalid input', input);
return;
}
var cues = input; // Get cues from track
if (!cues) {
var track = captions.getCurrentTrack.call(this);
cues = Array.from((track || {}).activeCues || []).map(function (cue) {
return cue.getCueAsHTML();
}).map(getHTML);
} // Set new caption text
var content = cues.map(function (cueText) {
return cueText.trim();
}).join('\n');
var changed = content !== this.elements.captions.innerHTML;
if (changed) {
// Empty the container and create a new child element
emptyElement(this.elements.captions);
var caption = createElement('span', getAttributesFromSelector(this.config.selectors.caption));
caption.innerHTML = content;
this.elements.captions.appendChild(caption); // Trigger event
triggerEvent.call(this, this.media, 'cuechange');
}
}
};
// ==========================================================================
// Plyr default config
// ==========================================================================
var defaults = {
// Disable
enabled: true,
// Custom media title
title: '',
// Logging to console
debug: false,
// Auto play (if supported)
autoplay: false,
// Only allow one media playing at once (vimeo only)
autopause: true,
// Allow inline playback on iOS (this effects YouTube/Vimeo - HTML5 requires the attribute present)
// TODO: Remove iosNative fullscreen option in favour of this (logic needs work)
playsinline: true,
// Default time to skip when rewind/fast forward
seekTime: 10,
// Default volume
volume: 1,
muted: false,
// Pass a custom duration
duration: null,
// Display the media duration on load in the current time position
// If you have opted to display both duration and currentTime, this is ignored
displayDuration: true,
// Invert the current time to be a countdown
invertTime: true,
// Clicking the currentTime inverts it's value to show time left rather than elapsed
toggleInvert: true,
// Aspect ratio (for embeds)
ratio: '16:9',
// Click video container to play/pause
clickToPlay: true,
// Auto hide the controls
hideControls: true,
// Reset to start when playback ended
resetOnEnd: false,
// Disable the standard context menu
disableContextMenu: true,
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.3.12/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
// Quality default
quality: {
default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240]
},
// Set loops
loop: {
active: false // start: null,
// end: null,
},
// Speed default and options to display
speed: {
selected: 1,
options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
},
// Keyboard shortcut settings
keyboard: {
focused: true,
global: false
},
// Display tooltips
tooltips: {
controls: false,
seek: true
},
// Captions settings
captions: {
active: false,
language: 'auto',
// Listen to new tracks added after Plyr is initialized.
// This is needed for streaming captions, but may result in unselectable options
update: false
},
// Fullscreen settings
fullscreen: {
enabled: true,
// Allow fullscreen?
fallback: true,
// Fallback for vintage browsers
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
},
// Local storage
storage: {
enabled: true,
key: 'plyr'
},
// Default controls
controls: ['play-large', // 'restart',
// 'rewind',
'play', // 'fast-forward',
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download',
'fullscreen'],
settings: ['captions', 'quality', 'speed'],
// Localisation
i18n: {
restart: 'Restart',
rewind: 'Rewind {seektime}s',
play: 'Play',
pause: 'Pause',
fastForward: 'Forward {seektime}s',
seek: 'Seek',
seekLabel: '{currentTime} of {duration}',
played: 'Played',
buffered: 'Buffered',
currentTime: 'Current time',
duration: 'Duration',
volume: 'Volume',
mute: 'Mute',
unmute: 'Unmute',
enableCaptions: 'Enable captions',
disableCaptions: 'Disable captions',
download: 'Download',
enterFullscreen: 'Enter fullscreen',
exitFullscreen: 'Exit fullscreen',
frameTitle: 'Player for {title}',
captions: 'Captions',
settings: 'Settings',
menuBack: 'Go back to previous menu',
speed: 'Speed',
normal: 'Normal',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
end: 'End',
all: 'All',
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad',
qualityBadge: {
2160: '4K',
1440: 'HD',
1080: 'HD',
720: 'HD',
576: 'SD',
480: 'SD'
}
},
// URLs
urls: {
download: null,
vimeo: {
sdk: 'https://player.vimeo.com/api/player.js',
iframe: 'https://player.vimeo.com/video/{0}?{1}',
api: 'https://vimeo.com/api/v2/video/{0}.json'
},
youtube: {
sdk: 'https://www.youtube.com/iframe_api',
api: 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title))&part=snippet'
},
googleIMA: {
sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js'
}
},
// Custom control listeners
listeners: {
seek: null,
play: null,
pause: null,
restart: null,
rewind: null,
fastForward: null,
mute: null,
volume: null,
captions: null,
download: null,
fullscreen: null,
pip: null,
airplay: null,
speed: null,
quality: null,
loop: null,
language: null
},
// Events to watch and bubble
events: [// Events to watch on HTML5 media elements and bubble
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events
'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
'statechange', // Quality
'qualitychange', // Ads
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
// Selectors
// Change these to match your template if using custom HTML
selectors: {
editable: 'input, textarea, select, [contenteditable]',
container: '.plyr',
controls: {
container: null,
wrapper: '.plyr__controls'
},
labels: '[data-plyr]',
buttons: {
play: '[data-plyr="play"]',
pause: '[data-plyr="pause"]',
restart: '[data-plyr="restart"]',
rewind: '[data-plyr="rewind"]',
fastForward: '[data-plyr="fast-forward"]',
mute: '[data-plyr="mute"]',
captions: '[data-plyr="captions"]',
download: '[data-plyr="download"]',
fullscreen: '[data-plyr="fullscreen"]',
pip: '[data-plyr="pip"]',
airplay: '[data-plyr="airplay"]',
settings: '[data-plyr="settings"]',
loop: '[data-plyr="loop"]'
},
inputs: {
seek: '[data-plyr="seek"]',
volume: '[data-plyr="volume"]',
speed: '[data-plyr="speed"]',
language: '[data-plyr="language"]',
quality: '[data-plyr="quality"]'
},
display: {
currentTime: '.plyr__time--current',
duration: '.plyr__time--duration',
buffer: '.plyr__progress__buffer',
loop: '.plyr__progress__loop',
// Used later
volume: '.plyr__volume--display'
},
progress: '.plyr__progress',
captions: '.plyr__captions',
caption: '.plyr__caption',
menu: {
quality: '.js-plyr__menu__list--quality'
}
},
// Class hooks added to the player in different states
classNames: {
type: 'plyr--{0}',
provider: 'plyr--{0}',
video: 'plyr__video-wrapper',
embed: 'plyr__video-embed',
embedContainer: 'plyr__video-embed__container',
poster: 'plyr__poster',
posterEnabled: 'plyr__poster-enabled',
ads: 'plyr__ads',
control: 'plyr__control',
controlPressed: 'plyr__control--pressed',
playing: 'plyr--playing',
paused: 'plyr--paused',
stopped: 'plyr--stopped',
loading: 'plyr--loading',
hover: 'plyr--hover',
tooltip: 'plyr__tooltip',
cues: 'plyr__cues',
hidden: 'plyr__sr-only',
hideControls: 'plyr--hide-controls',
isIos: 'plyr--is-ios',
isTouch: 'plyr--is-touch',
uiSupported: 'plyr--full-ui',
noTransition: 'plyr--no-transition',
display: {
time: 'plyr__time'
},
menu: {
value: 'plyr__menu__value',
badge: 'plyr__badge',
open: 'plyr--menu-open'
},
captions: {
enabled: 'plyr--captions-enabled',
active: 'plyr--captions-active'
},
fullscreen: {
enabled: 'plyr--fullscreen-enabled',
fallback: 'plyr--fullscreen-fallback'
},
pip: {
supported: 'plyr--pip-supported',
active: 'plyr--pip-active'
},
airplay: {
supported: 'plyr--airplay-supported',
active: 'plyr--airplay-active'
},
tabFocus: 'plyr__tab-focus'
},
// Embed attributes
attributes: {
embed: {
provider: 'data-plyr-provider',
id: 'data-plyr-embed-id'
}
},
// API keys
keys: {
google: null
},
// Advertisements plugin
// Register for an account here: http://vi.ai/publisher-video-monetization/?aid=plyrio
ads: {
enabled: false,
publisherId: ''
}
};
// ==========================================================================
// Plyr supported types and providers
// ==========================================================================
var providers = {
html5: 'html5',
youtube: 'youtube',
vimeo: 'vimeo'
};
var types = {
audio: 'audio',
video: 'video'
};
/**
* Get provider by URL
* @param {String} url
*/
function getProviderByUrl(url) {
// YouTube
if (/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/.test(url)) {
return providers.youtube;
} // Vimeo
if (/^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(url)) {
return providers.vimeo;
}
return null;
}
// ==========================================================================
// Console wrapper
// ==========================================================================
var noop = function noop() {};
var Console =
/*#__PURE__*/
function () {
function Console() {
var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
_classCallCheck(this, Console);
this.enabled = window.console && enabled;
if (this.enabled) {
this.log('Debugging enabled');
}
}
_createClass(Console, [{
key: "log",
get: function get() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.log, console) : noop;
}
}, {
key: "warn",
get: function get() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop;
}
}, {
key: "error",
get: function get() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.error, console) : noop;
}
}]);
return Console;
}();
function onChange() {
if (!this.enabled) {
return;
} // Update toggle button
var button = this.player.elements.buttons.fullscreen;
if (is$1.element(button)) {
button.pressed = this.active;
} // Trigger an event
triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true); // Trap focus in container
if (!browser.isIos) {
trapFocus.call(this.player, this.target, this.active);
}
}
function toggleFallback() {
var _this = this;
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Store or restore scroll position
if (toggle) {
this.scrollPosition = {
x: window.scrollX || 0,
y: window.scrollY || 0
};
} else {
window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
} // Toggle scroll
document.body.style.overflow = toggle ? 'hidden' : ''; // Toggle class hook
toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle); // Force full viewport on iPhone X+
if (browser.isIos) {
var viewport = document.head.querySelector('meta[name="viewport"]');
var property = 'viewport-fit=cover'; // Inject the viewport meta if required
if (!viewport) {
viewport = document.createElement('meta');
viewport.setAttribute('name', 'viewport');
} // Check if the property already exists
var hasProperty = is$1.string(viewport.content) && viewport.content.includes(property);
if (toggle) {
this.cleanupViewport = !hasProperty;
if (!hasProperty) {
viewport.content += ",".concat(property);
}
} else if (this.cleanupViewport) {
viewport.content = viewport.content.split(',').filter(function (part) {
return part.trim() !== property;
}).join(',');
} // Force a repaint as sometimes Safari doesn't want to fill the screen
setTimeout(function () {
return repaint(_this.target);
}, 100);
} // Toggle button and fire events
onChange.call(this);
}
var Fullscreen =
/*#__PURE__*/
function () {
function Fullscreen(player) {
var _this2 = this;
_classCallCheck(this, Fullscreen);
// Keep reference to parent
this.player = player; // Get prefix
this.prefix = Fullscreen.prefix;
this.property = Fullscreen.property; // Scroll position
this.scrollPosition = {
x: 0,
y: 0
}; // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
// TODO: Filter for target??
onChange.call(_this2);
}); // Fullscreen toggle on double click
on.call(this.player, this.player.elements.container, 'dblclick', function (event) {
// Ignore double click in controls
if (is$1.element(_this2.player.elements.controls) && _this2.player.elements.controls.contains(event.target)) {
return;
}
_this2.toggle();
}); // Update the UI
this.update();
} // Determine if native supported
_createClass(Fullscreen, [{
key: "update",
// Update UI
value: function update() {
if (this.enabled) {
this.player.debug.log("".concat(Fullscreen.native ? 'Native' : 'Fallback', " fullscreen enabled"));
} else {
this.player.debug.log('Fullscreen not supported and fallback disabled');
} // Add styling hook to show button
toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.enabled);
} // Make an element fullscreen
}, {
key: "enter",
value: function enter() {
if (!this.enabled) {
return;
} // iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) {
this.target.webkitEnterFullscreen();
} else if (!Fullscreen.native) {
toggleFallback.call(this, true);
} else if (!this.prefix) {
this.target.requestFullscreen();
} else if (!is$1.empty(this.prefix)) {
this.target["".concat(this.prefix, "Request").concat(this.property)]();
}
} // Bail from fullscreen
}, {
key: "exit",
value: function exit() {
if (!this.enabled) {
return;
} // iOS native fullscreen
if (browser.isIos && this.player.config.fullscreen.iosNative) {
this.target.webkitExitFullscreen();
this.player.play();
} else if (!Fullscreen.native) {
toggleFallback.call(this, false);
} else if (!this.prefix) {
(document.cancelFullScreen || document.exitFullscreen).call(document);
} else if (!is$1.empty(this.prefix)) {
var action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
document["".concat(this.prefix).concat(action).concat(this.property)]();
}
} // Toggle state
}, {
key: "toggle",
value: function toggle() {
if (!this.active) {
this.enter();
} else {
this.exit();
}
}
}, {
key: "enabled",
// Determine if fullscreen is enabled
get: function get() {
return (Fullscreen.native || this.player.config.fullscreen.fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;
} // Get active state
}, {
key: "active",
get: function get() {
if (!this.enabled) {
return false;
} // Fallback using classname
if (!Fullscreen.native) {
return hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
}
var element = !this.prefix ? document.fullscreenElement : document["".concat(this.prefix).concat(this.property, "Element")];
return element === this.target;
} // Get target element
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
}
}], [{
key: "native",
get: function get() {
return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);
} // Get the prefix for handlers
}, {
key: "prefix",
get: function get() {
// No prefix
if (is$1.function(document.exitFullscreen)) {
return '';
} // Check for fullscreen support by vendor prefix
var value = '';
var prefixes = ['webkit', 'moz', 'ms'];
prefixes.some(function (pre) {
if (is$1.function(document["".concat(pre, "ExitFullscreen")]) || is$1.function(document["".concat(pre, "CancelFullScreen")])) {
value = pre;
return true;
}
return false;
});
return value;
}
}, {
key: "property",
get: function get() {
return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
}
}]);
return Fullscreen;
}();
// 20.2.2.28 Math.sign(x)
var _mathSign = Math.sign || function sign(x) {
// eslint-disable-next-line no-self-compare
return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
};
// 20.2.2.28 Math.sign(x)
_export(_export.S, 'Math', { sign: _mathSign });
// ==========================================================================
// Load image avoiding xhr/fetch CORS issues
// Server status can't be obtained this way unfortunately, so this uses "naturalWidth" to determine if the image has loaded
// By default it checks if it is at least 1px, but you can add a second argument to change this
// ==========================================================================
function loadImage(src) {
var minWidth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
return new Promise(function (resolve, reject) {
var image = new Image();
var handler = function handler() {
delete image.onload;
delete image.onerror;
(image.naturalWidth >= minWidth ? resolve : reject)(image);
};
Object.assign(image, {
onload: handler,
onerror: handler,
src: src
});
});
}
var ui = {
addStyleHook: function addStyleHook() {
toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
},
// Toggle native HTML5 media controls
toggleNativeControls: function toggleNativeControls() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (toggle && this.isHTML5) {
this.media.setAttribute('controls', '');
} else {
this.media.removeAttribute('controls');
}
},
// Setup the UI
build: function build() {
var _this = this;
// Re-attach media element listeners
// TODO: Use event bubbling?
this.listeners.media(); // Don't setup interface if no support
if (!this.supported.ui) {
this.debug.warn("Basic support only for ".concat(this.provider, " ").concat(this.type)); // Restore native controls
ui.toggleNativeControls.call(this, true); // Bail
return;
} // Inject custom controls if not present
if (!is$1.element(this.elements.controls)) {
// Inject custom controls
controls.inject.call(this); // Re-attach control listeners
this.listeners.controls();
} // Remove native controls
ui.toggleNativeControls.call(this); // Setup captions for HTML5
if (this.isHTML5) {
captions.setup.call(this);
} // Reset volume
this.volume = null; // Reset mute state
this.muted = null; // Reset speed
this.speed = null; // Reset loop state
this.loop = null; // Reset quality setting
this.quality = null; // Reset volume display
controls.updateVolume.call(this); // Reset time display
controls.timeUpdate.call(this); // Update the UI
ui.checkPlaying.call(this); // Check for picture-in-picture support
toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); // Check for airplay support
toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5); // Add iOS class
toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); // Add touch class
toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch); // Ready for API calls
this.ready = true; // Ready event at end of execution stack
setTimeout(function () {
triggerEvent.call(_this, _this.media, 'ready');
}, 0); // Set the title
ui.setTitle.call(this); // Assure the poster image is set, if the property was added before the element was created
if (this.poster) {
ui.setPoster.call(this, this.poster, false).catch(function () {});
} // Manually set the duration if user has overridden it.
// The event listeners for it doesn't get called if preload is disabled (#701)
if (this.config.duration) {
controls.durationUpdate.call(this);
}
},
// Setup aria attribute for play and iframe title
setTitle: function setTitle() {
// Find the current text
var label = i18n.get('play', this.config); // If there's a media title set, use that for the label
if (is$1.string(this.config.title) && !is$1.empty(this.config.title)) {
label += ", ".concat(this.config.title);
} // If there's a play button, set label
Array.from(this.elements.buttons.play || []).forEach(function (button) {
button.setAttribute('aria-label', label);
}); // Set iframe title
// https://github.com/sampotts/plyr/issues/124
if (this.isEmbed) {
var iframe = getElement.call(this, 'iframe');
if (!is$1.element(iframe)) {
return;
} // Default to media type
var title = !is$1.empty(this.config.title) ? this.config.title : 'video';
var format = i18n.get('frameTitle', this.config);
iframe.setAttribute('title', format.replace('{title}', title));
}
},
// Toggle poster
togglePoster: function togglePoster(enable) {
toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable);
},
// Set the poster image (async)
// Used internally for the poster setter, with the passive option forced to false
setPoster: function setPoster(poster) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// Don't override if call is passive
if (passive && this.poster) {
return Promise.reject(new Error('Poster already set'));
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
return loadImage(poster);
}).catch(function (err) {
// Hide poster on error unless it's been set by another call
if (poster === _this2.poster) {
ui.togglePoster.call(_this2, false);
} // Rethrow
throw err;
}).then(function () {
// Prevent race conditions
if (poster !== _this2.poster) {
throw new Error('setPoster cancelled by later call to setPoster');
}
}).then(function () {
Object.assign(_this2.elements.poster.style, {
backgroundImage: "url('".concat(poster, "')"),
// Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
backgroundSize: ''
});
ui.togglePoster.call(_this2, true);
return poster;
});
},
// Check playing state
checkPlaying: function checkPlaying(event) {
var _this3 = this;
// Class hooks
toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); // Set state
Array.from(this.elements.buttons.play || []).forEach(function (target) {
target.pressed = _this3.playing;
}); // Only update controls on non timeupdate events
if (is$1.event(event) && event.type === 'timeupdate') {
return;
} // Toggle controls
ui.toggleControls.call(this);
},
// Check if media is loading
checkLoading: function checkLoading(event) {
var _this4 = this;
this.loading = ['stalled', 'waiting'].includes(event.type); // Clear timer
clearTimeout(this.timers.loading); // Timer to prevent flicker when seeking
this.timers.loading = setTimeout(function () {
// Update progress bar loading class state
toggleClass(_this4.elements.container, _this4.config.classNames.loading, _this4.loading); // Update controls visibility
ui.toggleControls.call(_this4);
}, this.loading ? 250 : 0);
},
// Toggle controls based on state and `force` argument
toggleControls: function toggleControls(force) {
var controls$$1 = this.elements.controls;
if (controls$$1 && this.config.hideControls) {
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover || recentTouchSeek));
}
}
};
var Listeners =
/*#__PURE__*/
function () {
function Listeners(player) {
_classCallCheck(this, Listeners);
this.player = player;
this.lastKey = null;
this.focusTimer = null;
this.lastKeyDown = null;
this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
this.setTabFocus = this.setTabFocus.bind(this);
this.firstTouch = this.firstTouch.bind(this);
} // Handle key presses
_createClass(Listeners, [{
key: "handleKey",
value: function handleKey(event) {
var player = this.player;
var elements = player.elements;
var code = event.keyCode ? event.keyCode : event.which;
var pressed = event.type === 'keydown';
var repeat = pressed && code === this.lastKey; // Bail if a modifier key is set
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
return;
} // If the event is bubbled from the media element
// Firefox doesn't get the keycode for whatever reason
if (!is$1.number(code)) {
return;
} // Seek by the number keys
var seekByKey = function seekByKey() {
// Divide the max duration into 10th's and times by the number value
player.currentTime = player.duration / 10 * (code - 48);
}; // Handle the key on keydown
// Reset on keyup
if (pressed) {
// Check focused element
// and if the focused element is not editable (e.g. text input)
// and any that accept key input http://webaim.org/techniques/keyboard/
var focused = document.activeElement;
if (is$1.element(focused)) {
var editable = player.config.selectors.editable;
var seek = elements.inputs.seek;
if (focused !== seek && matches(focused, editable)) {
return;
}
if (event.which === 32 && matches(focused, 'button, [role^="menuitem"]')) {
return;
}
} // Which keycodes should we prevent default
var preventDefault = [32, 37, 38, 39, 40, 48, 49, 50, 51, 52, 53, 54, 56, 57, 67, 70, 73, 75, 76, 77, 79]; // If the code is found prevent default (e.g. prevent scrolling for arrows)
if (preventDefault.includes(code)) {
event.preventDefault();
event.stopPropagation();
}
switch (code) {
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
// 0-9
if (!repeat) {
seekByKey();
}
break;
case 32:
case 75:
// Space and K key
if (!repeat) {
player.togglePlay();
}
break;
case 38:
// Arrow up
player.increaseVolume(0.1);
break;
case 40:
// Arrow down
player.decreaseVolume(0.1);
break;
case 77:
// M key
if (!repeat) {
player.muted = !player.muted;
}
break;
case 39:
// Arrow forward
player.forward();
break;
case 37:
// Arrow back
player.rewind();
break;
case 70:
// F key
player.fullscreen.toggle();
break;
case 67:
// C key
if (!repeat) {
player.toggleCaptions();
}
break;
case 76:
// L key
player.loop = !player.loop;
break;
/* case 73:
this.setLoop('start');
break;
case 76:
this.setLoop();
break;
case 79:
this.setLoop('end');
break; */
default:
break;
} // Escape is handle natively when in full screen
// So we only need to worry about non native
if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
player.fullscreen.toggle();
} // Store last code for next cycle
this.lastKey = code;
} else {
this.lastKey = null;
}
} // Toggle menu
}, {
key: "toggleMenu",
value: function toggleMenu(event) {
controls.toggleMenu.call(this.player, event);
} // Device is touch enabled
}, {
key: "firstTouch",
value: function firstTouch() {
var player = this.player;
var elements = player.elements;
player.touch = true; // Add touch class
toggleClass(elements.container, player.config.classNames.isTouch, true);
}
}, {
key: "setTabFocus",
value: function setTabFocus(event) {
var player = this.player;
var elements = player.elements;
clearTimeout(this.focusTimer); // Ignore any key other than tab
if (event.type === 'keydown' && event.which !== 9) {
return;
} // Store reference to event timeStamp
if (event.type === 'keydown') {
this.lastKeyDown = event.timeStamp;
} // Remove current classes
var removeCurrent = function removeCurrent() {
var className = player.config.classNames.tabFocus;
var current = getElements.call(player, ".".concat(className));
toggleClass(current, className, false);
}; // Determine if a key was pressed to trigger this event
var wasKeyDown = event.timeStamp - this.lastKeyDown <= 20; // Ignore focus events if a key was pressed prior
if (event.type === 'focus' && !wasKeyDown) {
return;
} // Remove all current
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
} // Global window & document listeners
}, {
key: "global",
value: function global() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var player = this.player; // Keyboard shortcuts
if (player.config.keyboard.global) {
toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false);
} // Click anywhere closes menu
toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle); // Detect touch by events
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
key: "container",
value: function container() {
var player = this.player;
var elements = player.elements; // Keyboard shortcuts
if (!player.config.keyboard.global && player.config.keyboard.focused) {
on.call(player, elements.container, 'keydown keyup', this.handleKey, false);
} // Toggle controls on mouse events and entering fullscreen
on.call(player, elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = elements.controls; // Remove button states for fullscreen
if (controls$$1 && event.type === 'enterfullscreen') {
controls$$1.pressed = false;
controls$$1.hover = false;
} // Show, then hide after a timeout unless another control event occurs
var show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
var delay = 0;
if (show) {
ui.toggleControls.call(player, true); // Use longer timeout for touch devices
delay = player.touch ? 3000 : 2000;
} // Clear timer
clearTimeout(player.timers.controls); // Set new timer to prevent flicker when seeking
player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(player, false);
}, delay);
});
} // Listen for media events
}, {
key: "media",
value: function media() {
var player = this.player;
var elements = player.elements; // Time change on media
on.call(player, player.media, 'timeupdate seeking seeked', function (event) {
return controls.timeUpdate.call(player, event);
}); // Display duration
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(player, event);
}); // Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(player, player.media, 'canplay', function () {
toggleHidden(elements.volume, !player.hasAudio);
toggleHidden(elements.buttons.mute, !player.hasAudio);
}); // Handle the media finishing
on.call(player, player.media, 'ended', function () {
// Show poster on end
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart
player.restart();
}
}); // Check for buffer progress
on.call(player, player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(player, event);
}); // Handle volume changes
on.call(player, player.media, 'volumechange', function (event) {
return controls.updateVolume.call(player, event);
}); // Handle play/pause
on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(player, event);
}); // Loading state
on.call(player, player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(player, event);
}); // If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
on.call(player, player.media, 'playing', function () {
if (!player.ads) {
return;
} // If ads are enabled, wait for them first
if (player.ads.enabled && !player.ads.initialized) {
// Wait for manager response
player.ads.managerPromise.then(function () {
return player.ads.play();
}).catch(function () {
return player.play();
});
}
}); // Click video
if (player.supported.ui && player.config.clickToPlay && !player.isAudio) {
// Re-fetch the wrapper
var wrapper = getElement.call(player, ".".concat(player.config.classNames.video)); // Bail if there's no wrapper (this should never happen)
if (!is$1.element(wrapper)) {
return;
} // On click play, pause or restart
on.call(player, elements.container, 'click', function (event) {
var targets = [elements.container, wrapper]; // Ignore if click if not container or in video wrapper
if (!targets.includes(event.target) && !wrapper.contains(event.target)) {
return;
} // Touch devices will just show controls (if hidden)
if (player.touch && player.config.hideControls) {
return;
}
if (player.ended) {
player.restart();
player.play();
} else {
player.togglePlay();
}
});
} // Disable right click
if (player.supported.ui && player.config.disableContextMenu) {
on.call(player, elements.wrapper, 'contextmenu', function (event) {
event.preventDefault();
}, false);
} // Volume change
on.call(player, player.media, 'volumechange', function () {
// Save to storage
player.storage.set({
volume: player.volume,
muted: player.muted
});
}); // Speed change
on.call(player, player.media, 'ratechange', function () {
// Update UI
controls.updateSetting.call(player, 'speed'); // Save to storage
player.storage.set({
speed: player.speed
});
}); // Quality change
on.call(player, player.media, 'qualitychange', function (event) {
// Update UI
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
}); // Update download link when ready and if quality changes
on.call(player, player.media, 'ready qualitychange', function () {
controls.setDownloadLink.call(player);
}); // Proxy events to container
// Bubble up key events for Edge
var proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(player, player.media, proxyEvents, function (event) {
var _event$detail = event.detail,
detail = _event$detail === void 0 ? {} : _event$detail; // Get error details from media
if (event.type === 'error') {
detail = player.media.error;
}
triggerEvent.call(player, elements.container, event.type, true, detail);
});
} // Run default and custom handlers
}, {
key: "proxy",
value: function proxy(event, defaultHandler, customHandlerKey) {
var player = this.player;
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is$1.function(customHandler);
var returned = true; // Execute custom handler
if (hasCustomHandler) {
returned = customHandler.call(player, event);
} // Only call default handler if not prevented in custom handler
if (returned && is$1.function(defaultHandler)) {
defaultHandler.call(player, event);
}
} // Trigger custom and default handlers
}, {
key: "bind",
value: function bind(element, type, defaultHandler, customHandlerKey) {
var _this = this;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var player = this.player;
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is$1.function(customHandler);
on.call(player, element, type, function (event) {
return _this.proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler);
} // Listen for control events
}, {
key: "controls",
value: function controls$$1() {
var _this2 = this;
var player = this.player;
var elements = player.elements; // IE doesn't support input event, so we fallback to change
var inputEvent = browser.isIE ? 'change' : 'input'; // Play/pause toggle
if (elements.buttons.play) {
Array.from(elements.buttons.play).forEach(function (button) {
_this2.bind(button, 'click', player.togglePlay, 'play');
});
} // Pause
this.bind(elements.buttons.restart, 'click', player.restart, 'restart'); // Rewind
this.bind(elements.buttons.rewind, 'click', player.rewind, 'rewind'); // Rewind
this.bind(elements.buttons.fastForward, 'click', player.forward, 'fastForward'); // Mute toggle
this.bind(elements.buttons.mute, 'click', function () {
player.muted = !player.muted;
}, 'mute'); // Captions toggle
this.bind(elements.buttons.captions, 'click', function () {
return player.toggleCaptions();
}); // Download
this.bind(elements.buttons.download, 'click', function () {
triggerEvent.call(player, player.media, 'download');
}, 'download'); // Fullscreen toggle
this.bind(elements.buttons.fullscreen, 'click', function () {
player.fullscreen.toggle();
}, 'fullscreen'); // Picture-in-Picture
this.bind(elements.buttons.pip, 'click', function () {
player.pip = 'toggle';
}, 'pip'); // Airplay
this.bind(elements.buttons.airplay, 'click', player.airplay, 'airplay'); // Settings menu - click toggle
this.bind(elements.buttons.settings, 'click', function (event) {
// Prevent the document click listener closing the menu
event.stopPropagation();
controls.toggleMenu.call(player, event);
}); // Settings menu - keyboard toggle
// We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220143
this.bind(elements.buttons.settings, 'keyup', function (event) {
var code = event.which; // We only care about space and return
if (![13, 32].includes(code)) {
return;
} // Because return triggers a click anyway, all we need to do is set focus
if (code === 13) {
controls.focusFirstMenuItem.call(player, null, true);
return;
} // Prevent scroll
event.preventDefault(); // Prevent playing video (Firefox)
event.stopPropagation(); // Toggle menu
controls.toggleMenu.call(player, event);
}, null, false // Can't be passive as we're preventing default
); // Escape closes menu
this.bind(elements.settings.menu, 'keydown', function (event) {
if (event.which === 27) {
controls.toggleMenu.call(player, event);
}
}); // Set range input alternative "value", which matches the tooltip time (#954)
this.bind(elements.inputs.seek, 'mousedown mousemove', function (event) {
var rect = elements.progress.getBoundingClientRect();
var percent = 100 / rect.width * (event.pageX - rect.left);
event.currentTarget.setAttribute('seek-value', percent);
}); // Pause while seeking
this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
var seek = event.currentTarget;
var code = event.keyCode ? event.keyCode : event.which;
var attribute = 'play-on-seeked';
if (is$1.keyboardEvent(event) && code !== 39 && code !== 37) {
return;
} // Record seek time so we can prevent hiding controls for a few seconds after seek
player.lastSeekTime = Date.now(); // Was playing before?
var play = seek.hasAttribute(attribute); // Done seeking
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute(attribute);
player.play();
} else if (!done && player.playing) {
seek.setAttribute(attribute, '');
player.pause();
}
}); // Fix range inputs on iOS
// Super weird iOS bug where after you interact with an <input type="range">,
// it takes over further interactions on the page. This is a hack
if (browser.isIos) {
var inputs = getElements.call(player, 'input[type="range"]');
Array.from(inputs).forEach(function (input) {
return _this2.bind(input, inputEvent, function (event) {
return repaint(event.target);
});
});
} // Seek
this.bind(elements.inputs.seek, inputEvent, function (event) {
var seek = event.currentTarget; // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954)
var seekTo = seek.getAttribute('seek-value');
if (is$1.empty(seekTo)) {
seekTo = seek.value;
}
seek.removeAttribute('seek-value');
player.currentTime = seekTo / seek.max * player.duration;
}, 'seek'); // Seek tooltip
this.bind(elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(player, event);
}); // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) {
_this2.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(player, event.target);
});
});
} // Current time invert
// Only if one time element is used for both currentTime and duration
if (player.config.toggleInvert && !is$1.element(elements.display.duration)) {
this.bind(elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start
if (player.currentTime === 0) {
return;
}
player.config.invertTime = !player.config.invertTime;
controls.timeUpdate.call(player);
});
} // Volume
this.bind(elements.inputs.volume, inputEvent, function (event) {
player.volume = event.target.value;
}, 'volume'); // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
}); // Show controls when they receive focus (e.g., when using keyboard tab key)
this.bind(elements.controls, 'focusin', function () {
var config = player.config,
elements = player.elements,
timers = player.timers; // Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, true); // Toggle
ui.toggleControls.call(player, true); // Restore transition
setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0); // Delay a little more for mouse users
var delay = _this2.touch ? 3000 : 4000; // Clear timer
clearTimeout(timers.controls); // Hide again after delay
timers.controls = setTimeout(function () {
return ui.toggleControls.call(player, false);
}, delay);
}); // Mouse wheel for volume
this.bind(elements.inputs.volume, 'wheel', function (event) {
// Detect "natural" scroll - suppored on OS X Safari only
// Other browsers on OS X will be inverted until support improves
var inverted = event.webkitDirectionInvertedFromDevice; // Get delta from event. Invert if `inverted` is true
var _map = [event.deltaX, -event.deltaY].map(function (value) {
return inverted ? -value : value;
}),
_map2 = _slicedToArray(_map, 2),
x = _map2[0],
y = _map2[1]; // Using the biggest delta, normalize to 1 or -1 (or 0 if no delta)
var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); // Change the volume by 2%
player.increaseVolume(direction / 50); // Don't break page scrolling at max and min
var volume = player.media.volume;
if (direction === 1 && volume < 1 || direction === -1 && volume > 0) {
event.preventDefault();
}
}, 'volume', false);
}
}]);
return Listeners;
}();
var dP$3 = _objectDp.f;
var FProto = Function.prototype;
var nameRE = /^\s*function ([^ (]*)/;
var NAME$1 = 'name';
// 19.2.4.2 name
NAME$1 in FProto || _descriptors && dP$3(FProto, NAME$1, {
configurable: true,
get: function () {
try {
return ('' + this).match(nameRE)[1];
} catch (e) {
return '';
}
}
});
// @@match logic
_fixReWks('match', 1, function (defined, MATCH, $match) {
// 21.1.3.11 String.prototype.match(regexp)
return [function match(regexp) {
var O = defined(this);
var fn = regexp == undefined ? undefined : regexp[MATCH];
return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
}, $match];
});
var loadjs_umd = createCommonjsModule(function (module, exports) {
(function(root, factory) {
if (typeof undefined === 'function' && undefined.amd) {
undefined([], factory);
} else {
module.exports = factory();
}
}(commonjsGlobal, function() {
/**
* Global dependencies.
* @global {Object} document - DOM
*/
var devnull = function() {},
bundleIdCache = {},
bundleResultCache = {},
bundleCallbackQueue = {};
/**
* Subscribe to bundle load event.
* @param {string[]} bundleIds - Bundle ids
* @param {Function} callbackFn - The callback function
*/
function subscribe(bundleIds, callbackFn) {
// listify
bundleIds = bundleIds.push ? bundleIds : [bundleIds];
var depsNotFound = [],
i = bundleIds.length,
numWaiting = i,
fn,
bundleId,
r,
q;
// define callback function
fn = function (bundleId, pathsNotFound) {
if (pathsNotFound.length) depsNotFound.push(bundleId);
numWaiting--;
if (!numWaiting) callbackFn(depsNotFound);
};
// register callback
while (i--) {
bundleId = bundleIds[i];
// execute callback if in result cache
r = bundleResultCache[bundleId];
if (r) {
fn(bundleId, r);
continue;
}
// add to callback queue
q = bundleCallbackQueue[bundleId] = bundleCallbackQueue[bundleId] || [];
q.push(fn);
}
}
/**
* Publish bundle load event.
* @param {string} bundleId - Bundle id
* @param {string[]} pathsNotFound - List of files not found
*/
function publish(bundleId, pathsNotFound) {
// exit if id isn't defined
if (!bundleId) return;
var q = bundleCallbackQueue[bundleId];
// cache result
bundleResultCache[bundleId] = pathsNotFound;
// exit if queue is empty
if (!q) return;
// empty callback queue
while (q.length) {
q[0](bundleId, pathsNotFound);
q.splice(0, 1);
}
}
/**
* Execute callbacks.
* @param {Object or Function} args - The callback args
* @param {string[]} depsNotFound - List of dependencies not found
*/
function executeCallbacks(args, depsNotFound) {
// accept function as argument
if (args.call) args = {success: args};
// success and error callbacks
if (depsNotFound.length) (args.error || devnull)(depsNotFound);
else (args.success || devnull)(args);
}
/**
* Load individual file.
* @param {string} path - The file path
* @param {Function} callbackFn - The callback function
*/
function loadFile(path, callbackFn, args, numTries) {
var doc = document,
async = args.async,
maxTries = (args.numRetries || 0) + 1,
beforeCallbackFn = args.before || devnull,
pathStripped = path.replace(/^(css|img)!/, ''),
isCss,
e;
numTries = numTries || 0;
if (/(^css!|\.css$)/.test(path)) {
isCss = true;
// css
e = doc.createElement('link');
e.rel = 'stylesheet';
e.href = pathStripped; //.replace(/^css!/, ''); // remove "css!" prefix
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) {
// image
e = doc.createElement('img');
e.src = pathStripped;
} else {
// javascript
e = doc.createElement('script');
e.src = path;
e.async = async === undefined ? true : async;
}
e.onload = e.onerror = e.onbeforeload = function (ev) {
var result = ev.type[0];
// Note: The following code isolates IE using `hideFocus` and treats empty
// stylesheets as failures to get around lack of onerror support
if (isCss && 'hideFocus' in e) {
try {
if (!e.sheet.cssText.length) result = 'e';
} catch (x) {
// sheets objects created from load errors don't allow access to
// `cssText`
result = 'e';
}
}
// handle retries in case of load failure
if (result == 'e') {
// increment counter
numTries += 1;
// exit function and try again
if (numTries < maxTries) {
return loadFile(path, callbackFn, args, numTries);
}
}
// execute callback
callbackFn(path, result, ev.defaultPrevented);
};
// add to document (unless callback returns `false`)
if (beforeCallbackFn(path, e) !== false) doc.head.appendChild(e);
}
/**
* Load multiple files.
* @param {string[]} paths - The file paths
* @param {Function} callbackFn - The callback function
*/
function loadFiles(paths, callbackFn, args) {
// listify paths
paths = paths.push ? paths : [paths];
var numWaiting = paths.length,
x = numWaiting,
pathsNotFound = [],
fn,
i;
// define callback function
fn = function(path, result, defaultPrevented) {
// handle error
if (result == 'e') pathsNotFound.push(path);
// handle beforeload event. If defaultPrevented then that means the load
// will be blocked (ex. Ghostery/ABP on Safari)
if (result == 'b') {
if (defaultPrevented) pathsNotFound.push(path);
else return;
}
numWaiting--;
if (!numWaiting) callbackFn(pathsNotFound);
};
// load scripts
for (i=0; i < x; i++) loadFile(paths[i], fn, args);
}
/**
* Initiate script load and register bundle.
* @param {(string|string[])} paths - The file paths
* @param {(string|Function)} [arg1] - The bundleId or success callback
* @param {Function} [arg2] - The success or error callback
* @param {Function} [arg3] - The error callback
*/
function loadjs(paths, arg1, arg2) {
var bundleId,
args;
// bundleId (if string)
if (arg1 && arg1.trim) bundleId = arg1;
// args (default is {})
args = (bundleId ? arg2 : arg1) || {};
// throw error if bundle is already defined
if (bundleId) {
if (bundleId in bundleIdCache) {
throw "LoadJS";
} else {
bundleIdCache[bundleId] = true;
}
}
// load scripts
loadFiles(paths, function (pathsNotFound) {
// execute callbacks
executeCallbacks(args, pathsNotFound);
// publish bundle load event
publish(bundleId, pathsNotFound);
}, args);
}
/**
* Execute callbacks when dependencies have been satisfied.
* @param {(string|string[])} deps - List of bundle ids
* @param {Object} args - success/error arguments
*/
loadjs.ready = function ready(deps, args) {
// subscribe to bundle load event
subscribe(deps, function (depsNotFound) {
// execute callbacks
executeCallbacks(args, depsNotFound);
});
return loadjs;
};
/**
* Manually satisfy bundle dependencies.
* @param {string} bundleId - The bundle id
*/
loadjs.done = function done(bundleId) {
publish(bundleId, []);
};
/**
* Reset loadjs dependencies statuses
*/
loadjs.reset = function reset() {
bundleIdCache = {};
bundleResultCache = {};
bundleCallbackQueue = {};
};
/**
* Determine if bundle has already been defined
* @param String} bundleId - The bundle id
*/
loadjs.isDefined = function isDefined(bundleId) {
return bundleId in bundleIdCache;
};
// export
return loadjs;
}));
});
function loadScript(url) {
return new Promise(function (resolve, reject) {
loadjs_umd(url, {
success: resolve,
error: reject
});
});
}
function parseId(url) {
if (is$1.empty(url)) {
return null;
}
if (is$1.number(Number(url))) {
return url;
}
var regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
return url.match(regex) ? RegExp.$2 : url;
} // Get aspect ratio for dimensions
function getAspectRatio(width, height) {
var getRatio = function getRatio(w, h) {
return h === 0 ? w : getRatio(h, w % h);
};
var ratio = getRatio(width, height);
return "".concat(width / ratio, ":").concat(height / ratio);
} // Set playback state and trigger change (only on actual change)
function assurePlaybackState(play) {
if (play && !this.embed.hasPlayed) {
this.embed.hasPlayed = true;
}
if (this.media.paused === play) {
this.media.paused = !play;
triggerEvent.call(this, this.media, play ? 'play' : 'pause');
}
}
var vimeo = {
setup: function setup() {
var _this = this;
// Add embed class for responsive
toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set intial ratio
vimeo.setAspectRatio.call(this); // Load the API if not already
if (!is$1.object(window.Vimeo)) {
loadScript(this.config.urls.vimeo.sdk).then(function () {
vimeo.ready.call(_this);
}).catch(function (error) {
_this.debug.warn('Vimeo API failed to load', error);
});
} else {
vimeo.ready.call(this);
}
},
// Set aspect ratio
// For Vimeo we have an extra 300% height <div> to hide the standard controls and UI
setAspectRatio: function setAspectRatio(input) {
var _split = (is$1.string(input) ? input : this.config.ratio).split(':'),
_split2 = _slicedToArray(_split, 2),
x = _split2[0],
y = _split2[1];
var padding = 100 / x * y;
this.elements.wrapper.style.paddingBottom = "".concat(padding, "%");
if (this.supported.ui) {
var height = 240;
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
}
},
// API Ready
ready: function ready$$1() {
var _this2 = this;
var player = this; // Get Vimeo params for the iframe
var options = {
loop: player.config.loop.active,
autoplay: player.autoplay,
// muted: player.muted,
byline: false,
portrait: false,
title: false,
speed: true,
transparent: 0,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
};
var params = buildUrlParams(options); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
if (is$1.empty(source)) {
source = player.media.getAttribute(player.config.attributes.embed.id);
}
var id = parseId(source); // Build an iframe
var iframe = createElement('iframe');
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Get poster, if already set
var poster = player.poster; // Inject the package
var wrapper = createElement('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$1.empty(response)) {
return;
} // Get the URL for thumbnail
var url = new URL(response[0].thumbnail_large); // Get original image
url.pathname = "".concat(url.pathname.split('_')[0], ".jpg"); // Set and show poster
ui.setPoster.call(player, url.href).catch(function () {});
}); // Setup instance
// https://github.com/vimeo/player.js
player.embed = new window.Vimeo.Player(iframe, {
autopause: player.config.autopause,
muted: player.muted
});
player.media.paused = true;
player.media.currentTime = 0; // Disable native text track rendering
if (player.supported.ui) {
player.embed.disableTextTrack();
} // Create a faux HTML5 API using the Vimeo API
player.media.play = function () {
assurePlaybackState.call(player, true);
return player.embed.play();
};
player.media.pause = function () {
assurePlaybackState.call(player, false);
return player.embed.pause();
};
player.media.stop = function () {
player.pause();
player.currentTime = 0;
}; // Seeking
var currentTime = player.media.currentTime;
Object.defineProperty(player.media, 'currentTime', {
get: function get() {
return currentTime;
},
set: function set(time) {
// Vimeo will automatically play on seek if the video hasn't been played before
// Get current paused state and volume etc
var embed = player.embed,
media = player.media,
paused = player.paused,
volume = player.volume;
var restorePause = paused && !embed.hasPlayed; // Set seeking state and trigger event
media.seeking = true;
triggerEvent.call(player, media, 'seeking'); // If paused, mute until seek is complete
Promise.resolve(restorePause && embed.setVolume(0)) // Seek
.then(function () {
return embed.setCurrentTime(time);
}) // Restore paused
.then(function () {
return restorePause && embed.pause();
}) // Restore volume
.then(function () {
return restorePause && embed.setVolume(volume);
}).catch(function () {// Do nothing
});
}
}); // Playback speed
var speed = player.config.speed.selected;
Object.defineProperty(player.media, 'playbackRate', {
get: function get() {
return speed;
},
set: function set(input) {
player.embed.setPlaybackRate(input).then(function () {
speed = input;
triggerEvent.call(player, player.media, 'ratechange');
}).catch(function (error) {
// Hide menu item (and menu if empty)
if (error.name === 'Error') {
controls.setSpeedMenu.call(player, []);
}
});
}
}); // Volume
var volume = player.config.volume;
Object.defineProperty(player.media, 'volume', {
get: function get() {
return volume;
},
set: function set(input) {
player.embed.setVolume(input).then(function () {
volume = input;
triggerEvent.call(player, player.media, 'volumechange');
});
}
}); // Muted
var muted = player.config.muted;
Object.defineProperty(player.media, 'muted', {
get: function get() {
return muted;
},
set: function set(input) {
var toggle = is$1.boolean(input) ? input : false;
player.embed.setVolume(toggle ? 0 : player.config.volume).then(function () {
muted = toggle;
triggerEvent.call(player, player.media, 'volumechange');
});
}
}); // Loop
var loop = player.config.loop;
Object.defineProperty(player.media, 'loop', {
get: function get() {
return loop;
},
set: function set(input) {
var toggle = is$1.boolean(input) ? input : player.config.loop.active;
player.embed.setLoop(toggle).then(function () {
loop = toggle;
});
}
}); // Source
var currentSrc;
player.embed.getVideoUrl().then(function (value) {
currentSrc = value;
controls.setDownloadLink.call(player);
}).catch(function (error) {
_this2.debug.warn(error);
});
Object.defineProperty(player.media, 'currentSrc', {
get: function get() {
return currentSrc;
}
}); // Ended
Object.defineProperty(player.media, 'ended', {
get: function get() {
return player.currentTime === player.duration;
}
}); // Set aspect ratio based on video size
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(function (dimensions) {
var ratio = getAspectRatio(dimensions[0], dimensions[1]);
vimeo.setAspectRatio.call(_this2, ratio);
}); // Set autopause
player.embed.setAutopause(player.config.autopause).then(function (state) {
player.config.autopause = state;
}); // Get title
player.embed.getVideoTitle().then(function (title) {
player.config.title = title;
ui.setTitle.call(_this2);
}); // Get current time
player.embed.getCurrentTime().then(function (value) {
currentTime = value;
triggerEvent.call(player, player.media, 'timeupdate');
}); // Get duration
player.embed.getDuration().then(function (value) {
player.media.duration = value;
triggerEvent.call(player, player.media, 'durationchange');
}); // Get captions
player.embed.getTextTracks().then(function (tracks) {
player.media.textTracks = tracks;
captions.setup.call(player);
});
player.embed.on('cuechange', function (_ref) {
var _ref$cues = _ref.cues,
cues = _ref$cues === void 0 ? [] : _ref$cues;
var strippedCues = cues.map(function (cue) {
return stripHTML(cue.text);
});
captions.updateCues.call(player, strippedCues);
});
player.embed.on('loaded', function () {
// Assure state and events are updated on autoplay
player.embed.getPaused().then(function (paused) {
assurePlaybackState.call(player, !paused);
if (!paused) {
triggerEvent.call(player, player.media, 'playing');
}
});
if (is$1.element(player.embed.element) && player.supported.ui) {
var frame = player.embed.element; // Fix keyboard focus issues
// https://github.com/sampotts/plyr/issues/317
frame.setAttribute('tabindex', -1);
}
});
player.embed.on('play', function () {
assurePlaybackState.call(player, true);
triggerEvent.call(player, player.media, 'playing');
});
player.embed.on('pause', function () {
assurePlaybackState.call(player, false);
});
player.embed.on('timeupdate', function (data) {
player.media.seeking = false;
currentTime = data.seconds;
triggerEvent.call(player, player.media, 'timeupdate');
});
player.embed.on('progress', function (data) {
player.media.buffered = data.percent;
triggerEvent.call(player, player.media, 'progress'); // Check all loaded
if (parseInt(data.percent, 10) === 1) {
triggerEvent.call(player, player.media, 'canplaythrough');
} // Get duration as if we do it before load, it gives an incorrect value
// https://github.com/sampotts/plyr/issues/891
player.embed.getDuration().then(function (value) {
if (value !== player.media.duration) {
player.media.duration = value;
triggerEvent.call(player, player.media, 'durationchange');
}
});
});
player.embed.on('seeked', function () {
player.media.seeking = false;
triggerEvent.call(player, player.media, 'seeked');
});
player.embed.on('ended', function () {
player.media.paused = true;
triggerEvent.call(player, player.media, 'ended');
});
player.embed.on('error', function (detail) {
player.media.error = detail;
triggerEvent.call(player, player.media, 'error');
}); // Rebuild UI
setTimeout(function () {
return ui.build.call(player);
}, 0);
}
};
function parseId$1(url) {
if (is$1.empty(url)) {
return null;
}
var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
return url.match(regex) ? RegExp.$2 : url;
} // Set playback state and trigger change (only on actual change)
function assurePlaybackState$1(play) {
if (play && !this.embed.hasPlayed) {
this.embed.hasPlayed = true;
}
if (this.media.paused === play) {
this.media.paused = !play;
triggerEvent.call(this, this.media, play ? 'play' : 'pause');
}
}
var youtube = {
setup: function setup() {
var _this = this;
// Add embed class for responsive
toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio
youtube.setAspectRatio.call(this); // Setup API
if (is$1.object(window.YT) && is$1.function(window.YT.Player)) {
youtube.ready.call(this);
} else {
// Load the API
loadScript(this.config.urls.youtube.sdk).catch(function (error) {
_this.debug.warn('YouTube API failed to load', error);
}); // Setup callback for the API
// YouTube has it's own system of course...
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || []; // Add to queue
window.onYouTubeReadyCallbacks.push(function () {
youtube.ready.call(_this);
}); // Set callback to process queue
window.onYouTubeIframeAPIReady = function () {
window.onYouTubeReadyCallbacks.forEach(function (callback) {
callback();
});
};
}
},
// Get the media title
getTitle: function getTitle(videoId) {
var _this2 = this;
// Try via undocumented API method first
// This method disappears now and then though...
// https://github.com/sampotts/plyr/issues/709
if (is$1.function(this.embed.getVideoData)) {
var _this$embed$getVideoD = this.embed.getVideoData(),
title = _this$embed$getVideoD.title;
if (is$1.empty(title)) {
this.config.title = title;
ui.setTitle.call(this);
return;
}
} // Or via Google API
var key = this.config.keys.google;
if (is$1.string(key) && !is$1.empty(key)) {
var url = format(this.config.urls.youtube.api, videoId, key);
fetch(url).then(function (result) {
if (is$1.object(result)) {
_this2.config.title = result.items[0].snippet.title;
ui.setTitle.call(_this2);
}
}).catch(function () {});
}
},
// Set aspect ratio
setAspectRatio: function setAspectRatio() {
var ratio = this.config.ratio.split(':');
this.elements.wrapper.style.paddingBottom = "".concat(100 / ratio[0] * ratio[1], "%");
},
// API ready
ready: function ready$$1() {
var player = this; // Ignore already setup (race condition)
var currentId = player.media.getAttribute('id');
if (!is$1.empty(currentId) && currentId.startsWith('youtube-')) {
return;
} // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
if (is$1.empty(source)) {
source = player.media.getAttribute(this.config.attributes.embed.id);
} // Replace the <iframe> with a <div> due to YouTube API issues
var videoId = parseId$1(source);
var id = generateId(player.provider); // Get poster, if already set
var poster = player.poster; // Replace media element
var container = createElement('div', {
id: id,
poster: poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
var posterSrc = function posterSrc(format$$1) {
return "https://img.youtube.com/vi/".concat(videoId, "/").concat(format$$1, "default.jpg");
}; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)
loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded
.catch(function () {
return loadImage(posterSrc('sd'), 121);
}) // 480p padded 4:3
.catch(function () {
return loadImage(posterSrc('hq'));
}) // 360p padded 4:3. Always exists
.then(function (image) {
return ui.setPoster.call(player, image.src);
}).then(function (posterSrc) {
// If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
if (!posterSrc.includes('maxres')) {
player.elements.poster.style.backgroundSize = 'cover';
}
}).catch(function () {}); // Setup instance
// https://developers.google.com/youtube/iframe_api_reference
player.embed = new window.YT.Player(id, {
videoId: videoId,
playerVars: {
autoplay: player.config.autoplay ? 1 : 0,
// Autoplay
hl: player.config.hl,
// iframe interface language
controls: player.supported.ui ? 0 : 1,
// Only show controls if not fully supported
rel: 0,
// No related vids
showinfo: 0,
// Hide info
iv_load_policy: 3,
// Hide annotations
modestbranding: 1,
// Hide logos as much as possible (they still show one in the corner when paused)
disablekb: 1,
// Disable keyboard as we handle it
playsinline: 1,
// Allow iOS inline playback
// Tracking for stats
// origin: window ? `${window.location.protocol}//${window.location.host}` : null,
widget_referrer: window ? window.location.href : null,
// Captions are flaky on YouTube
cc_load_policy: player.captions.active ? 1 : 0,
cc_lang_pref: player.config.captions.language
},
events: {
onError: function onError(event) {
// YouTube may fire onError twice, so only handle it once
if (!player.media.error) {
var code = event.data; // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
var message = {
2: 'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.',
5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
101: 'The owner of the requested video does not allow it to be played in embedded players.',
150: 'The owner of the requested video does not allow it to be played in embedded players.'
}[code] || 'An unknown error occured';
player.media.error = {
code: code,
message: message
};
triggerEvent.call(player, player.media, 'error');
}
},
onPlaybackRateChange: function onPlaybackRateChange(event) {
// Get the instance
var instance = event.target; // Get current speed
player.media.playbackRate = instance.getPlaybackRate();
triggerEvent.call(player, player.media, 'ratechange');
},
onReady: function onReady(event) {
// Bail if onReady has already been called. See issue #1108
if (is$1.function(player.media.play)) {
return;
} // Get the instance
var instance = event.target; // Get the title
youtube.getTitle.call(player, videoId); // Create a faux HTML5 API using the YouTube API
player.media.play = function () {
assurePlaybackState$1.call(player, true);
instance.playVideo();
};
player.media.pause = function () {
assurePlaybackState$1.call(player, false);
instance.pauseVideo();
};
player.media.stop = function () {
instance.stopVideo();
};
player.media.duration = instance.getDuration();
player.media.paused = true; // Seeking
player.media.currentTime = 0;
Object.defineProperty(player.media, 'currentTime', {
get: function get() {
return Number(instance.getCurrentTime());
},
set: function set(time) {
// If paused and never played, mute audio preventively (YouTube starts playing on seek if the video hasn't been played yet).
if (player.paused && !player.embed.hasPlayed) {
player.embed.mute();
} // Set seeking state and trigger event
player.media.seeking = true;
triggerEvent.call(player, player.media, 'seeking'); // Seek after events sent
instance.seekTo(time);
}
}); // Playback speed
Object.defineProperty(player.media, 'playbackRate', {
get: function get() {
return instance.getPlaybackRate();
},
set: function set(input) {
instance.setPlaybackRate(input);
}
}); // Volume
var volume = player.config.volume;
Object.defineProperty(player.media, 'volume', {
get: function get() {
return volume;
},
set: function set(input) {
volume = input;
instance.setVolume(volume * 100);
triggerEvent.call(player, player.media, 'volumechange');
}
}); // Muted
var muted = player.config.muted;
Object.defineProperty(player.media, 'muted', {
get: function get() {
return muted;
},
set: function set(input) {
var toggle = is$1.boolean(input) ? input : muted;
muted = toggle;
instance[toggle ? 'mute' : 'unMute']();
triggerEvent.call(player, player.media, 'volumechange');
}
}); // Source
Object.defineProperty(player.media, 'currentSrc', {
get: function get() {
return instance.getVideoUrl();
}
}); // Ended
Object.defineProperty(player.media, 'ended', {
get: function get() {
return player.currentTime === player.duration;
}
}); // Get available speeds
player.options.speed = instance.getAvailablePlaybackRates(); // Set the tabindex to avoid focus entering iframe
if (player.supported.ui) {
player.media.setAttribute('tabindex', -1);
}
triggerEvent.call(player, player.media, 'timeupdate');
triggerEvent.call(player, player.media, 'durationchange'); // Reset timer
clearInterval(player.timers.buffering); // Setup buffering
player.timers.buffering = setInterval(function () {
// Get loaded % from YouTube
player.media.buffered = instance.getVideoLoadedFraction(); // Trigger progress only when we actually buffer something
if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {
triggerEvent.call(player, player.media, 'progress');
} // Set last buffer point
player.media.lastBuffered = player.media.buffered; // Bail if we're at 100%
if (player.media.buffered === 1) {
clearInterval(player.timers.buffering); // Trigger event
triggerEvent.call(player, player.media, 'canplaythrough');
}
}, 200); // Rebuild UI
setTimeout(function () {
return ui.build.call(player);
}, 50);
},
onStateChange: function onStateChange(event) {
// Get the instance
var instance = event.target; // Reset timer
clearInterval(player.timers.playing);
var seeked = player.media.seeking && [1, 2].includes(event.data);
if (seeked) {
// Unset seeking and fire seeked event
player.media.seeking = false;
triggerEvent.call(player, player.media, 'seeked');
} // Handle events
// -1 Unstarted
// 0 Ended
// 1 Playing
// 2 Paused
// 3 Buffering
// 5 Video cued
switch (event.data) {
case -1:
// Update scrubber
triggerEvent.call(player, player.media, 'timeupdate'); // Get loaded % from YouTube
player.media.buffered = instance.getVideoLoadedFraction();
triggerEvent.call(player, player.media, 'progress');
break;
case 0:
assurePlaybackState$1.call(player, false); // YouTube doesn't support loop for a single video, so mimick it.
if (player.media.loop) {
// YouTube needs a call to `stopVideo` before playing again
instance.stopVideo();
instance.playVideo();
} else {
triggerEvent.call(player, player.media, 'ended');
}
break;
case 1:
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
if (player.media.paused && !player.embed.hasPlayed) {
player.media.pause();
} else {
assurePlaybackState$1.call(player, true);
triggerEvent.call(player, player.media, 'playing'); // Poll to get playback progress
player.timers.playing = setInterval(function () {
triggerEvent.call(player, player.media, 'timeupdate');
}, 50); // Check duration again due to YouTube bug
// https://github.com/sampotts/plyr/issues/374
// https://code.google.com/p/gdata-issues/issues/detail?id=8690
if (player.media.duration !== instance.getDuration()) {
player.media.duration = instance.getDuration();
triggerEvent.call(player, player.media, 'durationchange');
}
}
break;
case 2:
// Restore audio (YouTube starts playing on seek if the video hasn't been played yet)
if (!player.muted) {
player.embed.unMute();
}
assurePlaybackState$1.call(player, false);
break;
default:
break;
}
triggerEvent.call(player, player.elements.container, 'statechange', false, {
code: event.data
});
}
}
});
}
};
var media = {
// Setup media
setup: function setup() {
// If there's no media, bail
if (!this.media) {
this.debug.warn('No media element found!');
return;
} // Add type class
toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true); // Add provider class
toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true); // Add video class for embeds
// This will require changes if audio embeds are added
if (this.isEmbed) {
toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', 'video'), true);
} // Inject the player wrapper
if (this.isVideo) {
// Create the wrapper div
this.elements.wrapper = createElement('div', {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap(this.media, this.elements.wrapper); // Faux poster container
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
html5.extend.call(this);
} else if (this.isYouTube) {
youtube.setup.call(this);
} else if (this.isVimeo) {
vimeo.setup.call(this);
}
}
};
var Ads =
/*#__PURE__*/
function () {
/**
* Ads constructor.
* @param {object} player
* @return {Ads}
*/
function Ads(player) {
var _this = this;
_classCallCheck(this, Ads);
this.player = player;
this.publisherId = player.config.ads.publisherId;
this.playing = false;
this.initialized = false;
this.elements = {
container: null,
displayContainer: null
};
this.manager = null;
this.loader = null;
this.cuePoints = null;
this.events = {};
this.safetyTimer = null;
this.countdownTimer = null; // Setup a promise to resolve when the IMA manager is ready
this.managerPromise = new Promise(function (resolve, reject) {
// The ad is loaded and ready
_this.on('loaded', resolve); // Ads failed
_this.on('error', reject);
});
this.load();
}
_createClass(Ads, [{
key: "load",
/**
* Load the IMA SDK
*/
value: function load() {
var _this2 = this;
if (this.enabled) {
// Check if the Google IMA3 SDK is loaded or load it ourselves
if (!is$1.object(window.google) || !is$1.object(window.google.ima)) {
loadScript(this.player.config.urls.googleIMA.sdk).then(function () {
_this2.ready();
}).catch(function () {
// Script failed to load or is blocked
_this2.trigger('error', new Error('Google IMA SDK failed to load'));
});
} else {
this.ready();
}
}
}
/**
* Get the ads instance ready
*/
}, {
key: "ready",
value: function ready$$1() {
var _this3 = this;
// Start ticking our safety timer. If the whole advertisement
// thing doesn't resolve within our set time; we bail
this.startSafetyTimer(12000, 'ready()'); // Clear the safety timer
this.managerPromise.then(function () {
_this3.clearSafetyTimer('onAdsManagerLoaded()');
}); // Set listeners on the Plyr instance
this.listeners(); // Setup the IMA SDK
this.setupIMA();
} // Build the default tag URL
}, {
key: "setupIMA",
/**
* In order for the SDK to display ads for our video, we need to tell it where to put them,
* so here we define our ad container. This div is set up to render on top of the video player.
* Using the code below, we tell the SDK to render ads within that div. We also provide a
* handle to the content video player - the SDK will poll the current time of our player to
* properly place mid-rolls. After we create the ad display container, we initialize it. On
* mobile devices, this initialization is done as the result of a user action.
*/
value: function setupIMA() {
// Create the container for our advertisements
this.elements.container = createElement('div', {
class: this.player.config.classNames.ads
});
this.player.elements.container.appendChild(this.elements.container); // So we can run VPAID2
google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); // Set language
google.ima.settings.setLocale(this.player.config.ads.language); // We assume the adContainer is the video container of the plyr element
// that will house the ads
this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container); // Request video ads to be pre-loaded
this.requestAds();
}
/**
* Request advertisements
*/
}, {
key: "requestAds",
value: function requestAds() {
var _this4 = this;
var container = this.player.elements.container;
try {
// Create ads loader
this.loader = new google.ima.AdsLoader(this.elements.displayContainer); // Listen and respond to ads loaded and error events
this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, function (event) {
return _this4.onAdsManagerLoaded(event);
}, false);
this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (error) {
return _this4.onAdError(error);
}, false); // Request video ads
var request = new google.ima.AdsRequest();
request.adTagUrl = this.tagUrl; // Specify the linear and nonlinear slot sizes. This helps the SDK
// to select the correct creative if multiple are returned
request.linearAdSlotWidth = container.offsetWidth;
request.linearAdSlotHeight = container.offsetHeight;
request.nonLinearAdSlotWidth = container.offsetWidth;
request.nonLinearAdSlotHeight = container.offsetHeight; // We only overlay ads as we only support video.
request.forceNonLinearFullSlot = false; // Mute based on current state
request.setAdWillPlayMuted(!this.player.muted);
this.loader.requestAds(request);
} catch (e) {
this.onAdError(e);
}
}
/**
* Update the ad countdown
* @param {boolean} start
*/
}, {
key: "pollCountdown",
value: function pollCountdown() {
var _this5 = this;
var start = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (!start) {
clearInterval(this.countdownTimer);
this.elements.container.removeAttribute('data-badge-text');
return;
}
var update = function update() {
var time = formatTime(Math.max(_this5.manager.getRemainingTime(), 0));
var label = "".concat(i18n.get('advertisement', _this5.player.config), " - ").concat(time);
_this5.elements.container.setAttribute('data-badge-text', label);
};
this.countdownTimer = setInterval(update, 100);
}
/**
* This method is called whenever the ads are ready inside the AdDisplayContainer
* @param {Event} adsManagerLoadedEvent
*/
}, {
key: "onAdsManagerLoaded",
value: function onAdsManagerLoaded(event) {
var _this6 = this;
// Load could occur after a source change (race condition)
if (!this.enabled) {
return;
} // Get the ads manager
var settings = new google.ima.AdsRenderingSettings(); // Tell the SDK to save and restore content video state on our behalf
settings.restoreCustomPlaybackStateOnAdBreakComplete = true;
settings.enablePreloading = true; // The SDK is polling currentTime on the contentPlayback. And needs a duration
// so it can determine when to start the mid- and post-roll
this.manager = event.getAdsManager(this.player, settings); // Get the cue points for any mid-rolls by filtering out the pre- and post-roll
this.cuePoints = this.manager.getCuePoints(); // Add advertisement cue's within the time line if available
if (!is$1.empty(this.cuePoints)) {
this.cuePoints.forEach(function (cuePoint) {
if (cuePoint !== 0 && cuePoint !== -1 && cuePoint < _this6.player.duration) {
var seekElement = _this6.player.elements.progress;
if (is$1.element(seekElement)) {
var cuePercentage = 100 / _this6.player.duration * cuePoint;
var cue = createElement('span', {
class: _this6.player.config.classNames.cues
});
cue.style.left = "".concat(cuePercentage.toString(), "%");
seekElement.appendChild(cue);
}
}
});
} // Set volume to match player
this.manager.setVolume(this.player.volume); // Add listeners to the required events
// Advertisement error events
this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (error) {
return _this6.onAdError(error);
}); // Advertisement regular events
Object.keys(google.ima.AdEvent.Type).forEach(function (type) {
_this6.manager.addEventListener(google.ima.AdEvent.Type[type], function (event) {
return _this6.onAdEvent(event);
});
}); // Resolve our adsManager
this.trigger('loaded');
}
/**
* This is where all the event handling takes place. Retrieve the ad from the event. Some
* events (e.g. ALL_ADS_COMPLETED) don't have the ad object associated
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type
* @param {Event} event
*/
}, {
key: "onAdEvent",
value: function onAdEvent(event) {
var _this7 = this;
var container = this.player.elements.container; // Retrieve the ad from the event. Some events (e.g. ALL_ADS_COMPLETED)
// don't have ad object associated
var ad = event.getAd(); // Proxy event
var dispatchEvent = function dispatchEvent(type) {
var event = "ads".concat(type.replace(/_/g, '').toLowerCase());
triggerEvent.call(_this7.player, _this7.player.media, event);
};
switch (event.type) {
case google.ima.AdEvent.Type.LOADED:
// This is the first event sent for an ad - it is possible to determine whether the
// ad is a video ad or an overlay
this.trigger('loaded'); // Bubble event
dispatchEvent(event.type); // Start countdown
this.pollCountdown(true);
if (!ad.isLinear()) {
// Position AdDisplayContainer correctly for overlay
ad.width = container.offsetWidth;
ad.height = container.offsetHeight;
} // console.info('Ad type: ' + event.getAd().getAdPodInfo().getPodIndex());
// console.info('Ad time: ' + event.getAd().getAdPodInfo().getTimeOffset());
break;
case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
// All ads for the current videos are done. We can now request new advertisements
// in case the video is re-played
// Fire event
dispatchEvent(event.type); // TODO: Example for what happens when a next video in a playlist would be loaded.
// So here we load a new video when all ads are done.
// Then we load new ads within a new adsManager. When the video
// Is started - after - the ads are loaded, then we get ads.
// You can also easily test cancelling and reloading by running
// player.ads.cancel() and player.ads.play from the console I guess.
// this.player.source = {
// type: 'video',
// title: 'View From A Blue Moon',
// sources: [{
// src:
// 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4', type:
// 'video/mp4', }], poster:
// 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg', tracks:
// [ { kind: 'captions', label: 'English', srclang: 'en', src:
// 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
// default: true, }, { kind: 'captions', label: 'French', srclang: 'fr', src:
// 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt', }, ],
// };
// TODO: So there is still this thing where a video should only be allowed to start
// playing when the IMA SDK is ready or has failed
this.loadAds();
break;
case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:
// This event indicates the ad has started - the video player can adjust the UI,
// for example display a pause button and remaining time. Fired when content should
// be paused. This usually happens right before an ad is about to cover the content
dispatchEvent(event.type);
this.pauseContent();
break;
case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED:
// This event indicates the ad has finished - the video player can perform
// appropriate UI actions, such as removing the timer for remaining time detection.
// Fired when content should be resumed. This usually happens when an ad finishes
// or collapses
dispatchEvent(event.type);
this.pollCountdown();
this.resumeContent();
break;
case google.ima.AdEvent.Type.STARTED:
case google.ima.AdEvent.Type.MIDPOINT:
case google.ima.AdEvent.Type.COMPLETE:
case google.ima.AdEvent.Type.IMPRESSION:
case google.ima.AdEvent.Type.CLICK:
dispatchEvent(event.type);
break;
default:
break;
}
}
/**
* Any ad error handling comes through here
* @param {Event} event
*/
}, {
key: "onAdError",
value: function onAdError(event) {
this.cancel();
this.player.debug.warn('Ads error', event);
}
/**
* Setup hooks for Plyr and window events. This ensures
* the mid- and post-roll launch at the correct time. And
* resize the advertisement when the player resizes
*/
}, {
key: "listeners",
value: function listeners() {
var _this8 = this;
var container = this.player.elements.container;
var time; // Add listeners to the required events
this.player.on('ended', function () {
_this8.loader.contentComplete();
});
this.player.on('seeking', function () {
time = _this8.player.currentTime;
return time;
});
this.player.on('seeked', function () {
var seekedTime = _this8.player.currentTime;
if (is$1.empty(_this8.cuePoints)) {
return;
}
_this8.cuePoints.forEach(function (cuePoint, index) {
if (time < cuePoint && cuePoint < seekedTime) {
_this8.manager.discardAdBreak();
_this8.cuePoints.splice(index, 1);
}
});
}); // Listen to the resizing of the window. And resize ad accordingly
// TODO: eventually implement ResizeObserver
window.addEventListener('resize', function () {
if (_this8.manager) {
_this8.manager.resize(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);
}
});
}
/**
* Initialize the adsManager and start playing advertisements
*/
}, {
key: "play",
value: function play() {
var _this9 = this;
var container = this.player.elements.container;
if (!this.managerPromise) {
this.resumeContent();
} // Play the requested advertisement whenever the adsManager is ready
this.managerPromise.then(function () {
// Initialize the container. Must be done via a user action on mobile devices
_this9.elements.displayContainer.initialize();
try {
if (!_this9.initialized) {
// Initialize the ads manager. Ad rules playlist will start at this time
_this9.manager.init(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will
// start at this time; the call will be ignored for ad rules
_this9.manager.start();
}
_this9.initialized = true;
} catch (adError) {
// An error may be thrown if there was a problem with the
// VAST response
_this9.onAdError(adError);
}
}).catch(function () {});
}
/**
* Resume our video
*/
}, {
key: "resumeContent",
value: function resumeContent() {
// Hide the advertisement container
this.elements.container.style.zIndex = ''; // Ad is stopped
this.playing = false; // Play our video
if (this.player.currentTime < this.player.duration) {
this.player.play();
}
}
/**
* Pause our video
*/
}, {
key: "pauseContent",
value: function pauseContent() {
// Show the advertisement container
this.elements.container.style.zIndex = 3; // Ad is playing.
this.playing = true; // Pause our video.
this.player.pause();
}
/**
* Destroy the adsManager so we can grab new ads after this. If we don't then we're not
* allowed to call new ads based on google policies, as they interpret this as an accidental
* video requests. https://developers.google.com/interactive-
* media-ads/docs/sdks/android/faq#8
*/
}, {
key: "cancel",
value: function cancel() {
// Pause our video
if (this.initialized) {
this.resumeContent();
} // Tell our instance that we're done for now
this.trigger('error'); // Re-create our adsManager
this.loadAds();
}
/**
* Re-create our adsManager
*/
}, {
key: "loadAds",
value: function loadAds() {
var _this10 = this;
// Tell our adsManager to go bye bye
this.managerPromise.then(function () {
// Destroy our adsManager
if (_this10.manager) {
_this10.manager.destroy();
} // Re-set our adsManager promises
_this10.managerPromise = new Promise(function (resolve) {
_this10.on('loaded', resolve);
_this10.player.debug.log(_this10.manager);
}); // Now request some new advertisements
_this10.requestAds();
}).catch(function () {});
}
/**
* Handles callbacks after an ad event was invoked
* @param {string} event - Event type
*/
}, {
key: "trigger",
value: function trigger(event) {
var _this11 = this;
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var handlers = this.events[event];
if (is$1.array(handlers)) {
handlers.forEach(function (handler) {
if (is$1.function(handler)) {
handler.apply(_this11, args);
}
});
}
}
/**
* Add event listeners
* @param {string} event - Event type
* @param {function} callback - Callback for when event occurs
* @return {Ads}
*/
}, {
key: "on",
value: function on$$1(event, callback) {
if (!is$1.array(this.events[event])) {
this.events[event] = [];
}
this.events[event].push(callback);
return this;
}
/**
* Setup a safety timer for when the ad network doesn't respond for whatever reason.
* The advertisement has 12 seconds to get its things together. We stop this timer when the
* advertisement is playing, or when a user action is required to start, then we clear the
* timer on ad ready
* @param {number} time
* @param {string} from
*/
}, {
key: "startSafetyTimer",
value: function startSafetyTimer(time, from) {
var _this12 = this;
this.player.debug.log("Safety timer invoked from: ".concat(from));
this.safetyTimer = setTimeout(function () {
_this12.cancel();
_this12.clearSafetyTimer('startSafetyTimer()');
}, time);
}
/**
* Clear our safety timer(s)
* @param {string} from
*/
}, {
key: "clearSafetyTimer",
value: function clearSafetyTimer(from) {
if (!is$1.nullOrUndefined(this.safetyTimer)) {
this.player.debug.log("Safety timer cleared from: ".concat(from));
clearTimeout(this.safetyTimer);
this.safetyTimer = null;
}
}
}, {
key: "enabled",
get: function get() {
return this.player.isHTML5 && this.player.isVideo && this.player.config.ads.enabled && !is$1.empty(this.publisherId);
}
}, {
key: "tagUrl",
get: function get() {
var params = {
AV_PUBLISHERID: '58c25bb0073ef448b1087ad6',
AV_CHANNELID: '5a0458dc28a06145e4519d21',
AV_URL: window.location.hostname,
cb: Date.now(),
AV_WIDTH: 640,
AV_HEIGHT: 480,
AV_CDIM2: this.publisherId
};
var base = 'https://go.aniview.com/api/adserver6/vast/';
return "".concat(base, "?").concat(buildUrlParams(params));
}
}]);
return Ads;
}();
var source = {
// Add elements to HTML5 media (source, tracks, etc)
insertElements: function insertElements(type, attributes) {
var _this = this;
if (is$1.string(attributes)) {
insertElement(type, this.media, {
src: attributes
});
} else if (is$1.array(attributes)) {
attributes.forEach(function (attribute) {
insertElement(type, _this.media, attribute);
});
}
},
// Update source
// Sources are not checked for support so be careful
change: function change(input) {
var _this2 = this;
if (!getDeep(input, 'sources.length')) {
this.debug.warn('Invalid source format');
return;
} // Cancel current network requests
html5.cancelRequests.call(this); // Destroy instance and re-setup
this.destroy.call(this, function () {
// Reset quality options
_this2.options.quality = []; // Remove elements
removeElement(_this2.media);
_this2.media = null; // Reset class name
if (is$1.element(_this2.elements.container)) {
_this2.elements.container.removeAttribute('class');
} // Set the type and provider
var sources = input.sources,
type = input.type;
var _sources = _slicedToArray(sources, 1),
_sources$ = _sources[0],
_sources$$provider = _sources$.provider,
provider = _sources$$provider === void 0 ? providers.html5 : _sources$$provider,
src = _sources$.src;
var tagName = provider === 'html5' ? type : 'div';
var attributes = provider === 'html5' ? {} : {
src: src
};
Object.assign(_this2, {
provider: provider,
type: type,
// Check for support
supported: support.check(type, provider, _this2.config.playsinline),
// Create new element
media: createElement(tagName, attributes)
}); // Inject the new element
_this2.elements.container.appendChild(_this2.media); // Autoplay the new source?
if (is$1.boolean(input.autoplay)) {
_this2.config.autoplay = input.autoplay;
} // Set attributes for audio and video
if (_this2.isHTML5) {
if (_this2.config.crossorigin) {
_this2.media.setAttribute('crossorigin', '');
}
if (_this2.config.autoplay) {
_this2.media.setAttribute('autoplay', '');
}
if (!is$1.empty(input.poster)) {
_this2.poster = input.poster;
}
if (_this2.config.loop.active) {
_this2.media.setAttribute('loop', '');
}
if (_this2.config.muted) {
_this2.media.setAttribute('muted', '');
}
if (_this2.config.playsinline) {
_this2.media.setAttribute('playsinline', '');
}
} // Restore class hook
ui.addStyleHook.call(_this2); // Set new sources for html5
if (_this2.isHTML5) {
source.insertElements.call(_this2, 'source', sources);
} // Set video title
_this2.config.title = input.title; // Set up from scratch
media.setup.call(_this2); // HTML5 stuff
if (_this2.isHTML5) {
// Setup captions
if ('tracks' in input) {
source.insertElements.call(_this2, 'track', input.tracks);
} // Load HTML5 sources
_this2.media.load();
} // If HTML5 or embed but not fully supported, setupInterface and call ready now
if (_this2.isHTML5 || _this2.isEmbed && !_this2.supported.ui) {
// Setup interface
ui.build.call(_this2);
} // Update the fullscreen support
_this2.fullscreen.update();
}, true);
}
};
// TODO: Use a WeakMap for private globals
// const globals = new WeakMap();
// Plyr instance
var Plyr =
/*#__PURE__*/
function () {
function Plyr(target, options) {
var _this = this;
_classCallCheck(this, Plyr);
this.timers = {}; // State
this.ready = false;
this.loading = false;
this.failed = false; // Touch device
this.touch = support.touch; // Set the media element
this.media = target; // String selector passed
if (is$1.string(this.media)) {
this.media = document.querySelectorAll(this.media);
} // jQuery, NodeList or Array passed, use first element
if (window.jQuery && this.media instanceof jQuery || is$1.nodeList(this.media) || is$1.array(this.media)) {
// eslint-disable-next-line
this.media = this.media[0];
} // Set config
this.config = extend({}, defaults, Plyr.defaults, options || {}, function () {
try {
return JSON.parse(_this.media.getAttribute('data-plyr-config'));
} catch (e) {
return {};
}
}()); // Elements cache
this.elements = {
container: null,
captions: null,
buttons: {},
display: {},
progress: {},
inputs: {},
settings: {
popup: null,
menu: null,
panels: {},
buttons: {}
}
}; // Captions
this.captions = {
active: null,
currentTrack: -1,
meta: new WeakMap()
}; // Fullscreen
this.fullscreen = {
active: false
}; // Options
this.options = {
speed: [],
quality: []
}; // Debugging
// TODO: move to globals
this.debug = new Console(this.config.debug); // Log config options and support
this.debug.log('Config', this.config);
this.debug.log('Support', support); // We need an element to setup
if (is$1.nullOrUndefined(this.media) || !is$1.element(this.media)) {
this.debug.error('Setup failed: no suitable element passed');
return;
} // Bail if the element is initialized
if (this.media.plyr) {
this.debug.warn('Target already setup');
return;
} // Bail if not enabled
if (!this.config.enabled) {
this.debug.error('Setup failed: disabled by config');
return;
} // Bail if disabled or no basic support
// You may want to disable certain UAs etc
if (!support.check().api) {
this.debug.error('Setup failed: no support');
return;
} // Cache original element state for .destroy()
var clone = this.media.cloneNode(true);
clone.autoplay = false;
this.elements.original = clone; // Set media type based on tag or data attribute
// Supported: video, audio, vimeo, youtube
var type = this.media.tagName.toLowerCase(); // Embed properties
var iframe = null;
var url = null; // Different setup based on type
switch (type) {
case 'div':
// Find the frame
iframe = this.media.querySelector('iframe'); // <iframe> type
if (is$1.element(iframe)) {
// Detect provider
url = parseUrl(iframe.getAttribute('src'));
this.provider = getProviderByUrl(url.toString()); // Rework elements
this.elements.container = this.media;
this.media = iframe; // Reset classname
this.elements.container.className = ''; // Get attributes from URL and set config
if (url.search.length) {
var truthy = ['1', 'true'];
if (truthy.includes(url.searchParams.get('autoplay'))) {
this.config.autoplay = true;
}
if (truthy.includes(url.searchParams.get('loop'))) {
this.config.loop.active = true;
} // TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(url.searchParams.get('playsinline'));
this.config.hl = url.searchParams.get('hl'); // TODO: Should this be setting language?
} else {
this.config.playsinline = true;
}
}
} else {
// <div> with attributes
this.provider = this.media.getAttribute(this.config.attributes.embed.provider); // Remove attribute
this.media.removeAttribute(this.config.attributes.embed.provider);
} // Unsupported or missing provider
if (is$1.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
this.debug.error('Setup failed: Invalid provider');
return;
} // Audio will come later for external providers
this.type = types.video;
break;
case 'video':
case 'audio':
this.type = type;
this.provider = providers.html5; // Get config from attributes
if (this.media.hasAttribute('crossorigin')) {
this.config.crossorigin = true;
}
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
this.config.muted = true;
}
if (this.media.hasAttribute('loop')) {
this.config.loop.active = true;
}
break;
default:
this.debug.error('Setup failed: unsupported type');
return;
} // Check for support again but with type
this.supported = support.check(this.type, this.provider, this.config.playsinline); // If no support for even API, bail
if (!this.supported.api) {
this.debug.error('Setup failed: no support');
return;
}
this.eventListeners = []; // Create listeners
this.listeners = new Listeners(this); // Setup local storage for user settings
this.storage = new Storage(this); // Store reference
this.media.plyr = this; // Wrap media
if (!is$1.element(this.elements.container)) {
this.elements.container = createElement('div');
wrap(this.media, this.elements.container);
} // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
if (this.config.debug) {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
} // Autoplay if required
if (this.config.autoplay) {
this.play();
} // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
this.lastSeekTime = 0;
} // ---------------------------------------
// API
// ---------------------------------------
/**
* Types and provider helpers
*/
_createClass(Plyr, [{
key: "play",
/**
* Play the media, or play the advertisement (if they are not blocked)
*/
value: function play() {
if (!is$1.function(this.media.play)) {
return null;
} // Return the promise (for HTML5)
return this.media.play();
}
/**
* Pause the media
*/
}, {
key: "pause",
value: function pause() {
if (!this.playing || !is$1.function(this.media.pause)) {
return;
}
this.media.pause();
}
/**
* Get playing state
*/
}, {
key: "togglePlay",
/**
* Toggle playback based on current status
* @param {boolean} input
*/
value: function togglePlay(input) {
// Toggle based on current state if nothing passed
var toggle = is$1.boolean(input) ? input : !this.playing;
if (toggle) {
this.play();
} else {
this.pause();
}
}
/**
* Stop playback
*/
}, {
key: "stop",
value: function stop() {
if (this.isHTML5) {
this.pause();
this.restart();
} else if (is$1.function(this.media.stop)) {
this.media.stop();
}
}
/**
* Restart playback
*/
}, {
key: "restart",
value: function restart() {
this.currentTime = 0;
}
/**
* Rewind
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
*/
}, {
key: "rewind",
value: function rewind(seekTime) {
this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime);
}
/**
* Fast forward
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
*/
}, {
key: "forward",
value: function forward(seekTime) {
this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime);
}
/**
* Seek to a time
* @param {number} input - where to seek to in seconds. Defaults to 0 (the start)
*/
}, {
key: "increaseVolume",
/**
* Increase volume
* @param {boolean} step - How much to decrease by (between 0 and 1)
*/
value: function increaseVolume(step) {
var volume = this.media.muted ? 0 : this.volume;
this.volume = volume + (is$1.number(step) ? step : 0);
}
/**
* Decrease volume
* @param {boolean} step - How much to decrease by (between 0 and 1)
*/
}, {
key: "decreaseVolume",
value: function decreaseVolume(step) {
this.increaseVolume(-step);
}
/**
* Set muted state
* @param {boolean} mute
*/
}, {
key: "toggleCaptions",
/**
* Toggle captions
* @param {boolean} input - Whether to enable captions
*/
value: function toggleCaptions(input) {
captions.toggle.call(this, input, false);
}
/**
* Set the caption track by index
* @param {number} - Caption index
*/
}, {
key: "airplay",
/**
* Trigger the airplay dialog
* TODO: update player with state, support, enabled
*/
value: function airplay() {
// Show dialog if supported
if (support.airplay) {
this.media.webkitShowPlaybackTargetPicker();
}
}
/**
* Toggle the player controls
* @param {boolean} [toggle] - Whether to show the controls
*/
}, {
key: "toggleControls",
value: function toggleControls(toggle) {
// Don't toggle if missing UI support or if it's audio
if (this.supported.ui && !this.isAudio) {
// Get state before change
var isHidden = hasClass(this.elements.container, this.config.classNames.hideControls); // Negate the argument if not undefined since adding the class to hides the controls
var force = typeof toggle === 'undefined' ? undefined : !toggle; // Apply and get updated state
var hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force); // Close menu
if (hiding && this.config.controls.includes('settings') && !is$1.empty(this.config.settings)) {
controls.toggleMenu.call(this, false);
} // Trigger event on change
if (hiding !== isHidden) {
var eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName);
}
return !hiding;
}
return false;
}
/**
* Add event listeners
* @param {string} event - Event type
* @param {function} callback - Callback for when event occurs
*/
}, {
key: "on",
value: function on$$1(event, callback) {
on.call(this, this.elements.container, event, callback);
}
/**
* Add event listeners once
* @param {string} event - Event type
* @param {function} callback - Callback for when event occurs
*/
}, {
key: "once",
value: function once$$1(event, callback) {
once.call(this, this.elements.container, event, callback);
}
/**
* Remove event listeners
* @param {string} event - Event type
* @param {function} callback - Callback for when event occurs
*/
}, {
key: "off",
value: function off$$1(event, callback) {
off(this.elements.container, event, callback);
}
/**
* Destroy an instance
* Event listeners are removed when elements are removed
* http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
* @param {function} callback - Callback for when destroy is complete
* @param {boolean} soft - Whether it's a soft destroy (for source changes etc)
*/
}, {
key: "destroy",
value: function destroy(callback) {
var _this2 = this;
var soft = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!this.ready) {
return;
}
var done = function done() {
// Reset overflow (incase destroyed while in fullscreen)
document.body.style.overflow = ''; // GC for embed
_this2.embed = null; // If it's a soft destroy, make minimal changes
if (soft) {
if (Object.keys(_this2.elements).length) {
// Remove elements
removeElement(_this2.elements.buttons.play);
removeElement(_this2.elements.captions);
removeElement(_this2.elements.controls);
removeElement(_this2.elements.wrapper); // Clear for GC
_this2.elements.buttons.play = null;
_this2.elements.captions = null;
_this2.elements.controls = null;
_this2.elements.wrapper = null;
} // Callback
if (is$1.function(callback)) {
callback();
}
} else {
// Unbind listeners
unbindListeners.call(_this2); // Replace the container with the original element provided
replaceElement(_this2.elements.original, _this2.elements.container); // Event
triggerEvent.call(_this2, _this2.elements.original, 'destroyed', true); // Callback
if (is$1.function(callback)) {
callback.call(_this2.elements.original);
} // Reset state
_this2.ready = false; // Clear for garbage collection
setTimeout(function () {
_this2.elements = null;
_this2.media = null;
}, 200);
}
}; // Stop playback
this.stop(); // Provider specific stuff
if (this.isHTML5) {
// Clear timeout
clearTimeout(this.timers.loading); // Restore native video controls
ui.toggleNativeControls.call(this, true); // Clean up
done();
} else if (this.isYouTube) {
// Clear timers
clearInterval(this.timers.buffering);
clearInterval(this.timers.playing); // Destroy YouTube API
if (this.embed !== null && is$1.function(this.embed.destroy)) {
this.embed.destroy();
} // Clean up
done();
} else if (this.isVimeo) {
// Destroy Vimeo API
// then clean up (wait, to prevent postmessage errors)
if (this.embed !== null) {
this.embed.unload().then(done);
} // Vimeo does not always return
setTimeout(done, 200);
}
}
/**
* Check for support for a mime type (HTML5 only)
* @param {string} type - Mime type
*/
}, {
key: "supports",
value: function supports(type) {
return support.mime.call(this, type);
}
/**
* Check for support
* @param {string} type - Player type (audio/video)
* @param {string} provider - Provider (html5/youtube/vimeo)
* @param {bool} inline - Where player has `playsinline` sttribute
*/
}, {
key: "isHTML5",
get: function get() {
return Boolean(this.provider === providers.html5);
}
}, {
key: "isEmbed",
get: function get() {
return Boolean(this.isYouTube || this.isVimeo);
}
}, {
key: "isYouTube",
get: function get() {
return Boolean(this.provider === providers.youtube);
}
}, {
key: "isVimeo",
get: function get() {
return Boolean(this.provider === providers.vimeo);
}
}, {
key: "isVideo",
get: function get() {
return Boolean(this.type === types.video);
}
}, {
key: "isAudio",
get: function get() {
return Boolean(this.type === types.audio);
}
}, {
key: "playing",
get: function get() {
return Boolean(this.ready && !this.paused && !this.ended);
}
/**
* Get paused state
*/
}, {
key: "paused",
get: function get() {
return Boolean(this.media.paused);
}
/**
* Get stopped state
*/
}, {
key: "stopped",
get: function get() {
return Boolean(this.paused && this.currentTime === 0);
}
/**
* Get ended state
*/
}, {
key: "ended",
get: function get() {
return Boolean(this.media.ended);
}
}, {
key: "currentTime",
set: function set(input) {
// Bail if media duration isn't available yet
if (!this.duration) {
return;
} // Validate input
var inputIsValid = is$1.number(input) && input > 0; // Set
this.media.currentTime = inputIsValid ? Math.min(input, this.duration) : 0; // Logging
this.debug.log("Seeking to ".concat(this.currentTime, " seconds"));
}
/**
* Get current time
*/
,
get: function get() {
return Number(this.media.currentTime);
}
/**
* Get buffered
*/
}, {
key: "buffered",
get: function get() {
var buffered = this.media.buffered; // YouTube / Vimeo return a float between 0-1
if (is$1.number(buffered)) {
return buffered;
} // HTML5
// TODO: Handle buffered chunks of the media
// (i.e. seek to another section buffers only that section)
if (buffered && buffered.length && this.duration > 0) {
return buffered.end(0) / this.duration;
}
return 0;
}
/**
* Get seeking status
*/
}, {
key: "seeking",
get: function get() {
return Boolean(this.media.seeking);
}
/**
* Get the duration of the current media
*/
}, {
key: "duration",
get: function get() {
// Faux duration set via config
var fauxDuration = parseFloat(this.config.duration); // Media duration can be NaN or Infinity before the media has loaded
var realDuration = (this.media || {}).duration;
var duration = !is$1.number(realDuration) || realDuration === Infinity ? 0 : realDuration; // If config duration is funky, use regular duration
return fauxDuration || duration;
}
/**
* Set the player volume
* @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage
*/
}, {
key: "volume",
set: function set(value) {
var volume = value;
var max = 1;
var min = 0;
if (is$1.string(volume)) {
volume = Number(volume);
} // Load volume from storage if no value specified
if (!is$1.number(volume)) {
volume = this.storage.get('volume');
} // Use config if all else fails
if (!is$1.number(volume)) {
volume = this.config.volume;
} // Maximum is volumeMax
if (volume > max) {
volume = max;
} // Minimum is volumeMin
if (volume < min) {
volume = min;
} // Update config
this.config.volume = volume; // Set the player volume
this.media.volume = volume; // If muted, and we're increasing volume manually, reset muted state
if (!is$1.empty(value) && this.muted && volume > 0) {
this.muted = false;
}
}
/**
* Get the current player volume
*/
,
get: function get() {
return Number(this.media.volume);
}
}, {
key: "muted",
set: function set(mute) {
var toggle = mute; // Load muted state from storage
if (!is$1.boolean(toggle)) {
toggle = this.storage.get('muted');
} // Use config if all else fails
if (!is$1.boolean(toggle)) {
toggle = this.config.muted;
} // Update config
this.config.muted = toggle; // Set mute on the player
this.media.muted = toggle;
}
/**
* Get current muted state
*/
,
get: function get() {
return Boolean(this.media.muted);
}
/**
* Check if the media has audio
*/
}, {
key: "hasAudio",
get: function get() {
// Assume yes for all non HTML5 (as we can't tell...)
if (!this.isHTML5) {
return true;
}
if (this.isAudio) {
return true;
} // Get audio tracks
return Boolean(this.media.mozHasAudio) || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);
}
/**
* Set playback speed
* @param {number} speed - the speed of playback (0.5-2.0)
*/
}, {
key: "speed",
set: function set(input) {
var speed = null;
if (is$1.number(input)) {
speed = input;
}
if (!is$1.number(speed)) {
speed = this.storage.get('speed');
}
if (!is$1.number(speed)) {
speed = this.config.speed.selected;
} // Set min/max
if (speed < 0.1) {
speed = 0.1;
}
if (speed > 2.0) {
speed = 2.0;
}
if (!this.config.speed.options.includes(speed)) {
this.debug.warn("Unsupported speed (".concat(speed, ")"));
return;
} // Update config
this.config.speed.selected = speed; // Set media speed
this.media.playbackRate = speed;
}
/**
* Get current playback speed
*/
,
get: function get() {
return Number(this.media.playbackRate);
}
/**
* Set playback quality
* Currently HTML5 & YouTube only
* @param {number} input - Quality level
*/
}, {
key: "quality",
set: function set(input) {
var config = this.config.quality;
var options = this.options.quality;
if (!options.length) {
return;
}
var quality = [!is$1.empty(input) && Number(input), this.storage.get('quality'), config.selected, config.default].find(is$1.number);
if (!options.includes(quality)) {
var value = closest(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value;
} // Update config
config.selected = quality; // Set quality
this.media.quality = quality;
}
/**
* Get current quality level
*/
,
get: function get() {
return this.media.quality;
}
/**
* Toggle loop
* TODO: Finish fancy new logic. Set the indicator on load as user may pass loop as config
* @param {boolean} input - Whether to loop or not
*/
}, {
key: "loop",
set: function set(input) {
var toggle = is$1.boolean(input) ? input : this.config.loop.active;
this.config.loop.active = toggle;
this.media.loop = toggle; // Set default to be a true toggle
/* const type = ['start', 'end', 'all', 'none', 'toggle'].includes(input) ? input : 'toggle';
switch (type) {
case 'start':
if (this.config.loop.end && this.config.loop.end <= this.currentTime) {
this.config.loop.end = null;
}
this.config.loop.start = this.currentTime;
// this.config.loop.indicator.start = this.elements.display.played.value;
break;
case 'end':
if (this.config.loop.start >= this.currentTime) {
return this;
}
this.config.loop.end = this.currentTime;
// this.config.loop.indicator.end = this.elements.display.played.value;
break;
case 'all':
this.config.loop.start = 0;
this.config.loop.end = this.duration - 2;
this.config.loop.indicator.start = 0;
this.config.loop.indicator.end = 100;
break;
case 'toggle':
if (this.config.loop.active) {
this.config.loop.start = 0;
this.config.loop.end = null;
} else {
this.config.loop.start = 0;
this.config.loop.end = this.duration - 2;
}
break;
default:
this.config.loop.start = 0;
this.config.loop.end = null;
break;
} */
}
/**
* Get current loop state
*/
,
get: function get() {
return Boolean(this.media.loop);
}
/**
* Set new media source
* @param {object} input - The new source object (see docs)
*/
}, {
key: "source",
set: function set(input) {
source.change.call(this, input);
}
/**
* Get current source
*/
,
get: function get() {
return this.media.currentSrc;
}
/**
* Get a download URL (either source or custom)
*/
}, {
key: "download",
get: function get() {
var download = this.config.urls.download;
return is$1.url(download) ? download : this.source;
}
/**
* Set the poster image for a video
* @param {input} - the URL for the new poster image
*/
}, {
key: "poster",
set: function set(input) {
if (!this.isVideo) {
this.debug.warn('Poster can only be set for video');
return;
}
ui.setPoster.call(this, input, false).catch(function () {});
}
/**
* Get the current poster image
*/
,
get: function get() {
if (!this.isVideo) {
return null;
}
return this.media.getAttribute('poster');
}
/**
* Set the autoplay state
* @param {boolean} input - Whether to autoplay or not
*/
}, {
key: "autoplay",
set: function set(input) {
var toggle = is$1.boolean(input) ? input : this.config.autoplay;
this.config.autoplay = toggle;
}
/**
* Get the current autoplay state
*/
,
get: function get() {
return Boolean(this.config.autoplay);
}
}, {
key: "currentTrack",
set: function set(input) {
captions.set.call(this, input, false);
}
/**
* Get the current caption track index (-1 if disabled)
*/
,
get: function get() {
var _this$captions = this.captions,
toggled = _this$captions.toggled,
currentTrack = _this$captions.currentTrack;
return toggled ? currentTrack : -1;
}
/**
* Set the wanted language for captions
* Since tracks can be added later it won't update the actual caption track until there is a matching track
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
*/
}, {
key: "language",
set: function set(input) {
captions.setLanguage.call(this, input, false);
}
/**
* Get the current track's language
*/
,
get: function get() {
return (captions.getCurrentTrack.call(this) || {}).language;
}
/**
* Toggle picture-in-picture playback on WebKit/MacOS
* TODO: update player with state, support, enabled
* TODO: detect outside changes
*/
}, {
key: "pip",
set: function set(input) {
var states = {
pip: 'picture-in-picture',
inline: 'inline'
}; // Bail if no support
if (!support.pip) {
return;
} // Toggle based on current state if not passed
var toggle = is$1.boolean(input) ? input : this.pip === states.inline; // Toggle based on current state
this.media.webkitSetPresentationMode(toggle ? states.pip : states.inline);
}
/**
* Get the current picture-in-picture state
*/
,
get: function get() {
if (!support.pip) {
return null;
}
return this.media.webkitPresentationMode;
}
}], [{
key: "supported",
value: function supported(type, provider, inline) {
return support.check(type, provider, inline);
}
/**
* Load an SVG sprite into the page
* @param {string} url - URL for the SVG sprite
* @param {string} [id] - Unique ID
*/
}, {
key: "loadSprite",
value: function loadSprite$$1(url, id) {
return loadSprite(url, id);
}
/**
* Setup multiple instances
* @param {*} selector
* @param {object} options
*/
}, {
key: "setup",
value: function setup(selector) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var targets = null;
if (is$1.string(selector)) {
targets = Array.from(document.querySelectorAll(selector));
} else if (is$1.nodeList(selector)) {
targets = Array.from(selector);
} else if (is$1.array(selector)) {
targets = selector.filter(is$1.element);
}
if (is$1.empty(targets)) {
return null;
}
return targets.map(function (t) {
return new Plyr(t, options);
});
}
}]);
return Plyr;
}();
Plyr.defaults = cloneDeep(defaults);
// ==========================================================================
return Plyr;
})));
//# sourceMappingURL=plyr.polyfilled.js.map