1 | typeof navigator === "object" && (function (global, factory) {
|
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
3 | typeof define === 'function' && define.amd ? define('Plyr', factory) :
|
4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Plyr = factory());
|
5 | })(this, (function () { 'use strict';
|
6 |
|
7 | function _defineProperty$1(obj, key, value) {
|
8 | key = _toPropertyKey(key);
|
9 | if (key in obj) {
|
10 | Object.defineProperty(obj, key, {
|
11 | value: value,
|
12 | enumerable: true,
|
13 | configurable: true,
|
14 | writable: true
|
15 | });
|
16 | } else {
|
17 | obj[key] = value;
|
18 | }
|
19 | return obj;
|
20 | }
|
21 | function _toPrimitive(input, hint) {
|
22 | if (typeof input !== "object" || input === null) return input;
|
23 | var prim = input[Symbol.toPrimitive];
|
24 | if (prim !== undefined) {
|
25 | var res = prim.call(input, hint || "default");
|
26 | if (typeof res !== "object") return res;
|
27 | throw new TypeError("@@toPrimitive must return a primitive value.");
|
28 | }
|
29 | return (hint === "string" ? String : Number)(input);
|
30 | }
|
31 | function _toPropertyKey(arg) {
|
32 | var key = _toPrimitive(arg, "string");
|
33 | return typeof key === "symbol" ? key : String(key);
|
34 | }
|
35 |
|
36 | function _classCallCheck(e, t) {
|
37 | if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function");
|
38 | }
|
39 | function _defineProperties(e, t) {
|
40 | for (var n = 0; n < t.length; n++) {
|
41 | var r = t[n];
|
42 | r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r);
|
43 | }
|
44 | }
|
45 | function _createClass(e, t, n) {
|
46 | return t && _defineProperties(e.prototype, t), n && _defineProperties(e, n), e;
|
47 | }
|
48 | function _defineProperty(e, t, n) {
|
49 | return t in e ? Object.defineProperty(e, t, {
|
50 | value: n,
|
51 | enumerable: !0,
|
52 | configurable: !0,
|
53 | writable: !0
|
54 | }) : e[t] = n, e;
|
55 | }
|
56 | function ownKeys(e, t) {
|
57 | var n = Object.keys(e);
|
58 | if (Object.getOwnPropertySymbols) {
|
59 | var r = Object.getOwnPropertySymbols(e);
|
60 | t && (r = r.filter(function (t) {
|
61 | return Object.getOwnPropertyDescriptor(e, t).enumerable;
|
62 | })), n.push.apply(n, r);
|
63 | }
|
64 | return n;
|
65 | }
|
66 | function _objectSpread2(e) {
|
67 | for (var t = 1; t < arguments.length; t++) {
|
68 | var n = null != arguments[t] ? arguments[t] : {};
|
69 | t % 2 ? ownKeys(Object(n), !0).forEach(function (t) {
|
70 | _defineProperty(e, t, n[t]);
|
71 | }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : ownKeys(Object(n)).forEach(function (t) {
|
72 | Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t));
|
73 | });
|
74 | }
|
75 | return e;
|
76 | }
|
77 | var defaults$1 = {
|
78 | addCSS: !0,
|
79 | thumbWidth: 15,
|
80 | watch: !0
|
81 | };
|
82 | function matches$1(e, t) {
|
83 | return function () {
|
84 | return Array.from(document.querySelectorAll(t)).includes(this);
|
85 | }.call(e, t);
|
86 | }
|
87 | function trigger(e, t) {
|
88 | if (e && t) {
|
89 | var n = new Event(t, {
|
90 | bubbles: !0
|
91 | });
|
92 | e.dispatchEvent(n);
|
93 | }
|
94 | }
|
95 | var getConstructor$1 = function (e) {
|
96 | return null != e ? e.constructor : null;
|
97 | },
|
98 | instanceOf$1 = function (e, t) {
|
99 | return !!(e && t && e instanceof t);
|
100 | },
|
101 | isNullOrUndefined$1 = function (e) {
|
102 | return null == e;
|
103 | },
|
104 | isObject$1 = function (e) {
|
105 | return getConstructor$1(e) === Object;
|
106 | },
|
107 | isNumber$1 = function (e) {
|
108 | return getConstructor$1(e) === Number && !Number.isNaN(e);
|
109 | },
|
110 | isString$1 = function (e) {
|
111 | return getConstructor$1(e) === String;
|
112 | },
|
113 | isBoolean$1 = function (e) {
|
114 | return getConstructor$1(e) === Boolean;
|
115 | },
|
116 | isFunction$1 = function (e) {
|
117 | return getConstructor$1(e) === Function;
|
118 | },
|
119 | isArray$1 = function (e) {
|
120 | return Array.isArray(e);
|
121 | },
|
122 | isNodeList$1 = function (e) {
|
123 | return instanceOf$1(e, NodeList);
|
124 | },
|
125 | isElement$1 = function (e) {
|
126 | return instanceOf$1(e, Element);
|
127 | },
|
128 | isEvent$1 = function (e) {
|
129 | return instanceOf$1(e, Event);
|
130 | },
|
131 | isEmpty$1 = function (e) {
|
132 | return isNullOrUndefined$1(e) || (isString$1(e) || isArray$1(e) || isNodeList$1(e)) && !e.length || isObject$1(e) && !Object.keys(e).length;
|
133 | },
|
134 | is$1 = {
|
135 | nullOrUndefined: isNullOrUndefined$1,
|
136 | object: isObject$1,
|
137 | number: isNumber$1,
|
138 | string: isString$1,
|
139 | boolean: isBoolean$1,
|
140 | function: isFunction$1,
|
141 | array: isArray$1,
|
142 | nodeList: isNodeList$1,
|
143 | element: isElement$1,
|
144 | event: isEvent$1,
|
145 | empty: isEmpty$1
|
146 | };
|
147 | function getDecimalPlaces(e) {
|
148 | var t = "".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
149 | return t ? Math.max(0, (t[1] ? t[1].length : 0) - (t[2] ? +t[2] : 0)) : 0;
|
150 | }
|
151 | function round(e, t) {
|
152 | if (1 > t) {
|
153 | var n = getDecimalPlaces(t);
|
154 | return parseFloat(e.toFixed(n));
|
155 | }
|
156 | return Math.round(e / t) * t;
|
157 | }
|
158 | var RangeTouch = function () {
|
159 | function e(t, n) {
|
160 | _classCallCheck(this, e), is$1.element(t) ? this.element = t : is$1.string(t) && (this.element = document.querySelector(t)), is$1.element(this.element) && is$1.empty(this.element.rangeTouch) && (this.config = _objectSpread2({}, defaults$1, {}, n), this.init());
|
161 | }
|
162 | return _createClass(e, [{
|
163 | key: "init",
|
164 | value: function () {
|
165 | e.enabled && (this.config.addCSS && (this.element.style.userSelect = "none", this.element.style.webKitUserSelect = "none", this.element.style.touchAction = "manipulation"), this.listeners(!0), this.element.rangeTouch = this);
|
166 | }
|
167 | }, {
|
168 | key: "destroy",
|
169 | value: function () {
|
170 | e.enabled && (this.config.addCSS && (this.element.style.userSelect = "", this.element.style.webKitUserSelect = "", this.element.style.touchAction = ""), this.listeners(!1), this.element.rangeTouch = null);
|
171 | }
|
172 | }, {
|
173 | key: "listeners",
|
174 | value: function (e) {
|
175 | var t = this,
|
176 | n = e ? "addEventListener" : "removeEventListener";
|
177 | ["touchstart", "touchmove", "touchend"].forEach(function (e) {
|
178 | t.element[n](e, function (e) {
|
179 | return t.set(e);
|
180 | }, !1);
|
181 | });
|
182 | }
|
183 | }, {
|
184 | key: "get",
|
185 | value: function (t) {
|
186 | if (!e.enabled || !is$1.event(t)) return null;
|
187 | var n,
|
188 | r = t.target,
|
189 | i = t.changedTouches[0],
|
190 | o = parseFloat(r.getAttribute("min")) || 0,
|
191 | s = parseFloat(r.getAttribute("max")) || 100,
|
192 | u = parseFloat(r.getAttribute("step")) || 1,
|
193 | c = r.getBoundingClientRect(),
|
194 | a = 100 / c.width * (this.config.thumbWidth / 2) / 100;
|
195 | return 0 > (n = 100 / c.width * (i.clientX - c.left)) ? n = 0 : 100 < n && (n = 100), 50 > n ? n -= (100 - 2 * n) * a : 50 < n && (n += 2 * (n - 50) * a), o + round(n / 100 * (s - o), u);
|
196 | }
|
197 | }, {
|
198 | key: "set",
|
199 | value: function (t) {
|
200 | e.enabled && is$1.event(t) && !t.target.disabled && (t.preventDefault(), t.target.value = this.get(t), trigger(t.target, "touchend" === t.type ? "change" : "input"));
|
201 | }
|
202 | }], [{
|
203 | key: "setup",
|
204 | value: function (t) {
|
205 | var n = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {},
|
206 | r = null;
|
207 | if (is$1.empty(t) || is$1.string(t) ? r = Array.from(document.querySelectorAll(is$1.string(t) ? t : 'input[type="range"]')) : is$1.element(t) ? r = [t] : is$1.nodeList(t) ? r = Array.from(t) : is$1.array(t) && (r = t.filter(is$1.element)), is$1.empty(r)) return null;
|
208 | var i = _objectSpread2({}, defaults$1, {}, n);
|
209 | if (is$1.string(t) && i.watch) {
|
210 | var o = new MutationObserver(function (n) {
|
211 | Array.from(n).forEach(function (n) {
|
212 | Array.from(n.addedNodes).forEach(function (n) {
|
213 | is$1.element(n) && matches$1(n, t) && new e(n, i);
|
214 | });
|
215 | });
|
216 | });
|
217 | o.observe(document.body, {
|
218 | childList: !0,
|
219 | subtree: !0
|
220 | });
|
221 | }
|
222 | return r.map(function (t) {
|
223 | return new e(t, n);
|
224 | });
|
225 | }
|
226 | }, {
|
227 | key: "enabled",
|
228 | get: function () {
|
229 | return "ontouchstart" in document.documentElement;
|
230 | }
|
231 | }]), e;
|
232 | }();
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | const getConstructor = input => input !== null && typeof input !== 'undefined' ? input.constructor : null;
|
239 | const instanceOf = (input, constructor) => Boolean(input && constructor && input instanceof constructor);
|
240 | const isNullOrUndefined = input => input === null || typeof input === 'undefined';
|
241 | const isObject = input => getConstructor(input) === Object;
|
242 | const isNumber = input => getConstructor(input) === Number && !Number.isNaN(input);
|
243 | const isString = input => getConstructor(input) === String;
|
244 | const isBoolean = input => getConstructor(input) === Boolean;
|
245 | const isFunction = input => typeof input === 'function';
|
246 | const isArray = input => Array.isArray(input);
|
247 | const isWeakMap = input => instanceOf(input, WeakMap);
|
248 | const isNodeList = input => instanceOf(input, NodeList);
|
249 | const isTextNode = input => getConstructor(input) === Text;
|
250 | const isEvent = input => instanceOf(input, Event);
|
251 | const isKeyboardEvent = input => instanceOf(input, KeyboardEvent);
|
252 | const isCue = input => instanceOf(input, window.TextTrackCue) || instanceOf(input, window.VTTCue);
|
253 | const isTrack = input => instanceOf(input, TextTrack) || !isNullOrUndefined(input) && isString(input.kind);
|
254 | const isPromise = input => instanceOf(input, Promise) && isFunction(input.then);
|
255 | const isElement = input => input !== null && typeof input === 'object' && input.nodeType === 1 && typeof input.style === 'object' && typeof input.ownerDocument === 'object';
|
256 | const isEmpty = input => isNullOrUndefined(input) || (isString(input) || isArray(input) || isNodeList(input)) && !input.length || isObject(input) && !Object.keys(input).length;
|
257 | const isUrl = input => {
|
258 |
|
259 | if (instanceOf(input, window.URL)) {
|
260 | return true;
|
261 | }
|
262 |
|
263 |
|
264 | if (!isString(input)) {
|
265 | return false;
|
266 | }
|
267 |
|
268 |
|
269 | let string = input;
|
270 | if (!input.startsWith('http://') || !input.startsWith('https://')) {
|
271 | string = `http://${input}`;
|
272 | }
|
273 | try {
|
274 | return !isEmpty(new URL(string).hostname);
|
275 | } catch (_) {
|
276 | return false;
|
277 | }
|
278 | };
|
279 | var is = {
|
280 | nullOrUndefined: isNullOrUndefined,
|
281 | object: isObject,
|
282 | number: isNumber,
|
283 | string: isString,
|
284 | boolean: isBoolean,
|
285 | function: isFunction,
|
286 | array: isArray,
|
287 | weakMap: isWeakMap,
|
288 | nodeList: isNodeList,
|
289 | element: isElement,
|
290 | textNode: isTextNode,
|
291 | event: isEvent,
|
292 | keyboardEvent: isKeyboardEvent,
|
293 | cue: isCue,
|
294 | track: isTrack,
|
295 | promise: isPromise,
|
296 | url: isUrl,
|
297 | empty: isEmpty
|
298 | };
|
299 |
|
300 | // ==========================================================================
|
301 | const transitionEndEvent = (() => {
|
302 | const element = document.createElement('span');
|
303 | const events = {
|
304 | WebkitTransition: 'webkitTransitionEnd',
|
305 | MozTransition: 'transitionend',
|
306 | OTransition: 'oTransitionEnd otransitionend',
|
307 | transition: 'transitionend'
|
308 | };
|
309 | const type = Object.keys(events).find(event => element.style[event] !== undefined);
|
310 | return is.string(type) ? events[type] : false;
|
311 | })();
|
312 |
|
313 |
|
314 | function repaint(element, delay) {
|
315 | setTimeout(() => {
|
316 | try {
|
317 |
|
318 | element.hidden = true;
|
319 |
|
320 |
|
321 | element.offsetHeight;
|
322 |
|
323 |
|
324 | element.hidden = false;
|
325 | } catch (_) {
|
326 |
|
327 | }
|
328 | }, delay);
|
329 | }
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 | const isIE = Boolean(window.document.documentMode);
|
337 | const isEdge = /Edge/g.test(navigator.userAgent);
|
338 | const isWebKit = 'WebkitAppearance' in document.documentElement.style && !/Edge/g.test(navigator.userAgent);
|
339 | const isIPhone = /iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1;
|
340 |
|
341 | const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
|
342 | const isIos = /iPad|iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1;
|
343 | var browser = {
|
344 | isIE,
|
345 | isEdge,
|
346 | isWebKit,
|
347 | isIPhone,
|
348 | isIPadOS,
|
349 | isIos
|
350 | };
|
351 |
|
352 |
|
353 |
|
354 |
|
355 | function cloneDeep(object) {
|
356 | return JSON.parse(JSON.stringify(object));
|
357 | }
|
358 |
|
359 |
|
360 | function getDeep(object, path) {
|
361 | return path.split('.').reduce((obj, key) => obj && obj[key], object);
|
362 | }
|
363 |
|
364 |
|
365 | function extend(target = {}, ...sources) {
|
366 | if (!sources.length) {
|
367 | return target;
|
368 | }
|
369 | const source = sources.shift();
|
370 | if (!is.object(source)) {
|
371 | return target;
|
372 | }
|
373 | Object.keys(source).forEach(key => {
|
374 | if (is.object(source[key])) {
|
375 | if (!Object.keys(target).includes(key)) {
|
376 | Object.assign(target, {
|
377 | [key]: {}
|
378 | });
|
379 | }
|
380 | extend(target[key], source[key]);
|
381 | } else {
|
382 | Object.assign(target, {
|
383 | [key]: source[key]
|
384 | });
|
385 | }
|
386 | });
|
387 | return extend(target, ...sources);
|
388 | }
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | function wrap(elements, wrapper) {
|
394 |
|
395 | const targets = elements.length ? elements : [elements];
|
396 |
|
397 |
|
398 |
|
399 | Array.from(targets).reverse().forEach((element, index) => {
|
400 | const child = index > 0 ? wrapper.cloneNode(true) : wrapper;
|
401 |
|
402 | const parent = element.parentNode;
|
403 | const sibling = element.nextSibling;
|
404 |
|
405 |
|
406 |
|
407 | child.appendChild(element);
|
408 |
|
409 |
|
410 |
|
411 |
|
412 | if (sibling) {
|
413 | parent.insertBefore(child, sibling);
|
414 | } else {
|
415 | parent.appendChild(child);
|
416 | }
|
417 | });
|
418 | }
|
419 |
|
420 |
|
421 | function setAttributes(element, attributes) {
|
422 | if (!is.element(element) || is.empty(attributes)) return;
|
423 |
|
424 |
|
425 |
|
426 | Object.entries(attributes).filter(([, value]) => !is.nullOrUndefined(value)).forEach(([key, value]) => element.setAttribute(key, value));
|
427 | }
|
428 |
|
429 |
|
430 | function createElement(type, attributes, text) {
|
431 |
|
432 | const element = document.createElement(type);
|
433 |
|
434 |
|
435 | if (is.object(attributes)) {
|
436 | setAttributes(element, attributes);
|
437 | }
|
438 |
|
439 |
|
440 | if (is.string(text)) {
|
441 | element.innerText = text;
|
442 | }
|
443 |
|
444 |
|
445 | return element;
|
446 | }
|
447 |
|
448 |
|
449 | function insertAfter(element, target) {
|
450 | if (!is.element(element) || !is.element(target)) return;
|
451 | target.parentNode.insertBefore(element, target.nextSibling);
|
452 | }
|
453 |
|
454 |
|
455 | function insertElement(type, parent, attributes, text) {
|
456 | if (!is.element(parent)) return;
|
457 | parent.appendChild(createElement(type, attributes, text));
|
458 | }
|
459 |
|
460 |
|
461 | function removeElement(element) {
|
462 | if (is.nodeList(element) || is.array(element)) {
|
463 | Array.from(element).forEach(removeElement);
|
464 | return;
|
465 | }
|
466 | if (!is.element(element) || !is.element(element.parentNode)) {
|
467 | return;
|
468 | }
|
469 | element.parentNode.removeChild(element);
|
470 | }
|
471 |
|
472 |
|
473 | function emptyElement(element) {
|
474 | if (!is.element(element)) return;
|
475 | let {
|
476 | length
|
477 | } = element.childNodes;
|
478 | while (length > 0) {
|
479 | element.removeChild(element.lastChild);
|
480 | length -= 1;
|
481 | }
|
482 | }
|
483 |
|
484 |
|
485 | function replaceElement(newChild, oldChild) {
|
486 | if (!is.element(oldChild) || !is.element(oldChild.parentNode) || !is.element(newChild)) return null;
|
487 | oldChild.parentNode.replaceChild(newChild, oldChild);
|
488 | return newChild;
|
489 | }
|
490 |
|
491 |
|
492 | function getAttributesFromSelector(sel, existingAttributes) {
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 | if (!is.string(sel) || is.empty(sel)) return {};
|
499 | const attributes = {};
|
500 | const existing = extend({}, existingAttributes);
|
501 | sel.split(',').forEach(s => {
|
502 |
|
503 | const selector = s.trim();
|
504 | const className = selector.replace('.', '');
|
505 | const stripped = selector.replace(/[[\]]/g, '');
|
506 |
|
507 | const parts = stripped.split('=');
|
508 | const [key] = parts;
|
509 | const value = parts.length > 1 ? parts[1].replace(/["']/g, '') : '';
|
510 |
|
511 | const start = selector.charAt(0);
|
512 | switch (start) {
|
513 | case '.':
|
514 |
|
515 | if (is.string(existing.class)) {
|
516 | attributes.class = `${existing.class} ${className}`;
|
517 | } else {
|
518 | attributes.class = className;
|
519 | }
|
520 | break;
|
521 | case '#':
|
522 |
|
523 | attributes.id = selector.replace('#', '');
|
524 | break;
|
525 | case '[':
|
526 |
|
527 | attributes[key] = value;
|
528 | break;
|
529 | }
|
530 | });
|
531 | return extend(existing, attributes);
|
532 | }
|
533 |
|
534 |
|
535 | function toggleHidden(element, hidden) {
|
536 | if (!is.element(element)) return;
|
537 | let hide = hidden;
|
538 | if (!is.boolean(hide)) {
|
539 | hide = !element.hidden;
|
540 | }
|
541 |
|
542 |
|
543 | element.hidden = hide;
|
544 | }
|
545 |
|
546 |
|
547 | function toggleClass(element, className, force) {
|
548 | if (is.nodeList(element)) {
|
549 | return Array.from(element).map(e => toggleClass(e, className, force));
|
550 | }
|
551 | if (is.element(element)) {
|
552 | let method = 'toggle';
|
553 | if (typeof force !== 'undefined') {
|
554 | method = force ? 'add' : 'remove';
|
555 | }
|
556 | element.classList[method](className);
|
557 | return element.classList.contains(className);
|
558 | }
|
559 | return false;
|
560 | }
|
561 |
|
562 |
|
563 | function hasClass(element, className) {
|
564 | return is.element(element) && element.classList.contains(className);
|
565 | }
|
566 |
|
567 |
|
568 | function matches(element, selector) {
|
569 | const {
|
570 | prototype
|
571 | } = Element;
|
572 | function match() {
|
573 | return Array.from(document.querySelectorAll(selector)).includes(this);
|
574 | }
|
575 | const method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
|
576 | return method.call(element, selector);
|
577 | }
|
578 |
|
579 |
|
580 | function closest$1(element, selector) {
|
581 | const {
|
582 | prototype
|
583 | } = Element;
|
584 |
|
585 |
|
586 | function closestElement() {
|
587 | let el = this;
|
588 | do {
|
589 | if (matches.matches(el, selector)) return el;
|
590 | el = el.parentElement || el.parentNode;
|
591 | } while (el !== null && el.nodeType === 1);
|
592 | return null;
|
593 | }
|
594 | const method = prototype.closest || closestElement;
|
595 | return method.call(element, selector);
|
596 | }
|
597 |
|
598 |
|
599 | function getElements(selector) {
|
600 | return this.elements.container.querySelectorAll(selector);
|
601 | }
|
602 |
|
603 |
|
604 | function getElement(selector) {
|
605 | return this.elements.container.querySelector(selector);
|
606 | }
|
607 |
|
608 |
|
609 | function setFocus(element = null, focusVisible = false) {
|
610 | if (!is.element(element)) return;
|
611 |
|
612 |
|
613 | element.focus({
|
614 | preventScroll: true,
|
615 | focusVisible
|
616 | });
|
617 | }
|
618 |
|
619 |
|
620 |
|
621 |
|
622 | const defaultCodecs = {
|
623 | 'audio/ogg': 'vorbis',
|
624 | 'audio/wav': '1',
|
625 | 'video/webm': 'vp8, vorbis',
|
626 | 'video/mp4': 'avc1.42E01E, mp4a.40.2',
|
627 | 'video/ogg': 'theora'
|
628 | };
|
629 |
|
630 |
|
631 | const support = {
|
632 |
|
633 | audio: 'canPlayType' in document.createElement('audio'),
|
634 | video: 'canPlayType' in document.createElement('video'),
|
635 |
|
636 |
|
637 | check(type, provider) {
|
638 | const api = support[type] || provider !== 'html5';
|
639 | const ui = api && support.rangeInput;
|
640 | return {
|
641 | api,
|
642 | ui
|
643 | };
|
644 | },
|
645 |
|
646 |
|
647 | pip: (() => {
|
648 |
|
649 |
|
650 |
|
651 | if (browser.isIPhone) {
|
652 | return false;
|
653 | }
|
654 |
|
655 |
|
656 |
|
657 | if (is.function(createElement('video').webkitSetPresentationMode)) {
|
658 | return true;
|
659 | }
|
660 |
|
661 |
|
662 |
|
663 | if (document.pictureInPictureEnabled && !createElement('video').disablePictureInPicture) {
|
664 | return true;
|
665 | }
|
666 | return false;
|
667 | })(),
|
668 |
|
669 |
|
670 | airplay: is.function(window.WebKitPlaybackTargetAvailabilityEvent),
|
671 |
|
672 |
|
673 | playsinline: 'playsInline' in document.createElement('video'),
|
674 |
|
675 |
|
676 |
|
677 | mime(input) {
|
678 | if (is.empty(input)) {
|
679 | return false;
|
680 | }
|
681 | const [mediaType] = input.split('/');
|
682 | let type = input;
|
683 |
|
684 |
|
685 | if (!this.isHTML5 || mediaType !== this.type) {
|
686 | return false;
|
687 | }
|
688 |
|
689 |
|
690 | if (Object.keys(defaultCodecs).includes(type)) {
|
691 | type += `; codecs="${defaultCodecs[input]}"`;
|
692 | }
|
693 | try {
|
694 | return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
|
695 | } catch (_) {
|
696 | return false;
|
697 | }
|
698 | },
|
699 |
|
700 | textTracks: 'textTracks' in document.createElement('video'),
|
701 |
|
702 | rangeInput: (() => {
|
703 | const range = document.createElement('input');
|
704 | range.type = 'range';
|
705 | return range.type === 'range';
|
706 | })(),
|
707 |
|
708 |
|
709 | touch: 'ontouchstart' in document.documentElement,
|
710 |
|
711 | transitions: transitionEndEvent !== false,
|
712 |
|
713 |
|
714 | reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches
|
715 | };
|
716 |
|
717 |
|
718 |
|
719 |
|
720 |
|
721 |
|
722 | const supportsPassiveListeners = (() => {
|
723 |
|
724 | let supported = false;
|
725 | try {
|
726 | const options = Object.defineProperty({}, 'passive', {
|
727 | get() {
|
728 | supported = true;
|
729 | return null;
|
730 | }
|
731 | });
|
732 | window.addEventListener('test', null, options);
|
733 | window.removeEventListener('test', null, options);
|
734 | } catch (_) {
|
735 |
|
736 | }
|
737 | return supported;
|
738 | })();
|
739 |
|
740 |
|
741 | function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) {
|
742 |
|
743 | if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) {
|
744 | return;
|
745 | }
|
746 |
|
747 |
|
748 | const events = event.split(' ');
|
749 |
|
750 |
|
751 | let options = capture;
|
752 |
|
753 |
|
754 | if (supportsPassiveListeners) {
|
755 | options = {
|
756 |
|
757 | passive,
|
758 |
|
759 | capture
|
760 | };
|
761 | }
|
762 |
|
763 |
|
764 | events.forEach(type => {
|
765 | if (this && this.eventListeners && toggle) {
|
766 |
|
767 | this.eventListeners.push({
|
768 | element,
|
769 | type,
|
770 | callback,
|
771 | options
|
772 | });
|
773 | }
|
774 | element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
|
775 | });
|
776 | }
|
777 |
|
778 |
|
779 | function on(element, events = '', callback, passive = true, capture = false) {
|
780 | toggleListener.call(this, element, events, callback, true, passive, capture);
|
781 | }
|
782 |
|
783 |
|
784 | function off(element, events = '', callback, passive = true, capture = false) {
|
785 | toggleListener.call(this, element, events, callback, false, passive, capture);
|
786 | }
|
787 |
|
788 |
|
789 | function once(element, events = '', callback, passive = true, capture = false) {
|
790 | const onceCallback = (...args) => {
|
791 | off(element, events, onceCallback, passive, capture);
|
792 | callback.apply(this, args);
|
793 | };
|
794 | toggleListener.call(this, element, events, onceCallback, true, passive, capture);
|
795 | }
|
796 |
|
797 |
|
798 | function triggerEvent(element, type = '', bubbles = false, detail = {}) {
|
799 |
|
800 | if (!is.element(element) || is.empty(type)) {
|
801 | return;
|
802 | }
|
803 |
|
804 |
|
805 | const event = new CustomEvent(type, {
|
806 | bubbles,
|
807 | detail: {
|
808 | ...detail,
|
809 | plyr: this
|
810 | }
|
811 | });
|
812 |
|
813 |
|
814 | element.dispatchEvent(event);
|
815 | }
|
816 |
|
817 |
|
818 | function unbindListeners() {
|
819 | if (this && this.eventListeners) {
|
820 | this.eventListeners.forEach(item => {
|
821 | const {
|
822 | element,
|
823 | type,
|
824 | callback,
|
825 | options
|
826 | } = item;
|
827 | element.removeEventListener(type, callback, options);
|
828 | });
|
829 | this.eventListeners = [];
|
830 | }
|
831 | }
|
832 |
|
833 |
|
834 | function ready() {
|
835 | return new Promise(resolve => this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve)).then(() => {});
|
836 | }
|
837 |
|
838 | |
839 |
|
840 |
|
841 |
|
842 |
|
843 |
|
844 | function silencePromise(value) {
|
845 | if (is.promise(value)) {
|
846 | value.then(null, () => {});
|
847 | }
|
848 | }
|
849 |
|
850 |
|
851 |
|
852 |
|
853 | function dedupe(array) {
|
854 | if (!is.array(array)) {
|
855 | return array;
|
856 | }
|
857 | return array.filter((item, index) => array.indexOf(item) === index);
|
858 | }
|
859 |
|
860 |
|
861 | function closest(array, value) {
|
862 | if (!is.array(array) || !array.length) {
|
863 | return null;
|
864 | }
|
865 | return array.reduce((prev, curr) => Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev);
|
866 | }
|
867 |
|
868 |
|
869 |
|
870 |
|
871 | function supportsCSS(declaration) {
|
872 | if (!window || !window.CSS) {
|
873 | return false;
|
874 | }
|
875 | return window.CSS.supports(declaration);
|
876 | }
|
877 |
|
878 |
|
879 | const standardRatios = [[1, 1], [4, 3], [3, 4], [5, 4], [4, 5], [3, 2], [2, 3], [16, 10], [10, 16], [16, 9], [9, 16], [21, 9], [9, 21], [32, 9], [9, 32]].reduce((out, [x, y]) => ({
|
880 | ...out,
|
881 | [x / y]: [x, y]
|
882 | }), {});
|
883 |
|
884 |
|
885 | function validateAspectRatio(input) {
|
886 | if (!is.array(input) && (!is.string(input) || !input.includes(':'))) {
|
887 | return false;
|
888 | }
|
889 | const ratio = is.array(input) ? input : input.split(':');
|
890 | return ratio.map(Number).every(is.number);
|
891 | }
|
892 |
|
893 |
|
894 | function reduceAspectRatio(ratio) {
|
895 | if (!is.array(ratio) || !ratio.every(is.number)) {
|
896 | return null;
|
897 | }
|
898 | const [width, height] = ratio;
|
899 | const getDivider = (w, h) => h === 0 ? w : getDivider(h, w % h);
|
900 | const divider = getDivider(width, height);
|
901 | return [width / divider, height / divider];
|
902 | }
|
903 |
|
904 |
|
905 | function getAspectRatio(input) {
|
906 | const parse = ratio => validateAspectRatio(ratio) ? ratio.split(':').map(Number) : null;
|
907 |
|
908 | let ratio = parse(input);
|
909 |
|
910 |
|
911 | if (ratio === null) {
|
912 | ratio = parse(this.config.ratio);
|
913 | }
|
914 |
|
915 |
|
916 | if (ratio === null && !is.empty(this.embed) && is.array(this.embed.ratio)) {
|
917 | ({
|
918 | ratio
|
919 | } = this.embed);
|
920 | }
|
921 |
|
922 |
|
923 | if (ratio === null && this.isHTML5) {
|
924 | const {
|
925 | videoWidth,
|
926 | videoHeight
|
927 | } = this.media;
|
928 | ratio = [videoWidth, videoHeight];
|
929 | }
|
930 | return reduceAspectRatio(ratio);
|
931 | }
|
932 |
|
933 |
|
934 | function setAspectRatio(input) {
|
935 | if (!this.isVideo) {
|
936 | return {};
|
937 | }
|
938 | const {
|
939 | wrapper
|
940 | } = this.elements;
|
941 | const ratio = getAspectRatio.call(this, input);
|
942 | if (!is.array(ratio)) {
|
943 | return {};
|
944 | }
|
945 | const [x, y] = reduceAspectRatio(ratio);
|
946 | const useNative = supportsCSS(`aspect-ratio: ${x}/${y}`);
|
947 | const padding = 100 / x * y;
|
948 | if (useNative) {
|
949 | wrapper.style.aspectRatio = `${x}/${y}`;
|
950 | } else {
|
951 | wrapper.style.paddingBottom = `${padding}%`;
|
952 | }
|
953 |
|
954 |
|
955 | if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
|
956 | const height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
|
957 | const offset = (height - padding) / (height / 50);
|
958 | if (this.fullscreen.active) {
|
959 | wrapper.style.paddingBottom = null;
|
960 | } else {
|
961 | this.media.style.transform = `translateY(-${offset}%)`;
|
962 | }
|
963 | } else if (this.isHTML5) {
|
964 | wrapper.classList.add(this.config.classNames.videoFixedRatio);
|
965 | }
|
966 | return {
|
967 | padding,
|
968 | ratio
|
969 | };
|
970 | }
|
971 |
|
972 |
|
973 | function roundAspectRatio(x, y, tolerance = 0.05) {
|
974 | const ratio = x / y;
|
975 | const closestRatio = closest(Object.keys(standardRatios), ratio);
|
976 |
|
977 |
|
978 | if (Math.abs(closestRatio - ratio) <= tolerance) {
|
979 | return standardRatios[closestRatio];
|
980 | }
|
981 |
|
982 |
|
983 | return [x, y];
|
984 | }
|
985 |
|
986 |
|
987 |
|
988 | function getViewportSize() {
|
989 | const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
990 | const height = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
|
991 | return [width, height];
|
992 | }
|
993 |
|
994 |
|
995 | const html5 = {
|
996 | getSources() {
|
997 | if (!this.isHTML5) {
|
998 | return [];
|
999 | }
|
1000 | const sources = Array.from(this.media.querySelectorAll('source'));
|
1001 |
|
1002 |
|
1003 | return sources.filter(source => {
|
1004 | const type = source.getAttribute('type');
|
1005 | if (is.empty(type)) {
|
1006 | return true;
|
1007 | }
|
1008 | return support.mime.call(this, type);
|
1009 | });
|
1010 | },
|
1011 |
|
1012 | getQualityOptions() {
|
1013 |
|
1014 | if (this.config.quality.forced) {
|
1015 | return this.config.quality.options;
|
1016 | }
|
1017 |
|
1018 |
|
1019 | return html5.getSources.call(this).map(source => Number(source.getAttribute('size'))).filter(Boolean);
|
1020 | },
|
1021 | setup() {
|
1022 | if (!this.isHTML5) {
|
1023 | return;
|
1024 | }
|
1025 | const player = this;
|
1026 |
|
1027 |
|
1028 | player.options.speed = player.config.speed.options;
|
1029 |
|
1030 |
|
1031 | if (!is.empty(this.config.ratio)) {
|
1032 | setAspectRatio.call(player);
|
1033 | }
|
1034 |
|
1035 |
|
1036 | Object.defineProperty(player.media, 'quality', {
|
1037 | get() {
|
1038 |
|
1039 | const sources = html5.getSources.call(player);
|
1040 | const source = sources.find(s => s.getAttribute('src') === player.source);
|
1041 |
|
1042 |
|
1043 | return source && Number(source.getAttribute('size'));
|
1044 | },
|
1045 | set(input) {
|
1046 | if (player.quality === input) {
|
1047 | return;
|
1048 | }
|
1049 |
|
1050 |
|
1051 | if (player.config.quality.forced && is.function(player.config.quality.onChange)) {
|
1052 | player.config.quality.onChange(input);
|
1053 | } else {
|
1054 |
|
1055 | const sources = html5.getSources.call(player);
|
1056 |
|
1057 | const source = sources.find(s => Number(s.getAttribute('size')) === input);
|
1058 |
|
1059 |
|
1060 | if (!source) {
|
1061 | return;
|
1062 | }
|
1063 |
|
1064 |
|
1065 | const {
|
1066 | currentTime,
|
1067 | paused,
|
1068 | preload,
|
1069 | readyState,
|
1070 | playbackRate
|
1071 | } = player.media;
|
1072 |
|
1073 |
|
1074 | player.media.src = source.getAttribute('src');
|
1075 |
|
1076 |
|
1077 | if (preload !== 'none' || readyState) {
|
1078 |
|
1079 | player.once('loadedmetadata', () => {
|
1080 | player.speed = playbackRate;
|
1081 | player.currentTime = currentTime;
|
1082 |
|
1083 |
|
1084 | if (!paused) {
|
1085 | silencePromise(player.play());
|
1086 | }
|
1087 | });
|
1088 |
|
1089 |
|
1090 | player.media.load();
|
1091 | }
|
1092 | }
|
1093 |
|
1094 |
|
1095 | triggerEvent.call(player, player.media, 'qualitychange', false, {
|
1096 | quality: input
|
1097 | });
|
1098 | }
|
1099 | });
|
1100 | },
|
1101 |
|
1102 |
|
1103 | cancelRequests() {
|
1104 | if (!this.isHTML5) {
|
1105 | return;
|
1106 | }
|
1107 |
|
1108 |
|
1109 | removeElement(html5.getSources.call(this));
|
1110 |
|
1111 |
|
1112 |
|
1113 |
|
1114 | this.media.setAttribute('src', this.config.blankVideo);
|
1115 |
|
1116 |
|
1117 |
|
1118 |
|
1119 | this.media.load();
|
1120 |
|
1121 |
|
1122 | this.debug.log('Cancelled network requests');
|
1123 | }
|
1124 | };
|
1125 |
|
1126 |
|
1127 |
|
1128 |
|
1129 | function generateId(prefix) {
|
1130 | return `${prefix}-${Math.floor(Math.random() * 10000)}`;
|
1131 | }
|
1132 |
|
1133 |
|
1134 | function format(input, ...args) {
|
1135 | if (is.empty(input)) return input;
|
1136 | return input.toString().replace(/{(\d+)}/g, (_, i) => args[i].toString());
|
1137 | }
|
1138 |
|
1139 |
|
1140 | function getPercentage(current, max) {
|
1141 | if (current === 0 || max === 0 || Number.isNaN(current) || Number.isNaN(max)) {
|
1142 | return 0;
|
1143 | }
|
1144 | return (current / max * 100).toFixed(2);
|
1145 | }
|
1146 |
|
1147 |
|
1148 | const replaceAll = (input = '', find = '', replace = '') => input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
|
1149 |
|
1150 |
|
1151 | const toTitleCase = (input = '') => input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.slice(1).toLowerCase());
|
1152 |
|
1153 |
|
1154 | function toPascalCase(input = '') {
|
1155 | let string = input.toString();
|
1156 |
|
1157 |
|
1158 | string = replaceAll(string, '-', ' ');
|
1159 |
|
1160 |
|
1161 | string = replaceAll(string, '_', ' ');
|
1162 |
|
1163 |
|
1164 | string = toTitleCase(string);
|
1165 |
|
1166 |
|
1167 | return replaceAll(string, ' ', '');
|
1168 | }
|
1169 |
|
1170 |
|
1171 | function toCamelCase(input = '') {
|
1172 | let string = input.toString();
|
1173 |
|
1174 |
|
1175 | string = toPascalCase(string);
|
1176 |
|
1177 |
|
1178 | return string.charAt(0).toLowerCase() + string.slice(1);
|
1179 | }
|
1180 |
|
1181 |
|
1182 | function stripHTML(source) {
|
1183 | const fragment = document.createDocumentFragment();
|
1184 | const element = document.createElement('div');
|
1185 | fragment.appendChild(element);
|
1186 | element.innerHTML = source;
|
1187 | return fragment.firstChild.innerText;
|
1188 | }
|
1189 |
|
1190 |
|
1191 | function getHTML(element) {
|
1192 | const wrapper = document.createElement('div');
|
1193 | wrapper.appendChild(element);
|
1194 | return wrapper.innerHTML;
|
1195 | }
|
1196 |
|
1197 |
|
1198 |
|
1199 |
|
1200 | const resources = {
|
1201 | pip: 'PIP',
|
1202 | airplay: 'AirPlay',
|
1203 | html5: 'HTML5',
|
1204 | vimeo: 'Vimeo',
|
1205 | youtube: 'YouTube'
|
1206 | };
|
1207 | const i18n = {
|
1208 | get(key = '', config = {}) {
|
1209 | if (is.empty(key) || is.empty(config)) {
|
1210 | return '';
|
1211 | }
|
1212 | let string = getDeep(config.i18n, key);
|
1213 | if (is.empty(string)) {
|
1214 | if (Object.keys(resources).includes(key)) {
|
1215 | return resources[key];
|
1216 | }
|
1217 | return '';
|
1218 | }
|
1219 | const replace = {
|
1220 | '{seektime}': config.seekTime,
|
1221 | '{title}': config.title
|
1222 | };
|
1223 | Object.entries(replace).forEach(([k, v]) => {
|
1224 | string = replaceAll(string, k, v);
|
1225 | });
|
1226 | return string;
|
1227 | }
|
1228 | };
|
1229 |
|
1230 | class Storage {
|
1231 | constructor(player) {
|
1232 | _defineProperty$1(this, "get", key => {
|
1233 | if (!Storage.supported || !this.enabled) {
|
1234 | return null;
|
1235 | }
|
1236 | const store = window.localStorage.getItem(this.key);
|
1237 | if (is.empty(store)) {
|
1238 | return null;
|
1239 | }
|
1240 | const json = JSON.parse(store);
|
1241 | return is.string(key) && key.length ? json[key] : json;
|
1242 | });
|
1243 | _defineProperty$1(this, "set", object => {
|
1244 |
|
1245 | if (!Storage.supported || !this.enabled) {
|
1246 | return;
|
1247 | }
|
1248 |
|
1249 |
|
1250 | if (!is.object(object)) {
|
1251 | return;
|
1252 | }
|
1253 |
|
1254 |
|
1255 | let storage = this.get();
|
1256 |
|
1257 |
|
1258 | if (is.empty(storage)) {
|
1259 | storage = {};
|
1260 | }
|
1261 |
|
1262 |
|
1263 | extend(storage, object);
|
1264 |
|
1265 |
|
1266 | try {
|
1267 | window.localStorage.setItem(this.key, JSON.stringify(storage));
|
1268 | } catch (_) {
|
1269 |
|
1270 | }
|
1271 | });
|
1272 | this.enabled = player.config.storage.enabled;
|
1273 | this.key = player.config.storage.key;
|
1274 | }
|
1275 |
|
1276 |
|
1277 | static get supported() {
|
1278 | try {
|
1279 | if (!('localStorage' in window)) {
|
1280 | return false;
|
1281 | }
|
1282 | const test = '___test';
|
1283 |
|
1284 |
|
1285 |
|
1286 | window.localStorage.setItem(test, test);
|
1287 | window.localStorage.removeItem(test);
|
1288 | return true;
|
1289 | } catch (_) {
|
1290 | return false;
|
1291 | }
|
1292 | }
|
1293 | }
|
1294 |
|
1295 |
|
1296 |
|
1297 |
|
1298 |
|
1299 |
|
1300 | function fetch(url, responseType = 'text') {
|
1301 | return new Promise((resolve, reject) => {
|
1302 | try {
|
1303 | const request = new XMLHttpRequest();
|
1304 |
|
1305 |
|
1306 | if (!('withCredentials' in request)) {
|
1307 | return;
|
1308 | }
|
1309 | request.addEventListener('load', () => {
|
1310 | if (responseType === 'text') {
|
1311 | try {
|
1312 | resolve(JSON.parse(request.responseText));
|
1313 | } catch (_) {
|
1314 | resolve(request.responseText);
|
1315 | }
|
1316 | } else {
|
1317 | resolve(request.response);
|
1318 | }
|
1319 | });
|
1320 | request.addEventListener('error', () => {
|
1321 | throw new Error(request.status);
|
1322 | });
|
1323 | request.open('GET', url, true);
|
1324 |
|
1325 |
|
1326 | request.responseType = responseType;
|
1327 | request.send();
|
1328 | } catch (error) {
|
1329 | reject(error);
|
1330 | }
|
1331 | });
|
1332 | }
|
1333 |
|
1334 |
|
1335 |
|
1336 |
|
1337 | function loadSprite(url, id) {
|
1338 | if (!is.string(url)) {
|
1339 | return;
|
1340 | }
|
1341 | const prefix = 'cache';
|
1342 | const hasId = is.string(id);
|
1343 | let isCached = false;
|
1344 | const exists = () => document.getElementById(id) !== null;
|
1345 | const update = (container, data) => {
|
1346 |
|
1347 | container.innerHTML = data;
|
1348 |
|
1349 |
|
1350 | if (hasId && exists()) {
|
1351 | return;
|
1352 | }
|
1353 |
|
1354 |
|
1355 | document.body.insertAdjacentElement('afterbegin', container);
|
1356 | };
|
1357 |
|
1358 |
|
1359 | if (!hasId || !exists()) {
|
1360 | const useStorage = Storage.supported;
|
1361 |
|
1362 | const container = document.createElement('div');
|
1363 | container.setAttribute('hidden', '');
|
1364 | if (hasId) {
|
1365 | container.setAttribute('id', id);
|
1366 | }
|
1367 |
|
1368 |
|
1369 | if (useStorage) {
|
1370 | const cached = window.localStorage.getItem(`${prefix}-${id}`);
|
1371 | isCached = cached !== null;
|
1372 | if (isCached) {
|
1373 | const data = JSON.parse(cached);
|
1374 | update(container, data.content);
|
1375 | }
|
1376 | }
|
1377 |
|
1378 |
|
1379 | fetch(url).then(result => {
|
1380 | if (is.empty(result)) {
|
1381 | return;
|
1382 | }
|
1383 | if (useStorage) {
|
1384 | try {
|
1385 | window.localStorage.setItem(`${prefix}-${id}`, JSON.stringify({
|
1386 | content: result
|
1387 | }));
|
1388 | } catch (_) {
|
1389 |
|
1390 | }
|
1391 | }
|
1392 | update(container, result);
|
1393 | }).catch(() => {});
|
1394 | }
|
1395 | }
|
1396 |
|
1397 |
|
1398 |
|
1399 |
|
1400 | const getHours = value => Math.trunc(value / 60 / 60 % 60, 10);
|
1401 | const getMinutes = value => Math.trunc(value / 60 % 60, 10);
|
1402 | const getSeconds = value => Math.trunc(value % 60, 10);
|
1403 |
|
1404 |
|
1405 | function formatTime(time = 0, displayHours = false, inverted = false) {
|
1406 |
|
1407 | if (!is.number(time)) {
|
1408 | return formatTime(undefined, displayHours, inverted);
|
1409 | }
|
1410 |
|
1411 |
|
1412 | const format = value => `0${value}`.slice(-2);
|
1413 |
|
1414 | let hours = getHours(time);
|
1415 | const mins = getMinutes(time);
|
1416 | const secs = getSeconds(time);
|
1417 |
|
1418 |
|
1419 | if (displayHours || hours > 0) {
|
1420 | hours = `${hours}:`;
|
1421 | } else {
|
1422 | hours = '';
|
1423 | }
|
1424 |
|
1425 |
|
1426 | return `${inverted && time > 0 ? '-' : ''}${hours}${format(mins)}:${format(secs)}`;
|
1427 | }
|
1428 |
|
1429 |
|
1430 |
|
1431 |
|
1432 | const controls = {
|
1433 |
|
1434 | getIconUrl() {
|
1435 | const url = new URL(this.config.iconUrl, window.location);
|
1436 | const host = window.location.host ? window.location.host : window.top.location.host;
|
1437 | const cors = url.host !== host || browser.isIE && !window.svg4everybody;
|
1438 | return {
|
1439 | url: this.config.iconUrl,
|
1440 | cors
|
1441 | };
|
1442 | },
|
1443 |
|
1444 | findElements() {
|
1445 | try {
|
1446 | this.elements.controls = getElement.call(this, this.config.selectors.controls.wrapper);
|
1447 |
|
1448 |
|
1449 | this.elements.buttons = {
|
1450 | play: getElements.call(this, this.config.selectors.buttons.play),
|
1451 | pause: getElement.call(this, this.config.selectors.buttons.pause),
|
1452 | restart: getElement.call(this, this.config.selectors.buttons.restart),
|
1453 | rewind: getElement.call(this, this.config.selectors.buttons.rewind),
|
1454 | fastForward: getElement.call(this, this.config.selectors.buttons.fastForward),
|
1455 | mute: getElement.call(this, this.config.selectors.buttons.mute),
|
1456 | pip: getElement.call(this, this.config.selectors.buttons.pip),
|
1457 | airplay: getElement.call(this, this.config.selectors.buttons.airplay),
|
1458 | settings: getElement.call(this, this.config.selectors.buttons.settings),
|
1459 | captions: getElement.call(this, this.config.selectors.buttons.captions),
|
1460 | fullscreen: getElement.call(this, this.config.selectors.buttons.fullscreen)
|
1461 | };
|
1462 |
|
1463 |
|
1464 | this.elements.progress = getElement.call(this, this.config.selectors.progress);
|
1465 |
|
1466 |
|
1467 | this.elements.inputs = {
|
1468 | seek: getElement.call(this, this.config.selectors.inputs.seek),
|
1469 | volume: getElement.call(this, this.config.selectors.inputs.volume)
|
1470 | };
|
1471 |
|
1472 |
|
1473 | this.elements.display = {
|
1474 | buffer: getElement.call(this, this.config.selectors.display.buffer),
|
1475 | currentTime: getElement.call(this, this.config.selectors.display.currentTime),
|
1476 | duration: getElement.call(this, this.config.selectors.display.duration)
|
1477 | };
|
1478 |
|
1479 |
|
1480 | if (is.element(this.elements.progress)) {
|
1481 | this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);
|
1482 | }
|
1483 | return true;
|
1484 | } catch (error) {
|
1485 |
|
1486 | this.debug.warn('It looks like there is a problem with your custom controls HTML', error);
|
1487 |
|
1488 |
|
1489 | this.toggleNativeControls(true);
|
1490 | return false;
|
1491 | }
|
1492 | },
|
1493 |
|
1494 | createIcon(type, attributes) {
|
1495 | const namespace = 'http://www.w3.org/2000/svg';
|
1496 | const iconUrl = controls.getIconUrl.call(this);
|
1497 | const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${this.config.iconPrefix}`;
|
1498 |
|
1499 | const icon = document.createElementNS(namespace, 'svg');
|
1500 | setAttributes(icon, extend(attributes, {
|
1501 | 'aria-hidden': 'true',
|
1502 | focusable: 'false'
|
1503 | }));
|
1504 |
|
1505 |
|
1506 | const use = document.createElementNS(namespace, 'use');
|
1507 | const path = `${iconPath}-${type}`;
|
1508 |
|
1509 |
|
1510 |
|
1511 |
|
1512 | if ('href' in use) {
|
1513 | use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
1514 | }
|
1515 |
|
1516 |
|
1517 | use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
1518 |
|
1519 |
|
1520 | icon.appendChild(use);
|
1521 | return icon;
|
1522 | },
|
1523 |
|
1524 | createLabel(key, attr = {}) {
|
1525 | const text = i18n.get(key, this.config);
|
1526 | const attributes = {
|
1527 | ...attr,
|
1528 | class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
1529 | };
|
1530 | return createElement('span', attributes, text);
|
1531 | },
|
1532 |
|
1533 | createBadge(text) {
|
1534 | if (is.empty(text)) {
|
1535 | return null;
|
1536 | }
|
1537 | const badge = createElement('span', {
|
1538 | class: this.config.classNames.menu.value
|
1539 | });
|
1540 | badge.appendChild(createElement('span', {
|
1541 | class: this.config.classNames.menu.badge
|
1542 | }, text));
|
1543 | return badge;
|
1544 | },
|
1545 |
|
1546 | createButton(buttonType, attr) {
|
1547 | const attributes = extend({}, attr);
|
1548 | let type = toCamelCase(buttonType);
|
1549 | const props = {
|
1550 | element: 'button',
|
1551 | toggle: false,
|
1552 | label: null,
|
1553 | icon: null,
|
1554 | labelPressed: null,
|
1555 | iconPressed: null
|
1556 | };
|
1557 | ['element', 'icon', 'label'].forEach(key => {
|
1558 | if (Object.keys(attributes).includes(key)) {
|
1559 | props[key] = attributes[key];
|
1560 | delete attributes[key];
|
1561 | }
|
1562 | });
|
1563 |
|
1564 |
|
1565 | if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
|
1566 | attributes.type = 'button';
|
1567 | }
|
1568 |
|
1569 |
|
1570 | if (Object.keys(attributes).includes('class')) {
|
1571 | if (!attributes.class.split(' ').some(c => c === this.config.classNames.control)) {
|
1572 | extend(attributes, {
|
1573 | class: `${attributes.class} ${this.config.classNames.control}`
|
1574 | });
|
1575 | }
|
1576 | } else {
|
1577 | attributes.class = this.config.classNames.control;
|
1578 | }
|
1579 |
|
1580 |
|
1581 | switch (buttonType) {
|
1582 | case 'play':
|
1583 | props.toggle = true;
|
1584 | props.label = 'play';
|
1585 | props.labelPressed = 'pause';
|
1586 | props.icon = 'play';
|
1587 | props.iconPressed = 'pause';
|
1588 | break;
|
1589 | case 'mute':
|
1590 | props.toggle = true;
|
1591 | props.label = 'mute';
|
1592 | props.labelPressed = 'unmute';
|
1593 | props.icon = 'volume';
|
1594 | props.iconPressed = 'muted';
|
1595 | break;
|
1596 | case 'captions':
|
1597 | props.toggle = true;
|
1598 | props.label = 'enableCaptions';
|
1599 | props.labelPressed = 'disableCaptions';
|
1600 | props.icon = 'captions-off';
|
1601 | props.iconPressed = 'captions-on';
|
1602 | break;
|
1603 | case 'fullscreen':
|
1604 | props.toggle = true;
|
1605 | props.label = 'enterFullscreen';
|
1606 | props.labelPressed = 'exitFullscreen';
|
1607 | props.icon = 'enter-fullscreen';
|
1608 | props.iconPressed = 'exit-fullscreen';
|
1609 | break;
|
1610 | case 'play-large':
|
1611 | attributes.class += ` ${this.config.classNames.control}--overlaid`;
|
1612 | type = 'play';
|
1613 | props.label = 'play';
|
1614 | props.icon = 'play';
|
1615 | break;
|
1616 | default:
|
1617 | if (is.empty(props.label)) {
|
1618 | props.label = type;
|
1619 | }
|
1620 | if (is.empty(props.icon)) {
|
1621 | props.icon = buttonType;
|
1622 | }
|
1623 | }
|
1624 | const button = createElement(props.element);
|
1625 |
|
1626 |
|
1627 | if (props.toggle) {
|
1628 |
|
1629 | button.appendChild(controls.createIcon.call(this, props.iconPressed, {
|
1630 | class: 'icon--pressed'
|
1631 | }));
|
1632 | button.appendChild(controls.createIcon.call(this, props.icon, {
|
1633 | class: 'icon--not-pressed'
|
1634 | }));
|
1635 |
|
1636 |
|
1637 | button.appendChild(controls.createLabel.call(this, props.labelPressed, {
|
1638 | class: 'label--pressed'
|
1639 | }));
|
1640 | button.appendChild(controls.createLabel.call(this, props.label, {
|
1641 | class: 'label--not-pressed'
|
1642 | }));
|
1643 | } else {
|
1644 | button.appendChild(controls.createIcon.call(this, props.icon));
|
1645 | button.appendChild(controls.createLabel.call(this, props.label));
|
1646 | }
|
1647 |
|
1648 |
|
1649 | extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
1650 | setAttributes(button, attributes);
|
1651 |
|
1652 |
|
1653 | if (type === 'play') {
|
1654 | if (!is.array(this.elements.buttons[type])) {
|
1655 | this.elements.buttons[type] = [];
|
1656 | }
|
1657 | this.elements.buttons[type].push(button);
|
1658 | } else {
|
1659 | this.elements.buttons[type] = button;
|
1660 | }
|
1661 | return button;
|
1662 | },
|
1663 |
|
1664 | createRange(type, attributes) {
|
1665 |
|
1666 | const input = createElement('input', extend(getAttributesFromSelector(this.config.selectors.inputs[type]), {
|
1667 | type: 'range',
|
1668 | min: 0,
|
1669 | max: 100,
|
1670 | step: 0.01,
|
1671 | value: 0,
|
1672 | autocomplete: 'off',
|
1673 |
|
1674 | role: 'slider',
|
1675 | 'aria-label': i18n.get(type, this.config),
|
1676 | 'aria-valuemin': 0,
|
1677 | 'aria-valuemax': 100,
|
1678 | 'aria-valuenow': 0
|
1679 | }, attributes));
|
1680 | this.elements.inputs[type] = input;
|
1681 |
|
1682 |
|
1683 | controls.updateRangeFill.call(this, input);
|
1684 |
|
1685 |
|
1686 | RangeTouch.setup(input);
|
1687 | return input;
|
1688 | },
|
1689 |
|
1690 | createProgress(type, attributes) {
|
1691 | const progress = createElement('progress', extend(getAttributesFromSelector(this.config.selectors.display[type]), {
|
1692 | min: 0,
|
1693 | max: 100,
|
1694 | value: 0,
|
1695 | role: 'progressbar',
|
1696 | 'aria-hidden': true
|
1697 | }, attributes));
|
1698 |
|
1699 |
|
1700 | if (type !== 'volume') {
|
1701 | progress.appendChild(createElement('span', null, '0'));
|
1702 | const suffixKey = {
|
1703 | played: 'played',
|
1704 | buffer: 'buffered'
|
1705 | }[type];
|
1706 | const suffix = suffixKey ? i18n.get(suffixKey, this.config) : '';
|
1707 | progress.innerText = `% ${suffix.toLowerCase()}`;
|
1708 | }
|
1709 | this.elements.display[type] = progress;
|
1710 | return progress;
|
1711 | },
|
1712 |
|
1713 | createTime(type, attrs) {
|
1714 | const attributes = getAttributesFromSelector(this.config.selectors.display[type], attrs);
|
1715 | const container = createElement('div', extend(attributes, {
|
1716 | class: `${attributes.class ? attributes.class : ''} ${this.config.classNames.display.time} `.trim(),
|
1717 | 'aria-label': i18n.get(type, this.config),
|
1718 | role: 'timer'
|
1719 | }), '00:00');
|
1720 |
|
1721 |
|
1722 | this.elements.display[type] = container;
|
1723 | return container;
|
1724 | },
|
1725 |
|
1726 |
|
1727 |
|
1728 | bindMenuItemShortcuts(menuItem, type) {
|
1729 |
|
1730 | on.call(this, menuItem, 'keydown keyup', event => {
|
1731 |
|
1732 | if (![' ', 'ArrowUp', 'ArrowDown', 'ArrowRight'].includes(event.key)) {
|
1733 | return;
|
1734 | }
|
1735 |
|
1736 |
|
1737 | event.preventDefault();
|
1738 | event.stopPropagation();
|
1739 |
|
1740 |
|
1741 | if (event.type === 'keydown') {
|
1742 | return;
|
1743 | }
|
1744 | const isRadioButton = matches(menuItem, '[role="menuitemradio"]');
|
1745 |
|
1746 |
|
1747 | if (!isRadioButton && [' ', 'ArrowRight'].includes(event.key)) {
|
1748 | controls.showMenuPanel.call(this, type, true);
|
1749 | } else {
|
1750 | let target;
|
1751 | if (event.key !== ' ') {
|
1752 | if (event.key === 'ArrowDown' || isRadioButton && event.key === 'ArrowRight') {
|
1753 | target = menuItem.nextElementSibling;
|
1754 | if (!is.element(target)) {
|
1755 | target = menuItem.parentNode.firstElementChild;
|
1756 | }
|
1757 | } else {
|
1758 | target = menuItem.previousElementSibling;
|
1759 | if (!is.element(target)) {
|
1760 | target = menuItem.parentNode.lastElementChild;
|
1761 | }
|
1762 | }
|
1763 | setFocus.call(this, target, true);
|
1764 | }
|
1765 | }
|
1766 | }, false);
|
1767 |
|
1768 |
|
1769 |
|
1770 | on.call(this, menuItem, 'keyup', event => {
|
1771 | if (event.key !== 'Return') return;
|
1772 | controls.focusFirstMenuItem.call(this, null, true);
|
1773 | });
|
1774 | },
|
1775 |
|
1776 | createMenuItem({
|
1777 | value,
|
1778 | list,
|
1779 | type,
|
1780 | title,
|
1781 | badge = null,
|
1782 | checked = false
|
1783 | }) {
|
1784 | const attributes = getAttributesFromSelector(this.config.selectors.inputs[type]);
|
1785 | const menuItem = createElement('button', extend(attributes, {
|
1786 | type: 'button',
|
1787 | role: 'menuitemradio',
|
1788 | class: `${this.config.classNames.control} ${attributes.class ? attributes.class : ''}`.trim(),
|
1789 | 'aria-checked': checked,
|
1790 | value
|
1791 | }));
|
1792 | const flex = createElement('span');
|
1793 |
|
1794 |
|
1795 | flex.innerHTML = title;
|
1796 | if (is.element(badge)) {
|
1797 | flex.appendChild(badge);
|
1798 | }
|
1799 | menuItem.appendChild(flex);
|
1800 |
|
1801 |
|
1802 | Object.defineProperty(menuItem, 'checked', {
|
1803 | enumerable: true,
|
1804 | get() {
|
1805 | return menuItem.getAttribute('aria-checked') === 'true';
|
1806 | },
|
1807 | set(check) {
|
1808 |
|
1809 | if (check) {
|
1810 | Array.from(menuItem.parentNode.children).filter(node => matches(node, '[role="menuitemradio"]')).forEach(node => node.setAttribute('aria-checked', 'false'));
|
1811 | }
|
1812 | menuItem.setAttribute('aria-checked', check ? 'true' : 'false');
|
1813 | }
|
1814 | });
|
1815 | this.listeners.bind(menuItem, 'click keyup', event => {
|
1816 | if (is.keyboardEvent(event) && event.key !== ' ') {
|
1817 | return;
|
1818 | }
|
1819 | event.preventDefault();
|
1820 | event.stopPropagation();
|
1821 | menuItem.checked = true;
|
1822 | switch (type) {
|
1823 | case 'language':
|
1824 | this.currentTrack = Number(value);
|
1825 | break;
|
1826 | case 'quality':
|
1827 | this.quality = value;
|
1828 | break;
|
1829 | case 'speed':
|
1830 | this.speed = parseFloat(value);
|
1831 | break;
|
1832 | }
|
1833 | controls.showMenuPanel.call(this, 'home', is.keyboardEvent(event));
|
1834 | }, type, false);
|
1835 | controls.bindMenuItemShortcuts.call(this, menuItem, type);
|
1836 | list.appendChild(menuItem);
|
1837 | },
|
1838 |
|
1839 | formatTime(time = 0, inverted = false) {
|
1840 |
|
1841 | if (!is.number(time)) {
|
1842 | return time;
|
1843 | }
|
1844 |
|
1845 |
|
1846 | const forceHours = getHours(this.duration) > 0;
|
1847 | return formatTime(time, forceHours, inverted);
|
1848 | },
|
1849 |
|
1850 | updateTimeDisplay(target = null, time = 0, inverted = false) {
|
1851 |
|
1852 | if (!is.element(target) || !is.number(time)) {
|
1853 | return;
|
1854 | }
|
1855 |
|
1856 |
|
1857 | target.innerText = controls.formatTime(time, inverted);
|
1858 | },
|
1859 |
|
1860 | updateVolume() {
|
1861 | if (!this.supported.ui) {
|
1862 | return;
|
1863 | }
|
1864 |
|
1865 |
|
1866 | if (is.element(this.elements.inputs.volume)) {
|
1867 | controls.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);
|
1868 | }
|
1869 |
|
1870 |
|
1871 | if (is.element(this.elements.buttons.mute)) {
|
1872 | this.elements.buttons.mute.pressed = this.muted || this.volume === 0;
|
1873 | }
|
1874 | },
|
1875 |
|
1876 | setRange(target, value = 0) {
|
1877 | if (!is.element(target)) {
|
1878 | return;
|
1879 | }
|
1880 |
|
1881 |
|
1882 | target.value = value;
|
1883 |
|
1884 |
|
1885 | controls.updateRangeFill.call(this, target);
|
1886 | },
|
1887 |
|
1888 | updateProgress(event) {
|
1889 | if (!this.supported.ui || !is.event(event)) {
|
1890 | return;
|
1891 | }
|
1892 | let value = 0;
|
1893 | const setProgress = (target, input) => {
|
1894 | const val = is.number(input) ? input : 0;
|
1895 | const progress = is.element(target) ? target : this.elements.display.buffer;
|
1896 |
|
1897 |
|
1898 | if (is.element(progress)) {
|
1899 | progress.value = val;
|
1900 |
|
1901 |
|
1902 | const label = progress.getElementsByTagName('span')[0];
|
1903 | if (is.element(label)) {
|
1904 | label.childNodes[0].nodeValue = val;
|
1905 | }
|
1906 | }
|
1907 | };
|
1908 | if (event) {
|
1909 | switch (event.type) {
|
1910 |
|
1911 | case 'timeupdate':
|
1912 | case 'seeking':
|
1913 | case 'seeked':
|
1914 | value = getPercentage(this.currentTime, this.duration);
|
1915 |
|
1916 |
|
1917 | if (event.type === 'timeupdate') {
|
1918 | controls.setRange.call(this, this.elements.inputs.seek, value);
|
1919 | }
|
1920 | break;
|
1921 |
|
1922 |
|
1923 | case 'playing':
|
1924 | case 'progress':
|
1925 | setProgress(this.elements.display.buffer, this.buffered * 100);
|
1926 | break;
|
1927 | }
|
1928 | }
|
1929 | },
|
1930 |
|
1931 | updateRangeFill(target) {
|
1932 |
|
1933 | const range = is.event(target) ? target.target : target;
|
1934 |
|
1935 |
|
1936 | if (!is.element(range) || range.getAttribute('type') !== 'range') {
|
1937 | return;
|
1938 | }
|
1939 |
|
1940 |
|
1941 | if (matches(range, this.config.selectors.inputs.seek)) {
|
1942 | range.setAttribute('aria-valuenow', this.currentTime);
|
1943 | const currentTime = controls.formatTime(this.currentTime);
|
1944 | const duration = controls.formatTime(this.duration);
|
1945 | const format = i18n.get('seekLabel', this.config);
|
1946 | range.setAttribute('aria-valuetext', format.replace('{currentTime}', currentTime).replace('{duration}', duration));
|
1947 | } else if (matches(range, this.config.selectors.inputs.volume)) {
|
1948 | const percent = range.value * 100;
|
1949 | range.setAttribute('aria-valuenow', percent);
|
1950 | range.setAttribute('aria-valuetext', `${percent.toFixed(1)}%`);
|
1951 | } else {
|
1952 | range.setAttribute('aria-valuenow', range.value);
|
1953 | }
|
1954 |
|
1955 |
|
1956 | if (!browser.isWebKit && !browser.isIPadOS) {
|
1957 | return;
|
1958 | }
|
1959 |
|
1960 |
|
1961 | range.style.setProperty('--value', `${range.value / range.max * 100}%`);
|
1962 | },
|
1963 |
|
1964 | updateSeekTooltip(event) {
|
1965 | var _this$config$markers, _this$config$markers$;
|
1966 |
|
1967 | if (!this.config.tooltips.seek || !is.element(this.elements.inputs.seek) || !is.element(this.elements.display.seekTooltip) || this.duration === 0) {
|
1968 | return;
|
1969 | }
|
1970 | const tipElement = this.elements.display.seekTooltip;
|
1971 | const visible = `${this.config.classNames.tooltip}--visible`;
|
1972 | const toggle = show => toggleClass(tipElement, visible, show);
|
1973 |
|
1974 |
|
1975 | if (this.touch) {
|
1976 | toggle(false);
|
1977 | return;
|
1978 | }
|
1979 |
|
1980 |
|
1981 | let percent = 0;
|
1982 | const clientRect = this.elements.progress.getBoundingClientRect();
|
1983 | if (is.event(event)) {
|
1984 | percent = 100 / clientRect.width * (event.pageX - clientRect.left);
|
1985 | } else if (hasClass(tipElement, visible)) {
|
1986 | percent = parseFloat(tipElement.style.left, 10);
|
1987 | } else {
|
1988 | return;
|
1989 | }
|
1990 |
|
1991 |
|
1992 | if (percent < 0) {
|
1993 | percent = 0;
|
1994 | } else if (percent > 100) {
|
1995 | percent = 100;
|
1996 | }
|
1997 | const time = this.duration / 100 * percent;
|
1998 |
|
1999 |
|
2000 | tipElement.innerText = controls.formatTime(time);
|
2001 |
|
2002 |
|
2003 | const point = (_this$config$markers = this.config.markers) === null || _this$config$markers === void 0 ? void 0 : (_this$config$markers$ = _this$config$markers.points) === null || _this$config$markers$ === void 0 ? void 0 : _this$config$markers$.find(({
|
2004 | time: t
|
2005 | }) => t === Math.round(time));
|
2006 |
|
2007 |
|
2008 | if (point) {
|
2009 | tipElement.insertAdjacentHTML('afterbegin', `${point.label}<br>`);
|
2010 | }
|
2011 |
|
2012 |
|
2013 | tipElement.style.left = `${percent}%`;
|
2014 |
|
2015 |
|
2016 |
|
2017 | if (is.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) {
|
2018 | toggle(event.type === 'mouseenter');
|
2019 | }
|
2020 | },
|
2021 |
|
2022 | timeUpdate(event) {
|
2023 |
|
2024 | const invert = !is.element(this.elements.display.duration) && this.config.invertTime;
|
2025 |
|
2026 |
|
2027 | controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);
|
2028 |
|
2029 |
|
2030 | if (event && event.type === 'timeupdate' && this.media.seeking) {
|
2031 | return;
|
2032 | }
|
2033 |
|
2034 |
|
2035 | controls.updateProgress.call(this, event);
|
2036 | },
|
2037 |
|
2038 | durationUpdate() {
|
2039 |
|
2040 | if (!this.supported.ui || !this.config.invertTime && this.currentTime) {
|
2041 | return;
|
2042 | }
|
2043 |
|
2044 |
|
2045 |
|
2046 |
|
2047 |
|
2048 | if (this.duration >= 2 ** 32) {
|
2049 | toggleHidden(this.elements.display.currentTime, true);
|
2050 | toggleHidden(this.elements.progress, true);
|
2051 | return;
|
2052 | }
|
2053 |
|
2054 |
|
2055 | if (is.element(this.elements.inputs.seek)) {
|
2056 | this.elements.inputs.seek.setAttribute('aria-valuemax', this.duration);
|
2057 | }
|
2058 |
|
2059 |
|
2060 | const hasDuration = is.element(this.elements.display.duration);
|
2061 |
|
2062 |
|
2063 | if (!hasDuration && this.config.displayDuration && this.paused) {
|
2064 | controls.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);
|
2065 | }
|
2066 |
|
2067 |
|
2068 | if (hasDuration) {
|
2069 | controls.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);
|
2070 | }
|
2071 | if (this.config.markers.enabled) {
|
2072 | controls.setMarkers.call(this);
|
2073 | }
|
2074 |
|
2075 |
|
2076 | controls.updateSeekTooltip.call(this);
|
2077 | },
|
2078 |
|
2079 | toggleMenuButton(setting, toggle) {
|
2080 | toggleHidden(this.elements.settings.buttons[setting], !toggle);
|
2081 | },
|
2082 |
|
2083 | updateSetting(setting, container, input) {
|
2084 | const pane = this.elements.settings.panels[setting];
|
2085 | let value = null;
|
2086 | let list = container;
|
2087 | if (setting === 'captions') {
|
2088 | value = this.currentTrack;
|
2089 | } else {
|
2090 | value = !is.empty(input) ? input : this[setting];
|
2091 |
|
2092 |
|
2093 | if (is.empty(value)) {
|
2094 | value = this.config[setting].default;
|
2095 | }
|
2096 |
|
2097 |
|
2098 | if (!is.empty(this.options[setting]) && !this.options[setting].includes(value)) {
|
2099 | this.debug.warn(`Unsupported value of '${value}' for ${setting}`);
|
2100 | return;
|
2101 | }
|
2102 |
|
2103 |
|
2104 | if (!this.config[setting].options.includes(value)) {
|
2105 | this.debug.warn(`Disabled value of '${value}' for ${setting}`);
|
2106 | return;
|
2107 | }
|
2108 | }
|
2109 |
|
2110 |
|
2111 | if (!is.element(list)) {
|
2112 | list = pane && pane.querySelector('[role="menu"]');
|
2113 | }
|
2114 |
|
2115 |
|
2116 | if (!is.element(list)) {
|
2117 | return;
|
2118 | }
|
2119 |
|
2120 |
|
2121 | const label = this.elements.settings.buttons[setting].querySelector(`.${this.config.classNames.menu.value}`);
|
2122 | label.innerHTML = controls.getLabel.call(this, setting, value);
|
2123 |
|
2124 |
|
2125 | const target = list && list.querySelector(`[value="${value}"]`);
|
2126 | if (is.element(target)) {
|
2127 | target.checked = true;
|
2128 | }
|
2129 | },
|
2130 |
|
2131 | getLabel(setting, value) {
|
2132 | switch (setting) {
|
2133 | case 'speed':
|
2134 | return value === 1 ? i18n.get('normal', this.config) : `${value}×`;
|
2135 | case 'quality':
|
2136 | if (is.number(value)) {
|
2137 | const label = i18n.get(`qualityLabel.${value}`, this.config);
|
2138 | if (!label.length) {
|
2139 | return `${value}p`;
|
2140 | }
|
2141 | return label;
|
2142 | }
|
2143 | return toTitleCase(value);
|
2144 | case 'captions':
|
2145 | return captions.getLabel.call(this);
|
2146 | default:
|
2147 | return null;
|
2148 | }
|
2149 | },
|
2150 |
|
2151 | setQualityMenu(options) {
|
2152 |
|
2153 | if (!is.element(this.elements.settings.panels.quality)) {
|
2154 | return;
|
2155 | }
|
2156 | const type = 'quality';
|
2157 | const list = this.elements.settings.panels.quality.querySelector('[role="menu"]');
|
2158 |
|
2159 |
|
2160 | if (is.array(options)) {
|
2161 | this.options.quality = dedupe(options).filter(quality => this.config.quality.options.includes(quality));
|
2162 | }
|
2163 |
|
2164 |
|
2165 | const toggle = !is.empty(this.options.quality) && this.options.quality.length > 1;
|
2166 | controls.toggleMenuButton.call(this, type, toggle);
|
2167 |
|
2168 |
|
2169 | emptyElement(list);
|
2170 |
|
2171 |
|
2172 | controls.checkMenu.call(this);
|
2173 |
|
2174 |
|
2175 | if (!toggle) {
|
2176 | return;
|
2177 | }
|
2178 |
|
2179 |
|
2180 | const getBadge = quality => {
|
2181 | const label = i18n.get(`qualityBadge.${quality}`, this.config);
|
2182 | if (!label.length) {
|
2183 | return null;
|
2184 | }
|
2185 | return controls.createBadge.call(this, label);
|
2186 | };
|
2187 |
|
2188 |
|
2189 | this.options.quality.sort((a, b) => {
|
2190 | const sorting = this.config.quality.options;
|
2191 | return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
2192 | }).forEach(quality => {
|
2193 | controls.createMenuItem.call(this, {
|
2194 | value: quality,
|
2195 | list,
|
2196 | type,
|
2197 | title: controls.getLabel.call(this, 'quality', quality),
|
2198 | badge: getBadge(quality)
|
2199 | });
|
2200 | });
|
2201 | controls.updateSetting.call(this, type, list);
|
2202 | },
|
2203 |
|
2204 | |
2205 |
|
2206 |
|
2207 |
|
2208 |
|
2209 |
|
2210 |
|
2211 |
|
2212 |
|
2213 |
|
2214 |
|
2215 |
|
2216 |
|
2217 |
|
2218 |
|
2219 |
|
2220 |
|
2221 |
|
2222 |
|
2223 |
|
2224 |
|
2225 |
|
2226 |
|
2227 |
|
2228 |
|
2229 |
|
2230 |
|
2231 |
|
2232 |
|
2233 |
|
2234 |
|
2235 |
|
2236 |
|
2237 |
|
2238 |
|
2239 |
|
2240 |
|
2241 |
|
2242 |
|
2243 | setCaptionsMenu() {
|
2244 |
|
2245 | if (!is.element(this.elements.settings.panels.captions)) {
|
2246 | return;
|
2247 | }
|
2248 |
|
2249 |
|
2250 | const type = 'captions';
|
2251 | const list = this.elements.settings.panels.captions.querySelector('[role="menu"]');
|
2252 | const tracks = captions.getTracks.call(this);
|
2253 | const toggle = Boolean(tracks.length);
|
2254 |
|
2255 |
|
2256 | controls.toggleMenuButton.call(this, type, toggle);
|
2257 |
|
2258 |
|
2259 | emptyElement(list);
|
2260 |
|
2261 |
|
2262 | controls.checkMenu.call(this);
|
2263 |
|
2264 |
|
2265 | if (!toggle) {
|
2266 | return;
|
2267 | }
|
2268 |
|
2269 |
|
2270 | const options = tracks.map((track, value) => ({
|
2271 | value,
|
2272 | checked: this.captions.toggled && this.currentTrack === value,
|
2273 | title: captions.getLabel.call(this, track),
|
2274 | badge: track.language && controls.createBadge.call(this, track.language.toUpperCase()),
|
2275 | list,
|
2276 | type: 'language'
|
2277 | }));
|
2278 |
|
2279 |
|
2280 | options.unshift({
|
2281 | value: -1,
|
2282 | checked: !this.captions.toggled,
|
2283 | title: i18n.get('disabled', this.config),
|
2284 | list,
|
2285 | type: 'language'
|
2286 | });
|
2287 |
|
2288 |
|
2289 | options.forEach(controls.createMenuItem.bind(this));
|
2290 | controls.updateSetting.call(this, type, list);
|
2291 | },
|
2292 |
|
2293 | setSpeedMenu() {
|
2294 |
|
2295 | if (!is.element(this.elements.settings.panels.speed)) {
|
2296 | return;
|
2297 | }
|
2298 | const type = 'speed';
|
2299 | const list = this.elements.settings.panels.speed.querySelector('[role="menu"]');
|
2300 |
|
2301 |
|
2302 | this.options.speed = this.options.speed.filter(o => o >= this.minimumSpeed && o <= this.maximumSpeed);
|
2303 |
|
2304 |
|
2305 | const toggle = !is.empty(this.options.speed) && this.options.speed.length > 1;
|
2306 | controls.toggleMenuButton.call(this, type, toggle);
|
2307 |
|
2308 |
|
2309 | emptyElement(list);
|
2310 |
|
2311 |
|
2312 | controls.checkMenu.call(this);
|
2313 |
|
2314 |
|
2315 | if (!toggle) {
|
2316 | return;
|
2317 | }
|
2318 |
|
2319 |
|
2320 | this.options.speed.forEach(speed => {
|
2321 | controls.createMenuItem.call(this, {
|
2322 | value: speed,
|
2323 | list,
|
2324 | type,
|
2325 | title: controls.getLabel.call(this, 'speed', speed)
|
2326 | });
|
2327 | });
|
2328 | controls.updateSetting.call(this, type, list);
|
2329 | },
|
2330 |
|
2331 | checkMenu() {
|
2332 | const {
|
2333 | buttons
|
2334 | } = this.elements.settings;
|
2335 | const visible = !is.empty(buttons) && Object.values(buttons).some(button => !button.hidden);
|
2336 | toggleHidden(this.elements.settings.menu, !visible);
|
2337 | },
|
2338 |
|
2339 | focusFirstMenuItem(pane, focusVisible = false) {
|
2340 | if (this.elements.settings.popup.hidden) {
|
2341 | return;
|
2342 | }
|
2343 | let target = pane;
|
2344 | if (!is.element(target)) {
|
2345 | target = Object.values(this.elements.settings.panels).find(p => !p.hidden);
|
2346 | }
|
2347 | const firstItem = target.querySelector('[role^="menuitem"]');
|
2348 | setFocus.call(this, firstItem, focusVisible);
|
2349 | },
|
2350 |
|
2351 | toggleMenu(input) {
|
2352 | const {
|
2353 | popup
|
2354 | } = this.elements.settings;
|
2355 | const button = this.elements.buttons.settings;
|
2356 |
|
2357 |
|
2358 | if (!is.element(popup) || !is.element(button)) {
|
2359 | return;
|
2360 | }
|
2361 |
|
2362 |
|
2363 | const {
|
2364 | hidden
|
2365 | } = popup;
|
2366 | let show = hidden;
|
2367 | if (is.boolean(input)) {
|
2368 | show = input;
|
2369 | } else if (is.keyboardEvent(input) && input.key === 'Escape') {
|
2370 | show = false;
|
2371 | } else if (is.event(input)) {
|
2372 |
|
2373 |
|
2374 | const target = is.function(input.composedPath) ? input.composedPath()[0] : input.target;
|
2375 | const isMenuItem = popup.contains(target);
|
2376 |
|
2377 |
|
2378 |
|
2379 |
|
2380 | if (isMenuItem || !isMenuItem && input.target !== button && show) {
|
2381 | return;
|
2382 | }
|
2383 | }
|
2384 |
|
2385 |
|
2386 | button.setAttribute('aria-expanded', show);
|
2387 |
|
2388 |
|
2389 | toggleHidden(popup, !show);
|
2390 |
|
2391 |
|
2392 | toggleClass(this.elements.container, this.config.classNames.menu.open, show);
|
2393 |
|
2394 |
|
2395 | if (show && is.keyboardEvent(input)) {
|
2396 | controls.focusFirstMenuItem.call(this, null, true);
|
2397 | } else if (!show && !hidden) {
|
2398 |
|
2399 | setFocus.call(this, button, is.keyboardEvent(input));
|
2400 | }
|
2401 | },
|
2402 |
|
2403 | getMenuSize(tab) {
|
2404 | const clone = tab.cloneNode(true);
|
2405 | clone.style.position = 'absolute';
|
2406 | clone.style.opacity = 0;
|
2407 | clone.removeAttribute('hidden');
|
2408 |
|
2409 |
|
2410 | tab.parentNode.appendChild(clone);
|
2411 |
|
2412 |
|
2413 | const width = clone.scrollWidth;
|
2414 | const height = clone.scrollHeight;
|
2415 |
|
2416 |
|
2417 | removeElement(clone);
|
2418 | return {
|
2419 | width,
|
2420 | height
|
2421 | };
|
2422 | },
|
2423 |
|
2424 | showMenuPanel(type = '', focusVisible = false) {
|
2425 | const target = this.elements.container.querySelector(`#plyr-settings-${this.id}-${type}`);
|
2426 |
|
2427 |
|
2428 | if (!is.element(target)) {
|
2429 | return;
|
2430 | }
|
2431 |
|
2432 |
|
2433 | const container = target.parentNode;
|
2434 | const current = Array.from(container.children).find(node => !node.hidden);
|
2435 |
|
2436 |
|
2437 | if (support.transitions && !support.reducedMotion) {
|
2438 |
|
2439 | container.style.width = `${current.scrollWidth}px`;
|
2440 | container.style.height = `${current.scrollHeight}px`;
|
2441 |
|
2442 |
|
2443 | const size = controls.getMenuSize.call(this, target);
|
2444 |
|
2445 |
|
2446 | const restore = event => {
|
2447 |
|
2448 | if (event.target !== container || !['width', 'height'].includes(event.propertyName)) {
|
2449 | return;
|
2450 | }
|
2451 |
|
2452 |
|
2453 | container.style.width = '';
|
2454 | container.style.height = '';
|
2455 |
|
2456 |
|
2457 | off.call(this, container, transitionEndEvent, restore);
|
2458 | };
|
2459 |
|
2460 |
|
2461 | on.call(this, container, transitionEndEvent, restore);
|
2462 |
|
2463 |
|
2464 | container.style.width = `${size.width}px`;
|
2465 | container.style.height = `${size.height}px`;
|
2466 | }
|
2467 |
|
2468 |
|
2469 | toggleHidden(current, true);
|
2470 |
|
2471 |
|
2472 | toggleHidden(target, false);
|
2473 |
|
2474 |
|
2475 | controls.focusFirstMenuItem.call(this, target, focusVisible);
|
2476 | },
|
2477 |
|
2478 | setDownloadUrl() {
|
2479 | const button = this.elements.buttons.download;
|
2480 |
|
2481 |
|
2482 | if (!is.element(button)) {
|
2483 | return;
|
2484 | }
|
2485 |
|
2486 |
|
2487 | button.setAttribute('href', this.download);
|
2488 | },
|
2489 |
|
2490 | create(data) {
|
2491 | const {
|
2492 | bindMenuItemShortcuts,
|
2493 | createButton,
|
2494 | createProgress,
|
2495 | createRange,
|
2496 | createTime,
|
2497 | setQualityMenu,
|
2498 | setSpeedMenu,
|
2499 | showMenuPanel
|
2500 | } = controls;
|
2501 | this.elements.controls = null;
|
2502 |
|
2503 |
|
2504 | if (is.array(this.config.controls) && this.config.controls.includes('play-large')) {
|
2505 | this.elements.container.appendChild(createButton.call(this, 'play-large'));
|
2506 | }
|
2507 |
|
2508 |
|
2509 | const container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper));
|
2510 | this.elements.controls = container;
|
2511 |
|
2512 |
|
2513 | const defaultAttributes = {
|
2514 | class: 'plyr__controls__item'
|
2515 | };
|
2516 |
|
2517 |
|
2518 | dedupe(is.array(this.config.controls) ? this.config.controls : []).forEach(control => {
|
2519 |
|
2520 | if (control === 'restart') {
|
2521 | container.appendChild(createButton.call(this, 'restart', defaultAttributes));
|
2522 | }
|
2523 |
|
2524 |
|
2525 | if (control === 'rewind') {
|
2526 | container.appendChild(createButton.call(this, 'rewind', defaultAttributes));
|
2527 | }
|
2528 |
|
2529 |
|
2530 | if (control === 'play') {
|
2531 | container.appendChild(createButton.call(this, 'play', defaultAttributes));
|
2532 | }
|
2533 |
|
2534 |
|
2535 | if (control === 'fast-forward') {
|
2536 | container.appendChild(createButton.call(this, 'fast-forward', defaultAttributes));
|
2537 | }
|
2538 |
|
2539 |
|
2540 | if (control === 'progress') {
|
2541 | const progressContainer = createElement('div', {
|
2542 | class: `${defaultAttributes.class} plyr__progress__container`
|
2543 | });
|
2544 | const progress = createElement('div', getAttributesFromSelector(this.config.selectors.progress));
|
2545 |
|
2546 |
|
2547 | progress.appendChild(createRange.call(this, 'seek', {
|
2548 | id: `plyr-seek-${data.id}`
|
2549 | }));
|
2550 |
|
2551 |
|
2552 | progress.appendChild(createProgress.call(this, 'buffer'));
|
2553 |
|
2554 |
|
2555 |
|
2556 |
|
2557 | if (this.config.tooltips.seek) {
|
2558 | const tooltip = createElement('span', {
|
2559 | class: this.config.classNames.tooltip
|
2560 | }, '00:00');
|
2561 | progress.appendChild(tooltip);
|
2562 | this.elements.display.seekTooltip = tooltip;
|
2563 | }
|
2564 | this.elements.progress = progress;
|
2565 | progressContainer.appendChild(this.elements.progress);
|
2566 | container.appendChild(progressContainer);
|
2567 | }
|
2568 |
|
2569 |
|
2570 | if (control === 'current-time') {
|
2571 | container.appendChild(createTime.call(this, 'currentTime', defaultAttributes));
|
2572 | }
|
2573 |
|
2574 |
|
2575 | if (control === 'duration') {
|
2576 | container.appendChild(createTime.call(this, 'duration', defaultAttributes));
|
2577 | }
|
2578 |
|
2579 |
|
2580 | if (control === 'mute' || control === 'volume') {
|
2581 | let {
|
2582 | volume
|
2583 | } = this.elements;
|
2584 |
|
2585 |
|
2586 | if (!is.element(volume) || !container.contains(volume)) {
|
2587 | volume = createElement('div', extend({}, defaultAttributes, {
|
2588 | class: `${defaultAttributes.class} plyr__volume`.trim()
|
2589 | }));
|
2590 | this.elements.volume = volume;
|
2591 | container.appendChild(volume);
|
2592 | }
|
2593 |
|
2594 |
|
2595 | if (control === 'mute') {
|
2596 | volume.appendChild(createButton.call(this, 'mute'));
|
2597 | }
|
2598 |
|
2599 |
|
2600 |
|
2601 |
|
2602 | if (control === 'volume' && !browser.isIos && !browser.isIPadOS) {
|
2603 |
|
2604 | const attributes = {
|
2605 | max: 1,
|
2606 | step: 0.05,
|
2607 | value: this.config.volume
|
2608 | };
|
2609 |
|
2610 |
|
2611 | volume.appendChild(createRange.call(this, 'volume', extend(attributes, {
|
2612 | id: `plyr-volume-${data.id}`
|
2613 | })));
|
2614 | }
|
2615 | }
|
2616 |
|
2617 |
|
2618 | if (control === 'captions') {
|
2619 | container.appendChild(createButton.call(this, 'captions', defaultAttributes));
|
2620 | }
|
2621 |
|
2622 |
|
2623 | if (control === 'settings' && !is.empty(this.config.settings)) {
|
2624 | const wrapper = createElement('div', extend({}, defaultAttributes, {
|
2625 | class: `${defaultAttributes.class} plyr__menu`.trim(),
|
2626 | hidden: ''
|
2627 | }));
|
2628 | wrapper.appendChild(createButton.call(this, 'settings', {
|
2629 | 'aria-haspopup': true,
|
2630 | 'aria-controls': `plyr-settings-${data.id}`,
|
2631 | 'aria-expanded': false
|
2632 | }));
|
2633 | const popup = createElement('div', {
|
2634 | class: 'plyr__menu__container',
|
2635 | id: `plyr-settings-${data.id}`,
|
2636 | hidden: ''
|
2637 | });
|
2638 | const inner = createElement('div');
|
2639 | const home = createElement('div', {
|
2640 | id: `plyr-settings-${data.id}-home`
|
2641 | });
|
2642 |
|
2643 |
|
2644 | const menu = createElement('div', {
|
2645 | role: 'menu'
|
2646 | });
|
2647 | home.appendChild(menu);
|
2648 | inner.appendChild(home);
|
2649 | this.elements.settings.panels.home = home;
|
2650 |
|
2651 |
|
2652 | this.config.settings.forEach(type => {
|
2653 |
|
2654 | const menuItem = createElement('button', extend(getAttributesFromSelector(this.config.selectors.buttons.settings), {
|
2655 | type: 'button',
|
2656 | class: `${this.config.classNames.control} ${this.config.classNames.control}--forward`,
|
2657 | role: 'menuitem',
|
2658 | 'aria-haspopup': true,
|
2659 | hidden: ''
|
2660 | }));
|
2661 |
|
2662 |
|
2663 | bindMenuItemShortcuts.call(this, menuItem, type);
|
2664 |
|
2665 |
|
2666 | on.call(this, menuItem, 'click', () => {
|
2667 | showMenuPanel.call(this, type, false);
|
2668 | });
|
2669 | const flex = createElement('span', null, i18n.get(type, this.config));
|
2670 | const value = createElement('span', {
|
2671 | class: this.config.classNames.menu.value
|
2672 | });
|
2673 |
|
2674 |
|
2675 | value.innerHTML = data[type];
|
2676 | flex.appendChild(value);
|
2677 | menuItem.appendChild(flex);
|
2678 | menu.appendChild(menuItem);
|
2679 |
|
2680 |
|
2681 | const pane = createElement('div', {
|
2682 | id: `plyr-settings-${data.id}-${type}`,
|
2683 | hidden: ''
|
2684 | });
|
2685 |
|
2686 |
|
2687 | const backButton = createElement('button', {
|
2688 | type: 'button',
|
2689 | class: `${this.config.classNames.control} ${this.config.classNames.control}--back`
|
2690 | });
|
2691 |
|
2692 |
|
2693 | backButton.appendChild(createElement('span', {
|
2694 | 'aria-hidden': true
|
2695 | }, i18n.get(type, this.config)));
|
2696 |
|
2697 |
|
2698 | backButton.appendChild(createElement('span', {
|
2699 | class: this.config.classNames.hidden
|
2700 | }, i18n.get('menuBack', this.config)));
|
2701 |
|
2702 |
|
2703 | on.call(this, pane, 'keydown', event => {
|
2704 | if (event.key !== 'ArrowLeft') return;
|
2705 |
|
2706 |
|
2707 | event.preventDefault();
|
2708 | event.stopPropagation();
|
2709 |
|
2710 |
|
2711 | showMenuPanel.call(this, 'home', true);
|
2712 | }, false);
|
2713 |
|
2714 |
|
2715 | on.call(this, backButton, 'click', () => {
|
2716 | showMenuPanel.call(this, 'home', false);
|
2717 | });
|
2718 |
|
2719 |
|
2720 | pane.appendChild(backButton);
|
2721 |
|
2722 |
|
2723 | pane.appendChild(createElement('div', {
|
2724 | role: 'menu'
|
2725 | }));
|
2726 | inner.appendChild(pane);
|
2727 | this.elements.settings.buttons[type] = menuItem;
|
2728 | this.elements.settings.panels[type] = pane;
|
2729 | });
|
2730 | popup.appendChild(inner);
|
2731 | wrapper.appendChild(popup);
|
2732 | container.appendChild(wrapper);
|
2733 | this.elements.settings.popup = popup;
|
2734 | this.elements.settings.menu = wrapper;
|
2735 | }
|
2736 |
|
2737 |
|
2738 | if (control === 'pip' && support.pip) {
|
2739 | container.appendChild(createButton.call(this, 'pip', defaultAttributes));
|
2740 | }
|
2741 |
|
2742 |
|
2743 | if (control === 'airplay' && support.airplay) {
|
2744 | container.appendChild(createButton.call(this, 'airplay', defaultAttributes));
|
2745 | }
|
2746 |
|
2747 |
|
2748 | if (control === 'download') {
|
2749 | const attributes = extend({}, defaultAttributes, {
|
2750 | element: 'a',
|
2751 | href: this.download,
|
2752 | target: '_blank'
|
2753 | });
|
2754 |
|
2755 |
|
2756 | if (this.isHTML5) {
|
2757 | attributes.download = '';
|
2758 | }
|
2759 | const {
|
2760 | download
|
2761 | } = this.config.urls;
|
2762 | if (!is.url(download) && this.isEmbed) {
|
2763 | extend(attributes, {
|
2764 | icon: `logo-${this.provider}`,
|
2765 | label: this.provider
|
2766 | });
|
2767 | }
|
2768 | container.appendChild(createButton.call(this, 'download', attributes));
|
2769 | }
|
2770 |
|
2771 |
|
2772 | if (control === 'fullscreen') {
|
2773 | container.appendChild(createButton.call(this, 'fullscreen', defaultAttributes));
|
2774 | }
|
2775 | });
|
2776 |
|
2777 |
|
2778 | if (this.isHTML5) {
|
2779 | setQualityMenu.call(this, html5.getQualityOptions.call(this));
|
2780 | }
|
2781 | setSpeedMenu.call(this);
|
2782 | return container;
|
2783 | },
|
2784 |
|
2785 | inject() {
|
2786 |
|
2787 | if (this.config.loadSprite) {
|
2788 | const icon = controls.getIconUrl.call(this);
|
2789 |
|
2790 |
|
2791 | if (icon.cors) {
|
2792 | loadSprite(icon.url, 'sprite-plyr');
|
2793 | }
|
2794 | }
|
2795 |
|
2796 |
|
2797 | this.id = Math.floor(Math.random() * 10000);
|
2798 |
|
2799 |
|
2800 | let container = null;
|
2801 | this.elements.controls = null;
|
2802 |
|
2803 |
|
2804 | const props = {
|
2805 | id: this.id,
|
2806 | seektime: this.config.seekTime,
|
2807 | title: this.config.title
|
2808 | };
|
2809 | let update = true;
|
2810 |
|
2811 |
|
2812 | if (is.function(this.config.controls)) {
|
2813 | this.config.controls = this.config.controls.call(this, props);
|
2814 | }
|
2815 |
|
2816 |
|
2817 | if (!this.config.controls) {
|
2818 | this.config.controls = [];
|
2819 | }
|
2820 | if (is.element(this.config.controls) || is.string(this.config.controls)) {
|
2821 |
|
2822 | container = this.config.controls;
|
2823 | } else {
|
2824 |
|
2825 | container = controls.create.call(this, {
|
2826 | id: this.id,
|
2827 | seektime: this.config.seekTime,
|
2828 | speed: this.speed,
|
2829 | quality: this.quality,
|
2830 | captions: captions.getLabel.call(this)
|
2831 |
|
2832 |
|
2833 | });
|
2834 |
|
2835 | update = false;
|
2836 | }
|
2837 |
|
2838 |
|
2839 | const replace = input => {
|
2840 | let result = input;
|
2841 | Object.entries(props).forEach(([key, value]) => {
|
2842 | result = replaceAll(result, `{${key}}`, value);
|
2843 | });
|
2844 | return result;
|
2845 | };
|
2846 |
|
2847 |
|
2848 | if (update) {
|
2849 | if (is.string(this.config.controls)) {
|
2850 | container = replace(container);
|
2851 | }
|
2852 | }
|
2853 |
|
2854 |
|
2855 | let target;
|
2856 |
|
2857 |
|
2858 | if (is.string(this.config.selectors.controls.container)) {
|
2859 | target = document.querySelector(this.config.selectors.controls.container);
|
2860 | }
|
2861 |
|
2862 |
|
2863 | if (!is.element(target)) {
|
2864 | target = this.elements.container;
|
2865 | }
|
2866 |
|
2867 |
|
2868 | const insertMethod = is.element(container) ? 'insertAdjacentElement' : 'insertAdjacentHTML';
|
2869 | target[insertMethod]('afterbegin', container);
|
2870 |
|
2871 |
|
2872 | if (!is.element(this.elements.controls)) {
|
2873 | controls.findElements.call(this);
|
2874 | }
|
2875 |
|
2876 |
|
2877 | if (!is.empty(this.elements.buttons)) {
|
2878 | const addProperty = button => {
|
2879 | const className = this.config.classNames.controlPressed;
|
2880 | button.setAttribute('aria-pressed', 'false');
|
2881 | Object.defineProperty(button, 'pressed', {
|
2882 | configurable: true,
|
2883 | enumerable: true,
|
2884 | get() {
|
2885 | return hasClass(button, className);
|
2886 | },
|
2887 | set(pressed = false) {
|
2888 | toggleClass(button, className, pressed);
|
2889 | button.setAttribute('aria-pressed', pressed ? 'true' : 'false');
|
2890 | }
|
2891 | });
|
2892 | };
|
2893 |
|
2894 |
|
2895 | Object.values(this.elements.buttons).filter(Boolean).forEach(button => {
|
2896 | if (is.array(button) || is.nodeList(button)) {
|
2897 | Array.from(button).filter(Boolean).forEach(addProperty);
|
2898 | } else {
|
2899 | addProperty(button);
|
2900 | }
|
2901 | });
|
2902 | }
|
2903 |
|
2904 |
|
2905 | if (browser.isEdge) {
|
2906 | repaint(target);
|
2907 | }
|
2908 |
|
2909 |
|
2910 | if (this.config.tooltips.controls) {
|
2911 | const {
|
2912 | classNames,
|
2913 | selectors
|
2914 | } = this.config;
|
2915 | const selector = `${selectors.controls.wrapper} ${selectors.labels} .${classNames.hidden}`;
|
2916 | const labels = getElements.call(this, selector);
|
2917 | Array.from(labels).forEach(label => {
|
2918 | toggleClass(label, this.config.classNames.hidden, false);
|
2919 | toggleClass(label, this.config.classNames.tooltip, true);
|
2920 | });
|
2921 | }
|
2922 | },
|
2923 |
|
2924 | setMediaMetadata() {
|
2925 | try {
|
2926 | if ('mediaSession' in navigator) {
|
2927 | navigator.mediaSession.metadata = new window.MediaMetadata({
|
2928 | title: this.config.mediaMetadata.title,
|
2929 | artist: this.config.mediaMetadata.artist,
|
2930 | album: this.config.mediaMetadata.album,
|
2931 | artwork: this.config.mediaMetadata.artwork
|
2932 | });
|
2933 | }
|
2934 | } catch (_) {
|
2935 |
|
2936 | }
|
2937 | },
|
2938 |
|
2939 | setMarkers() {
|
2940 | var _this$config$markers2, _this$config$markers3;
|
2941 | if (!this.duration || this.elements.markers) return;
|
2942 |
|
2943 |
|
2944 | const points = (_this$config$markers2 = this.config.markers) === null || _this$config$markers2 === void 0 ? void 0 : (_this$config$markers3 = _this$config$markers2.points) === null || _this$config$markers3 === void 0 ? void 0 : _this$config$markers3.filter(({
|
2945 | time
|
2946 | }) => time > 0 && time < this.duration);
|
2947 | if (!(points !== null && points !== void 0 && points.length)) return;
|
2948 | const containerFragment = document.createDocumentFragment();
|
2949 | const pointsFragment = document.createDocumentFragment();
|
2950 | let tipElement = null;
|
2951 | const tipVisible = `${this.config.classNames.tooltip}--visible`;
|
2952 | const toggleTip = show => toggleClass(tipElement, tipVisible, show);
|
2953 |
|
2954 |
|
2955 | points.forEach(point => {
|
2956 | const markerElement = createElement('span', {
|
2957 | class: this.config.classNames.marker
|
2958 | }, '');
|
2959 | const left = `${point.time / this.duration * 100}%`;
|
2960 | if (tipElement) {
|
2961 |
|
2962 | markerElement.addEventListener('mouseenter', () => {
|
2963 | if (point.label) return;
|
2964 | tipElement.style.left = left;
|
2965 | tipElement.innerHTML = point.label;
|
2966 | toggleTip(true);
|
2967 | });
|
2968 |
|
2969 |
|
2970 | markerElement.addEventListener('mouseleave', () => {
|
2971 | toggleTip(false);
|
2972 | });
|
2973 | }
|
2974 | markerElement.addEventListener('click', () => {
|
2975 | this.currentTime = point.time;
|
2976 | });
|
2977 | markerElement.style.left = left;
|
2978 | pointsFragment.appendChild(markerElement);
|
2979 | });
|
2980 | containerFragment.appendChild(pointsFragment);
|
2981 |
|
2982 |
|
2983 | if (!this.config.tooltips.seek) {
|
2984 | tipElement = createElement('span', {
|
2985 | class: this.config.classNames.tooltip
|
2986 | }, '');
|
2987 | containerFragment.appendChild(tipElement);
|
2988 | }
|
2989 | this.elements.markers = {
|
2990 | points: pointsFragment,
|
2991 | tip: tipElement
|
2992 | };
|
2993 | this.elements.progress.appendChild(containerFragment);
|
2994 | }
|
2995 | };
|
2996 |
|
2997 |
|
2998 |
|
2999 | |
3000 |
|
3001 |
|
3002 |
|
3003 |
|
3004 | function parseUrl(input, safe = true) {
|
3005 | let url = input;
|
3006 | if (safe) {
|
3007 | const parser = document.createElement('a');
|
3008 | parser.href = url;
|
3009 | url = parser.href;
|
3010 | }
|
3011 | try {
|
3012 | return new URL(url);
|
3013 | } catch (_) {
|
3014 | return null;
|
3015 | }
|
3016 | }
|
3017 |
|
3018 |
|
3019 | function buildUrlParams(input) {
|
3020 | const params = new URLSearchParams();
|
3021 | if (is.object(input)) {
|
3022 | Object.entries(input).forEach(([key, value]) => {
|
3023 | params.set(key, value);
|
3024 | });
|
3025 | }
|
3026 | return params;
|
3027 | }
|
3028 |
|
3029 |
|
3030 | const captions = {
|
3031 |
|
3032 | setup() {
|
3033 |
|
3034 | if (!this.supported.ui) {
|
3035 | return;
|
3036 | }
|
3037 |
|
3038 |
|
3039 | if (!this.isVideo || this.isYouTube || this.isHTML5 && !support.textTracks) {
|
3040 |
|
3041 | if (is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
3042 | controls.setCaptionsMenu.call(this);
|
3043 | }
|
3044 | return;
|
3045 | }
|
3046 |
|
3047 |
|
3048 | if (!is.element(this.elements.captions)) {
|
3049 | this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions));
|
3050 | this.elements.captions.setAttribute('dir', 'auto');
|
3051 | insertAfter(this.elements.captions, this.elements.wrapper);
|
3052 | }
|
3053 |
|
3054 |
|
3055 |
|
3056 | if (browser.isIE && window.URL) {
|
3057 | const elements = this.media.querySelectorAll('track');
|
3058 | Array.from(elements).forEach(track => {
|
3059 | const src = track.getAttribute('src');
|
3060 | const url = parseUrl(src);
|
3061 | if (url !== null && url.hostname !== window.location.href.hostname && ['http:', 'https:'].includes(url.protocol)) {
|
3062 | fetch(src, 'blob').then(blob => {
|
3063 | track.setAttribute('src', window.URL.createObjectURL(blob));
|
3064 | }).catch(() => {
|
3065 | removeElement(track);
|
3066 | });
|
3067 | }
|
3068 | });
|
3069 | }
|
3070 |
|
3071 |
|
3072 |
|
3073 |
|
3074 |
|
3075 |
|
3076 |
|
3077 |
|
3078 | const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
|
3079 | const languages = dedupe(browserLanguages.map(language => language.split('-')[0]));
|
3080 | let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
|
3081 |
|
3082 |
|
3083 | if (language === 'auto') {
|
3084 | [language] = languages;
|
3085 | }
|
3086 | let active = this.storage.get('captions');
|
3087 | if (!is.boolean(active)) {
|
3088 | ({
|
3089 | active
|
3090 | } = this.config.captions);
|
3091 | }
|
3092 | Object.assign(this.captions, {
|
3093 | toggled: false,
|
3094 | active,
|
3095 | language,
|
3096 | languages
|
3097 | });
|
3098 |
|
3099 |
|
3100 | if (this.isHTML5) {
|
3101 | const trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
|
3102 | on.call(this, this.media.textTracks, trackEvents, captions.update.bind(this));
|
3103 | }
|
3104 |
|
3105 |
|
3106 | setTimeout(captions.update.bind(this), 0);
|
3107 | },
|
3108 |
|
3109 | update() {
|
3110 | const tracks = captions.getTracks.call(this, true);
|
3111 |
|
3112 | const {
|
3113 | active,
|
3114 | language,
|
3115 | meta,
|
3116 | currentTrackNode
|
3117 | } = this.captions;
|
3118 | const languageExists = Boolean(tracks.find(track => track.language === language));
|
3119 |
|
3120 |
|
3121 | if (this.isHTML5 && this.isVideo) {
|
3122 | tracks.filter(track => !meta.get(track)).forEach(track => {
|
3123 | this.debug.log('Track added', track);
|
3124 |
|
3125 |
|
3126 | meta.set(track, {
|
3127 | default: track.mode === 'showing'
|
3128 | });
|
3129 |
|
3130 |
|
3131 |
|
3132 |
|
3133 |
|
3134 | if (track.mode === 'showing') {
|
3135 |
|
3136 | track.mode = 'hidden';
|
3137 | }
|
3138 |
|
3139 |
|
3140 | on.call(this, track, 'cuechange', () => captions.updateCues.call(this));
|
3141 | });
|
3142 | }
|
3143 |
|
3144 |
|
3145 | if (languageExists && this.language !== language || !tracks.includes(currentTrackNode)) {
|
3146 | captions.setLanguage.call(this, language);
|
3147 | captions.toggle.call(this, active && languageExists);
|
3148 | }
|
3149 |
|
3150 |
|
3151 | if (this.elements) {
|
3152 | toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is.empty(tracks));
|
3153 | }
|
3154 |
|
3155 |
|
3156 | if (is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
3157 | controls.setCaptionsMenu.call(this);
|
3158 | }
|
3159 | },
|
3160 |
|
3161 |
|
3162 | toggle(input, passive = true) {
|
3163 |
|
3164 | if (!this.supported.ui) {
|
3165 | return;
|
3166 | }
|
3167 | const {
|
3168 | toggled
|
3169 | } = this.captions;
|
3170 | const activeClass = this.config.classNames.captions.active;
|
3171 |
|
3172 |
|
3173 | const active = is.nullOrUndefined(input) ? !toggled : input;
|
3174 |
|
3175 |
|
3176 | if (active !== toggled) {
|
3177 |
|
3178 | if (!passive) {
|
3179 | this.captions.active = active;
|
3180 | this.storage.set({
|
3181 | captions: active
|
3182 | });
|
3183 | }
|
3184 |
|
3185 |
|
3186 | if (!this.language && active && !passive) {
|
3187 | const tracks = captions.getTracks.call(this);
|
3188 | const track = captions.findTrack.call(this, [this.captions.language, ...this.captions.languages], true);
|
3189 |
|
3190 |
|
3191 | this.captions.language = track.language;
|
3192 |
|
3193 |
|
3194 | captions.set.call(this, tracks.indexOf(track));
|
3195 | return;
|
3196 | }
|
3197 |
|
3198 |
|
3199 | if (this.elements.buttons.captions) {
|
3200 | this.elements.buttons.captions.pressed = active;
|
3201 | }
|
3202 |
|
3203 |
|
3204 | toggleClass(this.elements.container, activeClass, active);
|
3205 | this.captions.toggled = active;
|
3206 |
|
3207 |
|
3208 | controls.updateSetting.call(this, 'captions');
|
3209 |
|
3210 |
|
3211 | triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
|
3212 | }
|
3213 |
|
3214 |
|
3215 |
|
3216 | setTimeout(() => {
|
3217 | if (active && this.captions.toggled) {
|
3218 | this.captions.currentTrackNode.mode = 'hidden';
|
3219 | }
|
3220 | });
|
3221 | },
|
3222 |
|
3223 |
|
3224 | set(index, passive = true) {
|
3225 | const tracks = captions.getTracks.call(this);
|
3226 |
|
3227 |
|
3228 | if (index === -1) {
|
3229 | captions.toggle.call(this, false, passive);
|
3230 | return;
|
3231 | }
|
3232 | if (!is.number(index)) {
|
3233 | this.debug.warn('Invalid caption argument', index);
|
3234 | return;
|
3235 | }
|
3236 | if (!(index in tracks)) {
|
3237 | this.debug.warn('Track not found', index);
|
3238 | return;
|
3239 | }
|
3240 | if (this.captions.currentTrack !== index) {
|
3241 | this.captions.currentTrack = index;
|
3242 | const track = tracks[index];
|
3243 | const {
|
3244 | language
|
3245 | } = track || {};
|
3246 |
|
3247 |
|
3248 | this.captions.currentTrackNode = track;
|
3249 |
|
3250 |
|
3251 | controls.updateSetting.call(this, 'captions');
|
3252 |
|
3253 |
|
3254 | if (!passive) {
|
3255 | this.captions.language = language;
|
3256 | this.storage.set({
|
3257 | language
|
3258 | });
|
3259 | }
|
3260 |
|
3261 |
|
3262 | if (this.isVimeo) {
|
3263 | this.embed.enableTextTrack(language);
|
3264 | }
|
3265 |
|
3266 |
|
3267 | triggerEvent.call(this, this.media, 'languagechange');
|
3268 | }
|
3269 |
|
3270 |
|
3271 | captions.toggle.call(this, true, passive);
|
3272 | if (this.isHTML5 && this.isVideo) {
|
3273 |
|
3274 | captions.updateCues.call(this);
|
3275 | }
|
3276 | },
|
3277 |
|
3278 |
|
3279 | setLanguage(input, passive = true) {
|
3280 | if (!is.string(input)) {
|
3281 | this.debug.warn('Invalid language argument', input);
|
3282 | return;
|
3283 | }
|
3284 |
|
3285 | const language = input.toLowerCase();
|
3286 | this.captions.language = language;
|
3287 |
|
3288 |
|
3289 | const tracks = captions.getTracks.call(this);
|
3290 | const track = captions.findTrack.call(this, [language]);
|
3291 | captions.set.call(this, tracks.indexOf(track), passive);
|
3292 | },
|
3293 |
|
3294 |
|
3295 |
|
3296 | getTracks(update = false) {
|
3297 |
|
3298 | const tracks = Array.from((this.media || {}).textTracks || []);
|
3299 |
|
3300 |
|
3301 | return tracks.filter(track => !this.isHTML5 || update || this.captions.meta.has(track)).filter(track => ['captions', 'subtitles'].includes(track.kind));
|
3302 | },
|
3303 |
|
3304 | findTrack(languages, force = false) {
|
3305 | const tracks = captions.getTracks.call(this);
|
3306 | const sortIsDefault = track => Number((this.captions.meta.get(track) || {}).default);
|
3307 | const sorted = Array.from(tracks).sort((a, b) => sortIsDefault(b) - sortIsDefault(a));
|
3308 | let track;
|
3309 | languages.every(language => {
|
3310 | track = sorted.find(t => t.language === language);
|
3311 | return !track;
|
3312 | });
|
3313 |
|
3314 |
|
3315 | return track || (force ? sorted[0] : undefined);
|
3316 | },
|
3317 |
|
3318 | getCurrentTrack() {
|
3319 | return captions.getTracks.call(this)[this.currentTrack];
|
3320 | },
|
3321 |
|
3322 | getLabel(track) {
|
3323 | let currentTrack = track;
|
3324 | if (!is.track(currentTrack) && support.textTracks && this.captions.toggled) {
|
3325 | currentTrack = captions.getCurrentTrack.call(this);
|
3326 | }
|
3327 | if (is.track(currentTrack)) {
|
3328 | if (!is.empty(currentTrack.label)) {
|
3329 | return currentTrack.label;
|
3330 | }
|
3331 | if (!is.empty(currentTrack.language)) {
|
3332 | return track.language.toUpperCase();
|
3333 | }
|
3334 | return i18n.get('enabled', this.config);
|
3335 | }
|
3336 | return i18n.get('disabled', this.config);
|
3337 | },
|
3338 |
|
3339 |
|
3340 | updateCues(input) {
|
3341 |
|
3342 | if (!this.supported.ui) {
|
3343 | return;
|
3344 | }
|
3345 | if (!is.element(this.elements.captions)) {
|
3346 | this.debug.warn('No captions element to render to');
|
3347 | return;
|
3348 | }
|
3349 |
|
3350 |
|
3351 | if (!is.nullOrUndefined(input) && !Array.isArray(input)) {
|
3352 | this.debug.warn('updateCues: Invalid input', input);
|
3353 | return;
|
3354 | }
|
3355 | let cues = input;
|
3356 |
|
3357 |
|
3358 | if (!cues) {
|
3359 | const track = captions.getCurrentTrack.call(this);
|
3360 | cues = Array.from((track || {}).activeCues || []).map(cue => cue.getCueAsHTML()).map(getHTML);
|
3361 | }
|
3362 |
|
3363 |
|
3364 | const content = cues.map(cueText => cueText.trim()).join('\n');
|
3365 | const changed = content !== this.elements.captions.innerHTML;
|
3366 | if (changed) {
|
3367 |
|
3368 | emptyElement(this.elements.captions);
|
3369 | const caption = createElement('span', getAttributesFromSelector(this.config.selectors.caption));
|
3370 | caption.innerHTML = content;
|
3371 | this.elements.captions.appendChild(caption);
|
3372 |
|
3373 |
|
3374 | triggerEvent.call(this, this.media, 'cuechange');
|
3375 | }
|
3376 | }
|
3377 | };
|
3378 |
|
3379 |
|
3380 |
|
3381 |
|
3382 |
|
3383 | const defaults = {
|
3384 |
|
3385 | enabled: true,
|
3386 |
|
3387 | title: '',
|
3388 |
|
3389 | debug: false,
|
3390 |
|
3391 | autoplay: false,
|
3392 |
|
3393 | autopause: true,
|
3394 |
|
3395 | playsinline: true,
|
3396 |
|
3397 | seekTime: 10,
|
3398 |
|
3399 | volume: 1,
|
3400 | muted: false,
|
3401 |
|
3402 | duration: null,
|
3403 |
|
3404 |
|
3405 | displayDuration: true,
|
3406 |
|
3407 | invertTime: true,
|
3408 |
|
3409 | toggleInvert: true,
|
3410 |
|
3411 |
|
3412 | ratio: null,
|
3413 |
|
3414 | clickToPlay: true,
|
3415 |
|
3416 | hideControls: true,
|
3417 |
|
3418 | resetOnEnd: false,
|
3419 |
|
3420 | disableContextMenu: true,
|
3421 |
|
3422 | loadSprite: true,
|
3423 | iconPrefix: 'plyr',
|
3424 | iconUrl: 'https://cdn.plyr.io/3.7.8/plyr.svg',
|
3425 |
|
3426 | blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
3427 |
|
3428 | quality: {
|
3429 | default: 576,
|
3430 |
|
3431 | options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
|
3432 | forced: false,
|
3433 | onChange: null
|
3434 | },
|
3435 |
|
3436 | loop: {
|
3437 | active: false
|
3438 |
|
3439 |
|
3440 | },
|
3441 |
|
3442 |
|
3443 | speed: {
|
3444 | selected: 1,
|
3445 |
|
3446 | options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4]
|
3447 | },
|
3448 |
|
3449 | keyboard: {
|
3450 | focused: true,
|
3451 | global: false
|
3452 | },
|
3453 |
|
3454 | tooltips: {
|
3455 | controls: false,
|
3456 | seek: true
|
3457 | },
|
3458 |
|
3459 | captions: {
|
3460 | active: false,
|
3461 | language: 'auto',
|
3462 |
|
3463 |
|
3464 | update: false
|
3465 | },
|
3466 |
|
3467 | fullscreen: {
|
3468 | enabled: true,
|
3469 |
|
3470 | fallback: true,
|
3471 |
|
3472 | iosNative: false
|
3473 |
|
3474 |
|
3475 |
|
3476 | },
|
3477 |
|
3478 |
|
3479 | storage: {
|
3480 | enabled: true,
|
3481 | key: 'plyr'
|
3482 | },
|
3483 |
|
3484 | controls: ['play-large',
|
3485 |
|
3486 |
|
3487 | 'play',
|
3488 |
|
3489 | 'progress', 'current-time',
|
3490 |
|
3491 | 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay',
|
3492 |
|
3493 | 'fullscreen'],
|
3494 | settings: ['captions', 'quality', 'speed'],
|
3495 |
|
3496 | i18n: {
|
3497 | restart: 'Restart',
|
3498 | rewind: 'Rewind {seektime}s',
|
3499 | play: 'Play',
|
3500 | pause: 'Pause',
|
3501 | fastForward: 'Forward {seektime}s',
|
3502 | seek: 'Seek',
|
3503 | seekLabel: '{currentTime} of {duration}',
|
3504 | played: 'Played',
|
3505 | buffered: 'Buffered',
|
3506 | currentTime: 'Current time',
|
3507 | duration: 'Duration',
|
3508 | volume: 'Volume',
|
3509 | mute: 'Mute',
|
3510 | unmute: 'Unmute',
|
3511 | enableCaptions: 'Enable captions',
|
3512 | disableCaptions: 'Disable captions',
|
3513 | download: 'Download',
|
3514 | enterFullscreen: 'Enter fullscreen',
|
3515 | exitFullscreen: 'Exit fullscreen',
|
3516 | frameTitle: 'Player for {title}',
|
3517 | captions: 'Captions',
|
3518 | settings: 'Settings',
|
3519 | pip: 'PIP',
|
3520 | menuBack: 'Go back to previous menu',
|
3521 | speed: 'Speed',
|
3522 | normal: 'Normal',
|
3523 | quality: 'Quality',
|
3524 | loop: 'Loop',
|
3525 | start: 'Start',
|
3526 | end: 'End',
|
3527 | all: 'All',
|
3528 | reset: 'Reset',
|
3529 | disabled: 'Disabled',
|
3530 | enabled: 'Enabled',
|
3531 | advertisement: 'Ad',
|
3532 | qualityBadge: {
|
3533 | 2160: '4K',
|
3534 | 1440: 'HD',
|
3535 | 1080: 'HD',
|
3536 | 720: 'HD',
|
3537 | 576: 'SD',
|
3538 | 480: 'SD'
|
3539 | }
|
3540 | },
|
3541 |
|
3542 | urls: {
|
3543 | download: null,
|
3544 | vimeo: {
|
3545 | sdk: 'https://player.vimeo.com/api/player.js',
|
3546 | iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
3547 | api: 'https://vimeo.com/api/oembed.json?url={0}'
|
3548 | },
|
3549 | youtube: {
|
3550 | sdk: 'https://www.youtube.com/iframe_api',
|
3551 | api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}'
|
3552 | },
|
3553 | googleIMA: {
|
3554 | sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js'
|
3555 | }
|
3556 | },
|
3557 |
|
3558 | listeners: {
|
3559 | seek: null,
|
3560 | play: null,
|
3561 | pause: null,
|
3562 | restart: null,
|
3563 | rewind: null,
|
3564 | fastForward: null,
|
3565 | mute: null,
|
3566 | volume: null,
|
3567 | captions: null,
|
3568 | download: null,
|
3569 | fullscreen: null,
|
3570 | pip: null,
|
3571 | airplay: null,
|
3572 | speed: null,
|
3573 | quality: null,
|
3574 | loop: null,
|
3575 | language: null
|
3576 | },
|
3577 |
|
3578 | events: [
|
3579 |
|
3580 |
|
3581 | 'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange',
|
3582 |
|
3583 | 'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready',
|
3584 |
|
3585 | 'statechange',
|
3586 |
|
3587 | 'qualitychange',
|
3588 |
|
3589 | 'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
|
3590 |
|
3591 |
|
3592 | selectors: {
|
3593 | editable: 'input, textarea, select, [contenteditable]',
|
3594 | container: '.plyr',
|
3595 | controls: {
|
3596 | container: null,
|
3597 | wrapper: '.plyr__controls'
|
3598 | },
|
3599 | labels: '[data-plyr]',
|
3600 | buttons: {
|
3601 | play: '[data-plyr="play"]',
|
3602 | pause: '[data-plyr="pause"]',
|
3603 | restart: '[data-plyr="restart"]',
|
3604 | rewind: '[data-plyr="rewind"]',
|
3605 | fastForward: '[data-plyr="fast-forward"]',
|
3606 | mute: '[data-plyr="mute"]',
|
3607 | captions: '[data-plyr="captions"]',
|
3608 | download: '[data-plyr="download"]',
|
3609 | fullscreen: '[data-plyr="fullscreen"]',
|
3610 | pip: '[data-plyr="pip"]',
|
3611 | airplay: '[data-plyr="airplay"]',
|
3612 | settings: '[data-plyr="settings"]',
|
3613 | loop: '[data-plyr="loop"]'
|
3614 | },
|
3615 | inputs: {
|
3616 | seek: '[data-plyr="seek"]',
|
3617 | volume: '[data-plyr="volume"]',
|
3618 | speed: '[data-plyr="speed"]',
|
3619 | language: '[data-plyr="language"]',
|
3620 | quality: '[data-plyr="quality"]'
|
3621 | },
|
3622 | display: {
|
3623 | currentTime: '.plyr__time--current',
|
3624 | duration: '.plyr__time--duration',
|
3625 | buffer: '.plyr__progress__buffer',
|
3626 | loop: '.plyr__progress__loop',
|
3627 |
|
3628 | volume: '.plyr__volume--display'
|
3629 | },
|
3630 | progress: '.plyr__progress',
|
3631 | captions: '.plyr__captions',
|
3632 | caption: '.plyr__caption'
|
3633 | },
|
3634 |
|
3635 | classNames: {
|
3636 | type: 'plyr--{0}',
|
3637 | provider: 'plyr--{0}',
|
3638 | video: 'plyr__video-wrapper',
|
3639 | embed: 'plyr__video-embed',
|
3640 | videoFixedRatio: 'plyr__video-wrapper--fixed-ratio',
|
3641 | embedContainer: 'plyr__video-embed__container',
|
3642 | poster: 'plyr__poster',
|
3643 | posterEnabled: 'plyr__poster-enabled',
|
3644 | ads: 'plyr__ads',
|
3645 | control: 'plyr__control',
|
3646 | controlPressed: 'plyr__control--pressed',
|
3647 | playing: 'plyr--playing',
|
3648 | paused: 'plyr--paused',
|
3649 | stopped: 'plyr--stopped',
|
3650 | loading: 'plyr--loading',
|
3651 | hover: 'plyr--hover',
|
3652 | tooltip: 'plyr__tooltip',
|
3653 | cues: 'plyr__cues',
|
3654 | marker: 'plyr__progress__marker',
|
3655 | hidden: 'plyr__sr-only',
|
3656 | hideControls: 'plyr--hide-controls',
|
3657 | isTouch: 'plyr--is-touch',
|
3658 | uiSupported: 'plyr--full-ui',
|
3659 | noTransition: 'plyr--no-transition',
|
3660 | display: {
|
3661 | time: 'plyr__time'
|
3662 | },
|
3663 | menu: {
|
3664 | value: 'plyr__menu__value',
|
3665 | badge: 'plyr__badge',
|
3666 | open: 'plyr--menu-open'
|
3667 | },
|
3668 | captions: {
|
3669 | enabled: 'plyr--captions-enabled',
|
3670 | active: 'plyr--captions-active'
|
3671 | },
|
3672 | fullscreen: {
|
3673 | enabled: 'plyr--fullscreen-enabled',
|
3674 | fallback: 'plyr--fullscreen-fallback'
|
3675 | },
|
3676 | pip: {
|
3677 | supported: 'plyr--pip-supported',
|
3678 | active: 'plyr--pip-active'
|
3679 | },
|
3680 | airplay: {
|
3681 | supported: 'plyr--airplay-supported',
|
3682 | active: 'plyr--airplay-active'
|
3683 | },
|
3684 | previewThumbnails: {
|
3685 |
|
3686 | thumbContainer: 'plyr__preview-thumb',
|
3687 | thumbContainerShown: 'plyr__preview-thumb--is-shown',
|
3688 | imageContainer: 'plyr__preview-thumb__image-container',
|
3689 | timeContainer: 'plyr__preview-thumb__time-container',
|
3690 |
|
3691 | scrubbingContainer: 'plyr__preview-scrubbing',
|
3692 | scrubbingContainerShown: 'plyr__preview-scrubbing--is-shown'
|
3693 | }
|
3694 | },
|
3695 |
|
3696 | attributes: {
|
3697 | embed: {
|
3698 | provider: 'data-plyr-provider',
|
3699 | id: 'data-plyr-embed-id',
|
3700 | hash: 'data-plyr-embed-hash'
|
3701 | }
|
3702 | },
|
3703 |
|
3704 |
|
3705 | ads: {
|
3706 | enabled: false,
|
3707 | publisherId: '',
|
3708 | tagUrl: ''
|
3709 | },
|
3710 |
|
3711 | previewThumbnails: {
|
3712 | enabled: false,
|
3713 | src: ''
|
3714 | },
|
3715 |
|
3716 | vimeo: {
|
3717 | byline: false,
|
3718 | portrait: false,
|
3719 | title: false,
|
3720 | speed: true,
|
3721 | transparent: false,
|
3722 |
|
3723 | customControls: true,
|
3724 | referrerPolicy: null,
|
3725 |
|
3726 |
|
3727 |
|
3728 | premium: false
|
3729 | },
|
3730 |
|
3731 | youtube: {
|
3732 | rel: 0,
|
3733 |
|
3734 | showinfo: 0,
|
3735 |
|
3736 | iv_load_policy: 3,
|
3737 |
|
3738 | modestbranding: 1,
|
3739 |
|
3740 |
|
3741 | customControls: true,
|
3742 | noCookie: false
|
3743 | },
|
3744 |
|
3745 |
|
3746 | mediaMetadata: {
|
3747 | title: '',
|
3748 | artist: '',
|
3749 | album: '',
|
3750 | artwork: []
|
3751 | },
|
3752 |
|
3753 | markers: {
|
3754 | enabled: false,
|
3755 | points: []
|
3756 | }
|
3757 | };
|
3758 |
|
3759 |
|
3760 |
|
3761 |
|
3762 |
|
3763 | const pip = {
|
3764 | active: 'picture-in-picture',
|
3765 | inactive: 'inline'
|
3766 | };
|
3767 |
|
3768 |
|
3769 |
|
3770 |
|
3771 |
|
3772 | const providers = {
|
3773 | html5: 'html5',
|
3774 | youtube: 'youtube',
|
3775 | vimeo: 'vimeo'
|
3776 | };
|
3777 | const types = {
|
3778 | audio: 'audio',
|
3779 | video: 'video'
|
3780 | };
|
3781 |
|
3782 | |
3783 |
|
3784 |
|
3785 |
|
3786 | function getProviderByUrl(url) {
|
3787 |
|
3788 | if (/^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(url)) {
|
3789 | return providers.youtube;
|
3790 | }
|
3791 |
|
3792 |
|
3793 | if (/^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(url)) {
|
3794 | return providers.vimeo;
|
3795 | }
|
3796 | return null;
|
3797 | }
|
3798 |
|
3799 |
|
3800 |
|
3801 |
|
3802 |
|
3803 | const noop = () => {};
|
3804 | class Console {
|
3805 | constructor(enabled = false) {
|
3806 | this.enabled = window.console && enabled;
|
3807 | if (this.enabled) {
|
3808 | this.log('Debugging enabled');
|
3809 | }
|
3810 | }
|
3811 | get log() {
|
3812 |
|
3813 | return this.enabled ? Function.prototype.bind.call(console.log, console) : noop;
|
3814 | }
|
3815 | get warn() {
|
3816 |
|
3817 | return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop;
|
3818 | }
|
3819 | get error() {
|
3820 |
|
3821 | return this.enabled ? Function.prototype.bind.call(console.error, console) : noop;
|
3822 | }
|
3823 | }
|
3824 |
|
3825 | class Fullscreen {
|
3826 | constructor(player) {
|
3827 | _defineProperty$1(this, "onChange", () => {
|
3828 | if (!this.supported) return;
|
3829 |
|
3830 |
|
3831 | const button = this.player.elements.buttons.fullscreen;
|
3832 | if (is.element(button)) {
|
3833 | button.pressed = this.active;
|
3834 | }
|
3835 |
|
3836 |
|
3837 | const target = this.target === this.player.media ? this.target : this.player.elements.container;
|
3838 |
|
3839 | triggerEvent.call(this.player, target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
|
3840 | });
|
3841 | _defineProperty$1(this, "toggleFallback", (toggle = false) => {
|
3842 |
|
3843 | if (toggle) {
|
3844 | this.scrollPosition = {
|
3845 | x: window.scrollX ?? 0,
|
3846 | y: window.scrollY ?? 0
|
3847 | };
|
3848 | } else {
|
3849 | window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
|
3850 | }
|
3851 |
|
3852 |
|
3853 | document.body.style.overflow = toggle ? 'hidden' : '';
|
3854 |
|
3855 |
|
3856 | toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
|
3857 |
|
3858 |
|
3859 | if (browser.isIos) {
|
3860 | let viewport = document.head.querySelector('meta[name="viewport"]');
|
3861 | const property = 'viewport-fit=cover';
|
3862 |
|
3863 |
|
3864 | if (!viewport) {
|
3865 | viewport = document.createElement('meta');
|
3866 | viewport.setAttribute('name', 'viewport');
|
3867 | }
|
3868 |
|
3869 |
|
3870 | const hasProperty = is.string(viewport.content) && viewport.content.includes(property);
|
3871 | if (toggle) {
|
3872 | this.cleanupViewport = !hasProperty;
|
3873 | if (!hasProperty) viewport.content += `,${property}`;
|
3874 | } else if (this.cleanupViewport) {
|
3875 | viewport.content = viewport.content.split(',').filter(part => part.trim() !== property).join(',');
|
3876 | }
|
3877 | }
|
3878 |
|
3879 |
|
3880 | this.onChange();
|
3881 | });
|
3882 |
|
3883 | _defineProperty$1(this, "trapFocus", event => {
|
3884 |
|
3885 | if (browser.isIos || browser.isIPadOS || !this.active || event.key !== 'Tab') return;
|
3886 |
|
3887 |
|
3888 | const focused = document.activeElement;
|
3889 | const focusable = getElements.call(this.player, 'a[href], button:not(:disabled), input:not(:disabled), [tabindex]');
|
3890 | const [first] = focusable;
|
3891 | const last = focusable[focusable.length - 1];
|
3892 | if (focused === last && !event.shiftKey) {
|
3893 |
|
3894 | first.focus();
|
3895 | event.preventDefault();
|
3896 | } else if (focused === first && event.shiftKey) {
|
3897 |
|
3898 | last.focus();
|
3899 | event.preventDefault();
|
3900 | }
|
3901 | });
|
3902 |
|
3903 | _defineProperty$1(this, "update", () => {
|
3904 | if (this.supported) {
|
3905 | let mode;
|
3906 | if (this.forceFallback) mode = 'Fallback (forced)';else if (Fullscreen.nativeSupported) mode = 'Native';else mode = 'Fallback';
|
3907 | this.player.debug.log(`${mode} fullscreen enabled`);
|
3908 | } else {
|
3909 | this.player.debug.log('Fullscreen not supported and fallback disabled');
|
3910 | }
|
3911 |
|
3912 |
|
3913 | toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.supported);
|
3914 | });
|
3915 |
|
3916 | _defineProperty$1(this, "enter", () => {
|
3917 | if (!this.supported) return;
|
3918 |
|
3919 |
|
3920 | if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
3921 | if (this.player.isVimeo) {
|
3922 | this.player.embed.requestFullscreen();
|
3923 | } else {
|
3924 | this.target.webkitEnterFullscreen();
|
3925 | }
|
3926 | } else if (!Fullscreen.nativeSupported || this.forceFallback) {
|
3927 | this.toggleFallback(true);
|
3928 | } else if (!this.prefix) {
|
3929 | this.target.requestFullscreen({
|
3930 | navigationUI: 'hide'
|
3931 | });
|
3932 | } else if (!is.empty(this.prefix)) {
|
3933 | this.target[`${this.prefix}Request${this.property}`]();
|
3934 | }
|
3935 | });
|
3936 |
|
3937 | _defineProperty$1(this, "exit", () => {
|
3938 | if (!this.supported) return;
|
3939 |
|
3940 |
|
3941 | if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
3942 | if (this.player.isVimeo) {
|
3943 | this.player.embed.exitFullscreen();
|
3944 | } else {
|
3945 | this.target.webkitEnterFullscreen();
|
3946 | }
|
3947 | silencePromise(this.player.play());
|
3948 | } else if (!Fullscreen.nativeSupported || this.forceFallback) {
|
3949 | this.toggleFallback(false);
|
3950 | } else if (!this.prefix) {
|
3951 | (document.cancelFullScreen || document.exitFullscreen).call(document);
|
3952 | } else if (!is.empty(this.prefix)) {
|
3953 | const action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
|
3954 | document[`${this.prefix}${action}${this.property}`]();
|
3955 | }
|
3956 | });
|
3957 |
|
3958 | _defineProperty$1(this, "toggle", () => {
|
3959 | if (!this.active) this.enter();else this.exit();
|
3960 | });
|
3961 |
|
3962 | this.player = player;
|
3963 |
|
3964 |
|
3965 | this.prefix = Fullscreen.prefix;
|
3966 | this.property = Fullscreen.property;
|
3967 |
|
3968 |
|
3969 | this.scrollPosition = {
|
3970 | x: 0,
|
3971 | y: 0
|
3972 | };
|
3973 |
|
3974 |
|
3975 | this.forceFallback = player.config.fullscreen.fallback === 'force';
|
3976 |
|
3977 |
|
3978 |
|
3979 | this.player.elements.fullscreen = player.config.fullscreen.container && closest$1(this.player.elements.container, player.config.fullscreen.container);
|
3980 |
|
3981 |
|
3982 |
|
3983 | on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => {
|
3984 |
|
3985 | this.onChange();
|
3986 | });
|
3987 |
|
3988 |
|
3989 | on.call(this.player, this.player.elements.container, 'dblclick', event => {
|
3990 |
|
3991 | if (is.element(this.player.elements.controls) && this.player.elements.controls.contains(event.target)) {
|
3992 | return;
|
3993 | }
|
3994 | this.player.listeners.proxy(event, this.toggle, 'fullscreen');
|
3995 | });
|
3996 |
|
3997 |
|
3998 | on.call(this, this.player.elements.container, 'keydown', event => this.trapFocus(event));
|
3999 |
|
4000 |
|
4001 | this.update();
|
4002 | }
|
4003 |
|
4004 |
|
4005 | static get nativeSupported() {
|
4006 | return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);
|
4007 | }
|
4008 |
|
4009 |
|
4010 | get useNative() {
|
4011 | return Fullscreen.nativeSupported && !this.forceFallback;
|
4012 | }
|
4013 |
|
4014 |
|
4015 | static get prefix() {
|
4016 |
|
4017 | if (is.function(document.exitFullscreen)) return '';
|
4018 |
|
4019 |
|
4020 | let value = '';
|
4021 | const prefixes = ['webkit', 'moz', 'ms'];
|
4022 | prefixes.some(pre => {
|
4023 | if (is.function(document[`${pre}ExitFullscreen`]) || is.function(document[`${pre}CancelFullScreen`])) {
|
4024 | value = pre;
|
4025 | return true;
|
4026 | }
|
4027 | return false;
|
4028 | });
|
4029 | return value;
|
4030 | }
|
4031 | static get property() {
|
4032 | return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
|
4033 | }
|
4034 |
|
4035 |
|
4036 | get supported() {
|
4037 | return [
|
4038 |
|
4039 | this.player.config.fullscreen.enabled,
|
4040 |
|
4041 | this.player.isVideo,
|
4042 |
|
4043 | Fullscreen.nativeSupported || this.player.config.fullscreen.fallback,
|
4044 |
|
4045 |
|
4046 | !this.player.isYouTube || Fullscreen.nativeSupported || !browser.isIos || this.player.config.playsinline && !this.player.config.fullscreen.iosNative].every(Boolean);
|
4047 | }
|
4048 |
|
4049 |
|
4050 | get active() {
|
4051 | if (!this.supported) return false;
|
4052 |
|
4053 |
|
4054 | if (!Fullscreen.nativeSupported || this.forceFallback) {
|
4055 | return hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
|
4056 | }
|
4057 | const element = !this.prefix ? this.target.getRootNode().fullscreenElement : this.target.getRootNode()[`${this.prefix}${this.property}Element`];
|
4058 | return element && element.shadowRoot ? element === this.target.getRootNode().host : element === this.target;
|
4059 | }
|
4060 |
|
4061 |
|
4062 | get target() {
|
4063 | return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen ?? this.player.elements.container;
|
4064 | }
|
4065 | }
|
4066 |
|
4067 |
|
4068 |
|
4069 |
|
4070 |
|
4071 |
|
4072 |
|
4073 | function loadImage(src, minWidth = 1) {
|
4074 | return new Promise((resolve, reject) => {
|
4075 | const image = new Image();
|
4076 | const handler = () => {
|
4077 | delete image.onload;
|
4078 | delete image.onerror;
|
4079 | (image.naturalWidth >= minWidth ? resolve : reject)(image);
|
4080 | };
|
4081 | Object.assign(image, {
|
4082 | onload: handler,
|
4083 | onerror: handler,
|
4084 | src
|
4085 | });
|
4086 | });
|
4087 | }
|
4088 |
|
4089 |
|
4090 | const ui = {
|
4091 | addStyleHook() {
|
4092 | toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
|
4093 | toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
|
4094 | },
|
4095 |
|
4096 | toggleNativeControls(toggle = false) {
|
4097 | if (toggle && this.isHTML5) {
|
4098 | this.media.setAttribute('controls', '');
|
4099 | } else {
|
4100 | this.media.removeAttribute('controls');
|
4101 | }
|
4102 | },
|
4103 |
|
4104 | build() {
|
4105 |
|
4106 |
|
4107 | this.listeners.media();
|
4108 |
|
4109 |
|
4110 | if (!this.supported.ui) {
|
4111 | this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);
|
4112 |
|
4113 |
|
4114 | ui.toggleNativeControls.call(this, true);
|
4115 |
|
4116 |
|
4117 | return;
|
4118 | }
|
4119 |
|
4120 |
|
4121 | if (!is.element(this.elements.controls)) {
|
4122 |
|
4123 | controls.inject.call(this);
|
4124 |
|
4125 |
|
4126 | this.listeners.controls();
|
4127 | }
|
4128 |
|
4129 |
|
4130 | ui.toggleNativeControls.call(this);
|
4131 |
|
4132 |
|
4133 | if (this.isHTML5) {
|
4134 | captions.setup.call(this);
|
4135 | }
|
4136 |
|
4137 |
|
4138 | this.volume = null;
|
4139 |
|
4140 |
|
4141 | this.muted = null;
|
4142 |
|
4143 |
|
4144 | this.loop = null;
|
4145 |
|
4146 |
|
4147 | this.quality = null;
|
4148 |
|
4149 |
|
4150 | this.speed = null;
|
4151 |
|
4152 |
|
4153 | controls.updateVolume.call(this);
|
4154 |
|
4155 |
|
4156 | controls.timeUpdate.call(this);
|
4157 |
|
4158 |
|
4159 | controls.durationUpdate.call(this);
|
4160 |
|
4161 |
|
4162 | ui.checkPlaying.call(this);
|
4163 |
|
4164 |
|
4165 | toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
|
4166 |
|
4167 |
|
4168 | toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
|
4169 |
|
4170 |
|
4171 | toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
|
4172 |
|
4173 |
|
4174 | this.ready = true;
|
4175 |
|
4176 |
|
4177 | setTimeout(() => {
|
4178 | triggerEvent.call(this, this.media, 'ready');
|
4179 | }, 0);
|
4180 |
|
4181 |
|
4182 | ui.setTitle.call(this);
|
4183 |
|
4184 |
|
4185 | if (this.poster) {
|
4186 | ui.setPoster.call(this, this.poster, false).catch(() => {});
|
4187 | }
|
4188 |
|
4189 |
|
4190 |
|
4191 | if (this.config.duration) {
|
4192 | controls.durationUpdate.call(this);
|
4193 | }
|
4194 |
|
4195 |
|
4196 | if (this.config.mediaMetadata) {
|
4197 | controls.setMediaMetadata.call(this);
|
4198 | }
|
4199 | },
|
4200 |
|
4201 | setTitle() {
|
4202 |
|
4203 | let label = i18n.get('play', this.config);
|
4204 |
|
4205 |
|
4206 | if (is.string(this.config.title) && !is.empty(this.config.title)) {
|
4207 | label += `, ${this.config.title}`;
|
4208 | }
|
4209 |
|
4210 |
|
4211 | Array.from(this.elements.buttons.play || []).forEach(button => {
|
4212 | button.setAttribute('aria-label', label);
|
4213 | });
|
4214 |
|
4215 |
|
4216 |
|
4217 | if (this.isEmbed) {
|
4218 | const iframe = getElement.call(this, 'iframe');
|
4219 | if (!is.element(iframe)) {
|
4220 | return;
|
4221 | }
|
4222 |
|
4223 |
|
4224 | const title = !is.empty(this.config.title) ? this.config.title : 'video';
|
4225 | const format = i18n.get('frameTitle', this.config);
|
4226 | iframe.setAttribute('title', format.replace('{title}', title));
|
4227 | }
|
4228 | },
|
4229 |
|
4230 | togglePoster(enable) {
|
4231 | toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable);
|
4232 | },
|
4233 |
|
4234 |
|
4235 | setPoster(poster, passive = true) {
|
4236 |
|
4237 | if (passive && this.poster) {
|
4238 | return Promise.reject(new Error('Poster already set'));
|
4239 | }
|
4240 |
|
4241 |
|
4242 | this.media.setAttribute('data-poster', poster);
|
4243 |
|
4244 |
|
4245 | this.elements.poster.removeAttribute('hidden');
|
4246 |
|
4247 |
|
4248 | return ready.call(this)
|
4249 |
|
4250 | .then(() => loadImage(poster)).catch(error => {
|
4251 |
|
4252 | if (poster === this.poster) {
|
4253 | ui.togglePoster.call(this, false);
|
4254 | }
|
4255 |
|
4256 | throw error;
|
4257 | }).then(() => {
|
4258 |
|
4259 | if (poster !== this.poster) {
|
4260 | throw new Error('setPoster cancelled by later call to setPoster');
|
4261 | }
|
4262 | }).then(() => {
|
4263 | Object.assign(this.elements.poster.style, {
|
4264 | backgroundImage: `url('${poster}')`,
|
4265 |
|
4266 | backgroundSize: ''
|
4267 | });
|
4268 | ui.togglePoster.call(this, true);
|
4269 | return poster;
|
4270 | });
|
4271 | },
|
4272 |
|
4273 | checkPlaying(event) {
|
4274 |
|
4275 | toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
|
4276 | toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
|
4277 | toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped);
|
4278 |
|
4279 |
|
4280 | Array.from(this.elements.buttons.play || []).forEach(target => {
|
4281 | Object.assign(target, {
|
4282 | pressed: this.playing
|
4283 | });
|
4284 | target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config));
|
4285 | });
|
4286 |
|
4287 |
|
4288 | if (is.event(event) && event.type === 'timeupdate') {
|
4289 | return;
|
4290 | }
|
4291 |
|
4292 |
|
4293 | ui.toggleControls.call(this);
|
4294 | },
|
4295 |
|
4296 | checkLoading(event) {
|
4297 | this.loading = ['stalled', 'waiting'].includes(event.type);
|
4298 |
|
4299 |
|
4300 | clearTimeout(this.timers.loading);
|
4301 |
|
4302 |
|
4303 | this.timers.loading = setTimeout(() => {
|
4304 |
|
4305 | toggleClass(this.elements.container, this.config.classNames.loading, this.loading);
|
4306 |
|
4307 |
|
4308 | ui.toggleControls.call(this);
|
4309 | }, this.loading ? 250 : 0);
|
4310 | },
|
4311 |
|
4312 | toggleControls(force) {
|
4313 | const {
|
4314 | controls: controlsElement
|
4315 | } = this.elements;
|
4316 | if (controlsElement && this.config.hideControls) {
|
4317 |
|
4318 | const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now();
|
4319 |
|
4320 |
|
4321 | this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
|
4322 | }
|
4323 | },
|
4324 |
|
4325 | migrateStyles() {
|
4326 |
|
4327 | Object.values({
|
4328 | ...this.media.style
|
4329 | })
|
4330 |
|
4331 | .filter(key => !is.empty(key) && is.string(key) && key.startsWith('--plyr')).forEach(key => {
|
4332 |
|
4333 | this.elements.container.style.setProperty(key, this.media.style.getPropertyValue(key));
|
4334 |
|
4335 |
|
4336 | this.media.style.removeProperty(key);
|
4337 | });
|
4338 |
|
4339 |
|
4340 | if (is.empty(this.media.style)) {
|
4341 | this.media.removeAttribute('style');
|
4342 | }
|
4343 | }
|
4344 | };
|
4345 |
|
4346 | class Listeners {
|
4347 | constructor(_player) {
|
4348 |
|
4349 | _defineProperty$1(this, "firstTouch", () => {
|
4350 | const {
|
4351 | player
|
4352 | } = this;
|
4353 | const {
|
4354 | elements
|
4355 | } = player;
|
4356 | player.touch = true;
|
4357 |
|
4358 |
|
4359 | toggleClass(elements.container, player.config.classNames.isTouch, true);
|
4360 | });
|
4361 |
|
4362 | _defineProperty$1(this, "global", (toggle = true) => {
|
4363 | const {
|
4364 | player
|
4365 | } = this;
|
4366 |
|
4367 |
|
4368 | if (player.config.keyboard.global) {
|
4369 | toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false);
|
4370 | }
|
4371 |
|
4372 |
|
4373 | toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
|
4374 |
|
4375 |
|
4376 | once.call(player, document.body, 'touchstart', this.firstTouch);
|
4377 | });
|
4378 |
|
4379 | _defineProperty$1(this, "container", () => {
|
4380 | const {
|
4381 | player
|
4382 | } = this;
|
4383 | const {
|
4384 | config,
|
4385 | elements,
|
4386 | timers
|
4387 | } = player;
|
4388 |
|
4389 |
|
4390 | if (!config.keyboard.global && config.keyboard.focused) {
|
4391 | on.call(player, elements.container, 'keydown keyup', this.handleKey, false);
|
4392 | }
|
4393 |
|
4394 |
|
4395 | on.call(player, elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', event => {
|
4396 | const {
|
4397 | controls: controlsElement
|
4398 | } = elements;
|
4399 |
|
4400 |
|
4401 | if (controlsElement && event.type === 'enterfullscreen') {
|
4402 | controlsElement.pressed = false;
|
4403 | controlsElement.hover = false;
|
4404 | }
|
4405 |
|
4406 |
|
4407 | const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
|
4408 | let delay = 0;
|
4409 | if (show) {
|
4410 | ui.toggleControls.call(player, true);
|
4411 |
|
4412 | delay = player.touch ? 3000 : 2000;
|
4413 | }
|
4414 |
|
4415 |
|
4416 | clearTimeout(timers.controls);
|
4417 |
|
4418 |
|
4419 | timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
|
4420 | });
|
4421 |
|
4422 |
|
4423 | const setGutter = () => {
|
4424 | if (!player.isVimeo || player.config.vimeo.premium) {
|
4425 | return;
|
4426 | }
|
4427 | const target = elements.wrapper;
|
4428 | const {
|
4429 | active
|
4430 | } = player.fullscreen;
|
4431 | const [videoWidth, videoHeight] = getAspectRatio.call(player);
|
4432 | const useNativeAspectRatio = supportsCSS(`aspect-ratio: ${videoWidth} / ${videoHeight}`);
|
4433 |
|
4434 |
|
4435 | if (!active) {
|
4436 | if (useNativeAspectRatio) {
|
4437 | target.style.width = null;
|
4438 | target.style.height = null;
|
4439 | } else {
|
4440 | target.style.maxWidth = null;
|
4441 | target.style.margin = null;
|
4442 | }
|
4443 | return;
|
4444 | }
|
4445 |
|
4446 |
|
4447 | const [viewportWidth, viewportHeight] = getViewportSize();
|
4448 | const overflow = viewportWidth / viewportHeight > videoWidth / videoHeight;
|
4449 | if (useNativeAspectRatio) {
|
4450 | target.style.width = overflow ? 'auto' : '100%';
|
4451 | target.style.height = overflow ? '100%' : 'auto';
|
4452 | } else {
|
4453 | target.style.maxWidth = overflow ? `${viewportHeight / videoHeight * videoWidth}px` : null;
|
4454 | target.style.margin = overflow ? '0 auto' : null;
|
4455 | }
|
4456 | };
|
4457 |
|
4458 |
|
4459 | const resized = () => {
|
4460 | clearTimeout(timers.resized);
|
4461 | timers.resized = setTimeout(setGutter, 50);
|
4462 | };
|
4463 | on.call(player, elements.container, 'enterfullscreen exitfullscreen', event => {
|
4464 | const {
|
4465 | target
|
4466 | } = player.fullscreen;
|
4467 |
|
4468 |
|
4469 | if (target !== elements.container) {
|
4470 | return;
|
4471 | }
|
4472 |
|
4473 |
|
4474 | if (!player.isEmbed && is.empty(player.config.ratio)) {
|
4475 | return;
|
4476 | }
|
4477 |
|
4478 |
|
4479 | setGutter();
|
4480 |
|
4481 |
|
4482 | const method = event.type === 'enterfullscreen' ? on : off;
|
4483 | method.call(player, window, 'resize', resized);
|
4484 | });
|
4485 | });
|
4486 |
|
4487 | _defineProperty$1(this, "media", () => {
|
4488 | const {
|
4489 | player
|
4490 | } = this;
|
4491 | const {
|
4492 | elements
|
4493 | } = player;
|
4494 |
|
4495 |
|
4496 | on.call(player, player.media, 'timeupdate seeking seeked', event => controls.timeUpdate.call(player, event));
|
4497 |
|
4498 |
|
4499 | on.call(player, player.media, 'durationchange loadeddata loadedmetadata', event => controls.durationUpdate.call(player, event));
|
4500 |
|
4501 |
|
4502 | on.call(player, player.media, 'ended', () => {
|
4503 |
|
4504 | if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
|
4505 |
|
4506 | player.restart();
|
4507 |
|
4508 |
|
4509 | player.pause();
|
4510 | }
|
4511 | });
|
4512 |
|
4513 |
|
4514 | on.call(player, player.media, 'progress playing seeking seeked', event => controls.updateProgress.call(player, event));
|
4515 |
|
4516 |
|
4517 | on.call(player, player.media, 'volumechange', event => controls.updateVolume.call(player, event));
|
4518 |
|
4519 |
|
4520 | on.call(player, player.media, 'playing play pause ended emptied timeupdate', event => ui.checkPlaying.call(player, event));
|
4521 |
|
4522 |
|
4523 | on.call(player, player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(player, event));
|
4524 |
|
4525 |
|
4526 | if (player.supported.ui && player.config.clickToPlay && !player.isAudio) {
|
4527 |
|
4528 | const wrapper = getElement.call(player, `.${player.config.classNames.video}`);
|
4529 |
|
4530 |
|
4531 | if (!is.element(wrapper)) {
|
4532 | return;
|
4533 | }
|
4534 |
|
4535 |
|
4536 | on.call(player, elements.container, 'click', event => {
|
4537 | const targets = [elements.container, wrapper];
|
4538 |
|
4539 |
|
4540 | if (!targets.includes(event.target) && !wrapper.contains(event.target)) {
|
4541 | return;
|
4542 | }
|
4543 |
|
4544 |
|
4545 | if (player.touch && player.config.hideControls) {
|
4546 | return;
|
4547 | }
|
4548 | if (player.ended) {
|
4549 | this.proxy(event, player.restart, 'restart');
|
4550 | this.proxy(event, () => {
|
4551 | silencePromise(player.play());
|
4552 | }, 'play');
|
4553 | } else {
|
4554 | this.proxy(event, () => {
|
4555 | silencePromise(player.togglePlay());
|
4556 | }, 'play');
|
4557 | }
|
4558 | });
|
4559 | }
|
4560 |
|
4561 |
|
4562 | if (player.supported.ui && player.config.disableContextMenu) {
|
4563 | on.call(player, elements.wrapper, 'contextmenu', event => {
|
4564 | event.preventDefault();
|
4565 | }, false);
|
4566 | }
|
4567 |
|
4568 |
|
4569 | on.call(player, player.media, 'volumechange', () => {
|
4570 |
|
4571 | player.storage.set({
|
4572 | volume: player.volume,
|
4573 | muted: player.muted
|
4574 | });
|
4575 | });
|
4576 |
|
4577 |
|
4578 | on.call(player, player.media, 'ratechange', () => {
|
4579 |
|
4580 | controls.updateSetting.call(player, 'speed');
|
4581 |
|
4582 |
|
4583 | player.storage.set({
|
4584 | speed: player.speed
|
4585 | });
|
4586 | });
|
4587 |
|
4588 |
|
4589 | on.call(player, player.media, 'qualitychange', event => {
|
4590 |
|
4591 | controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
4592 | });
|
4593 |
|
4594 |
|
4595 | on.call(player, player.media, 'ready qualitychange', () => {
|
4596 | controls.setDownloadUrl.call(player);
|
4597 | });
|
4598 |
|
4599 |
|
4600 |
|
4601 | const proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
|
4602 | on.call(player, player.media, proxyEvents, event => {
|
4603 | let {
|
4604 | detail = {}
|
4605 | } = event;
|
4606 |
|
4607 |
|
4608 | if (event.type === 'error') {
|
4609 | detail = player.media.error;
|
4610 | }
|
4611 | triggerEvent.call(player, elements.container, event.type, true, detail);
|
4612 | });
|
4613 | });
|
4614 |
|
4615 | _defineProperty$1(this, "proxy", (event, defaultHandler, customHandlerKey) => {
|
4616 | const {
|
4617 | player
|
4618 | } = this;
|
4619 | const customHandler = player.config.listeners[customHandlerKey];
|
4620 | const hasCustomHandler = is.function(customHandler);
|
4621 | let returned = true;
|
4622 |
|
4623 |
|
4624 | if (hasCustomHandler) {
|
4625 | returned = customHandler.call(player, event);
|
4626 | }
|
4627 |
|
4628 |
|
4629 | if (returned !== false && is.function(defaultHandler)) {
|
4630 | defaultHandler.call(player, event);
|
4631 | }
|
4632 | });
|
4633 |
|
4634 | _defineProperty$1(this, "bind", (element, type, defaultHandler, customHandlerKey, passive = true) => {
|
4635 | const {
|
4636 | player
|
4637 | } = this;
|
4638 | const customHandler = player.config.listeners[customHandlerKey];
|
4639 | const hasCustomHandler = is.function(customHandler);
|
4640 | on.call(player, element, type, event => this.proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler);
|
4641 | });
|
4642 |
|
4643 | _defineProperty$1(this, "controls", () => {
|
4644 | const {
|
4645 | player
|
4646 | } = this;
|
4647 | const {
|
4648 | elements
|
4649 | } = player;
|
4650 |
|
4651 | const inputEvent = browser.isIE ? 'change' : 'input';
|
4652 |
|
4653 |
|
4654 | if (elements.buttons.play) {
|
4655 | Array.from(elements.buttons.play).forEach(button => {
|
4656 | this.bind(button, 'click', () => {
|
4657 | silencePromise(player.togglePlay());
|
4658 | }, 'play');
|
4659 | });
|
4660 | }
|
4661 |
|
4662 |
|
4663 | this.bind(elements.buttons.restart, 'click', player.restart, 'restart');
|
4664 |
|
4665 |
|
4666 | this.bind(elements.buttons.rewind, 'click', () => {
|
4667 |
|
4668 | player.lastSeekTime = Date.now();
|
4669 | player.rewind();
|
4670 | }, 'rewind');
|
4671 |
|
4672 |
|
4673 | this.bind(elements.buttons.fastForward, 'click', () => {
|
4674 |
|
4675 | player.lastSeekTime = Date.now();
|
4676 | player.forward();
|
4677 | }, 'fastForward');
|
4678 |
|
4679 |
|
4680 | this.bind(elements.buttons.mute, 'click', () => {
|
4681 | player.muted = !player.muted;
|
4682 | }, 'mute');
|
4683 |
|
4684 |
|
4685 | this.bind(elements.buttons.captions, 'click', () => player.toggleCaptions());
|
4686 |
|
4687 |
|
4688 | this.bind(elements.buttons.download, 'click', () => {
|
4689 | triggerEvent.call(player, player.media, 'download');
|
4690 | }, 'download');
|
4691 |
|
4692 |
|
4693 | this.bind(elements.buttons.fullscreen, 'click', () => {
|
4694 | player.fullscreen.toggle();
|
4695 | }, 'fullscreen');
|
4696 |
|
4697 |
|
4698 | this.bind(elements.buttons.pip, 'click', () => {
|
4699 | player.pip = 'toggle';
|
4700 | }, 'pip');
|
4701 |
|
4702 |
|
4703 | this.bind(elements.buttons.airplay, 'click', player.airplay, 'airplay');
|
4704 |
|
4705 |
|
4706 | this.bind(elements.buttons.settings, 'click', event => {
|
4707 |
|
4708 | event.stopPropagation();
|
4709 | event.preventDefault();
|
4710 | controls.toggleMenu.call(player, event);
|
4711 | }, null, false);
|
4712 |
|
4713 |
|
4714 |
|
4715 |
|
4716 | this.bind(elements.buttons.settings, 'keyup', event => {
|
4717 | if (![' ', 'Enter'].includes(event.key)) {
|
4718 | return;
|
4719 | }
|
4720 |
|
4721 |
|
4722 | if (event.key === 'Enter') {
|
4723 | controls.focusFirstMenuItem.call(player, null, true);
|
4724 | return;
|
4725 | }
|
4726 |
|
4727 |
|
4728 | event.preventDefault();
|
4729 |
|
4730 |
|
4731 | event.stopPropagation();
|
4732 |
|
4733 |
|
4734 | controls.toggleMenu.call(player, event);
|
4735 | }, null, false
|
4736 | );
|
4737 |
|
4738 |
|
4739 | this.bind(elements.settings.menu, 'keydown', event => {
|
4740 | if (event.key === 'Escape') {
|
4741 | controls.toggleMenu.call(player, event);
|
4742 | }
|
4743 | });
|
4744 |
|
4745 |
|
4746 | this.bind(elements.inputs.seek, 'mousedown mousemove', event => {
|
4747 | const rect = elements.progress.getBoundingClientRect();
|
4748 | const percent = 100 / rect.width * (event.pageX - rect.left);
|
4749 | event.currentTarget.setAttribute('seek-value', percent);
|
4750 | });
|
4751 |
|
4752 |
|
4753 | this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', event => {
|
4754 | const seek = event.currentTarget;
|
4755 | const attribute = 'play-on-seeked';
|
4756 | if (is.keyboardEvent(event) && !['ArrowLeft', 'ArrowRight'].includes(event.key)) {
|
4757 | return;
|
4758 | }
|
4759 |
|
4760 |
|
4761 | player.lastSeekTime = Date.now();
|
4762 |
|
4763 |
|
4764 | const play = seek.hasAttribute(attribute);
|
4765 |
|
4766 | const done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
|
4767 |
|
4768 |
|
4769 | if (play && done) {
|
4770 | seek.removeAttribute(attribute);
|
4771 | silencePromise(player.play());
|
4772 | } else if (!done && player.playing) {
|
4773 | seek.setAttribute(attribute, '');
|
4774 | player.pause();
|
4775 | }
|
4776 | });
|
4777 |
|
4778 |
|
4779 |
|
4780 |
|
4781 | if (browser.isIos) {
|
4782 | const inputs = getElements.call(player, 'input[type="range"]');
|
4783 | Array.from(inputs).forEach(input => this.bind(input, inputEvent, event => repaint(event.target)));
|
4784 | }
|
4785 |
|
4786 |
|
4787 | this.bind(elements.inputs.seek, inputEvent, event => {
|
4788 | const seek = event.currentTarget;
|
4789 |
|
4790 | let seekTo = seek.getAttribute('seek-value');
|
4791 | if (is.empty(seekTo)) {
|
4792 | seekTo = seek.value;
|
4793 | }
|
4794 | seek.removeAttribute('seek-value');
|
4795 | player.currentTime = seekTo / seek.max * player.duration;
|
4796 | }, 'seek');
|
4797 |
|
4798 |
|
4799 | this.bind(elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(player, event));
|
4800 |
|
4801 |
|
4802 |
|
4803 | this.bind(elements.progress, 'mousemove touchmove', event => {
|
4804 | const {
|
4805 | previewThumbnails
|
4806 | } = player;
|
4807 | if (previewThumbnails && previewThumbnails.loaded) {
|
4808 | previewThumbnails.startMove(event);
|
4809 | }
|
4810 | });
|
4811 |
|
4812 |
|
4813 | this.bind(elements.progress, 'mouseleave touchend click', () => {
|
4814 | const {
|
4815 | previewThumbnails
|
4816 | } = player;
|
4817 | if (previewThumbnails && previewThumbnails.loaded) {
|
4818 | previewThumbnails.endMove(false, true);
|
4819 | }
|
4820 | });
|
4821 |
|
4822 |
|
4823 | this.bind(elements.progress, 'mousedown touchstart', event => {
|
4824 | const {
|
4825 | previewThumbnails
|
4826 | } = player;
|
4827 | if (previewThumbnails && previewThumbnails.loaded) {
|
4828 | previewThumbnails.startScrubbing(event);
|
4829 | }
|
4830 | });
|
4831 | this.bind(elements.progress, 'mouseup touchend', event => {
|
4832 | const {
|
4833 | previewThumbnails
|
4834 | } = player;
|
4835 | if (previewThumbnails && previewThumbnails.loaded) {
|
4836 | previewThumbnails.endScrubbing(event);
|
4837 | }
|
4838 | });
|
4839 |
|
4840 |
|
4841 | if (browser.isWebKit) {
|
4842 | Array.from(getElements.call(player, 'input[type="range"]')).forEach(element => {
|
4843 | this.bind(element, 'input', event => controls.updateRangeFill.call(player, event.target));
|
4844 | });
|
4845 | }
|
4846 |
|
4847 |
|
4848 |
|
4849 | if (player.config.toggleInvert && !is.element(elements.display.duration)) {
|
4850 | this.bind(elements.display.currentTime, 'click', () => {
|
4851 |
|
4852 | if (player.currentTime === 0) {
|
4853 | return;
|
4854 | }
|
4855 | player.config.invertTime = !player.config.invertTime;
|
4856 | controls.timeUpdate.call(player);
|
4857 | });
|
4858 | }
|
4859 |
|
4860 |
|
4861 | this.bind(elements.inputs.volume, inputEvent, event => {
|
4862 | player.volume = event.target.value;
|
4863 | }, 'volume');
|
4864 |
|
4865 |
|
4866 | this.bind(elements.controls, 'mouseenter mouseleave', event => {
|
4867 | elements.controls.hover = !player.touch && event.type === 'mouseenter';
|
4868 | });
|
4869 |
|
4870 |
|
4871 | if (elements.fullscreen) {
|
4872 | Array.from(elements.fullscreen.children).filter(c => !c.contains(elements.container)).forEach(child => {
|
4873 | this.bind(child, 'mouseenter mouseleave', event => {
|
4874 | if (elements.controls) {
|
4875 | elements.controls.hover = !player.touch && event.type === 'mouseenter';
|
4876 | }
|
4877 | });
|
4878 | });
|
4879 | }
|
4880 |
|
4881 |
|
4882 | this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
|
4883 | elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
4884 | });
|
4885 |
|
4886 |
|
4887 | this.bind(elements.controls, 'focusin', () => {
|
4888 | const {
|
4889 | config,
|
4890 | timers
|
4891 | } = player;
|
4892 |
|
4893 |
|
4894 | toggleClass(elements.controls, config.classNames.noTransition, true);
|
4895 |
|
4896 |
|
4897 | ui.toggleControls.call(player, true);
|
4898 |
|
4899 |
|
4900 | setTimeout(() => {
|
4901 | toggleClass(elements.controls, config.classNames.noTransition, false);
|
4902 | }, 0);
|
4903 |
|
4904 |
|
4905 | const delay = this.touch ? 3000 : 4000;
|
4906 |
|
4907 |
|
4908 | clearTimeout(timers.controls);
|
4909 |
|
4910 |
|
4911 | timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
|
4912 | });
|
4913 |
|
4914 |
|
4915 | this.bind(elements.inputs.volume, 'wheel', event => {
|
4916 |
|
4917 |
|
4918 | const inverted = event.webkitDirectionInvertedFromDevice;
|
4919 |
|
4920 | const [x, y] = [event.deltaX, -event.deltaY].map(value => inverted ? -value : value);
|
4921 |
|
4922 | const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
|
4923 |
|
4924 |
|
4925 | player.increaseVolume(direction / 50);
|
4926 |
|
4927 |
|
4928 | const {
|
4929 | volume
|
4930 | } = player.media;
|
4931 | if (direction === 1 && volume < 1 || direction === -1 && volume > 0) {
|
4932 | event.preventDefault();
|
4933 | }
|
4934 | }, 'volume', false);
|
4935 | });
|
4936 | this.player = _player;
|
4937 | this.lastKey = null;
|
4938 | this.focusTimer = null;
|
4939 | this.lastKeyDown = null;
|
4940 | this.handleKey = this.handleKey.bind(this);
|
4941 | this.toggleMenu = this.toggleMenu.bind(this);
|
4942 | this.firstTouch = this.firstTouch.bind(this);
|
4943 | }
|
4944 |
|
4945 |
|
4946 | handleKey(event) {
|
4947 | const {
|
4948 | player
|
4949 | } = this;
|
4950 | const {
|
4951 | elements
|
4952 | } = player;
|
4953 | const {
|
4954 | key,
|
4955 | type,
|
4956 | altKey,
|
4957 | ctrlKey,
|
4958 | metaKey,
|
4959 | shiftKey
|
4960 | } = event;
|
4961 | const pressed = type === 'keydown';
|
4962 | const repeat = pressed && key === this.lastKey;
|
4963 |
|
4964 |
|
4965 | if (altKey || ctrlKey || metaKey || shiftKey) {
|
4966 | return;
|
4967 | }
|
4968 |
|
4969 |
|
4970 |
|
4971 | if (!key) {
|
4972 | return;
|
4973 | }
|
4974 |
|
4975 |
|
4976 | const seekByIncrement = increment => {
|
4977 |
|
4978 | player.currentTime = player.duration / 10 * increment;
|
4979 | };
|
4980 |
|
4981 |
|
4982 |
|
4983 | if (pressed) {
|
4984 |
|
4985 |
|
4986 |
|
4987 | const focused = document.activeElement;
|
4988 | if (is.element(focused)) {
|
4989 | const {
|
4990 | editable
|
4991 | } = player.config.selectors;
|
4992 | const {
|
4993 | seek
|
4994 | } = elements.inputs;
|
4995 | if (focused !== seek && matches(focused, editable)) {
|
4996 | return;
|
4997 | }
|
4998 | if (event.key === ' ' && matches(focused, 'button, [role^="menuitem"]')) {
|
4999 | return;
|
5000 | }
|
5001 | }
|
5002 |
|
5003 |
|
5004 | const preventDefault = [' ', 'ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'c', 'f', 'k', 'l', 'm'];
|
5005 |
|
5006 |
|
5007 | if (preventDefault.includes(key)) {
|
5008 | event.preventDefault();
|
5009 | event.stopPropagation();
|
5010 | }
|
5011 | switch (key) {
|
5012 | case '0':
|
5013 | case '1':
|
5014 | case '2':
|
5015 | case '3':
|
5016 | case '4':
|
5017 | case '5':
|
5018 | case '6':
|
5019 | case '7':
|
5020 | case '8':
|
5021 | case '9':
|
5022 | if (!repeat) {
|
5023 | seekByIncrement(parseInt(key, 10));
|
5024 | }
|
5025 | break;
|
5026 | case ' ':
|
5027 | case 'k':
|
5028 | if (!repeat) {
|
5029 | silencePromise(player.togglePlay());
|
5030 | }
|
5031 | break;
|
5032 | case 'ArrowUp':
|
5033 | player.increaseVolume(0.1);
|
5034 | break;
|
5035 | case 'ArrowDown':
|
5036 | player.decreaseVolume(0.1);
|
5037 | break;
|
5038 | case 'm':
|
5039 | if (!repeat) {
|
5040 | player.muted = !player.muted;
|
5041 | }
|
5042 | break;
|
5043 | case 'ArrowRight':
|
5044 | player.forward();
|
5045 | break;
|
5046 | case 'ArrowLeft':
|
5047 | player.rewind();
|
5048 | break;
|
5049 | case 'f':
|
5050 | player.fullscreen.toggle();
|
5051 | break;
|
5052 | case 'c':
|
5053 | if (!repeat) {
|
5054 | player.toggleCaptions();
|
5055 | }
|
5056 | break;
|
5057 | case 'l':
|
5058 | player.loop = !player.loop;
|
5059 | break;
|
5060 | }
|
5061 |
|
5062 |
|
5063 |
|
5064 | if (key === 'Escape' && !player.fullscreen.usingNative && player.fullscreen.active) {
|
5065 | player.fullscreen.toggle();
|
5066 | }
|
5067 |
|
5068 |
|
5069 | this.lastKey = key;
|
5070 | } else {
|
5071 | this.lastKey = null;
|
5072 | }
|
5073 | }
|
5074 |
|
5075 |
|
5076 | toggleMenu(event) {
|
5077 | controls.toggleMenu.call(this.player, event);
|
5078 | }
|
5079 | }
|
5080 |
|
5081 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
5082 |
|
5083 | function createCommonjsModule(fn, module) {
|
5084 | return module = { exports: {} }, fn(module, module.exports), module.exports;
|
5085 | }
|
5086 |
|
5087 | var loadjs_umd = createCommonjsModule(function (module, exports) {
|
5088 | (function (root, factory) {
|
5089 | {
|
5090 | module.exports = factory();
|
5091 | }
|
5092 | })(commonjsGlobal, function () {
|
5093 | |
5094 |
|
5095 |
|
5096 |
|
5097 |
|
5098 | var devnull = function () {},
|
5099 | bundleIdCache = {},
|
5100 | bundleResultCache = {},
|
5101 | bundleCallbackQueue = {};
|
5102 |
|
5103 | |
5104 |
|
5105 |
|
5106 |
|
5107 |
|
5108 | function subscribe(bundleIds, callbackFn) {
|
5109 |
|
5110 | bundleIds = bundleIds.push ? bundleIds : [bundleIds];
|
5111 | var depsNotFound = [],
|
5112 | i = bundleIds.length,
|
5113 | numWaiting = i,
|
5114 | fn,
|
5115 | bundleId,
|
5116 | r,
|
5117 | q;
|
5118 |
|
5119 |
|
5120 | fn = function (bundleId, pathsNotFound) {
|
5121 | if (pathsNotFound.length) depsNotFound.push(bundleId);
|
5122 | numWaiting--;
|
5123 | if (!numWaiting) callbackFn(depsNotFound);
|
5124 | };
|
5125 |
|
5126 |
|
5127 | while (i--) {
|
5128 | bundleId = bundleIds[i];
|
5129 |
|
5130 |
|
5131 | r = bundleResultCache[bundleId];
|
5132 | if (r) {
|
5133 | fn(bundleId, r);
|
5134 | continue;
|
5135 | }
|
5136 |
|
5137 |
|
5138 | q = bundleCallbackQueue[bundleId] = bundleCallbackQueue[bundleId] || [];
|
5139 | q.push(fn);
|
5140 | }
|
5141 | }
|
5142 |
|
5143 | |
5144 |
|
5145 |
|
5146 |
|
5147 |
|
5148 | function publish(bundleId, pathsNotFound) {
|
5149 |
|
5150 | if (!bundleId) return;
|
5151 | var q = bundleCallbackQueue[bundleId];
|
5152 |
|
5153 |
|
5154 | bundleResultCache[bundleId] = pathsNotFound;
|
5155 |
|
5156 |
|
5157 | if (!q) return;
|
5158 |
|
5159 |
|
5160 | while (q.length) {
|
5161 | q[0](bundleId, pathsNotFound);
|
5162 | q.splice(0, 1);
|
5163 | }
|
5164 | }
|
5165 |
|
5166 | |
5167 |
|
5168 |
|
5169 |
|
5170 |
|
5171 | function executeCallbacks(args, depsNotFound) {
|
5172 |
|
5173 | if (args.call) args = {
|
5174 | success: args
|
5175 | };
|
5176 |
|
5177 |
|
5178 | if (depsNotFound.length) (args.error || devnull)(depsNotFound);else (args.success || devnull)(args);
|
5179 | }
|
5180 |
|
5181 | |
5182 |
|
5183 |
|
5184 |
|
5185 |
|
5186 | function loadFile(path, callbackFn, args, numTries) {
|
5187 | var doc = document,
|
5188 | async = args.async,
|
5189 | maxTries = (args.numRetries || 0) + 1,
|
5190 | beforeCallbackFn = args.before || devnull,
|
5191 | pathname = path.replace(/[\?|#].*$/, ''),
|
5192 | pathStripped = path.replace(/^(css|img)!/, ''),
|
5193 | isLegacyIECss,
|
5194 | e;
|
5195 | numTries = numTries || 0;
|
5196 | if (/(^css!|\.css$)/.test(pathname)) {
|
5197 |
|
5198 | e = doc.createElement('link');
|
5199 | e.rel = 'stylesheet';
|
5200 | e.href = pathStripped;
|
5201 |
|
5202 |
|
5203 | isLegacyIECss = 'hideFocus' in e;
|
5204 |
|
5205 |
|
5206 | if (isLegacyIECss && e.relList) {
|
5207 | isLegacyIECss = 0;
|
5208 | e.rel = 'preload';
|
5209 | e.as = 'style';
|
5210 | }
|
5211 | } else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) {
|
5212 |
|
5213 | e = doc.createElement('img');
|
5214 | e.src = pathStripped;
|
5215 | } else {
|
5216 |
|
5217 | e = doc.createElement('script');
|
5218 | e.src = path;
|
5219 | e.async = async === undefined ? true : async;
|
5220 | }
|
5221 | e.onload = e.onerror = e.onbeforeload = function (ev) {
|
5222 | var result = ev.type[0];
|
5223 |
|
5224 |
|
5225 |
|
5226 | if (isLegacyIECss) {
|
5227 | try {
|
5228 | if (!e.sheet.cssText.length) result = 'e';
|
5229 | } catch (x) {
|
5230 |
|
5231 |
|
5232 | if (x.code != 18) result = 'e';
|
5233 | }
|
5234 | }
|
5235 |
|
5236 |
|
5237 | if (result == 'e') {
|
5238 |
|
5239 | numTries += 1;
|
5240 |
|
5241 |
|
5242 | if (numTries < maxTries) {
|
5243 | return loadFile(path, callbackFn, args, numTries);
|
5244 | }
|
5245 | } else if (e.rel == 'preload' && e.as == 'style') {
|
5246 |
|
5247 | return e.rel = 'stylesheet';
|
5248 | }
|
5249 |
|
5250 |
|
5251 | callbackFn(path, result, ev.defaultPrevented);
|
5252 | };
|
5253 |
|
5254 |
|
5255 | if (beforeCallbackFn(path, e) !== false) doc.head.appendChild(e);
|
5256 | }
|
5257 |
|
5258 | |
5259 |
|
5260 |
|
5261 |
|
5262 |
|
5263 | function loadFiles(paths, callbackFn, args) {
|
5264 |
|
5265 | paths = paths.push ? paths : [paths];
|
5266 | var numWaiting = paths.length,
|
5267 | x = numWaiting,
|
5268 | pathsNotFound = [],
|
5269 | fn,
|
5270 | i;
|
5271 |
|
5272 |
|
5273 | fn = function (path, result, defaultPrevented) {
|
5274 |
|
5275 | if (result == 'e') pathsNotFound.push(path);
|
5276 |
|
5277 |
|
5278 |
|
5279 | if (result == 'b') {
|
5280 | if (defaultPrevented) pathsNotFound.push(path);else return;
|
5281 | }
|
5282 | numWaiting--;
|
5283 | if (!numWaiting) callbackFn(pathsNotFound);
|
5284 | };
|
5285 |
|
5286 |
|
5287 | for (i = 0; i < x; i++) loadFile(paths[i], fn, args);
|
5288 | }
|
5289 |
|
5290 | |
5291 |
|
5292 |
|
5293 |
|
5294 |
|
5295 |
|
5296 |
|
5297 |
|
5298 |
|
5299 | function loadjs(paths, arg1, arg2) {
|
5300 | var bundleId, args;
|
5301 |
|
5302 |
|
5303 | if (arg1 && arg1.trim) bundleId = arg1;
|
5304 |
|
5305 |
|
5306 | args = (bundleId ? arg2 : arg1) || {};
|
5307 |
|
5308 |
|
5309 | if (bundleId) {
|
5310 | if (bundleId in bundleIdCache) {
|
5311 | throw "LoadJS";
|
5312 | } else {
|
5313 | bundleIdCache[bundleId] = true;
|
5314 | }
|
5315 | }
|
5316 | function loadFn(resolve, reject) {
|
5317 | loadFiles(paths, function (pathsNotFound) {
|
5318 |
|
5319 | executeCallbacks(args, pathsNotFound);
|
5320 |
|
5321 |
|
5322 | if (resolve) {
|
5323 | executeCallbacks({
|
5324 | success: resolve,
|
5325 | error: reject
|
5326 | }, pathsNotFound);
|
5327 | }
|
5328 |
|
5329 |
|
5330 | publish(bundleId, pathsNotFound);
|
5331 | }, args);
|
5332 | }
|
5333 | if (args.returnPromise) return new Promise(loadFn);else loadFn();
|
5334 | }
|
5335 |
|
5336 | |
5337 |
|
5338 |
|
5339 |
|
5340 |
|
5341 | loadjs.ready = function ready(deps, args) {
|
5342 |
|
5343 | subscribe(deps, function (depsNotFound) {
|
5344 |
|
5345 | executeCallbacks(args, depsNotFound);
|
5346 | });
|
5347 | return loadjs;
|
5348 | };
|
5349 |
|
5350 | |
5351 |
|
5352 |
|
5353 |
|
5354 | loadjs.done = function done(bundleId) {
|
5355 | publish(bundleId, []);
|
5356 | };
|
5357 |
|
5358 | |
5359 |
|
5360 |
|
5361 | loadjs.reset = function reset() {
|
5362 | bundleIdCache = {};
|
5363 | bundleResultCache = {};
|
5364 | bundleCallbackQueue = {};
|
5365 | };
|
5366 |
|
5367 | |
5368 |
|
5369 |
|
5370 |
|
5371 | loadjs.isDefined = function isDefined(bundleId) {
|
5372 | return bundleId in bundleIdCache;
|
5373 | };
|
5374 |
|
5375 |
|
5376 | return loadjs;
|
5377 | });
|
5378 | });
|
5379 |
|
5380 |
|
5381 | function loadScript(url) {
|
5382 | return new Promise((resolve, reject) => {
|
5383 | loadjs_umd(url, {
|
5384 | success: resolve,
|
5385 | error: reject
|
5386 | });
|
5387 | });
|
5388 | }
|
5389 |
|
5390 |
|
5391 |
|
5392 |
|
5393 | function parseId$1(url) {
|
5394 | if (is.empty(url)) {
|
5395 | return null;
|
5396 | }
|
5397 | if (is.number(Number(url))) {
|
5398 | return url;
|
5399 | }
|
5400 | const regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
|
5401 | return url.match(regex) ? RegExp.$2 : url;
|
5402 | }
|
5403 |
|
5404 |
|
5405 | function parseHash(url) {
|
5406 | |
5407 |
|
5408 |
|
5409 |
|
5410 |
|
5411 |
|
5412 |
|
5413 | const regex = /^.*(vimeo.com\/|video\/)(\d+)(\?.*&*h=|\/)+([\d,a-f]+)/;
|
5414 | const found = url.match(regex);
|
5415 | return found && found.length === 5 ? found[4] : null;
|
5416 | }
|
5417 |
|
5418 |
|
5419 | function assurePlaybackState$1(play) {
|
5420 | if (play && !this.embed.hasPlayed) {
|
5421 | this.embed.hasPlayed = true;
|
5422 | }
|
5423 | if (this.media.paused === play) {
|
5424 | this.media.paused = !play;
|
5425 | triggerEvent.call(this, this.media, play ? 'play' : 'pause');
|
5426 | }
|
5427 | }
|
5428 | const vimeo = {
|
5429 | setup() {
|
5430 | const player = this;
|
5431 |
|
5432 |
|
5433 | toggleClass(player.elements.wrapper, player.config.classNames.embed, true);
|
5434 |
|
5435 |
|
5436 | player.options.speed = player.config.speed.options;
|
5437 |
|
5438 |
|
5439 | setAspectRatio.call(player);
|
5440 |
|
5441 |
|
5442 | if (!is.object(window.Vimeo)) {
|
5443 | loadScript(player.config.urls.vimeo.sdk).then(() => {
|
5444 | vimeo.ready.call(player);
|
5445 | }).catch(error => {
|
5446 | player.debug.warn('Vimeo SDK (player.js) failed to load', error);
|
5447 | });
|
5448 | } else {
|
5449 | vimeo.ready.call(player);
|
5450 | }
|
5451 | },
|
5452 |
|
5453 | ready() {
|
5454 | const player = this;
|
5455 | const config = player.config.vimeo;
|
5456 | const {
|
5457 | premium,
|
5458 | referrerPolicy,
|
5459 | ...frameParams
|
5460 | } = config;
|
5461 |
|
5462 | let source = player.media.getAttribute('src');
|
5463 | let hash = '';
|
5464 |
|
5465 | if (is.empty(source)) {
|
5466 | source = player.media.getAttribute(player.config.attributes.embed.id);
|
5467 |
|
5468 | hash = player.media.getAttribute(player.config.attributes.embed.hash);
|
5469 | } else {
|
5470 | hash = parseHash(source);
|
5471 | }
|
5472 | const hashParam = hash ? {
|
5473 | h: hash
|
5474 | } : {};
|
5475 |
|
5476 |
|
5477 | if (premium) {
|
5478 | Object.assign(frameParams, {
|
5479 | controls: false,
|
5480 | sidedock: false
|
5481 | });
|
5482 | }
|
5483 |
|
5484 |
|
5485 | const params = buildUrlParams({
|
5486 | loop: player.config.loop.active,
|
5487 | autoplay: player.autoplay,
|
5488 | muted: player.muted,
|
5489 | gesture: 'media',
|
5490 | playsinline: player.config.playsinline,
|
5491 |
|
5492 | ...hashParam,
|
5493 | ...frameParams
|
5494 | });
|
5495 | const id = parseId$1(source);
|
5496 |
|
5497 | const iframe = createElement('iframe');
|
5498 | const src = format(player.config.urls.vimeo.iframe, id, params);
|
5499 | iframe.setAttribute('src', src);
|
5500 | iframe.setAttribute('allowfullscreen', '');
|
5501 | iframe.setAttribute('allow', ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; '));
|
5502 |
|
5503 |
|
5504 | if (!is.empty(referrerPolicy)) {
|
5505 | iframe.setAttribute('referrerPolicy', referrerPolicy);
|
5506 | }
|
5507 |
|
5508 |
|
5509 | if (premium || !config.customControls) {
|
5510 | iframe.setAttribute('data-poster', player.poster);
|
5511 | player.media = replaceElement(iframe, player.media);
|
5512 | } else {
|
5513 | const wrapper = createElement('div', {
|
5514 | class: player.config.classNames.embedContainer,
|
5515 | 'data-poster': player.poster
|
5516 | });
|
5517 | wrapper.appendChild(iframe);
|
5518 | player.media = replaceElement(wrapper, player.media);
|
5519 | }
|
5520 |
|
5521 |
|
5522 | if (!config.customControls) {
|
5523 | fetch(format(player.config.urls.vimeo.api, src)).then(response => {
|
5524 | if (is.empty(response) || !response.thumbnail_url) {
|
5525 | return;
|
5526 | }
|
5527 |
|
5528 |
|
5529 | ui.setPoster.call(player, response.thumbnail_url).catch(() => {});
|
5530 | });
|
5531 | }
|
5532 |
|
5533 |
|
5534 |
|
5535 | player.embed = new window.Vimeo.Player(iframe, {
|
5536 | autopause: player.config.autopause,
|
5537 | muted: player.muted
|
5538 | });
|
5539 | player.media.paused = true;
|
5540 | player.media.currentTime = 0;
|
5541 |
|
5542 |
|
5543 | if (player.supported.ui) {
|
5544 | player.embed.disableTextTrack();
|
5545 | }
|
5546 |
|
5547 |
|
5548 | player.media.play = () => {
|
5549 | assurePlaybackState$1.call(player, true);
|
5550 | return player.embed.play();
|
5551 | };
|
5552 | player.media.pause = () => {
|
5553 | assurePlaybackState$1.call(player, false);
|
5554 | return player.embed.pause();
|
5555 | };
|
5556 | player.media.stop = () => {
|
5557 | player.pause();
|
5558 | player.currentTime = 0;
|
5559 | };
|
5560 |
|
5561 |
|
5562 | let {
|
5563 | currentTime
|
5564 | } = player.media;
|
5565 | Object.defineProperty(player.media, 'currentTime', {
|
5566 | get() {
|
5567 | return currentTime;
|
5568 | },
|
5569 | set(time) {
|
5570 |
|
5571 |
|
5572 |
|
5573 | const {
|
5574 | embed,
|
5575 | media,
|
5576 | paused,
|
5577 | volume
|
5578 | } = player;
|
5579 | const restorePause = paused && !embed.hasPlayed;
|
5580 |
|
5581 |
|
5582 | media.seeking = true;
|
5583 | triggerEvent.call(player, media, 'seeking');
|
5584 |
|
5585 |
|
5586 | Promise.resolve(restorePause && embed.setVolume(0))
|
5587 |
|
5588 | .then(() => embed.setCurrentTime(time))
|
5589 |
|
5590 | .then(() => restorePause && embed.pause())
|
5591 |
|
5592 | .then(() => restorePause && embed.setVolume(volume)).catch(() => {
|
5593 |
|
5594 | });
|
5595 | }
|
5596 | });
|
5597 |
|
5598 |
|
5599 | let speed = player.config.speed.selected;
|
5600 | Object.defineProperty(player.media, 'playbackRate', {
|
5601 | get() {
|
5602 | return speed;
|
5603 | },
|
5604 | set(input) {
|
5605 | player.embed.setPlaybackRate(input).then(() => {
|
5606 | speed = input;
|
5607 | triggerEvent.call(player, player.media, 'ratechange');
|
5608 | }).catch(() => {
|
5609 |
|
5610 | player.options.speed = [1];
|
5611 | });
|
5612 | }
|
5613 | });
|
5614 |
|
5615 |
|
5616 | let {
|
5617 | volume
|
5618 | } = player.config;
|
5619 | Object.defineProperty(player.media, 'volume', {
|
5620 | get() {
|
5621 | return volume;
|
5622 | },
|
5623 | set(input) {
|
5624 | player.embed.setVolume(input).then(() => {
|
5625 | volume = input;
|
5626 | triggerEvent.call(player, player.media, 'volumechange');
|
5627 | });
|
5628 | }
|
5629 | });
|
5630 |
|
5631 |
|
5632 | let {
|
5633 | muted
|
5634 | } = player.config;
|
5635 | Object.defineProperty(player.media, 'muted', {
|
5636 | get() {
|
5637 | return muted;
|
5638 | },
|
5639 | set(input) {
|
5640 | const toggle = is.boolean(input) ? input : false;
|
5641 | player.embed.setMuted(toggle ? true : player.config.muted).then(() => {
|
5642 | muted = toggle;
|
5643 | triggerEvent.call(player, player.media, 'volumechange');
|
5644 | });
|
5645 | }
|
5646 | });
|
5647 |
|
5648 |
|
5649 | let {
|
5650 | loop
|
5651 | } = player.config;
|
5652 | Object.defineProperty(player.media, 'loop', {
|
5653 | get() {
|
5654 | return loop;
|
5655 | },
|
5656 | set(input) {
|
5657 | const toggle = is.boolean(input) ? input : player.config.loop.active;
|
5658 | player.embed.setLoop(toggle).then(() => {
|
5659 | loop = toggle;
|
5660 | });
|
5661 | }
|
5662 | });
|
5663 |
|
5664 |
|
5665 | let currentSrc;
|
5666 | player.embed.getVideoUrl().then(value => {
|
5667 | currentSrc = value;
|
5668 | controls.setDownloadUrl.call(player);
|
5669 | }).catch(error => {
|
5670 | this.debug.warn(error);
|
5671 | });
|
5672 | Object.defineProperty(player.media, 'currentSrc', {
|
5673 | get() {
|
5674 | return currentSrc;
|
5675 | }
|
5676 | });
|
5677 |
|
5678 |
|
5679 | Object.defineProperty(player.media, 'ended', {
|
5680 | get() {
|
5681 | return player.currentTime === player.duration;
|
5682 | }
|
5683 | });
|
5684 |
|
5685 |
|
5686 | Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
|
5687 | const [width, height] = dimensions;
|
5688 | player.embed.ratio = roundAspectRatio(width, height);
|
5689 | setAspectRatio.call(this);
|
5690 | });
|
5691 |
|
5692 |
|
5693 | player.embed.setAutopause(player.config.autopause).then(state => {
|
5694 | player.config.autopause = state;
|
5695 | });
|
5696 |
|
5697 |
|
5698 | player.embed.getVideoTitle().then(title => {
|
5699 | player.config.title = title;
|
5700 | ui.setTitle.call(this);
|
5701 | });
|
5702 |
|
5703 |
|
5704 | player.embed.getCurrentTime().then(value => {
|
5705 | currentTime = value;
|
5706 | triggerEvent.call(player, player.media, 'timeupdate');
|
5707 | });
|
5708 |
|
5709 |
|
5710 | player.embed.getDuration().then(value => {
|
5711 | player.media.duration = value;
|
5712 | triggerEvent.call(player, player.media, 'durationchange');
|
5713 | });
|
5714 |
|
5715 |
|
5716 | player.embed.getTextTracks().then(tracks => {
|
5717 | player.media.textTracks = tracks;
|
5718 | captions.setup.call(player);
|
5719 | });
|
5720 | player.embed.on('cuechange', ({
|
5721 | cues = []
|
5722 | }) => {
|
5723 | const strippedCues = cues.map(cue => stripHTML(cue.text));
|
5724 | captions.updateCues.call(player, strippedCues);
|
5725 | });
|
5726 | player.embed.on('loaded', () => {
|
5727 |
|
5728 | player.embed.getPaused().then(paused => {
|
5729 | assurePlaybackState$1.call(player, !paused);
|
5730 | if (!paused) {
|
5731 | triggerEvent.call(player, player.media, 'playing');
|
5732 | }
|
5733 | });
|
5734 | if (is.element(player.embed.element) && player.supported.ui) {
|
5735 | const frame = player.embed.element;
|
5736 |
|
5737 |
|
5738 |
|
5739 | frame.setAttribute('tabindex', -1);
|
5740 | }
|
5741 | });
|
5742 | player.embed.on('bufferstart', () => {
|
5743 | triggerEvent.call(player, player.media, 'waiting');
|
5744 | });
|
5745 | player.embed.on('bufferend', () => {
|
5746 | triggerEvent.call(player, player.media, 'playing');
|
5747 | });
|
5748 | player.embed.on('play', () => {
|
5749 | assurePlaybackState$1.call(player, true);
|
5750 | triggerEvent.call(player, player.media, 'playing');
|
5751 | });
|
5752 | player.embed.on('pause', () => {
|
5753 | assurePlaybackState$1.call(player, false);
|
5754 | });
|
5755 | player.embed.on('timeupdate', data => {
|
5756 | player.media.seeking = false;
|
5757 | currentTime = data.seconds;
|
5758 | triggerEvent.call(player, player.media, 'timeupdate');
|
5759 | });
|
5760 | player.embed.on('progress', data => {
|
5761 | player.media.buffered = data.percent;
|
5762 | triggerEvent.call(player, player.media, 'progress');
|
5763 |
|
5764 |
|
5765 | if (parseInt(data.percent, 10) === 1) {
|
5766 | triggerEvent.call(player, player.media, 'canplaythrough');
|
5767 | }
|
5768 |
|
5769 |
|
5770 |
|
5771 | player.embed.getDuration().then(value => {
|
5772 | if (value !== player.media.duration) {
|
5773 | player.media.duration = value;
|
5774 | triggerEvent.call(player, player.media, 'durationchange');
|
5775 | }
|
5776 | });
|
5777 | });
|
5778 | player.embed.on('seeked', () => {
|
5779 | player.media.seeking = false;
|
5780 | triggerEvent.call(player, player.media, 'seeked');
|
5781 | });
|
5782 | player.embed.on('ended', () => {
|
5783 | player.media.paused = true;
|
5784 | triggerEvent.call(player, player.media, 'ended');
|
5785 | });
|
5786 | player.embed.on('error', detail => {
|
5787 | player.media.error = detail;
|
5788 | triggerEvent.call(player, player.media, 'error');
|
5789 | });
|
5790 |
|
5791 |
|
5792 | if (config.customControls) {
|
5793 | setTimeout(() => ui.build.call(player), 0);
|
5794 | }
|
5795 | }
|
5796 | };
|
5797 |
|
5798 |
|
5799 |
|
5800 |
|
5801 | function parseId(url) {
|
5802 | if (is.empty(url)) {
|
5803 | return null;
|
5804 | }
|
5805 | const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
|
5806 | return url.match(regex) ? RegExp.$2 : url;
|
5807 | }
|
5808 |
|
5809 |
|
5810 | function assurePlaybackState(play) {
|
5811 | if (play && !this.embed.hasPlayed) {
|
5812 | this.embed.hasPlayed = true;
|
5813 | }
|
5814 | if (this.media.paused === play) {
|
5815 | this.media.paused = !play;
|
5816 | triggerEvent.call(this, this.media, play ? 'play' : 'pause');
|
5817 | }
|
5818 | }
|
5819 | function getHost(config) {
|
5820 | if (config.noCookie) {
|
5821 | return 'https://www.youtube-nocookie.com';
|
5822 | }
|
5823 | if (window.location.protocol === 'http:') {
|
5824 | return 'http://www.youtube.com';
|
5825 | }
|
5826 |
|
5827 |
|
5828 | return undefined;
|
5829 | }
|
5830 | const youtube = {
|
5831 | setup() {
|
5832 |
|
5833 | toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
5834 |
|
5835 |
|
5836 | if (is.object(window.YT) && is.function(window.YT.Player)) {
|
5837 | youtube.ready.call(this);
|
5838 | } else {
|
5839 |
|
5840 | const callback = window.onYouTubeIframeAPIReady;
|
5841 |
|
5842 |
|
5843 | window.onYouTubeIframeAPIReady = () => {
|
5844 |
|
5845 | if (is.function(callback)) {
|
5846 | callback();
|
5847 | }
|
5848 | youtube.ready.call(this);
|
5849 | };
|
5850 |
|
5851 |
|
5852 | loadScript(this.config.urls.youtube.sdk).catch(error => {
|
5853 | this.debug.warn('YouTube API failed to load', error);
|
5854 | });
|
5855 | }
|
5856 | },
|
5857 |
|
5858 | getTitle(videoId) {
|
5859 | const url = format(this.config.urls.youtube.api, videoId);
|
5860 | fetch(url).then(data => {
|
5861 | if (is.object(data)) {
|
5862 | const {
|
5863 | title,
|
5864 | height,
|
5865 | width
|
5866 | } = data;
|
5867 |
|
5868 |
|
5869 | this.config.title = title;
|
5870 | ui.setTitle.call(this);
|
5871 |
|
5872 |
|
5873 | this.embed.ratio = roundAspectRatio(width, height);
|
5874 | }
|
5875 | setAspectRatio.call(this);
|
5876 | }).catch(() => {
|
5877 |
|
5878 | setAspectRatio.call(this);
|
5879 | });
|
5880 | },
|
5881 |
|
5882 | ready() {
|
5883 | const player = this;
|
5884 | const config = player.config.youtube;
|
5885 |
|
5886 | const currentId = player.media && player.media.getAttribute('id');
|
5887 | if (!is.empty(currentId) && currentId.startsWith('youtube-')) {
|
5888 | return;
|
5889 | }
|
5890 |
|
5891 |
|
5892 | let source = player.media.getAttribute('src');
|
5893 |
|
5894 |
|
5895 | if (is.empty(source)) {
|
5896 | source = player.media.getAttribute(this.config.attributes.embed.id);
|
5897 | }
|
5898 |
|
5899 |
|
5900 | const videoId = parseId(source);
|
5901 | const id = generateId(player.provider);
|
5902 |
|
5903 | const container = createElement('div', {
|
5904 | id,
|
5905 | 'data-poster': config.customControls ? player.poster : undefined
|
5906 | });
|
5907 | player.media = replaceElement(container, player.media);
|
5908 |
|
5909 |
|
5910 | if (config.customControls) {
|
5911 | const posterSrc = s => `https://i.ytimg.com/vi/${videoId}/${s}default.jpg`;
|
5912 |
|
5913 |
|
5914 | loadImage(posterSrc('maxres'), 121)
|
5915 | .catch(() => loadImage(posterSrc('sd'), 121))
|
5916 | .catch(() => loadImage(posterSrc('hq')))
|
5917 | .then(image => ui.setPoster.call(player, image.src)).then(src => {
|
5918 |
|
5919 | if (!src.includes('maxres')) {
|
5920 | player.elements.poster.style.backgroundSize = 'cover';
|
5921 | }
|
5922 | }).catch(() => {});
|
5923 | }
|
5924 |
|
5925 |
|
5926 |
|
5927 | player.embed = new window.YT.Player(player.media, {
|
5928 | videoId,
|
5929 | host: getHost(config),
|
5930 | playerVars: extend({}, {
|
5931 |
|
5932 | autoplay: player.config.autoplay ? 1 : 0,
|
5933 |
|
5934 | hl: player.config.hl,
|
5935 |
|
5936 | controls: player.supported.ui && config.customControls ? 0 : 1,
|
5937 |
|
5938 | disablekb: 1,
|
5939 |
|
5940 | playsinline: player.config.playsinline && !player.config.fullscreen.iosNative ? 1 : 0,
|
5941 |
|
5942 | cc_load_policy: player.captions.active ? 1 : 0,
|
5943 | cc_lang_pref: player.config.captions.language,
|
5944 |
|
5945 | widget_referrer: window ? window.location.href : null
|
5946 | }, config),
|
5947 | events: {
|
5948 | onError(event) {
|
5949 |
|
5950 | if (!player.media.error) {
|
5951 | const code = event.data;
|
5952 |
|
5953 | const message = {
|
5954 | 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.',
|
5955 | 5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
|
5956 | 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.',
|
5957 | 101: 'The owner of the requested video does not allow it to be played in embedded players.',
|
5958 | 150: 'The owner of the requested video does not allow it to be played in embedded players.'
|
5959 | }[code] || 'An unknown error occurred';
|
5960 | player.media.error = {
|
5961 | code,
|
5962 | message
|
5963 | };
|
5964 | triggerEvent.call(player, player.media, 'error');
|
5965 | }
|
5966 | },
|
5967 | onPlaybackRateChange(event) {
|
5968 |
|
5969 | const instance = event.target;
|
5970 |
|
5971 |
|
5972 | player.media.playbackRate = instance.getPlaybackRate();
|
5973 | triggerEvent.call(player, player.media, 'ratechange');
|
5974 | },
|
5975 | onReady(event) {
|
5976 |
|
5977 | if (is.function(player.media.play)) {
|
5978 | return;
|
5979 | }
|
5980 |
|
5981 | const instance = event.target;
|
5982 |
|
5983 |
|
5984 | youtube.getTitle.call(player, videoId);
|
5985 |
|
5986 |
|
5987 | player.media.play = () => {
|
5988 | assurePlaybackState.call(player, true);
|
5989 | instance.playVideo();
|
5990 | };
|
5991 | player.media.pause = () => {
|
5992 | assurePlaybackState.call(player, false);
|
5993 | instance.pauseVideo();
|
5994 | };
|
5995 | player.media.stop = () => {
|
5996 | instance.stopVideo();
|
5997 | };
|
5998 | player.media.duration = instance.getDuration();
|
5999 | player.media.paused = true;
|
6000 |
|
6001 |
|
6002 | player.media.currentTime = 0;
|
6003 | Object.defineProperty(player.media, 'currentTime', {
|
6004 | get() {
|
6005 | return Number(instance.getCurrentTime());
|
6006 | },
|
6007 | set(time) {
|
6008 |
|
6009 | if (player.paused && !player.embed.hasPlayed) {
|
6010 | player.embed.mute();
|
6011 | }
|
6012 |
|
6013 |
|
6014 | player.media.seeking = true;
|
6015 | triggerEvent.call(player, player.media, 'seeking');
|
6016 |
|
6017 |
|
6018 | instance.seekTo(time);
|
6019 | }
|
6020 | });
|
6021 |
|
6022 |
|
6023 | Object.defineProperty(player.media, 'playbackRate', {
|
6024 | get() {
|
6025 | return instance.getPlaybackRate();
|
6026 | },
|
6027 | set(input) {
|
6028 | instance.setPlaybackRate(input);
|
6029 | }
|
6030 | });
|
6031 |
|
6032 |
|
6033 | let {
|
6034 | volume
|
6035 | } = player.config;
|
6036 | Object.defineProperty(player.media, 'volume', {
|
6037 | get() {
|
6038 | return volume;
|
6039 | },
|
6040 | set(input) {
|
6041 | volume = input;
|
6042 | instance.setVolume(volume * 100);
|
6043 | triggerEvent.call(player, player.media, 'volumechange');
|
6044 | }
|
6045 | });
|
6046 |
|
6047 |
|
6048 | let {
|
6049 | muted
|
6050 | } = player.config;
|
6051 | Object.defineProperty(player.media, 'muted', {
|
6052 | get() {
|
6053 | return muted;
|
6054 | },
|
6055 | set(input) {
|
6056 | const toggle = is.boolean(input) ? input : muted;
|
6057 | muted = toggle;
|
6058 | instance[toggle ? 'mute' : 'unMute']();
|
6059 | instance.setVolume(volume * 100);
|
6060 | triggerEvent.call(player, player.media, 'volumechange');
|
6061 | }
|
6062 | });
|
6063 |
|
6064 |
|
6065 | Object.defineProperty(player.media, 'currentSrc', {
|
6066 | get() {
|
6067 | return instance.getVideoUrl();
|
6068 | }
|
6069 | });
|
6070 |
|
6071 |
|
6072 | Object.defineProperty(player.media, 'ended', {
|
6073 | get() {
|
6074 | return player.currentTime === player.duration;
|
6075 | }
|
6076 | });
|
6077 |
|
6078 |
|
6079 | const speeds = instance.getAvailablePlaybackRates();
|
6080 |
|
6081 | player.options.speed = speeds.filter(s => player.config.speed.options.includes(s));
|
6082 |
|
6083 |
|
6084 | if (player.supported.ui && config.customControls) {
|
6085 | player.media.setAttribute('tabindex', -1);
|
6086 | }
|
6087 | triggerEvent.call(player, player.media, 'timeupdate');
|
6088 | triggerEvent.call(player, player.media, 'durationchange');
|
6089 |
|
6090 |
|
6091 | clearInterval(player.timers.buffering);
|
6092 |
|
6093 |
|
6094 | player.timers.buffering = setInterval(() => {
|
6095 |
|
6096 | player.media.buffered = instance.getVideoLoadedFraction();
|
6097 |
|
6098 |
|
6099 | if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {
|
6100 | triggerEvent.call(player, player.media, 'progress');
|
6101 | }
|
6102 |
|
6103 |
|
6104 | player.media.lastBuffered = player.media.buffered;
|
6105 |
|
6106 |
|
6107 | if (player.media.buffered === 1) {
|
6108 | clearInterval(player.timers.buffering);
|
6109 |
|
6110 |
|
6111 | triggerEvent.call(player, player.media, 'canplaythrough');
|
6112 | }
|
6113 | }, 200);
|
6114 |
|
6115 |
|
6116 | if (config.customControls) {
|
6117 | setTimeout(() => ui.build.call(player), 50);
|
6118 | }
|
6119 | },
|
6120 | onStateChange(event) {
|
6121 |
|
6122 | const instance = event.target;
|
6123 |
|
6124 |
|
6125 | clearInterval(player.timers.playing);
|
6126 | const seeked = player.media.seeking && [1, 2].includes(event.data);
|
6127 | if (seeked) {
|
6128 |
|
6129 | player.media.seeking = false;
|
6130 | triggerEvent.call(player, player.media, 'seeked');
|
6131 | }
|
6132 |
|
6133 |
|
6134 |
|
6135 |
|
6136 |
|
6137 |
|
6138 |
|
6139 |
|
6140 | switch (event.data) {
|
6141 | case -1:
|
6142 |
|
6143 | triggerEvent.call(player, player.media, 'timeupdate');
|
6144 |
|
6145 |
|
6146 | player.media.buffered = instance.getVideoLoadedFraction();
|
6147 | triggerEvent.call(player, player.media, 'progress');
|
6148 | break;
|
6149 | case 0:
|
6150 | assurePlaybackState.call(player, false);
|
6151 |
|
6152 |
|
6153 | if (player.media.loop) {
|
6154 |
|
6155 | instance.stopVideo();
|
6156 | instance.playVideo();
|
6157 | } else {
|
6158 | triggerEvent.call(player, player.media, 'ended');
|
6159 | }
|
6160 | break;
|
6161 | case 1:
|
6162 |
|
6163 | if (config.customControls && !player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
|
6164 | player.media.pause();
|
6165 | } else {
|
6166 | assurePlaybackState.call(player, true);
|
6167 | triggerEvent.call(player, player.media, 'playing');
|
6168 |
|
6169 |
|
6170 | player.timers.playing = setInterval(() => {
|
6171 | triggerEvent.call(player, player.media, 'timeupdate');
|
6172 | }, 50);
|
6173 |
|
6174 |
|
6175 |
|
6176 |
|
6177 | if (player.media.duration !== instance.getDuration()) {
|
6178 | player.media.duration = instance.getDuration();
|
6179 | triggerEvent.call(player, player.media, 'durationchange');
|
6180 | }
|
6181 | }
|
6182 | break;
|
6183 | case 2:
|
6184 |
|
6185 | if (!player.muted) {
|
6186 | player.embed.unMute();
|
6187 | }
|
6188 | assurePlaybackState.call(player, false);
|
6189 | break;
|
6190 | case 3:
|
6191 |
|
6192 | triggerEvent.call(player, player.media, 'waiting');
|
6193 | break;
|
6194 | }
|
6195 | triggerEvent.call(player, player.elements.container, 'statechange', false, {
|
6196 | code: event.data
|
6197 | });
|
6198 | }
|
6199 | }
|
6200 | });
|
6201 | }
|
6202 | };
|
6203 |
|
6204 |
|
6205 | const media = {
|
6206 |
|
6207 | setup() {
|
6208 |
|
6209 | if (!this.media) {
|
6210 | this.debug.warn('No media element found!');
|
6211 | return;
|
6212 | }
|
6213 |
|
6214 |
|
6215 | toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true);
|
6216 |
|
6217 |
|
6218 | toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true);
|
6219 |
|
6220 |
|
6221 |
|
6222 | if (this.isEmbed) {
|
6223 | toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', 'video'), true);
|
6224 | }
|
6225 |
|
6226 |
|
6227 | if (this.isVideo) {
|
6228 |
|
6229 | this.elements.wrapper = createElement('div', {
|
6230 | class: this.config.classNames.video
|
6231 | });
|
6232 |
|
6233 |
|
6234 | wrap(this.media, this.elements.wrapper);
|
6235 |
|
6236 |
|
6237 | this.elements.poster = createElement('div', {
|
6238 | class: this.config.classNames.poster
|
6239 | });
|
6240 | this.elements.wrapper.appendChild(this.elements.poster);
|
6241 | }
|
6242 | if (this.isHTML5) {
|
6243 | html5.setup.call(this);
|
6244 | } else if (this.isYouTube) {
|
6245 | youtube.setup.call(this);
|
6246 | } else if (this.isVimeo) {
|
6247 | vimeo.setup.call(this);
|
6248 | }
|
6249 | }
|
6250 | };
|
6251 |
|
6252 | const destroy = instance => {
|
6253 |
|
6254 | if (instance.manager) {
|
6255 | instance.manager.destroy();
|
6256 | }
|
6257 |
|
6258 |
|
6259 | if (instance.elements.displayContainer) {
|
6260 | instance.elements.displayContainer.destroy();
|
6261 | }
|
6262 | instance.elements.container.remove();
|
6263 | };
|
6264 | class Ads {
|
6265 | |
6266 |
|
6267 |
|
6268 |
|
6269 |
|
6270 | constructor(player) {
|
6271 | |
6272 |
|
6273 |
|
6274 | _defineProperty$1(this, "load", () => {
|
6275 | if (!this.enabled) {
|
6276 | return;
|
6277 | }
|
6278 |
|
6279 |
|
6280 | if (!is.object(window.google) || !is.object(window.google.ima)) {
|
6281 | loadScript(this.player.config.urls.googleIMA.sdk).then(() => {
|
6282 | this.ready();
|
6283 | }).catch(() => {
|
6284 |
|
6285 | this.trigger('error', new Error('Google IMA SDK failed to load'));
|
6286 | });
|
6287 | } else {
|
6288 | this.ready();
|
6289 | }
|
6290 | });
|
6291 | |
6292 |
|
6293 |
|
6294 | _defineProperty$1(this, "ready", () => {
|
6295 |
|
6296 | if (!this.enabled) {
|
6297 | destroy(this);
|
6298 | }
|
6299 |
|
6300 |
|
6301 |
|
6302 | this.startSafetyTimer(12000, 'ready()');
|
6303 |
|
6304 |
|
6305 | this.managerPromise.then(() => {
|
6306 | this.clearSafetyTimer('onAdsManagerLoaded()');
|
6307 | });
|
6308 |
|
6309 |
|
6310 | this.listeners();
|
6311 |
|
6312 |
|
6313 | this.setupIMA();
|
6314 | });
|
6315 | |
6316 |
|
6317 |
|
6318 |
|
6319 |
|
6320 |
|
6321 |
|
6322 |
|
6323 | _defineProperty$1(this, "setupIMA", () => {
|
6324 |
|
6325 | this.elements.container = createElement('div', {
|
6326 | class: this.player.config.classNames.ads
|
6327 | });
|
6328 | this.player.elements.container.appendChild(this.elements.container);
|
6329 |
|
6330 |
|
6331 | google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);
|
6332 |
|
6333 |
|
6334 | google.ima.settings.setLocale(this.player.config.ads.language);
|
6335 |
|
6336 |
|
6337 | google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.player.config.playsinline);
|
6338 |
|
6339 |
|
6340 | this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container, this.player.media);
|
6341 |
|
6342 |
|
6343 | this.loader = new google.ima.AdsLoader(this.elements.displayContainer);
|
6344 |
|
6345 |
|
6346 | this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, event => this.onAdsManagerLoaded(event), false);
|
6347 | this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false);
|
6348 |
|
6349 |
|
6350 | this.requestAds();
|
6351 | });
|
6352 | |
6353 |
|
6354 |
|
6355 | _defineProperty$1(this, "requestAds", () => {
|
6356 | const {
|
6357 | container
|
6358 | } = this.player.elements;
|
6359 | try {
|
6360 |
|
6361 | const request = new google.ima.AdsRequest();
|
6362 | request.adTagUrl = this.tagUrl;
|
6363 |
|
6364 |
|
6365 |
|
6366 | request.linearAdSlotWidth = container.offsetWidth;
|
6367 | request.linearAdSlotHeight = container.offsetHeight;
|
6368 | request.nonLinearAdSlotWidth = container.offsetWidth;
|
6369 | request.nonLinearAdSlotHeight = container.offsetHeight;
|
6370 |
|
6371 |
|
6372 | request.forceNonLinearFullSlot = false;
|
6373 |
|
6374 |
|
6375 | request.setAdWillPlayMuted(!this.player.muted);
|
6376 | this.loader.requestAds(request);
|
6377 | } catch (error) {
|
6378 | this.onAdError(error);
|
6379 | }
|
6380 | });
|
6381 | |
6382 |
|
6383 |
|
6384 |
|
6385 | _defineProperty$1(this, "pollCountdown", (start = false) => {
|
6386 | if (!start) {
|
6387 | clearInterval(this.countdownTimer);
|
6388 | this.elements.container.removeAttribute('data-badge-text');
|
6389 | return;
|
6390 | }
|
6391 | const update = () => {
|
6392 | const time = formatTime(Math.max(this.manager.getRemainingTime(), 0));
|
6393 | const label = `${i18n.get('advertisement', this.player.config)} - ${time}`;
|
6394 | this.elements.container.setAttribute('data-badge-text', label);
|
6395 | };
|
6396 | this.countdownTimer = setInterval(update, 100);
|
6397 | });
|
6398 | |
6399 |
|
6400 |
|
6401 |
|
6402 | _defineProperty$1(this, "onAdsManagerLoaded", event => {
|
6403 |
|
6404 | if (!this.enabled) {
|
6405 | return;
|
6406 | }
|
6407 |
|
6408 |
|
6409 | const settings = new google.ima.AdsRenderingSettings();
|
6410 |
|
6411 |
|
6412 | settings.restoreCustomPlaybackStateOnAdBreakComplete = true;
|
6413 | settings.enablePreloading = true;
|
6414 |
|
6415 |
|
6416 |
|
6417 | this.manager = event.getAdsManager(this.player, settings);
|
6418 |
|
6419 |
|
6420 | this.cuePoints = this.manager.getCuePoints();
|
6421 |
|
6422 |
|
6423 |
|
6424 | this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error));
|
6425 |
|
6426 |
|
6427 | Object.keys(google.ima.AdEvent.Type).forEach(type => {
|
6428 | this.manager.addEventListener(google.ima.AdEvent.Type[type], e => this.onAdEvent(e));
|
6429 | });
|
6430 |
|
6431 |
|
6432 | this.trigger('loaded');
|
6433 | });
|
6434 | _defineProperty$1(this, "addCuePoints", () => {
|
6435 |
|
6436 | if (!is.empty(this.cuePoints)) {
|
6437 | this.cuePoints.forEach(cuePoint => {
|
6438 | if (cuePoint !== 0 && cuePoint !== -1 && cuePoint < this.player.duration) {
|
6439 | const seekElement = this.player.elements.progress;
|
6440 | if (is.element(seekElement)) {
|
6441 | const cuePercentage = 100 / this.player.duration * cuePoint;
|
6442 | const cue = createElement('span', {
|
6443 | class: this.player.config.classNames.cues
|
6444 | });
|
6445 | cue.style.left = `${cuePercentage.toString()}%`;
|
6446 | seekElement.appendChild(cue);
|
6447 | }
|
6448 | }
|
6449 | });
|
6450 | }
|
6451 | });
|
6452 | |
6453 |
|
6454 |
|
6455 |
|
6456 |
|
6457 |
|
6458 | _defineProperty$1(this, "onAdEvent", event => {
|
6459 | const {
|
6460 | container
|
6461 | } = this.player.elements;
|
6462 |
|
6463 |
|
6464 | const ad = event.getAd();
|
6465 | const adData = event.getAdData();
|
6466 |
|
6467 |
|
6468 | const dispatchEvent = type => {
|
6469 | triggerEvent.call(this.player, this.player.media, `ads${type.replace(/_/g, '').toLowerCase()}`);
|
6470 | };
|
6471 |
|
6472 |
|
6473 | dispatchEvent(event.type);
|
6474 | switch (event.type) {
|
6475 | case google.ima.AdEvent.Type.LOADED:
|
6476 |
|
6477 |
|
6478 | this.trigger('loaded');
|
6479 |
|
6480 |
|
6481 | this.pollCountdown(true);
|
6482 | if (!ad.isLinear()) {
|
6483 |
|
6484 | ad.width = container.offsetWidth;
|
6485 | ad.height = container.offsetHeight;
|
6486 | }
|
6487 |
|
6488 |
|
6489 |
|
6490 |
|
6491 | break;
|
6492 | case google.ima.AdEvent.Type.STARTED:
|
6493 |
|
6494 | this.manager.setVolume(this.player.volume);
|
6495 | break;
|
6496 | case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
|
6497 |
|
6498 |
|
6499 |
|
6500 |
|
6501 |
|
6502 |
|
6503 |
|
6504 |
|
6505 |
|
6506 |
|
6507 |
|
6508 |
|
6509 |
|
6510 |
|
6511 |
|
6512 |
|
6513 |
|
6514 |
|
6515 |
|
6516 |
|
6517 |
|
6518 |
|
6519 |
|
6520 |
|
6521 |
|
6522 |
|
6523 | if (this.player.ended) {
|
6524 | this.loadAds();
|
6525 | } else {
|
6526 |
|
6527 | this.loader.contentComplete();
|
6528 | }
|
6529 | break;
|
6530 | case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:
|
6531 |
|
6532 |
|
6533 |
|
6534 |
|
6535 | this.pauseContent();
|
6536 | break;
|
6537 | case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED:
|
6538 |
|
6539 |
|
6540 |
|
6541 |
|
6542 |
|
6543 | this.pollCountdown();
|
6544 | this.resumeContent();
|
6545 | break;
|
6546 | case google.ima.AdEvent.Type.LOG:
|
6547 | if (adData.adError) {
|
6548 | this.player.debug.warn(`Non-fatal ad error: ${adData.adError.getMessage()}`);
|
6549 | }
|
6550 | break;
|
6551 | }
|
6552 | });
|
6553 | |
6554 |
|
6555 |
|
6556 |
|
6557 | _defineProperty$1(this, "onAdError", event => {
|
6558 | this.cancel();
|
6559 | this.player.debug.warn('Ads error', event);
|
6560 | });
|
6561 | |
6562 |
|
6563 |
|
6564 |
|
6565 |
|
6566 | _defineProperty$1(this, "listeners", () => {
|
6567 | const {
|
6568 | container
|
6569 | } = this.player.elements;
|
6570 | let time;
|
6571 | this.player.on('canplay', () => {
|
6572 | this.addCuePoints();
|
6573 | });
|
6574 | this.player.on('ended', () => {
|
6575 | this.loader.contentComplete();
|
6576 | });
|
6577 | this.player.on('timeupdate', () => {
|
6578 | time = this.player.currentTime;
|
6579 | });
|
6580 | this.player.on('seeked', () => {
|
6581 | const seekedTime = this.player.currentTime;
|
6582 | if (is.empty(this.cuePoints)) {
|
6583 | return;
|
6584 | }
|
6585 | this.cuePoints.forEach((cuePoint, index) => {
|
6586 | if (time < cuePoint && cuePoint < seekedTime) {
|
6587 | this.manager.discardAdBreak();
|
6588 | this.cuePoints.splice(index, 1);
|
6589 | }
|
6590 | });
|
6591 | });
|
6592 |
|
6593 |
|
6594 |
|
6595 | window.addEventListener('resize', () => {
|
6596 | if (this.manager) {
|
6597 | this.manager.resize(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);
|
6598 | }
|
6599 | });
|
6600 | });
|
6601 | |
6602 |
|
6603 |
|
6604 | _defineProperty$1(this, "play", () => {
|
6605 | const {
|
6606 | container
|
6607 | } = this.player.elements;
|
6608 | if (!this.managerPromise) {
|
6609 | this.resumeContent();
|
6610 | }
|
6611 |
|
6612 |
|
6613 | this.managerPromise.then(() => {
|
6614 |
|
6615 | this.manager.setVolume(this.player.volume);
|
6616 |
|
6617 |
|
6618 | this.elements.displayContainer.initialize();
|
6619 | try {
|
6620 | if (!this.initialized) {
|
6621 |
|
6622 | this.manager.init(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);
|
6623 |
|
6624 |
|
6625 |
|
6626 | this.manager.start();
|
6627 | }
|
6628 | this.initialized = true;
|
6629 | } catch (adError) {
|
6630 |
|
6631 |
|
6632 | this.onAdError(adError);
|
6633 | }
|
6634 | }).catch(() => {});
|
6635 | });
|
6636 | |
6637 |
|
6638 |
|
6639 | _defineProperty$1(this, "resumeContent", () => {
|
6640 |
|
6641 | this.elements.container.style.zIndex = '';
|
6642 |
|
6643 |
|
6644 | this.playing = false;
|
6645 |
|
6646 |
|
6647 | silencePromise(this.player.media.play());
|
6648 | });
|
6649 | |
6650 |
|
6651 |
|
6652 | _defineProperty$1(this, "pauseContent", () => {
|
6653 |
|
6654 | this.elements.container.style.zIndex = 3;
|
6655 |
|
6656 |
|
6657 | this.playing = true;
|
6658 |
|
6659 |
|
6660 | this.player.media.pause();
|
6661 | });
|
6662 | |
6663 |
|
6664 |
|
6665 |
|
6666 |
|
6667 |
|
6668 | _defineProperty$1(this, "cancel", () => {
|
6669 |
|
6670 | if (this.initialized) {
|
6671 | this.resumeContent();
|
6672 | }
|
6673 |
|
6674 |
|
6675 | this.trigger('error');
|
6676 |
|
6677 |
|
6678 | this.loadAds();
|
6679 | });
|
6680 | |
6681 |
|
6682 |
|
6683 | _defineProperty$1(this, "loadAds", () => {
|
6684 |
|
6685 | this.managerPromise.then(() => {
|
6686 |
|
6687 | if (this.manager) {
|
6688 | this.manager.destroy();
|
6689 | }
|
6690 |
|
6691 |
|
6692 | this.managerPromise = new Promise(resolve => {
|
6693 | this.on('loaded', resolve);
|
6694 | this.player.debug.log(this.manager);
|
6695 | });
|
6696 |
|
6697 | this.initialized = false;
|
6698 |
|
6699 |
|
6700 | this.requestAds();
|
6701 | }).catch(() => {});
|
6702 | });
|
6703 | |
6704 |
|
6705 |
|
6706 |
|
6707 |
|
6708 | _defineProperty$1(this, "trigger", (event, ...args) => {
|
6709 | const handlers = this.events[event];
|
6710 | if (is.array(handlers)) {
|
6711 | handlers.forEach(handler => {
|
6712 | if (is.function(handler)) {
|
6713 | handler.apply(this, args);
|
6714 | }
|
6715 | });
|
6716 | }
|
6717 | });
|
6718 | |
6719 |
|
6720 |
|
6721 |
|
6722 |
|
6723 |
|
6724 | _defineProperty$1(this, "on", (event, callback) => {
|
6725 | if (!is.array(this.events[event])) {
|
6726 | this.events[event] = [];
|
6727 | }
|
6728 | this.events[event].push(callback);
|
6729 | return this;
|
6730 | });
|
6731 | |
6732 |
|
6733 |
|
6734 |
|
6735 |
|
6736 |
|
6737 |
|
6738 |
|
6739 | _defineProperty$1(this, "startSafetyTimer", (time, from) => {
|
6740 | this.player.debug.log(`Safety timer invoked from: ${from}`);
|
6741 | this.safetyTimer = setTimeout(() => {
|
6742 | this.cancel();
|
6743 | this.clearSafetyTimer('startSafetyTimer()');
|
6744 | }, time);
|
6745 | });
|
6746 | |
6747 |
|
6748 |
|
6749 |
|
6750 | _defineProperty$1(this, "clearSafetyTimer", from => {
|
6751 | if (!is.nullOrUndefined(this.safetyTimer)) {
|
6752 | this.player.debug.log(`Safety timer cleared from: ${from}`);
|
6753 | clearTimeout(this.safetyTimer);
|
6754 | this.safetyTimer = null;
|
6755 | }
|
6756 | });
|
6757 | this.player = player;
|
6758 | this.config = player.config.ads;
|
6759 | this.playing = false;
|
6760 | this.initialized = false;
|
6761 | this.elements = {
|
6762 | container: null,
|
6763 | displayContainer: null
|
6764 | };
|
6765 | this.manager = null;
|
6766 | this.loader = null;
|
6767 | this.cuePoints = null;
|
6768 | this.events = {};
|
6769 | this.safetyTimer = null;
|
6770 | this.countdownTimer = null;
|
6771 |
|
6772 |
|
6773 | this.managerPromise = new Promise((resolve, reject) => {
|
6774 |
|
6775 | this.on('loaded', resolve);
|
6776 |
|
6777 |
|
6778 | this.on('error', reject);
|
6779 | });
|
6780 | this.load();
|
6781 | }
|
6782 | get enabled() {
|
6783 | const {
|
6784 | config
|
6785 | } = this;
|
6786 | return this.player.isHTML5 && this.player.isVideo && config.enabled && (!is.empty(config.publisherId) || is.url(config.tagUrl));
|
6787 | }
|
6788 |
|
6789 | get tagUrl() {
|
6790 | const {
|
6791 | config
|
6792 | } = this;
|
6793 | if (is.url(config.tagUrl)) {
|
6794 | return config.tagUrl;
|
6795 | }
|
6796 | const params = {
|
6797 | AV_PUBLISHERID: '58c25bb0073ef448b1087ad6',
|
6798 | AV_CHANNELID: '5a0458dc28a06145e4519d21',
|
6799 | AV_URL: window.location.hostname,
|
6800 | cb: Date.now(),
|
6801 | AV_WIDTH: 640,
|
6802 | AV_HEIGHT: 480,
|
6803 | AV_CDIM2: config.publisherId
|
6804 | };
|
6805 | const base = 'https://go.aniview.com/api/adserver6/vast/';
|
6806 | return `${base}?${buildUrlParams(params)}`;
|
6807 | }
|
6808 | }
|
6809 |
|
6810 | |
6811 |
|
6812 |
|
6813 |
|
6814 |
|
6815 |
|
6816 |
|
6817 |
|
6818 |
|
6819 |
|
6820 |
|
6821 |
|
6822 | function clamp(input = 0, min = 0, max = 255) {
|
6823 | return Math.min(Math.max(input, min), max);
|
6824 | }
|
6825 |
|
6826 |
|
6827 | const parseVtt = vttDataString => {
|
6828 | const processedList = [];
|
6829 | const frames = vttDataString.split(/\r\n\r\n|\n\n|\r\r/);
|
6830 | frames.forEach(frame => {
|
6831 | const result = {};
|
6832 | const lines = frame.split(/\r\n|\n|\r/);
|
6833 | lines.forEach(line => {
|
6834 | if (!is.number(result.startTime)) {
|
6835 |
|
6836 | const matchTimes = line.match(/([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})/);
|
6837 |
|
6838 | if (matchTimes) {
|
6839 | result.startTime = Number(matchTimes[1] || 0) * 60 * 60 + Number(matchTimes[2]) * 60 + Number(matchTimes[3]) + Number(`0.${matchTimes[4]}`);
|
6840 | result.endTime = Number(matchTimes[6] || 0) * 60 * 60 + Number(matchTimes[7]) * 60 + Number(matchTimes[8]) + Number(`0.${matchTimes[9]}`);
|
6841 | }
|
6842 | } else if (!is.empty(line.trim()) && is.empty(result.text)) {
|
6843 |
|
6844 | const lineSplit = line.trim().split('#xywh=');
|
6845 | [result.text] = lineSplit;
|
6846 |
|
6847 |
|
6848 | if (lineSplit[1]) {
|
6849 | [result.x, result.y, result.w, result.h] = lineSplit[1].split(',');
|
6850 | }
|
6851 | }
|
6852 | });
|
6853 | if (result.text) {
|
6854 | processedList.push(result);
|
6855 | }
|
6856 | });
|
6857 | return processedList;
|
6858 | };
|
6859 |
|
6860 | |
6861 |
|
6862 |
|
6863 |
|
6864 |
|
6865 |
|
6866 |
|
6867 |
|
6868 |
|
6869 |
|
6870 |
|
6871 | const fitRatio = (ratio, outer) => {
|
6872 | const targetRatio = outer.width / outer.height;
|
6873 | const result = {};
|
6874 | if (ratio > targetRatio) {
|
6875 | result.width = outer.width;
|
6876 | result.height = 1 / ratio * outer.width;
|
6877 | } else {
|
6878 | result.height = outer.height;
|
6879 | result.width = ratio * outer.height;
|
6880 | }
|
6881 | return result;
|
6882 | };
|
6883 | class PreviewThumbnails {
|
6884 | |
6885 |
|
6886 |
|
6887 |
|
6888 |
|
6889 | constructor(player) {
|
6890 | _defineProperty$1(this, "load", () => {
|
6891 |
|
6892 | if (this.player.elements.display.seekTooltip) {
|
6893 | this.player.elements.display.seekTooltip.hidden = this.enabled;
|
6894 | }
|
6895 | if (!this.enabled) return;
|
6896 | this.getThumbnails().then(() => {
|
6897 | if (!this.enabled) {
|
6898 | return;
|
6899 | }
|
6900 |
|
6901 |
|
6902 | this.render();
|
6903 |
|
6904 |
|
6905 | this.determineContainerAutoSizing();
|
6906 |
|
6907 |
|
6908 | this.listeners();
|
6909 | this.loaded = true;
|
6910 | });
|
6911 | });
|
6912 |
|
6913 | _defineProperty$1(this, "getThumbnails", () => {
|
6914 | return new Promise(resolve => {
|
6915 | const {
|
6916 | src
|
6917 | } = this.player.config.previewThumbnails;
|
6918 | if (is.empty(src)) {
|
6919 | throw new Error('Missing previewThumbnails.src config attribute');
|
6920 | }
|
6921 |
|
6922 |
|
6923 | const sortAndResolve = () => {
|
6924 |
|
6925 | this.thumbnails.sort((x, y) => x.height - y.height);
|
6926 | this.player.debug.log('Preview thumbnails', this.thumbnails);
|
6927 | resolve();
|
6928 | };
|
6929 |
|
6930 |
|
6931 | if (is.function(src)) {
|
6932 | src(thumbnails => {
|
6933 | this.thumbnails = thumbnails;
|
6934 | sortAndResolve();
|
6935 | });
|
6936 | }
|
6937 |
|
6938 | else {
|
6939 |
|
6940 | const urls = is.string(src) ? [src] : src;
|
6941 |
|
6942 | const promises = urls.map(u => this.getThumbnail(u));
|
6943 |
|
6944 | Promise.all(promises).then(sortAndResolve);
|
6945 | }
|
6946 | });
|
6947 | });
|
6948 |
|
6949 | _defineProperty$1(this, "getThumbnail", url => {
|
6950 | return new Promise(resolve => {
|
6951 | fetch(url).then(response => {
|
6952 | const thumbnail = {
|
6953 | frames: parseVtt(response),
|
6954 | height: null,
|
6955 | urlPrefix: ''
|
6956 | };
|
6957 |
|
6958 |
|
6959 |
|
6960 |
|
6961 | if (!thumbnail.frames[0].text.startsWith('/') && !thumbnail.frames[0].text.startsWith('http://') && !thumbnail.frames[0].text.startsWith('https://')) {
|
6962 | thumbnail.urlPrefix = url.substring(0, url.lastIndexOf('/') + 1);
|
6963 | }
|
6964 |
|
6965 |
|
6966 | const tempImage = new Image();
|
6967 | tempImage.onload = () => {
|
6968 | thumbnail.height = tempImage.naturalHeight;
|
6969 | thumbnail.width = tempImage.naturalWidth;
|
6970 | this.thumbnails.push(thumbnail);
|
6971 | resolve();
|
6972 | };
|
6973 | tempImage.src = thumbnail.urlPrefix + thumbnail.frames[0].text;
|
6974 | });
|
6975 | });
|
6976 | });
|
6977 | _defineProperty$1(this, "startMove", event => {
|
6978 | if (!this.loaded) return;
|
6979 | if (!is.event(event) || !['touchmove', 'mousemove'].includes(event.type)) return;
|
6980 |
|
6981 |
|
6982 | if (!this.player.media.duration) return;
|
6983 | if (event.type === 'touchmove') {
|
6984 |
|
6985 | this.seekTime = this.player.media.duration * (this.player.elements.inputs.seek.value / 100);
|
6986 | } else {
|
6987 | var _this$player$config$m, _this$player$config$m2;
|
6988 |
|
6989 | const clientRect = this.player.elements.progress.getBoundingClientRect();
|
6990 | const percentage = 100 / clientRect.width * (event.pageX - clientRect.left);
|
6991 | this.seekTime = this.player.media.duration * (percentage / 100);
|
6992 | if (this.seekTime < 0) {
|
6993 |
|
6994 | this.seekTime = 0;
|
6995 | }
|
6996 | if (this.seekTime > this.player.media.duration - 1) {
|
6997 |
|
6998 | this.seekTime = this.player.media.duration - 1;
|
6999 | }
|
7000 | this.mousePosX = event.pageX;
|
7001 |
|
7002 |
|
7003 | this.elements.thumb.time.innerText = formatTime(this.seekTime);
|
7004 |
|
7005 |
|
7006 | const point = (_this$player$config$m = this.player.config.markers) === null || _this$player$config$m === void 0 ? void 0 : (_this$player$config$m2 = _this$player$config$m.points) === null || _this$player$config$m2 === void 0 ? void 0 : _this$player$config$m2.find(({
|
7007 | time: t
|
7008 | }) => t === Math.round(this.seekTime));
|
7009 |
|
7010 |
|
7011 | if (point) {
|
7012 |
|
7013 | this.elements.thumb.time.insertAdjacentHTML('afterbegin', `${point.label}<br>`);
|
7014 | }
|
7015 | }
|
7016 |
|
7017 |
|
7018 | this.showImageAtCurrentTime();
|
7019 | });
|
7020 | _defineProperty$1(this, "endMove", () => {
|
7021 | this.toggleThumbContainer(false, true);
|
7022 | });
|
7023 | _defineProperty$1(this, "startScrubbing", event => {
|
7024 |
|
7025 | if (is.nullOrUndefined(event.button) || event.button === false || event.button === 0) {
|
7026 | this.mouseDown = true;
|
7027 |
|
7028 |
|
7029 | if (this.player.media.duration) {
|
7030 | this.toggleScrubbingContainer(true);
|
7031 | this.toggleThumbContainer(false, true);
|
7032 |
|
7033 |
|
7034 | this.showImageAtCurrentTime();
|
7035 | }
|
7036 | }
|
7037 | });
|
7038 | _defineProperty$1(this, "endScrubbing", () => {
|
7039 | this.mouseDown = false;
|
7040 |
|
7041 |
|
7042 | if (Math.ceil(this.lastTime) === Math.ceil(this.player.media.currentTime)) {
|
7043 |
|
7044 | this.toggleScrubbingContainer(false);
|
7045 | } else {
|
7046 |
|
7047 | once.call(this.player, this.player.media, 'timeupdate', () => {
|
7048 |
|
7049 | if (!this.mouseDown) {
|
7050 | this.toggleScrubbingContainer(false);
|
7051 | }
|
7052 | });
|
7053 | }
|
7054 | });
|
7055 | |
7056 |
|
7057 |
|
7058 | _defineProperty$1(this, "listeners", () => {
|
7059 |
|
7060 | this.player.on('play', () => {
|
7061 | this.toggleThumbContainer(false, true);
|
7062 | });
|
7063 | this.player.on('seeked', () => {
|
7064 | this.toggleThumbContainer(false);
|
7065 | });
|
7066 | this.player.on('timeupdate', () => {
|
7067 | this.lastTime = this.player.media.currentTime;
|
7068 | });
|
7069 | });
|
7070 | |
7071 |
|
7072 |
|
7073 | _defineProperty$1(this, "render", () => {
|
7074 |
|
7075 | this.elements.thumb.container = createElement('div', {
|
7076 | class: this.player.config.classNames.previewThumbnails.thumbContainer
|
7077 | });
|
7078 |
|
7079 |
|
7080 | this.elements.thumb.imageContainer = createElement('div', {
|
7081 | class: this.player.config.classNames.previewThumbnails.imageContainer
|
7082 | });
|
7083 | this.elements.thumb.container.appendChild(this.elements.thumb.imageContainer);
|
7084 |
|
7085 |
|
7086 | const timeContainer = createElement('div', {
|
7087 | class: this.player.config.classNames.previewThumbnails.timeContainer
|
7088 | });
|
7089 | this.elements.thumb.time = createElement('span', {}, '00:00');
|
7090 | timeContainer.appendChild(this.elements.thumb.time);
|
7091 | this.elements.thumb.imageContainer.appendChild(timeContainer);
|
7092 |
|
7093 |
|
7094 | if (is.element(this.player.elements.progress)) {
|
7095 | this.player.elements.progress.appendChild(this.elements.thumb.container);
|
7096 | }
|
7097 |
|
7098 |
|
7099 | this.elements.scrubbing.container = createElement('div', {
|
7100 | class: this.player.config.classNames.previewThumbnails.scrubbingContainer
|
7101 | });
|
7102 | this.player.elements.wrapper.appendChild(this.elements.scrubbing.container);
|
7103 | });
|
7104 | _defineProperty$1(this, "destroy", () => {
|
7105 | if (this.elements.thumb.container) {
|
7106 | this.elements.thumb.container.remove();
|
7107 | }
|
7108 | if (this.elements.scrubbing.container) {
|
7109 | this.elements.scrubbing.container.remove();
|
7110 | }
|
7111 | });
|
7112 | _defineProperty$1(this, "showImageAtCurrentTime", () => {
|
7113 | if (this.mouseDown) {
|
7114 | this.setScrubbingContainerSize();
|
7115 | } else {
|
7116 | this.setThumbContainerSizeAndPos();
|
7117 | }
|
7118 |
|
7119 |
|
7120 |
|
7121 | const thumbNum = this.thumbnails[0].frames.findIndex(frame => this.seekTime >= frame.startTime && this.seekTime <= frame.endTime);
|
7122 | const hasThumb = thumbNum >= 0;
|
7123 | let qualityIndex = 0;
|
7124 |
|
7125 |
|
7126 | if (!this.mouseDown) {
|
7127 | this.toggleThumbContainer(hasThumb);
|
7128 | }
|
7129 |
|
7130 |
|
7131 | if (!hasThumb) {
|
7132 | return;
|
7133 | }
|
7134 |
|
7135 |
|
7136 | this.thumbnails.forEach((thumbnail, index) => {
|
7137 | if (this.loadedImages.includes(thumbnail.frames[thumbNum].text)) {
|
7138 | qualityIndex = index;
|
7139 | }
|
7140 | });
|
7141 |
|
7142 |
|
7143 | if (thumbNum !== this.showingThumb) {
|
7144 | this.showingThumb = thumbNum;
|
7145 | this.loadImage(qualityIndex);
|
7146 | }
|
7147 | });
|
7148 |
|
7149 | _defineProperty$1(this, "loadImage", (qualityIndex = 0) => {
|
7150 | const thumbNum = this.showingThumb;
|
7151 | const thumbnail = this.thumbnails[qualityIndex];
|
7152 | const {
|
7153 | urlPrefix
|
7154 | } = thumbnail;
|
7155 | const frame = thumbnail.frames[thumbNum];
|
7156 | const thumbFilename = thumbnail.frames[thumbNum].text;
|
7157 | const thumbUrl = urlPrefix + thumbFilename;
|
7158 | if (!this.currentImageElement || this.currentImageElement.dataset.filename !== thumbFilename) {
|
7159 |
|
7160 |
|
7161 | if (this.loadingImage && this.usingSprites) {
|
7162 | this.loadingImage.onload = null;
|
7163 | }
|
7164 |
|
7165 |
|
7166 |
|
7167 |
|
7168 | const previewImage = new Image();
|
7169 | previewImage.src = thumbUrl;
|
7170 | previewImage.dataset.index = thumbNum;
|
7171 | previewImage.dataset.filename = thumbFilename;
|
7172 | this.showingThumbFilename = thumbFilename;
|
7173 | this.player.debug.log(`Loading image: ${thumbUrl}`);
|
7174 |
|
7175 |
|
7176 | previewImage.onload = () => this.showImage(previewImage, frame, qualityIndex, thumbNum, thumbFilename, true);
|
7177 | this.loadingImage = previewImage;
|
7178 | this.removeOldImages(previewImage);
|
7179 | } else {
|
7180 |
|
7181 | this.showImage(this.currentImageElement, frame, qualityIndex, thumbNum, thumbFilename, false);
|
7182 | this.currentImageElement.dataset.index = thumbNum;
|
7183 | this.removeOldImages(this.currentImageElement);
|
7184 | }
|
7185 | });
|
7186 | _defineProperty$1(this, "showImage", (previewImage, frame, qualityIndex, thumbNum, thumbFilename, newImage = true) => {
|
7187 | this.player.debug.log(`Showing thumb: ${thumbFilename}. num: ${thumbNum}. qual: ${qualityIndex}. newimg: ${newImage}`);
|
7188 | this.setImageSizeAndOffset(previewImage, frame);
|
7189 | if (newImage) {
|
7190 | this.currentImageContainer.appendChild(previewImage);
|
7191 | this.currentImageElement = previewImage;
|
7192 | if (!this.loadedImages.includes(thumbFilename)) {
|
7193 | this.loadedImages.push(thumbFilename);
|
7194 | }
|
7195 | }
|
7196 |
|
7197 |
|
7198 |
|
7199 |
|
7200 | this.preloadNearby(thumbNum, true).then(this.preloadNearby(thumbNum, false)).then(this.getHigherQuality(qualityIndex, previewImage, frame, thumbFilename));
|
7201 | });
|
7202 |
|
7203 | _defineProperty$1(this, "removeOldImages", currentImage => {
|
7204 |
|
7205 | Array.from(this.currentImageContainer.children).forEach(image => {
|
7206 | if (image.tagName.toLowerCase() !== 'img') {
|
7207 | return;
|
7208 | }
|
7209 | const removeDelay = this.usingSprites ? 500 : 1000;
|
7210 | if (image.dataset.index !== currentImage.dataset.index && !image.dataset.deleting) {
|
7211 |
|
7212 |
|
7213 |
|
7214 | image.dataset.deleting = true;
|
7215 |
|
7216 |
|
7217 | const {
|
7218 | currentImageContainer
|
7219 | } = this;
|
7220 | setTimeout(() => {
|
7221 | currentImageContainer.removeChild(image);
|
7222 | this.player.debug.log(`Removing thumb: ${image.dataset.filename}`);
|
7223 | }, removeDelay);
|
7224 | }
|
7225 | });
|
7226 | });
|
7227 |
|
7228 |
|
7229 | _defineProperty$1(this, "preloadNearby", (thumbNum, forward = true) => {
|
7230 | return new Promise(resolve => {
|
7231 | setTimeout(() => {
|
7232 | const oldThumbFilename = this.thumbnails[0].frames[thumbNum].text;
|
7233 | if (this.showingThumbFilename === oldThumbFilename) {
|
7234 |
|
7235 | let thumbnailsClone;
|
7236 | if (forward) {
|
7237 | thumbnailsClone = this.thumbnails[0].frames.slice(thumbNum);
|
7238 | } else {
|
7239 | thumbnailsClone = this.thumbnails[0].frames.slice(0, thumbNum).reverse();
|
7240 | }
|
7241 | let foundOne = false;
|
7242 | thumbnailsClone.forEach(frame => {
|
7243 | const newThumbFilename = frame.text;
|
7244 | if (newThumbFilename !== oldThumbFilename) {
|
7245 |
|
7246 | if (!this.loadedImages.includes(newThumbFilename)) {
|
7247 | foundOne = true;
|
7248 | this.player.debug.log(`Preloading thumb filename: ${newThumbFilename}`);
|
7249 | const {
|
7250 | urlPrefix
|
7251 | } = this.thumbnails[0];
|
7252 | const thumbURL = urlPrefix + newThumbFilename;
|
7253 | const previewImage = new Image();
|
7254 | previewImage.src = thumbURL;
|
7255 | previewImage.onload = () => {
|
7256 | this.player.debug.log(`Preloaded thumb filename: ${newThumbFilename}`);
|
7257 | if (!this.loadedImages.includes(newThumbFilename)) this.loadedImages.push(newThumbFilename);
|
7258 |
|
7259 |
|
7260 | resolve();
|
7261 | };
|
7262 | }
|
7263 | }
|
7264 | });
|
7265 |
|
7266 |
|
7267 | if (!foundOne) {
|
7268 | resolve();
|
7269 | }
|
7270 | }
|
7271 | }, 300);
|
7272 | });
|
7273 | });
|
7274 |
|
7275 | _defineProperty$1(this, "getHigherQuality", (currentQualityIndex, previewImage, frame, thumbFilename) => {
|
7276 | if (currentQualityIndex < this.thumbnails.length - 1) {
|
7277 |
|
7278 | let previewImageHeight = previewImage.naturalHeight;
|
7279 | if (this.usingSprites) {
|
7280 | previewImageHeight = frame.h;
|
7281 | }
|
7282 | if (previewImageHeight < this.thumbContainerHeight) {
|
7283 |
|
7284 | setTimeout(() => {
|
7285 |
|
7286 | if (this.showingThumbFilename === thumbFilename) {
|
7287 | this.player.debug.log(`Showing higher quality thumb for: ${thumbFilename}`);
|
7288 | this.loadImage(currentQualityIndex + 1);
|
7289 | }
|
7290 | }, 300);
|
7291 | }
|
7292 | }
|
7293 | });
|
7294 | _defineProperty$1(this, "toggleThumbContainer", (toggle = false, clearShowing = false) => {
|
7295 | const className = this.player.config.classNames.previewThumbnails.thumbContainerShown;
|
7296 | this.elements.thumb.container.classList.toggle(className, toggle);
|
7297 | if (!toggle && clearShowing) {
|
7298 | this.showingThumb = null;
|
7299 | this.showingThumbFilename = null;
|
7300 | }
|
7301 | });
|
7302 | _defineProperty$1(this, "toggleScrubbingContainer", (toggle = false) => {
|
7303 | const className = this.player.config.classNames.previewThumbnails.scrubbingContainerShown;
|
7304 | this.elements.scrubbing.container.classList.toggle(className, toggle);
|
7305 | if (!toggle) {
|
7306 | this.showingThumb = null;
|
7307 | this.showingThumbFilename = null;
|
7308 | }
|
7309 | });
|
7310 | _defineProperty$1(this, "determineContainerAutoSizing", () => {
|
7311 | if (this.elements.thumb.imageContainer.clientHeight > 20 || this.elements.thumb.imageContainer.clientWidth > 20) {
|
7312 |
|
7313 | this.sizeSpecifiedInCSS = true;
|
7314 | }
|
7315 | });
|
7316 |
|
7317 | _defineProperty$1(this, "setThumbContainerSizeAndPos", () => {
|
7318 | const {
|
7319 | imageContainer
|
7320 | } = this.elements.thumb;
|
7321 | if (!this.sizeSpecifiedInCSS) {
|
7322 | const thumbWidth = Math.floor(this.thumbContainerHeight * this.thumbAspectRatio);
|
7323 | imageContainer.style.height = `${this.thumbContainerHeight}px`;
|
7324 | imageContainer.style.width = `${thumbWidth}px`;
|
7325 | } else if (imageContainer.clientHeight > 20 && imageContainer.clientWidth < 20) {
|
7326 | const thumbWidth = Math.floor(imageContainer.clientHeight * this.thumbAspectRatio);
|
7327 | imageContainer.style.width = `${thumbWidth}px`;
|
7328 | } else if (imageContainer.clientHeight < 20 && imageContainer.clientWidth > 20) {
|
7329 | const thumbHeight = Math.floor(imageContainer.clientWidth / this.thumbAspectRatio);
|
7330 | imageContainer.style.height = `${thumbHeight}px`;
|
7331 | }
|
7332 | this.setThumbContainerPos();
|
7333 | });
|
7334 | _defineProperty$1(this, "setThumbContainerPos", () => {
|
7335 | const scrubberRect = this.player.elements.progress.getBoundingClientRect();
|
7336 | const containerRect = this.player.elements.container.getBoundingClientRect();
|
7337 | const {
|
7338 | container
|
7339 | } = this.elements.thumb;
|
7340 |
|
7341 | const min = containerRect.left - scrubberRect.left + 10;
|
7342 | const max = containerRect.right - scrubberRect.left - container.clientWidth - 10;
|
7343 |
|
7344 | const position = this.mousePosX - scrubberRect.left - container.clientWidth / 2;
|
7345 | const clamped = clamp(position, min, max);
|
7346 |
|
7347 |
|
7348 | container.style.left = `${clamped}px`;
|
7349 |
|
7350 |
|
7351 | container.style.setProperty('--preview-arrow-offset', `${position - clamped}px`);
|
7352 | });
|
7353 |
|
7354 | _defineProperty$1(this, "setScrubbingContainerSize", () => {
|
7355 | const {
|
7356 | width,
|
7357 | height
|
7358 | } = fitRatio(this.thumbAspectRatio, {
|
7359 | width: this.player.media.clientWidth,
|
7360 | height: this.player.media.clientHeight
|
7361 | });
|
7362 | this.elements.scrubbing.container.style.width = `${width}px`;
|
7363 | this.elements.scrubbing.container.style.height = `${height}px`;
|
7364 | });
|
7365 |
|
7366 | _defineProperty$1(this, "setImageSizeAndOffset", (previewImage, frame) => {
|
7367 | if (!this.usingSprites) return;
|
7368 |
|
7369 |
|
7370 | const multiplier = this.thumbContainerHeight / frame.h;
|
7371 |
|
7372 |
|
7373 | previewImage.style.height = `${previewImage.naturalHeight * multiplier}px`;
|
7374 |
|
7375 | previewImage.style.width = `${previewImage.naturalWidth * multiplier}px`;
|
7376 |
|
7377 | previewImage.style.left = `-${frame.x * multiplier}px`;
|
7378 |
|
7379 | previewImage.style.top = `-${frame.y * multiplier}px`;
|
7380 | });
|
7381 | this.player = player;
|
7382 | this.thumbnails = [];
|
7383 | this.loaded = false;
|
7384 | this.lastMouseMoveTime = Date.now();
|
7385 | this.mouseDown = false;
|
7386 | this.loadedImages = [];
|
7387 | this.elements = {
|
7388 | thumb: {},
|
7389 | scrubbing: {}
|
7390 | };
|
7391 | this.load();
|
7392 | }
|
7393 | get enabled() {
|
7394 | return this.player.isHTML5 && this.player.isVideo && this.player.config.previewThumbnails.enabled;
|
7395 | }
|
7396 | get currentImageContainer() {
|
7397 | return this.mouseDown ? this.elements.scrubbing.container : this.elements.thumb.imageContainer;
|
7398 | }
|
7399 | get usingSprites() {
|
7400 | return Object.keys(this.thumbnails[0].frames[0]).includes('w');
|
7401 | }
|
7402 | get thumbAspectRatio() {
|
7403 | if (this.usingSprites) {
|
7404 | return this.thumbnails[0].frames[0].w / this.thumbnails[0].frames[0].h;
|
7405 | }
|
7406 | return this.thumbnails[0].width / this.thumbnails[0].height;
|
7407 | }
|
7408 | get thumbContainerHeight() {
|
7409 | if (this.mouseDown) {
|
7410 | const {
|
7411 | height
|
7412 | } = fitRatio(this.thumbAspectRatio, {
|
7413 | width: this.player.media.clientWidth,
|
7414 | height: this.player.media.clientHeight
|
7415 | });
|
7416 | return height;
|
7417 | }
|
7418 |
|
7419 |
|
7420 | if (this.sizeSpecifiedInCSS) {
|
7421 | return this.elements.thumb.imageContainer.clientHeight;
|
7422 | }
|
7423 | return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
|
7424 | }
|
7425 | get currentImageElement() {
|
7426 | return this.mouseDown ? this.currentScrubbingImageElement : this.currentThumbnailImageElement;
|
7427 | }
|
7428 | set currentImageElement(element) {
|
7429 | if (this.mouseDown) {
|
7430 | this.currentScrubbingImageElement = element;
|
7431 | } else {
|
7432 | this.currentThumbnailImageElement = element;
|
7433 | }
|
7434 | }
|
7435 | }
|
7436 |
|
7437 |
|
7438 | const source = {
|
7439 |
|
7440 | insertElements(type, attributes) {
|
7441 | if (is.string(attributes)) {
|
7442 | insertElement(type, this.media, {
|
7443 | src: attributes
|
7444 | });
|
7445 | } else if (is.array(attributes)) {
|
7446 | attributes.forEach(attribute => {
|
7447 | insertElement(type, this.media, attribute);
|
7448 | });
|
7449 | }
|
7450 | },
|
7451 |
|
7452 |
|
7453 | change(input) {
|
7454 | if (!getDeep(input, 'sources.length')) {
|
7455 | this.debug.warn('Invalid source format');
|
7456 | return;
|
7457 | }
|
7458 |
|
7459 |
|
7460 | html5.cancelRequests.call(this);
|
7461 |
|
7462 |
|
7463 | this.destroy.call(this, () => {
|
7464 |
|
7465 | this.options.quality = [];
|
7466 |
|
7467 |
|
7468 | removeElement(this.media);
|
7469 | this.media = null;
|
7470 |
|
7471 |
|
7472 | if (is.element(this.elements.container)) {
|
7473 | this.elements.container.removeAttribute('class');
|
7474 | }
|
7475 |
|
7476 |
|
7477 | const {
|
7478 | sources,
|
7479 | type
|
7480 | } = input;
|
7481 | const [{
|
7482 | provider = providers.html5,
|
7483 | src
|
7484 | }] = sources;
|
7485 | const tagName = provider === 'html5' ? type : 'div';
|
7486 | const attributes = provider === 'html5' ? {} : {
|
7487 | src
|
7488 | };
|
7489 | Object.assign(this, {
|
7490 | provider,
|
7491 | type,
|
7492 |
|
7493 | supported: support.check(type, provider, this.config.playsinline),
|
7494 |
|
7495 | media: createElement(tagName, attributes)
|
7496 | });
|
7497 |
|
7498 |
|
7499 | this.elements.container.appendChild(this.media);
|
7500 |
|
7501 |
|
7502 | if (is.boolean(input.autoplay)) {
|
7503 | this.config.autoplay = input.autoplay;
|
7504 | }
|
7505 |
|
7506 |
|
7507 | if (this.isHTML5) {
|
7508 | if (this.config.crossorigin) {
|
7509 | this.media.setAttribute('crossorigin', '');
|
7510 | }
|
7511 | if (this.config.autoplay) {
|
7512 | this.media.setAttribute('autoplay', '');
|
7513 | }
|
7514 | if (!is.empty(input.poster)) {
|
7515 | this.poster = input.poster;
|
7516 | }
|
7517 | if (this.config.loop.active) {
|
7518 | this.media.setAttribute('loop', '');
|
7519 | }
|
7520 | if (this.config.muted) {
|
7521 | this.media.setAttribute('muted', '');
|
7522 | }
|
7523 | if (this.config.playsinline) {
|
7524 | this.media.setAttribute('playsinline', '');
|
7525 | }
|
7526 | }
|
7527 |
|
7528 |
|
7529 | ui.addStyleHook.call(this);
|
7530 |
|
7531 |
|
7532 | if (this.isHTML5) {
|
7533 | source.insertElements.call(this, 'source', sources);
|
7534 | }
|
7535 |
|
7536 |
|
7537 | this.config.title = input.title;
|
7538 |
|
7539 |
|
7540 | media.setup.call(this);
|
7541 |
|
7542 |
|
7543 | if (this.isHTML5) {
|
7544 |
|
7545 | if (Object.keys(input).includes('tracks')) {
|
7546 | source.insertElements.call(this, 'track', input.tracks);
|
7547 | }
|
7548 | }
|
7549 |
|
7550 |
|
7551 | if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
|
7552 |
|
7553 | ui.build.call(this);
|
7554 | }
|
7555 |
|
7556 |
|
7557 | if (this.isHTML5) {
|
7558 | this.media.load();
|
7559 | }
|
7560 |
|
7561 |
|
7562 | if (!is.empty(input.previewThumbnails)) {
|
7563 | Object.assign(this.config.previewThumbnails, input.previewThumbnails);
|
7564 |
|
7565 |
|
7566 | if (this.previewThumbnails && this.previewThumbnails.loaded) {
|
7567 | this.previewThumbnails.destroy();
|
7568 | this.previewThumbnails = null;
|
7569 | }
|
7570 |
|
7571 |
|
7572 | if (this.config.previewThumbnails.enabled) {
|
7573 | this.previewThumbnails = new PreviewThumbnails(this);
|
7574 | }
|
7575 | }
|
7576 |
|
7577 |
|
7578 | this.fullscreen.update();
|
7579 | }, true);
|
7580 | }
|
7581 | };
|
7582 |
|
7583 |
|
7584 |
|
7585 |
|
7586 |
|
7587 |
|
7588 | class Plyr {
|
7589 | constructor(target, options) {
|
7590 | |
7591 |
|
7592 |
|
7593 | _defineProperty$1(this, "play", () => {
|
7594 | if (!is.function(this.media.play)) {
|
7595 | return null;
|
7596 | }
|
7597 |
|
7598 |
|
7599 | if (this.ads && this.ads.enabled) {
|
7600 | this.ads.managerPromise.then(() => this.ads.play()).catch(() => silencePromise(this.media.play()));
|
7601 | }
|
7602 |
|
7603 |
|
7604 | return this.media.play();
|
7605 | });
|
7606 | |
7607 |
|
7608 |
|
7609 | _defineProperty$1(this, "pause", () => {
|
7610 | if (!this.playing || !is.function(this.media.pause)) {
|
7611 | return null;
|
7612 | }
|
7613 | return this.media.pause();
|
7614 | });
|
7615 | |
7616 |
|
7617 |
|
7618 |
|
7619 | _defineProperty$1(this, "togglePlay", input => {
|
7620 |
|
7621 | const toggle = is.boolean(input) ? input : !this.playing;
|
7622 | if (toggle) {
|
7623 | return this.play();
|
7624 | }
|
7625 | return this.pause();
|
7626 | });
|
7627 | |
7628 |
|
7629 |
|
7630 | _defineProperty$1(this, "stop", () => {
|
7631 | if (this.isHTML5) {
|
7632 | this.pause();
|
7633 | this.restart();
|
7634 | } else if (is.function(this.media.stop)) {
|
7635 | this.media.stop();
|
7636 | }
|
7637 | });
|
7638 | |
7639 |
|
7640 |
|
7641 | _defineProperty$1(this, "restart", () => {
|
7642 | this.currentTime = 0;
|
7643 | });
|
7644 | |
7645 |
|
7646 |
|
7647 |
|
7648 | _defineProperty$1(this, "rewind", seekTime => {
|
7649 | this.currentTime -= is.number(seekTime) ? seekTime : this.config.seekTime;
|
7650 | });
|
7651 | |
7652 |
|
7653 |
|
7654 |
|
7655 | _defineProperty$1(this, "forward", seekTime => {
|
7656 | this.currentTime += is.number(seekTime) ? seekTime : this.config.seekTime;
|
7657 | });
|
7658 | |
7659 |
|
7660 |
|
7661 |
|
7662 | _defineProperty$1(this, "increaseVolume", step => {
|
7663 | const volume = this.media.muted ? 0 : this.volume;
|
7664 | this.volume = volume + (is.number(step) ? step : 0);
|
7665 | });
|
7666 | |
7667 |
|
7668 |
|
7669 |
|
7670 | _defineProperty$1(this, "decreaseVolume", step => {
|
7671 | this.increaseVolume(-step);
|
7672 | });
|
7673 | |
7674 |
|
7675 |
|
7676 |
|
7677 | _defineProperty$1(this, "airplay", () => {
|
7678 |
|
7679 | if (support.airplay) {
|
7680 | this.media.webkitShowPlaybackTargetPicker();
|
7681 | }
|
7682 | });
|
7683 | |
7684 |
|
7685 |
|
7686 |
|
7687 | _defineProperty$1(this, "toggleControls", toggle => {
|
7688 |
|
7689 | if (this.supported.ui && !this.isAudio) {
|
7690 |
|
7691 | const isHidden = hasClass(this.elements.container, this.config.classNames.hideControls);
|
7692 |
|
7693 | const force = typeof toggle === 'undefined' ? undefined : !toggle;
|
7694 |
|
7695 | const hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force);
|
7696 |
|
7697 |
|
7698 | if (hiding && is.array(this.config.controls) && this.config.controls.includes('settings') && !is.empty(this.config.settings)) {
|
7699 | controls.toggleMenu.call(this, false);
|
7700 | }
|
7701 |
|
7702 |
|
7703 | if (hiding !== isHidden) {
|
7704 | const eventName = hiding ? 'controlshidden' : 'controlsshown';
|
7705 | triggerEvent.call(this, this.media, eventName);
|
7706 | }
|
7707 | return !hiding;
|
7708 | }
|
7709 | return false;
|
7710 | });
|
7711 | |
7712 |
|
7713 |
|
7714 |
|
7715 |
|
7716 | _defineProperty$1(this, "on", (event, callback) => {
|
7717 | on.call(this, this.elements.container, event, callback);
|
7718 | });
|
7719 | |
7720 |
|
7721 |
|
7722 |
|
7723 |
|
7724 | _defineProperty$1(this, "once", (event, callback) => {
|
7725 | once.call(this, this.elements.container, event, callback);
|
7726 | });
|
7727 | |
7728 |
|
7729 |
|
7730 |
|
7731 |
|
7732 | _defineProperty$1(this, "off", (event, callback) => {
|
7733 | off(this.elements.container, event, callback);
|
7734 | });
|
7735 | |
7736 |
|
7737 |
|
7738 |
|
7739 |
|
7740 |
|
7741 |
|
7742 | _defineProperty$1(this, "destroy", (callback, soft = false) => {
|
7743 | if (!this.ready) {
|
7744 | return;
|
7745 | }
|
7746 | const done = () => {
|
7747 |
|
7748 | document.body.style.overflow = '';
|
7749 |
|
7750 |
|
7751 | this.embed = null;
|
7752 |
|
7753 |
|
7754 | if (soft) {
|
7755 | if (Object.keys(this.elements).length) {
|
7756 |
|
7757 | removeElement(this.elements.buttons.play);
|
7758 | removeElement(this.elements.captions);
|
7759 | removeElement(this.elements.controls);
|
7760 | removeElement(this.elements.wrapper);
|
7761 |
|
7762 |
|
7763 | this.elements.buttons.play = null;
|
7764 | this.elements.captions = null;
|
7765 | this.elements.controls = null;
|
7766 | this.elements.wrapper = null;
|
7767 | }
|
7768 |
|
7769 |
|
7770 | if (is.function(callback)) {
|
7771 | callback();
|
7772 | }
|
7773 | } else {
|
7774 |
|
7775 | unbindListeners.call(this);
|
7776 |
|
7777 |
|
7778 | html5.cancelRequests.call(this);
|
7779 |
|
7780 |
|
7781 | replaceElement(this.elements.original, this.elements.container);
|
7782 |
|
7783 |
|
7784 | triggerEvent.call(this, this.elements.original, 'destroyed', true);
|
7785 |
|
7786 |
|
7787 | if (is.function(callback)) {
|
7788 | callback.call(this.elements.original);
|
7789 | }
|
7790 |
|
7791 |
|
7792 | this.ready = false;
|
7793 |
|
7794 |
|
7795 | setTimeout(() => {
|
7796 | this.elements = null;
|
7797 | this.media = null;
|
7798 | }, 200);
|
7799 | }
|
7800 | };
|
7801 |
|
7802 |
|
7803 | this.stop();
|
7804 |
|
7805 |
|
7806 | clearTimeout(this.timers.loading);
|
7807 | clearTimeout(this.timers.controls);
|
7808 | clearTimeout(this.timers.resized);
|
7809 |
|
7810 |
|
7811 | if (this.isHTML5) {
|
7812 |
|
7813 | ui.toggleNativeControls.call(this, true);
|
7814 |
|
7815 |
|
7816 | done();
|
7817 | } else if (this.isYouTube) {
|
7818 |
|
7819 | clearInterval(this.timers.buffering);
|
7820 | clearInterval(this.timers.playing);
|
7821 |
|
7822 |
|
7823 | if (this.embed !== null && is.function(this.embed.destroy)) {
|
7824 | this.embed.destroy();
|
7825 | }
|
7826 |
|
7827 |
|
7828 | done();
|
7829 | } else if (this.isVimeo) {
|
7830 |
|
7831 |
|
7832 | if (this.embed !== null) {
|
7833 | this.embed.unload().then(done);
|
7834 | }
|
7835 |
|
7836 |
|
7837 | setTimeout(done, 200);
|
7838 | }
|
7839 | });
|
7840 | |
7841 |
|
7842 |
|
7843 |
|
7844 | _defineProperty$1(this, "supports", type => support.mime.call(this, type));
|
7845 | this.timers = {};
|
7846 |
|
7847 |
|
7848 | this.ready = false;
|
7849 | this.loading = false;
|
7850 | this.failed = false;
|
7851 |
|
7852 |
|
7853 | this.touch = support.touch;
|
7854 |
|
7855 |
|
7856 | this.media = target;
|
7857 |
|
7858 |
|
7859 | if (is.string(this.media)) {
|
7860 | this.media = document.querySelectorAll(this.media);
|
7861 | }
|
7862 |
|
7863 |
|
7864 | if (window.jQuery && this.media instanceof jQuery || is.nodeList(this.media) || is.array(this.media)) {
|
7865 |
|
7866 | this.media = this.media[0];
|
7867 | }
|
7868 |
|
7869 |
|
7870 | this.config = extend({}, defaults, Plyr.defaults, options || {}, (() => {
|
7871 | try {
|
7872 | return JSON.parse(this.media.getAttribute('data-plyr-config'));
|
7873 | } catch (_) {
|
7874 | return {};
|
7875 | }
|
7876 | })());
|
7877 |
|
7878 |
|
7879 | this.elements = {
|
7880 | container: null,
|
7881 | fullscreen: null,
|
7882 | captions: null,
|
7883 | buttons: {},
|
7884 | display: {},
|
7885 | progress: {},
|
7886 | inputs: {},
|
7887 | settings: {
|
7888 | popup: null,
|
7889 | menu: null,
|
7890 | panels: {},
|
7891 | buttons: {}
|
7892 | }
|
7893 | };
|
7894 |
|
7895 |
|
7896 | this.captions = {
|
7897 | active: null,
|
7898 | currentTrack: -1,
|
7899 | meta: new WeakMap()
|
7900 | };
|
7901 |
|
7902 |
|
7903 | this.fullscreen = {
|
7904 | active: false
|
7905 | };
|
7906 |
|
7907 |
|
7908 | this.options = {
|
7909 | speed: [],
|
7910 | quality: []
|
7911 | };
|
7912 |
|
7913 |
|
7914 |
|
7915 | this.debug = new Console(this.config.debug);
|
7916 |
|
7917 |
|
7918 | this.debug.log('Config', this.config);
|
7919 | this.debug.log('Support', support);
|
7920 |
|
7921 |
|
7922 | if (is.nullOrUndefined(this.media) || !is.element(this.media)) {
|
7923 | this.debug.error('Setup failed: no suitable element passed');
|
7924 | return;
|
7925 | }
|
7926 |
|
7927 |
|
7928 | if (this.media.plyr) {
|
7929 | this.debug.warn('Target already setup');
|
7930 | return;
|
7931 | }
|
7932 |
|
7933 |
|
7934 | if (!this.config.enabled) {
|
7935 | this.debug.error('Setup failed: disabled by config');
|
7936 | return;
|
7937 | }
|
7938 |
|
7939 |
|
7940 |
|
7941 | if (!support.check().api) {
|
7942 | this.debug.error('Setup failed: no support');
|
7943 | return;
|
7944 | }
|
7945 |
|
7946 |
|
7947 | const clone = this.media.cloneNode(true);
|
7948 | clone.autoplay = false;
|
7949 | this.elements.original = clone;
|
7950 |
|
7951 |
|
7952 |
|
7953 | const _type = this.media.tagName.toLowerCase();
|
7954 |
|
7955 | let iframe = null;
|
7956 | let url = null;
|
7957 |
|
7958 |
|
7959 | switch (_type) {
|
7960 | case 'div':
|
7961 |
|
7962 | iframe = this.media.querySelector('iframe');
|
7963 |
|
7964 |
|
7965 | if (is.element(iframe)) {
|
7966 |
|
7967 | url = parseUrl(iframe.getAttribute('src'));
|
7968 | this.provider = getProviderByUrl(url.toString());
|
7969 |
|
7970 |
|
7971 | this.elements.container = this.media;
|
7972 | this.media = iframe;
|
7973 |
|
7974 |
|
7975 | this.elements.container.className = '';
|
7976 |
|
7977 |
|
7978 | if (url.search.length) {
|
7979 | const truthy = ['1', 'true'];
|
7980 | if (truthy.includes(url.searchParams.get('autoplay'))) {
|
7981 | this.config.autoplay = true;
|
7982 | }
|
7983 | if (truthy.includes(url.searchParams.get('loop'))) {
|
7984 | this.config.loop.active = true;
|
7985 | }
|
7986 |
|
7987 |
|
7988 |
|
7989 | if (this.isYouTube) {
|
7990 | this.config.playsinline = truthy.includes(url.searchParams.get('playsinline'));
|
7991 | this.config.youtube.hl = url.searchParams.get('hl');
|
7992 | } else {
|
7993 | this.config.playsinline = true;
|
7994 | }
|
7995 | }
|
7996 | } else {
|
7997 |
|
7998 | this.provider = this.media.getAttribute(this.config.attributes.embed.provider);
|
7999 |
|
8000 |
|
8001 | this.media.removeAttribute(this.config.attributes.embed.provider);
|
8002 | }
|
8003 |
|
8004 |
|
8005 | if (is.empty(this.provider) || !Object.values(providers).includes(this.provider)) {
|
8006 | this.debug.error('Setup failed: Invalid provider');
|
8007 | return;
|
8008 | }
|
8009 |
|
8010 |
|
8011 | this.type = types.video;
|
8012 | break;
|
8013 | case 'video':
|
8014 | case 'audio':
|
8015 | this.type = _type;
|
8016 | this.provider = providers.html5;
|
8017 |
|
8018 |
|
8019 | if (this.media.hasAttribute('crossorigin')) {
|
8020 | this.config.crossorigin = true;
|
8021 | }
|
8022 | if (this.media.hasAttribute('autoplay')) {
|
8023 | this.config.autoplay = true;
|
8024 | }
|
8025 | if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
|
8026 | this.config.playsinline = true;
|
8027 | }
|
8028 | if (this.media.hasAttribute('muted')) {
|
8029 | this.config.muted = true;
|
8030 | }
|
8031 | if (this.media.hasAttribute('loop')) {
|
8032 | this.config.loop.active = true;
|
8033 | }
|
8034 | break;
|
8035 | default:
|
8036 | this.debug.error('Setup failed: unsupported type');
|
8037 | return;
|
8038 | }
|
8039 |
|
8040 |
|
8041 | this.supported = support.check(this.type, this.provider);
|
8042 |
|
8043 |
|
8044 | if (!this.supported.api) {
|
8045 | this.debug.error('Setup failed: no support');
|
8046 | return;
|
8047 | }
|
8048 | this.eventListeners = [];
|
8049 |
|
8050 |
|
8051 | this.listeners = new Listeners(this);
|
8052 |
|
8053 |
|
8054 | this.storage = new Storage(this);
|
8055 |
|
8056 |
|
8057 | this.media.plyr = this;
|
8058 |
|
8059 |
|
8060 | if (!is.element(this.elements.container)) {
|
8061 | this.elements.container = createElement('div');
|
8062 | wrap(this.media, this.elements.container);
|
8063 | }
|
8064 |
|
8065 |
|
8066 | ui.migrateStyles.call(this);
|
8067 |
|
8068 |
|
8069 | ui.addStyleHook.call(this);
|
8070 |
|
8071 |
|
8072 | media.setup.call(this);
|
8073 |
|
8074 |
|
8075 | if (this.config.debug) {
|
8076 | on.call(this, this.elements.container, this.config.events.join(' '), event => {
|
8077 | this.debug.log(`event: ${event.type}`);
|
8078 | });
|
8079 | }
|
8080 |
|
8081 |
|
8082 | this.fullscreen = new Fullscreen(this);
|
8083 |
|
8084 |
|
8085 |
|
8086 | if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
|
8087 | ui.build.call(this);
|
8088 | }
|
8089 |
|
8090 |
|
8091 | this.listeners.container();
|
8092 |
|
8093 |
|
8094 | this.listeners.global();
|
8095 |
|
8096 |
|
8097 | if (this.config.ads.enabled) {
|
8098 | this.ads = new Ads(this);
|
8099 | }
|
8100 |
|
8101 |
|
8102 | if (this.isHTML5 && this.config.autoplay) {
|
8103 | this.once('canplay', () => silencePromise(this.play()));
|
8104 | }
|
8105 |
|
8106 |
|
8107 | this.lastSeekTime = 0;
|
8108 |
|
8109 |
|
8110 | if (this.config.previewThumbnails.enabled) {
|
8111 | this.previewThumbnails = new PreviewThumbnails(this);
|
8112 | }
|
8113 | }
|
8114 |
|
8115 |
|
8116 |
|
8117 |
|
8118 |
|
8119 | |
8120 |
|
8121 |
|
8122 | get isHTML5() {
|
8123 | return this.provider === providers.html5;
|
8124 | }
|
8125 | get isEmbed() {
|
8126 | return this.isYouTube || this.isVimeo;
|
8127 | }
|
8128 | get isYouTube() {
|
8129 | return this.provider === providers.youtube;
|
8130 | }
|
8131 | get isVimeo() {
|
8132 | return this.provider === providers.vimeo;
|
8133 | }
|
8134 | get isVideo() {
|
8135 | return this.type === types.video;
|
8136 | }
|
8137 | get isAudio() {
|
8138 | return this.type === types.audio;
|
8139 | }
|
8140 | |
8141 |
|
8142 |
|
8143 | get playing() {
|
8144 | return Boolean(this.ready && !this.paused && !this.ended);
|
8145 | }
|
8146 |
|
8147 | |
8148 |
|
8149 |
|
8150 | get paused() {
|
8151 | return Boolean(this.media.paused);
|
8152 | }
|
8153 |
|
8154 | |
8155 |
|
8156 |
|
8157 | get stopped() {
|
8158 | return Boolean(this.paused && this.currentTime === 0);
|
8159 | }
|
8160 |
|
8161 | |
8162 |
|
8163 |
|
8164 | get ended() {
|
8165 | return Boolean(this.media.ended);
|
8166 | }
|
8167 | |
8168 |
|
8169 |
|
8170 |
|
8171 | set currentTime(input) {
|
8172 |
|
8173 | if (!this.duration) {
|
8174 | return;
|
8175 | }
|
8176 |
|
8177 |
|
8178 | const inputIsValid = is.number(input) && input > 0;
|
8179 |
|
8180 |
|
8181 | this.media.currentTime = inputIsValid ? Math.min(input, this.duration) : 0;
|
8182 |
|
8183 |
|
8184 | this.debug.log(`Seeking to ${this.currentTime} seconds`);
|
8185 | }
|
8186 |
|
8187 | |
8188 |
|
8189 |
|
8190 | get currentTime() {
|
8191 | return Number(this.media.currentTime);
|
8192 | }
|
8193 |
|
8194 | |
8195 |
|
8196 |
|
8197 | get buffered() {
|
8198 | const {
|
8199 | buffered
|
8200 | } = this.media;
|
8201 |
|
8202 |
|
8203 | if (is.number(buffered)) {
|
8204 | return buffered;
|
8205 | }
|
8206 |
|
8207 |
|
8208 |
|
8209 |
|
8210 | if (buffered && buffered.length && this.duration > 0) {
|
8211 | return buffered.end(0) / this.duration;
|
8212 | }
|
8213 | return 0;
|
8214 | }
|
8215 |
|
8216 | |
8217 |
|
8218 |
|
8219 | get seeking() {
|
8220 | return Boolean(this.media.seeking);
|
8221 | }
|
8222 |
|
8223 | |
8224 |
|
8225 |
|
8226 | get duration() {
|
8227 |
|
8228 | const fauxDuration = parseFloat(this.config.duration);
|
8229 |
|
8230 | const realDuration = (this.media || {}).duration;
|
8231 | const duration = !is.number(realDuration) || realDuration === Infinity ? 0 : realDuration;
|
8232 |
|
8233 |
|
8234 | return fauxDuration || duration;
|
8235 | }
|
8236 |
|
8237 | |
8238 |
|
8239 |
|
8240 |
|
8241 | set volume(value) {
|
8242 | let volume = value;
|
8243 | const max = 1;
|
8244 | const min = 0;
|
8245 | if (is.string(volume)) {
|
8246 | volume = Number(volume);
|
8247 | }
|
8248 |
|
8249 |
|
8250 | if (!is.number(volume)) {
|
8251 | volume = this.storage.get('volume');
|
8252 | }
|
8253 |
|
8254 |
|
8255 | if (!is.number(volume)) {
|
8256 | ({
|
8257 | volume
|
8258 | } = this.config);
|
8259 | }
|
8260 |
|
8261 |
|
8262 | if (volume > max) {
|
8263 | volume = max;
|
8264 | }
|
8265 |
|
8266 | if (volume < min) {
|
8267 | volume = min;
|
8268 | }
|
8269 |
|
8270 |
|
8271 | this.config.volume = volume;
|
8272 |
|
8273 |
|
8274 | this.media.volume = volume;
|
8275 |
|
8276 |
|
8277 | if (!is.empty(value) && this.muted && volume > 0) {
|
8278 | this.muted = false;
|
8279 | }
|
8280 | }
|
8281 |
|
8282 | |
8283 |
|
8284 |
|
8285 | get volume() {
|
8286 | return Number(this.media.volume);
|
8287 | }
|
8288 | |
8289 |
|
8290 |
|
8291 |
|
8292 | set muted(mute) {
|
8293 | let toggle = mute;
|
8294 |
|
8295 |
|
8296 | if (!is.boolean(toggle)) {
|
8297 | toggle = this.storage.get('muted');
|
8298 | }
|
8299 |
|
8300 |
|
8301 | if (!is.boolean(toggle)) {
|
8302 | toggle = this.config.muted;
|
8303 | }
|
8304 |
|
8305 |
|
8306 | this.config.muted = toggle;
|
8307 |
|
8308 |
|
8309 | this.media.muted = toggle;
|
8310 | }
|
8311 |
|
8312 | |
8313 |
|
8314 |
|
8315 | get muted() {
|
8316 | return Boolean(this.media.muted);
|
8317 | }
|
8318 |
|
8319 | |
8320 |
|
8321 |
|
8322 | get hasAudio() {
|
8323 |
|
8324 | if (!this.isHTML5) {
|
8325 | return true;
|
8326 | }
|
8327 | if (this.isAudio) {
|
8328 | return true;
|
8329 | }
|
8330 |
|
8331 |
|
8332 | return Boolean(this.media.mozHasAudio) || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);
|
8333 | }
|
8334 |
|
8335 | |
8336 |
|
8337 |
|
8338 |
|
8339 | set speed(input) {
|
8340 | let speed = null;
|
8341 | if (is.number(input)) {
|
8342 | speed = input;
|
8343 | }
|
8344 | if (!is.number(speed)) {
|
8345 | speed = this.storage.get('speed');
|
8346 | }
|
8347 | if (!is.number(speed)) {
|
8348 | speed = this.config.speed.selected;
|
8349 | }
|
8350 |
|
8351 |
|
8352 | const {
|
8353 | minimumSpeed: min,
|
8354 | maximumSpeed: max
|
8355 | } = this;
|
8356 | speed = clamp(speed, min, max);
|
8357 |
|
8358 |
|
8359 | this.config.speed.selected = speed;
|
8360 |
|
8361 |
|
8362 | setTimeout(() => {
|
8363 | if (this.media) {
|
8364 | this.media.playbackRate = speed;
|
8365 | }
|
8366 | }, 0);
|
8367 | }
|
8368 |
|
8369 | |
8370 |
|
8371 |
|
8372 | get speed() {
|
8373 | return Number(this.media.playbackRate);
|
8374 | }
|
8375 |
|
8376 | |
8377 |
|
8378 |
|
8379 | get minimumSpeed() {
|
8380 | if (this.isYouTube) {
|
8381 |
|
8382 | return Math.min(...this.options.speed);
|
8383 | }
|
8384 | if (this.isVimeo) {
|
8385 |
|
8386 | return 0.5;
|
8387 | }
|
8388 |
|
8389 |
|
8390 | return 0.0625;
|
8391 | }
|
8392 |
|
8393 | |
8394 |
|
8395 |
|
8396 | get maximumSpeed() {
|
8397 | if (this.isYouTube) {
|
8398 |
|
8399 | return Math.max(...this.options.speed);
|
8400 | }
|
8401 | if (this.isVimeo) {
|
8402 |
|
8403 | return 2;
|
8404 | }
|
8405 |
|
8406 |
|
8407 | return 16;
|
8408 | }
|
8409 |
|
8410 | |
8411 |
|
8412 |
|
8413 |
|
8414 |
|
8415 | set quality(input) {
|
8416 | const config = this.config.quality;
|
8417 | const options = this.options.quality;
|
8418 | if (!options.length) {
|
8419 | return;
|
8420 | }
|
8421 | let quality = [!is.empty(input) && Number(input), this.storage.get('quality'), config.selected, config.default].find(is.number);
|
8422 | let updateStorage = true;
|
8423 | if (!options.includes(quality)) {
|
8424 | const value = closest(options, quality);
|
8425 | this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`);
|
8426 | quality = value;
|
8427 |
|
8428 |
|
8429 | updateStorage = false;
|
8430 | }
|
8431 |
|
8432 |
|
8433 | config.selected = quality;
|
8434 |
|
8435 |
|
8436 | this.media.quality = quality;
|
8437 |
|
8438 |
|
8439 | if (updateStorage) {
|
8440 | this.storage.set({
|
8441 | quality
|
8442 | });
|
8443 | }
|
8444 | }
|
8445 |
|
8446 | |
8447 |
|
8448 |
|
8449 | get quality() {
|
8450 | return this.media.quality;
|
8451 | }
|
8452 |
|
8453 | |
8454 |
|
8455 |
|
8456 |
|
8457 |
|
8458 | set loop(input) {
|
8459 | const toggle = is.boolean(input) ? input : this.config.loop.active;
|
8460 | this.config.loop.active = toggle;
|
8461 | this.media.loop = toggle;
|
8462 |
|
8463 |
|
8464 | |
8465 |
|
8466 |
|
8467 |
|
8468 |
|
8469 |
|
8470 |
|
8471 |
|
8472 |
|
8473 |
|
8474 |
|
8475 |
|
8476 |
|
8477 |
|
8478 |
|
8479 |
|
8480 |
|
8481 |
|
8482 |
|
8483 |
|
8484 |
|
8485 |
|
8486 |
|
8487 |
|
8488 |
|
8489 |
|
8490 |
|
8491 |
|
8492 |
|
8493 |
|
8494 |
|
8495 |
|
8496 |
|
8497 |
|
8498 |
|
8499 |
|
8500 | }
|
8501 |
|
8502 | |
8503 |
|
8504 |
|
8505 | get loop() {
|
8506 | return Boolean(this.media.loop);
|
8507 | }
|
8508 |
|
8509 | |
8510 |
|
8511 |
|
8512 |
|
8513 | set source(input) {
|
8514 | source.change.call(this, input);
|
8515 | }
|
8516 |
|
8517 | |
8518 |
|
8519 |
|
8520 | get source() {
|
8521 | return this.media.currentSrc;
|
8522 | }
|
8523 |
|
8524 | |
8525 |
|
8526 |
|
8527 | get download() {
|
8528 | const {
|
8529 | download
|
8530 | } = this.config.urls;
|
8531 | return is.url(download) ? download : this.source;
|
8532 | }
|
8533 |
|
8534 | |
8535 |
|
8536 |
|
8537 | set download(input) {
|
8538 | if (!is.url(input)) {
|
8539 | return;
|
8540 | }
|
8541 | this.config.urls.download = input;
|
8542 | controls.setDownloadUrl.call(this);
|
8543 | }
|
8544 |
|
8545 | |
8546 |
|
8547 |
|
8548 |
|
8549 | set poster(input) {
|
8550 | if (!this.isVideo) {
|
8551 | this.debug.warn('Poster can only be set for video');
|
8552 | return;
|
8553 | }
|
8554 | ui.setPoster.call(this, input, false).catch(() => {});
|
8555 | }
|
8556 |
|
8557 | |
8558 |
|
8559 |
|
8560 | get poster() {
|
8561 | if (!this.isVideo) {
|
8562 | return null;
|
8563 | }
|
8564 | return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
|
8565 | }
|
8566 |
|
8567 | |
8568 |
|
8569 |
|
8570 | get ratio() {
|
8571 | if (!this.isVideo) {
|
8572 | return null;
|
8573 | }
|
8574 | const ratio = reduceAspectRatio(getAspectRatio.call(this));
|
8575 | return is.array(ratio) ? ratio.join(':') : ratio;
|
8576 | }
|
8577 |
|
8578 | |
8579 |
|
8580 |
|
8581 | set ratio(input) {
|
8582 | if (!this.isVideo) {
|
8583 | this.debug.warn('Aspect ratio can only be set for video');
|
8584 | return;
|
8585 | }
|
8586 | if (!is.string(input) || !validateAspectRatio(input)) {
|
8587 | this.debug.error(`Invalid aspect ratio specified (${input})`);
|
8588 | return;
|
8589 | }
|
8590 | this.config.ratio = reduceAspectRatio(input);
|
8591 | setAspectRatio.call(this);
|
8592 | }
|
8593 |
|
8594 | |
8595 |
|
8596 |
|
8597 |
|
8598 | set autoplay(input) {
|
8599 | this.config.autoplay = is.boolean(input) ? input : this.config.autoplay;
|
8600 | }
|
8601 |
|
8602 | |
8603 |
|
8604 |
|
8605 | get autoplay() {
|
8606 | return Boolean(this.config.autoplay);
|
8607 | }
|
8608 |
|
8609 | |
8610 |
|
8611 |
|
8612 |
|
8613 | toggleCaptions(input) {
|
8614 | captions.toggle.call(this, input, false);
|
8615 | }
|
8616 |
|
8617 | |
8618 |
|
8619 |
|
8620 |
|
8621 | set currentTrack(input) {
|
8622 | captions.set.call(this, input, false);
|
8623 | captions.setup.call(this);
|
8624 | }
|
8625 |
|
8626 | |
8627 |
|
8628 |
|
8629 | get currentTrack() {
|
8630 | const {
|
8631 | toggled,
|
8632 | currentTrack
|
8633 | } = this.captions;
|
8634 | return toggled ? currentTrack : -1;
|
8635 | }
|
8636 |
|
8637 | |
8638 |
|
8639 |
|
8640 |
|
8641 |
|
8642 | set language(input) {
|
8643 | captions.setLanguage.call(this, input, false);
|
8644 | }
|
8645 |
|
8646 | |
8647 |
|
8648 |
|
8649 | get language() {
|
8650 | return (captions.getCurrentTrack.call(this) || {}).language;
|
8651 | }
|
8652 |
|
8653 | |
8654 |
|
8655 |
|
8656 |
|
8657 |
|
8658 | set pip(input) {
|
8659 |
|
8660 | if (!support.pip) {
|
8661 | return;
|
8662 | }
|
8663 |
|
8664 |
|
8665 | const toggle = is.boolean(input) ? input : !this.pip;
|
8666 |
|
8667 |
|
8668 |
|
8669 | if (is.function(this.media.webkitSetPresentationMode)) {
|
8670 | this.media.webkitSetPresentationMode(toggle ? pip.active : pip.inactive);
|
8671 | }
|
8672 |
|
8673 |
|
8674 | if (is.function(this.media.requestPictureInPicture)) {
|
8675 | if (!this.pip && toggle) {
|
8676 | this.media.requestPictureInPicture();
|
8677 | } else if (this.pip && !toggle) {
|
8678 | document.exitPictureInPicture();
|
8679 | }
|
8680 | }
|
8681 | }
|
8682 |
|
8683 | |
8684 |
|
8685 |
|
8686 | get pip() {
|
8687 | if (!support.pip) {
|
8688 | return null;
|
8689 | }
|
8690 |
|
8691 |
|
8692 | if (!is.empty(this.media.webkitPresentationMode)) {
|
8693 | return this.media.webkitPresentationMode === pip.active;
|
8694 | }
|
8695 |
|
8696 |
|
8697 | return this.media === document.pictureInPictureElement;
|
8698 | }
|
8699 |
|
8700 | |
8701 |
|
8702 |
|
8703 | setPreviewThumbnails(thumbnailSource) {
|
8704 | if (this.previewThumbnails && this.previewThumbnails.loaded) {
|
8705 | this.previewThumbnails.destroy();
|
8706 | this.previewThumbnails = null;
|
8707 | }
|
8708 | Object.assign(this.config.previewThumbnails, thumbnailSource);
|
8709 |
|
8710 |
|
8711 | if (this.config.previewThumbnails.enabled) {
|
8712 | this.previewThumbnails = new PreviewThumbnails(this);
|
8713 | }
|
8714 | }
|
8715 | |
8716 |
|
8717 |
|
8718 |
|
8719 |
|
8720 | static supported(type, provider) {
|
8721 | return support.check(type, provider);
|
8722 | }
|
8723 |
|
8724 | |
8725 |
|
8726 |
|
8727 |
|
8728 |
|
8729 | static loadSprite(url, id) {
|
8730 | return loadSprite(url, id);
|
8731 | }
|
8732 |
|
8733 | |
8734 |
|
8735 |
|
8736 |
|
8737 |
|
8738 | static setup(selector, options = {}) {
|
8739 | let targets = null;
|
8740 | if (is.string(selector)) {
|
8741 | targets = Array.from(document.querySelectorAll(selector));
|
8742 | } else if (is.nodeList(selector)) {
|
8743 | targets = Array.from(selector);
|
8744 | } else if (is.array(selector)) {
|
8745 | targets = selector.filter(is.element);
|
8746 | }
|
8747 | if (is.empty(targets)) {
|
8748 | return null;
|
8749 | }
|
8750 | return targets.map(t => new Plyr(t, options));
|
8751 | }
|
8752 | }
|
8753 | Plyr.defaults = cloneDeep(defaults);
|
8754 |
|
8755 | return Plyr;
|
8756 |
|
8757 | }));
|
8758 |
|