diff options
Diffstat (limited to 'dist/plyr.mjs')
-rw-r--r-- | dist/plyr.mjs | 1617 |
1 files changed, 836 insertions, 781 deletions
diff --git a/dist/plyr.mjs b/dist/plyr.mjs index e30c272b..59572923 100644 --- a/dist/plyr.mjs +++ b/dist/plyr.mjs @@ -35,20 +35,50 @@ function _defineProperty(obj, key, value) { return obj; } +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; +} + function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - - return arr2; - } + if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayWithHoles(arr) { @@ -56,10 +86,11 @@ function _arrayWithHoles(arr) { } function _iterableToArray(iter) { - if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _iterableToArrayLimit(arr, i) { + if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; @@ -85,102 +116,142 @@ function _iterableToArrayLimit(arr, i) { return _arr; } -function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance"); +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(n); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } -function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); -} +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; -var defaults = { - addCSS: true, - // Add CSS to the element to improve usability (required here or in your CSS!) - thumbWidth: 15, - // The width of the thumb handle - watch: true // Watch for new elements that match a string target + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; -}; - -// Element matches a selector -function matches(element, selector) { - - function match() { - return Array.from(document.querySelectorAll(selector)).includes(this); - } - - var matches = match; - return matches.call(element, selector); + return arr2; } -// Trigger event -function trigger(element, type) { - if (!element || !type) { - return; - } // Create and dispatch the event - - - var event = new Event(type); // Dispatch the event - - element.dispatchEvent(event); +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } -// ========================================================================== -// Type checking utils -// ========================================================================== -var getConstructor = function getConstructor(input) { - return input !== null && typeof input !== 'undefined' ? input.constructor : null; -}; +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} -var instanceOf = function instanceOf(input, constructor) { - return Boolean(input && constructor && input instanceof constructor); -}; +function _classCallCheck$1(e, t) { + if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function"); +} -var isNullOrUndefined = function isNullOrUndefined(input) { - return input === null || typeof input === 'undefined'; -}; +function _defineProperties$1(e, t) { + for (var n = 0; n < t.length; n++) { + var r = t[n]; + r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r); + } +} -var isObject = function isObject(input) { - return getConstructor(input) === Object; -}; +function _createClass$1(e, t, n) { + return t && _defineProperties$1(e.prototype, t), n && _defineProperties$1(e, n), e; +} -var isNumber = function isNumber(input) { - return getConstructor(input) === Number && !Number.isNaN(input); -}; +function _defineProperty$1(e, t, n) { + return t in e ? Object.defineProperty(e, t, { + value: n, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[t] = n, e; +} -var isString = function isString(input) { - return getConstructor(input) === String; -}; +function ownKeys$1(e, t) { + var n = Object.keys(e); -var isBoolean = function isBoolean(input) { - return getConstructor(input) === Boolean; -}; + if (Object.getOwnPropertySymbols) { + var r = Object.getOwnPropertySymbols(e); + t && (r = r.filter(function (t) { + return Object.getOwnPropertyDescriptor(e, t).enumerable; + })), n.push.apply(n, r); + } -var isFunction = function isFunction(input) { - return getConstructor(input) === Function; -}; + return n; +} -var isArray = function isArray(input) { - return Array.isArray(input); -}; +function _objectSpread2$1(e) { + for (var t = 1; t < arguments.length; t++) { + var n = null != arguments[t] ? arguments[t] : {}; + t % 2 ? ownKeys$1(Object(n), !0).forEach(function (t) { + _defineProperty$1(e, t, n[t]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : ownKeys$1(Object(n)).forEach(function (t) { + Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t)); + }); + } -var isNodeList = function isNodeList(input) { - return instanceOf(input, NodeList); -}; + return e; +} -var isElement = function isElement(input) { - return instanceOf(input, Element); +var defaults = { + addCSS: !0, + thumbWidth: 15, + watch: !0 }; -var isEvent = function isEvent(input) { - return instanceOf(input, Event); -}; +function matches(e, t) { + return function () { + return Array.from(document.querySelectorAll(t)).includes(this); + }.call(e, t); +} -var isEmpty = function isEmpty(input) { - return isNullOrUndefined(input) || (isString(input) || isArray(input) || isNodeList(input)) && !input.length || isObject(input) && !Object.keys(input).length; -}; +function trigger(e, t) { + if (e && t) { + var n = new Event(t, { + bubbles: !0 + }); + e.dispatchEvent(n); + } +} -var is = { +var getConstructor = function getConstructor(e) { + return null != e ? e.constructor : null; +}, + instanceOf = function instanceOf(e, t) { + return !!(e && t && e instanceof t); +}, + isNullOrUndefined = function isNullOrUndefined(e) { + return null == e; +}, + isObject = function isObject(e) { + return getConstructor(e) === Object; +}, + isNumber = function isNumber(e) { + return getConstructor(e) === Number && !Number.isNaN(e); +}, + isString = function isString(e) { + return getConstructor(e) === String; +}, + isBoolean = function isBoolean(e) { + return getConstructor(e) === Boolean; +}, + isFunction = function isFunction(e) { + return getConstructor(e) === Function; +}, + isArray = function isArray(e) { + return Array.isArray(e); +}, + isNodeList = function isNodeList(e) { + return instanceOf(e, NodeList); +}, + isElement = function isElement(e) { + return instanceOf(e, Element); +}, + isEvent = function isEvent(e) { + return instanceOf(e, Event); +}, + isEmpty = function isEmpty(e) { + return isNullOrUndefined(e) || (isString(e) || isArray(e) || isNodeList(e)) && !e.length || isObject(e) && !Object.keys(e).length; +}, + is = { nullOrUndefined: isNullOrUndefined, object: isObject, number: isNumber, @@ -194,219 +265,98 @@ var is = { empty: isEmpty }; -// Get the number of decimal places -function getDecimalPlaces(value) { - var match = "".concat(value).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); - - if (!match) { - return 0; - } - - return Math.max(0, // Number of digits right of decimal point. - (match[1] ? match[1].length : 0) - ( // Adjust for scientific notation. - match[2] ? +match[2] : 0)); -} // Round to the nearest step +function getDecimalPlaces(e) { + var t = "".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); + return t ? Math.max(0, (t[1] ? t[1].length : 0) - (t[2] ? +t[2] : 0)) : 0; +} -function round(number, step) { - if (step < 1) { - var places = getDecimalPlaces(step); - return parseFloat(number.toFixed(places)); +function round(e, t) { + if (1 > t) { + var n = getDecimalPlaces(t); + return parseFloat(e.toFixed(n)); } - return Math.round(number / step) * step; + return Math.round(e / t) * t; } -var RangeTouch = -/*#__PURE__*/ -function () { - /** - * Setup a new instance - * @param {String|Element} target - * @param {Object} options - */ - function RangeTouch(target, options) { - _classCallCheck(this, RangeTouch); - - if (is.element(target)) { - // An Element is passed, use it directly - this.element = target; - } else if (is.string(target)) { - // A CSS Selector is passed, fetch it from the DOM - this.element = document.querySelector(target); - } - - if (!is.element(this.element) || !is.empty(this.element.rangeTouch)) { - return; - } - - this.config = Object.assign({}, defaults, options); - this.init(); +var RangeTouch = function () { + function e(t, n) { + _classCallCheck$1(this, e), is.element(t) ? this.element = t : is.string(t) && (this.element = document.querySelector(t)), is.element(this.element) && is.empty(this.element.rangeTouch) && (this.config = _objectSpread2$1({}, defaults, {}, n), this.init()); } - _createClass(RangeTouch, [{ + return _createClass$1(e, [{ key: "init", - value: function init() { - // Bail if not a touch enabled device - if (!RangeTouch.enabled) { - return; - } // Add useful CSS - - - if (this.config.addCSS) { - // TODO: Restore original values on destroy - this.element.style.userSelect = 'none'; - this.element.style.webKitUserSelect = 'none'; - this.element.style.touchAction = 'manipulation'; - } - - this.listeners(true); - this.element.rangeTouch = this; + value: function value() { + 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); } }, { key: "destroy", - value: function destroy() { - // Bail if not a touch enabled device - if (!RangeTouch.enabled) { - return; - } - - this.listeners(false); - this.element.rangeTouch = null; + value: function value() { + e.enabled && (this.config.addCSS && (this.element.style.userSelect = "", this.element.style.webKitUserSelect = "", this.element.style.touchAction = ""), this.listeners(!1), this.element.rangeTouch = null); } }, { key: "listeners", - value: function listeners(toggle) { - var _this = this; - - var method = toggle ? 'addEventListener' : 'removeEventListener'; // Listen for events - - ['touchstart', 'touchmove', 'touchend'].forEach(function (type) { - _this.element[method](type, function (event) { - return _this.set(event); - }, false); + value: function value(e) { + var t = this, + n = e ? "addEventListener" : "removeEventListener"; + ["touchstart", "touchmove", "touchend"].forEach(function (e) { + t.element[n](e, function (e) { + return t.set(e); + }, !1); }); } - /** - * Get the value based on touch position - * @param {Event} event - */ - }, { key: "get", - value: function get(event) { - if (!RangeTouch.enabled || !is.event(event)) { - return null; - } - - var input = event.target; - var touch = event.changedTouches[0]; - var min = parseFloat(input.getAttribute('min')) || 0; - var max = parseFloat(input.getAttribute('max')) || 100; - var step = parseFloat(input.getAttribute('step')) || 1; - var delta = max - min; // Calculate percentage - - var percent; - var clientRect = input.getBoundingClientRect(); - var thumbWidth = 100 / clientRect.width * (this.config.thumbWidth / 2) / 100; // Determine left percentage - - percent = 100 / clientRect.width * (touch.clientX - clientRect.left); // Don't allow outside bounds - - if (percent < 0) { - percent = 0; - } else if (percent > 100) { - percent = 100; - } // Factor in the thumb offset - - - if (percent < 50) { - percent -= (100 - percent * 2) * thumbWidth; - } else if (percent > 50) { - percent += (percent - 50) * 2 * thumbWidth; - } // Find the closest step to the mouse position - - - return min + round(delta * (percent / 100), step); + value: function value(t) { + if (!e.enabled || !is.event(t)) return null; + var n, + r = t.target, + i = t.changedTouches[0], + o = parseFloat(r.getAttribute("min")) || 0, + s = parseFloat(r.getAttribute("max")) || 100, + u = parseFloat(r.getAttribute("step")) || 1, + c = r.getBoundingClientRect(), + a = 100 / c.width * (this.config.thumbWidth / 2) / 100; + 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); } - /** - * Update range value based on position - * @param {Event} event - */ - }, { key: "set", - value: function set(event) { - if (!RangeTouch.enabled || !is.event(event) || event.target.disabled) { - return; - } // Prevent text highlight on iOS - - - event.preventDefault(); // Set value - - event.target.value = this.get(event); // Trigger event - - trigger(event.target, event.type === 'touchend' ? 'change' : 'input'); + value: function value(t) { + e.enabled && is.event(t) && !t.target.disabled && (t.preventDefault(), t.target.value = this.get(t), trigger(t.target, "touchend" === t.type ? "change" : "input")); } }], [{ key: "setup", - - /** - * Setup multiple instances - * @param {String|Element|NodeList|Array} target - * @param {Object} options - */ - value: function setup(target) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - var targets = null; - - if (is.empty(target) || is.string(target)) { - targets = Array.from(document.querySelectorAll(is.string(target) ? target : 'input[type="range"]')); - } else if (is.element(target)) { - targets = [target]; - } else if (is.nodeList(target)) { - targets = Array.from(target); - } else if (is.array(target)) { - targets = target.filter(is.element); - } - - if (is.empty(targets)) { - return null; - } - - var config = Object.assign({}, defaults, options); - - if (is.string(target) && config.watch) { - // Create an observer instance - var observer = new MutationObserver(function (mutations) { - Array.from(mutations).forEach(function (mutation) { - Array.from(mutation.addedNodes).forEach(function (node) { - if (!is.element(node) || !matches(node, target)) { - return; - } // eslint-disable-next-line no-unused-vars - - - var range = new RangeTouch(node, config); + value: function value(t) { + var n = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, + r = null; + if (is.empty(t) || is.string(t) ? r = Array.from(document.querySelectorAll(is.string(t) ? t : 'input[type="range"]')) : is.element(t) ? r = [t] : is.nodeList(t) ? r = Array.from(t) : is.array(t) && (r = t.filter(is.element)), is.empty(r)) return null; + + var i = _objectSpread2$1({}, defaults, {}, n); + + if (is.string(t) && i.watch) { + var o = new MutationObserver(function (n) { + Array.from(n).forEach(function (n) { + Array.from(n.addedNodes).forEach(function (n) { + is.element(n) && matches(n, t) && new e(n, i); }); }); - }); // Pass in the target node, as well as the observer options - - observer.observe(document.body, { - childList: true, - subtree: true + }); + o.observe(document.body, { + childList: !0, + subtree: !0 }); } - return targets.map(function (t) { - return new RangeTouch(t, options); + return r.map(function (t) { + return new e(t, n); }); } }, { key: "enabled", get: function get() { - return 'ontouchstart' in document.documentElement; + return "ontouchstart" in document.documentElement; } - }]); - - return RangeTouch; + }]), e; }(); // ========================================================================== @@ -481,7 +431,7 @@ var isTrack = function isTrack(input) { }; var isPromise = function isPromise(input) { - return instanceOf$1(input, Promise); + return instanceOf$1(input, Promise) && isFunction$1(input.then); }; var isEmpty$1 = function isEmpty(input) { @@ -535,151 +485,48 @@ var is$1 = { }; // ========================================================================== -// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md -// https://www.youtube.com/watch?v=NPM6172J22g - -var supportsPassiveListeners = function () { - // Test via a getter in the options object to see if the passive property is accessed - var supported = false; - - try { - var options = Object.defineProperty({}, 'passive', { - get: function get() { - supported = true; - return null; - } - }); - window.addEventListener('test', null, options); - window.removeEventListener('test', null, options); - } catch (e) {// Do nothing - } - - return supported; -}(); // Toggle event listener - - -function toggleListener(element, event, callback) { - var _this = this; - - var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; - var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; - var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; - - // Bail if no element, event, or callback - if (!element || !('addEventListener' in element) || is$1.empty(event) || !is$1.function(callback)) { - return; - } // Allow multiple events - - - var events = event.split(' '); // Build options - // Default to just the capture boolean for browsers with no passive listener support - - var options = capture; // If passive events listeners are supported - - if (supportsPassiveListeners) { - options = { - // Whether the listener can be passive (i.e. default never prevented) - passive: passive, - // Whether the listener is a capturing listener or not - capture: capture - }; - } // If a single node is passed, bind the event listener - - - events.forEach(function (type) { - if (_this && _this.eventListeners && toggle) { - // Cache event listener - _this.eventListeners.push({ - element: element, - type: type, - callback: callback, - options: options - }); - } - - element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options); +var transitionEndEvent = function () { + var element = document.createElement('span'); + var events = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + OTransition: 'oTransitionEnd otransitionend', + transition: 'transitionend' + }; + var type = Object.keys(events).find(function (event) { + return element.style[event] !== undefined; }); -} // Bind event handler - -function on(element) { - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; - toggleListener.call(this, element, events, callback, true, passive, capture); -} // Unbind event handler - -function off(element) { - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; - toggleListener.call(this, element, events, callback, false, passive, capture); -} // Bind once-only event handler - -function once(element) { - var _this2 = this; + return is$1.string(type) ? events[type] : false; +}(); // Force repaint of element - var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var callback = arguments.length > 2 ? arguments[2] : undefined; - var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; - var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; +function repaint(element, delay) { + setTimeout(function () { + try { + // eslint-disable-next-line no-param-reassign + element.hidden = true; // eslint-disable-next-line no-unused-expressions - var onceCallback = function onceCallback() { - off(element, events, onceCallback, passive, capture); + element.offsetHeight; // eslint-disable-next-line no-param-reassign - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; + element.hidden = false; + } catch (e) {// Do nothing } - - callback.apply(_this2, args); - }; - - toggleListener.call(this, element, events, onceCallback, true, passive, capture); -} // Trigger event - -function triggerEvent(element) { - var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - var bubbles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - var detail = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; - - // Bail if no element - if (!is$1.element(element) || is$1.empty(type)) { - return; - } // Create and dispatch the event - - - var event = new CustomEvent(type, { - bubbles: bubbles, - detail: Object.assign({}, detail, { - plyr: this - }) - }); // Dispatch the event - - element.dispatchEvent(event); -} // Unbind all cached event listeners - -function unbindListeners() { - if (this && this.eventListeners) { - this.eventListeners.forEach(function (item) { - var element = item.element, - type = item.type, - callback = item.callback, - options = item.options; - element.removeEventListener(type, callback, options); - }); - this.eventListeners = []; - } -} // Run method when / if player is ready - -function ready() { - var _this3 = this; - - return new Promise(function (resolve) { - return _this3.ready ? setTimeout(resolve, 0) : on.call(_this3, _this3.elements.container, 'ready', resolve); - }).then(function () {}); + }, delay); } +// ========================================================================== +// Browser sniffing +// Unfortunately, due to mixed support, UA sniffing is required +// ========================================================================== +var browser = { + isIE: + /* @cc_on!@ */ + !!document.documentMode, + isEdge: window.navigator.userAgent.includes('Edge'), + isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent), + isIPhone: /(iPhone|iPod)/gi.test(navigator.platform), + isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform) +}; + function cloneDeep(object) { return JSON.parse(JSON.stringify(object)); } // Get a nested value in an object @@ -880,9 +727,6 @@ function getAttributesFromSelector(sel, existingAttributes) { // Attribute selector attributes[key] = value; break; - - default: - break; } }); return extend(existing, attributes); @@ -897,13 +741,10 @@ function toggleHidden(element, hidden) { if (!is$1.boolean(hide)) { hide = !element.hidden; - } + } // eslint-disable-next-line no-param-reassign - if (hide) { - element.setAttribute('hidden', ''); - } else { - element.removeAttribute('hidden'); - } + + element.hidden = hide; } // Mirror Element.classList.toggle, with IE compatibility for "force" argument function toggleClass(element, className, force) { @@ -932,12 +773,14 @@ function hasClass(element, className) { } // Element matches selector function matches$1(element, selector) { + var _Element = Element, + prototype = _Element.prototype; function match() { return Array.from(document.querySelectorAll(selector)).includes(this); } - var method = match; + var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match; return method.call(element, selector); } // Find all elements @@ -947,41 +790,6 @@ function getElements(selector) { function getElement(selector) { return this.elements.container.querySelector(selector); -} // Trap focus inside container - -function trapFocus() { - var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var toggle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - if (!is$1.element(element)) { - return; - } - - var focusable = getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]'); - var first = focusable[0]; - var last = focusable[focusable.length - 1]; - - var trap = function trap(event) { - // Bail if not tab key or not fullscreen - if (event.key !== 'Tab' || event.keyCode !== 9) { - return; - } // Get the current focused element - - - var focused = document.activeElement; - - if (focused === last && !event.shiftKey) { - // Move focus to first element that can be tabbed if Shift isn't used - first.focus(); - event.preventDefault(); - } else if (focused === first && event.shiftKey) { - // Move focus to last element that can be tabbed if Shift is used - last.focus(); - event.preventDefault(); - } - }; - - toggleListener.call(this, this.elements.container, 'keydown', trap, toggle, false); } // Set focus and tab focus class function setFocus() { @@ -1002,47 +810,6 @@ function setFocus() { } } -// ========================================================================== -var transitionEndEvent = function () { - var element = document.createElement('span'); - var events = { - WebkitTransition: 'webkitTransitionEnd', - MozTransition: 'transitionend', - OTransition: 'oTransitionEnd otransitionend', - transition: 'transitionend' - }; - var type = Object.keys(events).find(function (event) { - return element.style[event] !== undefined; - }); - return is$1.string(type) ? events[type] : false; -}(); // Force repaint of element - -function repaint(element) { - setTimeout(function () { - try { - toggleHidden(element, true); - element.offsetHeight; // eslint-disable-line - - toggleHidden(element, false); - } catch (e) {// Do nothing - } - }, 0); -} - -// ========================================================================== -// Browser sniffing -// Unfortunately, due to mixed support, UA sniffing is required -// ========================================================================== -var browser = { - isIE: - /* @cc_on!@ */ - !!document.documentMode, - isEdge: window.navigator.userAgent.includes('Edge'), - isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent), - isIPhone: /(iPhone|iPod)/gi.test(navigator.platform), - isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform) -}; - var defaultCodecs = { 'audio/ogg': 'vorbis', 'audio/wav': '1', @@ -1140,6 +907,164 @@ var support = { reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches }; +// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md +// https://www.youtube.com/watch?v=NPM6172J22g + +var supportsPassiveListeners = function () { + // Test via a getter in the options object to see if the passive property is accessed + var supported = false; + + try { + var options = Object.defineProperty({}, 'passive', { + get: function get() { + supported = true; + return null; + } + }); + window.addEventListener('test', null, options); + window.removeEventListener('test', null, options); + } catch (e) {// Do nothing + } + + return supported; +}(); // Toggle event listener + + +function toggleListener(element, event, callback) { + var _this = this; + + var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; + var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; + + // Bail if no element, event, or callback + if (!element || !('addEventListener' in element) || is$1.empty(event) || !is$1.function(callback)) { + return; + } // Allow multiple events + + + var events = event.split(' '); // Build options + // Default to just the capture boolean for browsers with no passive listener support + + var options = capture; // If passive events listeners are supported + + if (supportsPassiveListeners) { + options = { + // Whether the listener can be passive (i.e. default never prevented) + passive: passive, + // Whether the listener is a capturing listener or not + capture: capture + }; + } // If a single node is passed, bind the event listener + + + events.forEach(function (type) { + if (_this && _this.eventListeners && toggle) { + // Cache event listener + _this.eventListeners.push({ + element: element, + type: type, + callback: callback, + options: options + }); + } + + element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options); + }); +} // Bind event handler + +function on(element) { + var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var callback = arguments.length > 2 ? arguments[2] : undefined; + var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; + var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + toggleListener.call(this, element, events, callback, true, passive, capture); +} // Unbind event handler + +function off(element) { + var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var callback = arguments.length > 2 ? arguments[2] : undefined; + var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; + var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + toggleListener.call(this, element, events, callback, false, passive, capture); +} // Bind once-only event handler + +function once(element) { + var _this2 = this; + + var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var callback = arguments.length > 2 ? arguments[2] : undefined; + var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; + var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + var onceCallback = function onceCallback() { + off(element, events, onceCallback, passive, capture); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + callback.apply(_this2, args); + }; + + toggleListener.call(this, element, events, onceCallback, true, passive, capture); +} // Trigger event + +function triggerEvent(element) { + var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var bubbles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + var detail = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + // Bail if no element + if (!is$1.element(element) || is$1.empty(type)) { + return; + } // Create and dispatch the event + + + var event = new CustomEvent(type, { + bubbles: bubbles, + detail: _objectSpread2({}, detail, { + plyr: this + }) + }); // Dispatch the event + + element.dispatchEvent(event); +} // Unbind all cached event listeners + +function unbindListeners() { + if (this && this.eventListeners) { + this.eventListeners.forEach(function (item) { + var element = item.element, + type = item.type, + callback = item.callback, + options = item.options; + element.removeEventListener(type, callback, options); + }); + this.eventListeners = []; + } +} // Run method when / if player is ready + +function ready() { + var _this3 = this; + + return new Promise(function (resolve) { + return _this3.ready ? setTimeout(resolve, 0) : on.call(_this3, _this3.elements.container, 'ready', resolve); + }).then(function () {}); +} + +/** + * Silence a Promise-like object. + * This is useful for avoiding non-harmful, but potentially confusing "uncaught + * play promise" rejection error messages. + * @param {Object} value An object that may or may not be `Promise`-like. + */ + +function silencePromise(value) { + if (is$1.promise(value)) { + value.then(null, function () {}); + } +} + function validateRatio(input) { if (!is$1.array(input) && (!is$1.string(input) || !input.includes(':'))) { return false; @@ -1166,12 +1091,8 @@ function reduceAspectRatio(ratio) { } function getAspectRatio(input) { var parse = function parse(ratio) { - if (!validateRatio(ratio)) { - return null; - } - - return ratio.split(':').map(Number); - }; // Provided ratio + return validateRatio(ratio) ? ratio.split(':').map(Number) : null; + }; // Try provided ratio var ratio = parse(input); // Get from config @@ -1201,6 +1122,7 @@ function setAspectRatio(input) { return {}; } + var wrapper = this.elements.wrapper; var ratio = getAspectRatio.call(this, input); var _ref = is$1.array(ratio) ? ratio : [0, 0], @@ -1209,14 +1131,14 @@ function setAspectRatio(input) { h = _ref2[1]; var padding = 100 / w * h; - this.elements.wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI + wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI if (this.isVimeo && this.supported.ui) { var height = 240; var offset = (height - padding) / (height / 50); this.media.style.transform = "translateY(-".concat(offset, "%)"); } else if (this.isHTML5) { - this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); + wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); } return { @@ -1248,19 +1170,29 @@ var html5 = { }, // Get quality levels getQualityOptions: function getQualityOptions() { - // Get sizes from <source> elements + // Whether we're forcing all options (e.g. for streaming) + if (this.config.quality.forced) { + return this.config.quality.options; + } // Get sizes from <source> elements + + return html5.getSources.call(this).map(function (source) { return Number(source.getAttribute('size')); }).filter(Boolean); }, - extend: function extend() { + setup: function setup() { if (!this.isHTML5) { return; } - var player = this; // Set aspect ratio if set + var player = this; // Set speed options from config + + player.options.speed = player.config.speed.options; // Set aspect ratio if fixed + + if (!is$1.empty(this.config.ratio)) { + setAspectRatio.call(player); + } // Quality - setAspectRatio.call(player); // Quality Object.defineProperty(player.media, 'quality', { get: function get() { @@ -1273,37 +1205,48 @@ var html5 = { return source && Number(source.getAttribute('size')); }, set: function set(input) { - // Get sources - var sources = html5.getSources.call(player); // Get first match for requested size + if (player.quality === input) { + return; + } // If we're using an an external handler... - var source = sources.find(function (s) { - return Number(s.getAttribute('size')) === input; - }); // No matching source found - if (!source) { - return; - } // Get current state + if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) { + player.config.quality.onChange(input); + } else { + // Get sources + var sources = html5.getSources.call(player); // Get first match for requested size + var source = sources.find(function (s) { + return Number(s.getAttribute('size')) === input; + }); // No matching source found - var _player$media = player.media, - currentTime = _player$media.currentTime, - paused = _player$media.paused, - preload = _player$media.preload, - readyState = _player$media.readyState; // Set new source + if (!source) { + return; + } // Get current state - player.media.src = source.getAttribute('src'); // Prevent loading if preload="none" and the current source isn't loaded (#1044) - if (preload !== 'none' || readyState) { - // Restore time - player.once('loadedmetadata', function () { - player.currentTime = currentTime; // Resume playing + var _player$media = player.media, + currentTime = _player$media.currentTime, + paused = _player$media.paused, + preload = _player$media.preload, + readyState = _player$media.readyState, + playbackRate = _player$media.playbackRate; // Set new source - if (!paused) { - player.play(); - } - }); // Load new source + player.media.src = source.getAttribute('src'); // Prevent loading if preload="none" and the current source isn't loaded (#1044) - player.media.load(); + if (preload !== 'none' || readyState) { + // Restore time + player.once('loadedmetadata', function () { + player.speed = playbackRate; + player.currentTime = currentTime; // Resume playing + + if (!paused) { + silencePromise(player.play()); + } + }); // Load new source + + player.media.load(); + } } // Trigger change event @@ -1476,9 +1419,7 @@ var i18n = { } }; -var Storage = -/*#__PURE__*/ -function () { +var Storage = /*#__PURE__*/function () { function Storage(player) { _classCallCheck(this, Storage); @@ -1674,7 +1615,7 @@ function formatTime() { // Bail if the value isn't a number if (!is$1.number(time)) { - return formatTime(null, displayHours, inverted); + return formatTime(undefined, displayHours, inverted); } // Format time component to add leading zero @@ -1760,7 +1701,7 @@ var controls = { var icon = document.createElementNS(namespace, 'svg'); setAttributes(icon, extend(attributes, { - role: 'presentation', + 'aria-hidden': 'true', focusable: 'false' })); // Create the <use> to reference sprite @@ -1783,9 +1724,11 @@ var controls = { createLabel: function createLabel(key) { var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var text = i18n.get(key, this.config); - var attributes = Object.assign({}, attr, { + + var attributes = _objectSpread2({}, attr, { class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') }); + return createElement('span', attributes, text); }, // Create a badge @@ -1995,7 +1938,7 @@ var controls = { var _this2 = this; // Navigate through menus via arrow keys and space - on(menuItem, 'keydown keyup', function (event) { + on.call(this, menuItem, 'keydown keyup', function (event) { // We only care about space and ⬆️ ⬇️️ ➡️ if (![32, 38, 39, 40].includes(event.which)) { return; @@ -2037,7 +1980,7 @@ var controls = { }, false); // Enter will fire a `click` event but we still need to manage focus // So we bind to keyup which fires after and set focus here - on(menuItem, 'keyup', function (event) { + on.call(this, menuItem, 'keyup', function (event) { if (event.which !== 13) { return; } @@ -2114,9 +2057,6 @@ var controls = { case 'speed': _this3.speed = parseFloat(value); break; - - default: - break; } controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event)); @@ -2225,9 +2165,6 @@ var controls = { case 'progress': setProgress(this.elements.display.buffer, this.buffered * 100); break; - - default: - break; } } }, @@ -2585,7 +2522,7 @@ var controls = { controls.updateSetting.call(this, type, list); }, // Set a list of available captions languages - setSpeedMenu: function setSpeedMenu(options) { + setSpeedMenu: function setSpeedMenu() { var _this8 = this; // Menu required @@ -2594,17 +2531,10 @@ var controls = { } var type = 'speed'; - var list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); // Set the speed options - - if (is$1.array(options)) { - this.options.speed = options; - } else if (this.isHTML5 || this.isVimeo) { - this.options.speed = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]; - } // Set options if passed and filter based on config - + var list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); // Filter out invalid speeds - this.options.speed = this.options.speed.filter(function (speed) { - return _this8.config.speed.options.includes(speed); + this.options.speed = this.options.speed.filter(function (o) { + return o >= _this8.minimumSpeed && o <= _this8.maximumSpeed; }); // Toggle the pane and tab var toggle = !is$1.empty(this.options.speed) && this.options.speed.length > 1; @@ -2881,9 +2811,11 @@ var controls = { if (control === 'mute') { volume.appendChild(createButton.call(_this10, 'mute')); } // Volume range control + // Ignored on iOS as it's handled globally + // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html - if (control === 'volume') { + if (control === 'volume' && !browser.isIos) { // Set the attributes var attributes = { max: 1, @@ -2942,7 +2874,7 @@ var controls = { bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click - on(menuItem, 'click', function () { + on.call(_this10, menuItem, 'click', function () { showMenuPanel.call(_this10, type, false); }); var flex = createElement('span', null, i18n.get(type, _this10.config)); @@ -2973,7 +2905,7 @@ var controls = { class: _this10.config.classNames.hidden }, i18n.get('menuBack', _this10.config))); // Go back via keyboard - on(pane, 'keydown', function (event) { + on.call(_this10, pane, 'keydown', function (event) { // We only care about <- if (event.which !== 37) { return; @@ -2986,7 +2918,7 @@ var controls = { showMenuPanel.call(_this10, 'home', true); }, false); // Go back via button click - on(backButton, 'click', function () { + on.call(_this10, backButton, 'click', function () { showMenuPanel.call(_this10, 'home', false); }); // Add to pane @@ -3023,7 +2955,12 @@ var controls = { element: 'a', href: _this10.download, target: '_blank' - }); + }); // Set download attribute for HTML5 only + + + if (_this10.isHTML5) { + _attributes.download = ''; + } var download = _this10.config.urls.download; @@ -3335,10 +3272,9 @@ var captions = { meta.set(track, { default: track.mode === 'showing' }); // Turn off native caption rendering to avoid double captions + // eslint-disable-next-line no-param-reassign - Object.assign(track, { - mode: 'hidden' - }); // Add event listener for cue changes + track.mode = 'hidden'; // Add event listener for cue changes on.call(_this, track, 'cuechange', function () { return captions.updateCues.call(_this); @@ -3355,7 +3291,7 @@ var captions = { toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is$1.empty(tracks)); // Update available languages in list - if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) { + if (is$1.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); } }, @@ -3643,13 +3579,16 @@ var defaults$1 = { // Sprite (for icons) loadSprite: true, iconPrefix: 'plyr', - iconUrl: 'https://cdn.plyr.io/3.5.2/plyr.svg', + iconUrl: 'https://cdn.plyr.io/3.5.10/plyr.svg', // Blank video (used to prevent errors on source change) blankVideo: 'https://cdn.plyr.io/static/blank.mp4', // Quality default quality: { default: 576, - options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240] + // The options to display in the UI, if available for the source media + options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240], + forced: false, + onChange: null }, // Set loops loop: { @@ -3660,7 +3599,8 @@ var defaults$1 = { // Speed default and options to display speed: { selected: 1, - options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] + // The options to display in the UI, if available for the source media (e.g. Vimeo and YouTube only support 0.5x-4x) + options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4] }, // Keyboard shortcut settings keyboard: { @@ -3698,7 +3638,8 @@ var defaults$1 = { controls: ['play-large', // 'restart', // 'rewind', 'play', // 'fast-forward', - 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download', + 'progress', 'current-time', // 'duration', + 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download', 'fullscreen'], settings: ['captions', 'quality', 'speed'], // Localisation @@ -3725,6 +3666,7 @@ var defaults$1 = { frameTitle: 'Player for {title}', captions: 'Captions', settings: 'Settings', + pip: 'PIP', menuBack: 'Go back to previous menu', speed: 'Speed', normal: 'Normal', @@ -3756,8 +3698,7 @@ var defaults$1 = { }, youtube: { sdk: 'https://www.youtube.com/iframe_api', - api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}' // 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title),fileDetails)&part=snippet', - + api: 'https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}' }, googleIMA: { sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js' @@ -3922,7 +3863,13 @@ var defaults$1 = { portrait: false, title: false, speed: true, - transparent: false + transparent: false, + // These settings require a pro or premium account to work + sidedock: false, + controls: false, + // Custom settings from Plyr + referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy + }, // YouTube plugin youtube: { @@ -3983,9 +3930,7 @@ function getProviderByUrl(url) { // ========================================================================== var noop = function noop() {}; -var Console = -/*#__PURE__*/ -function () { +var Console = /*#__PURE__*/function () { function Console() { var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; @@ -4021,123 +3966,144 @@ function () { return Console; }(); -function onChange() { - if (!this.enabled) { - return; - } // Update toggle button +var Fullscreen = /*#__PURE__*/function () { + function Fullscreen(player) { + var _this = this; + _classCallCheck(this, Fullscreen); - var button = this.player.elements.buttons.fullscreen; + // Keep reference to parent + this.player = player; // Get prefix - if (is$1.element(button)) { - button.pressed = this.active; - } // Trigger an event + this.prefix = Fullscreen.prefix; + this.property = Fullscreen.property; // Scroll position + this.scrollPosition = { + x: 0, + y: 0 + }; // Force the use of 'full window/browser' rather than fullscreen - triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true); // Trap focus in container + this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners + // Handle event (incase user presses escape etc) - if (!browser.isIos) { - trapFocus.call(this.player, this.target, this.active); - } -} + on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () { + // TODO: Filter for target?? + _this.onChange(); + }); // Fullscreen toggle on double click -function toggleFallback() { - var _this = this; + on.call(this.player, this.player.elements.container, 'dblclick', function (event) { + // Ignore double click in controls + if (is$1.element(_this.player.elements.controls) && _this.player.elements.controls.contains(event.target)) { + return; + } - var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + _this.toggle(); + }); // Tap focus when in fullscreen - // Store or restore scroll position - if (toggle) { - this.scrollPosition = { - x: window.scrollX || 0, - y: window.scrollY || 0 - }; - } else { - window.scrollTo(this.scrollPosition.x, this.scrollPosition.y); - } // Toggle scroll + on.call(this, this.player.elements.container, 'keydown', function (event) { + return _this.trapFocus(event); + }); // Update the UI + this.update(); + } // Determine if native supported - document.body.style.overflow = toggle ? 'hidden' : ''; // Toggle class hook - toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle); // Force full viewport on iPhone X+ + _createClass(Fullscreen, [{ + key: "onChange", + value: function onChange() { + if (!this.enabled) { + return; + } // Update toggle button - if (browser.isIos) { - var viewport = document.head.querySelector('meta[name="viewport"]'); - var property = 'viewport-fit=cover'; // Inject the viewport meta if required - if (!viewport) { - viewport = document.createElement('meta'); - viewport.setAttribute('name', 'viewport'); - } // Check if the property already exists + var button = this.player.elements.buttons.fullscreen; + if (is$1.element(button)) { + button.pressed = this.active; + } // Trigger an event - var hasProperty = is$1.string(viewport.content) && viewport.content.includes(property); - if (toggle) { - this.cleanupViewport = !hasProperty; + triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true); + } + }, { + key: "toggleFallback", + value: function toggleFallback() { + var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - if (!hasProperty) { - viewport.content += ",".concat(property); - } - } else if (this.cleanupViewport) { - viewport.content = viewport.content.split(',').filter(function (part) { - return part.trim() !== property; - }).join(','); - } // Force a repaint as sometimes Safari doesn't want to fill the screen + // Store or restore scroll position + if (toggle) { + this.scrollPosition = { + x: window.scrollX || 0, + y: window.scrollY || 0 + }; + } else { + window.scrollTo(this.scrollPosition.x, this.scrollPosition.y); + } // Toggle scroll - setTimeout(function () { - return repaint(_this.target); - }, 100); - } // Toggle button and fire events + document.body.style.overflow = toggle ? 'hidden' : ''; // Toggle class hook + toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle); // Force full viewport on iPhone X+ - onChange.call(this); -} + if (browser.isIos) { + var viewport = document.head.querySelector('meta[name="viewport"]'); + var property = 'viewport-fit=cover'; // Inject the viewport meta if required -var Fullscreen = -/*#__PURE__*/ -function () { - function Fullscreen(player) { - var _this2 = this; + if (!viewport) { + viewport = document.createElement('meta'); + viewport.setAttribute('name', 'viewport'); + } // Check if the property already exists - _classCallCheck(this, Fullscreen); - // Keep reference to parent - this.player = player; // Get prefix + var hasProperty = is$1.string(viewport.content) && viewport.content.includes(property); - this.prefix = Fullscreen.prefix; - this.property = Fullscreen.property; // Scroll position + if (toggle) { + this.cleanupViewport = !hasProperty; - this.scrollPosition = { - x: 0, - y: 0 - }; // Force the use of 'full window/browser' rather than fullscreen + if (!hasProperty) { + viewport.content += ",".concat(property); + } + } else if (this.cleanupViewport) { + viewport.content = viewport.content.split(',').filter(function (part) { + return part.trim() !== property; + }).join(','); + } + } // Toggle button and fire events - this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners - // Handle event (incase user presses escape etc) - on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () { - // TODO: Filter for target?? - onChange.call(_this2); - }); // Fullscreen toggle on double click + this.onChange(); + } // Trap focus inside container - on.call(this.player, this.player.elements.container, 'dblclick', function (event) { - // Ignore double click in controls - if (is$1.element(_this2.player.elements.controls) && _this2.player.elements.controls.contains(event.target)) { + }, { + key: "trapFocus", + value: function trapFocus(event) { + // Bail if iOS, not active, not the tab key + if (browser.isIos || !this.active || event.key !== 'Tab' || event.keyCode !== 9) { return; - } + } // Get the current focused element - _this2.toggle(); - }); // Update the UI - this.update(); - } // Determine if native supported + var focused = document.activeElement; + var focusable = getElements.call(this.player, 'a[href], button:not(:disabled), input:not(:disabled), [tabindex]'); + var _focusable = _slicedToArray(focusable, 1), + first = _focusable[0]; - _createClass(Fullscreen, [{ + var last = focusable[focusable.length - 1]; + + if (focused === last && !event.shiftKey) { + // Move focus to first element that can be tabbed if Shift isn't used + first.focus(); + event.preventDefault(); + } else if (focused === first && event.shiftKey) { + // Move focus to last element that can be tabbed if Shift is used + last.focus(); + event.preventDefault(); + } + } // Update UI + + }, { key: "update", - // Update UI value: function update() { if (this.enabled) { var mode; @@ -4170,9 +4136,11 @@ function () { if (browser.isIos && this.player.config.fullscreen.iosNative) { this.target.webkitEnterFullscreen(); } else if (!Fullscreen.native || this.forceFallback) { - toggleFallback.call(this, true); + this.toggleFallback(true); } else if (!this.prefix) { - this.target.requestFullscreen(); + this.target.requestFullscreen({ + navigationUI: 'hide' + }); } else if (!is$1.empty(this.prefix)) { this.target["".concat(this.prefix, "Request").concat(this.property)](); } @@ -4188,9 +4156,9 @@ function () { if (browser.isIos && this.player.config.fullscreen.iosNative) { this.target.webkitExitFullscreen(); - this.player.play(); + silencePromise(this.player.play()); } else if (!Fullscreen.native || this.forceFallback) { - toggleFallback.call(this, false); + this.toggleFallback(false); } else if (!this.prefix) { (document.cancelFullScreen || document.exitFullscreen).call(document); } else if (!is$1.empty(this.prefix)) { @@ -4235,7 +4203,7 @@ function () { } var element = !this.prefix ? document.fullscreenElement : document["".concat(this.prefix).concat(this.property, "Element")]; - return element === this.target; + return element && element.shadowRoot ? element === this.target.getRootNode().host : element === this.target; } // Get target element }, { @@ -4438,7 +4406,12 @@ var ui = { } // Set property synchronously to respect the call order - this.media.setAttribute('poster', poster); // Wait until ui is ready + this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute + + if (this.isHTML5) { + return Promise.resolve(poster); + } // Wait until ui is ready + return ready.call(this) // Load image .then(function () { @@ -4479,6 +4452,7 @@ var ui = { Object.assign(target, { pressed: _this3.playing }); + target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config)); }); // Only update controls on non timeupdate events if (is$1.event(event) && event.type === 'timeupdate') { @@ -4516,9 +4490,7 @@ var ui = { } }; -var Listeners = -/*#__PURE__*/ -function () { +var Listeners = /*#__PURE__*/function () { function Listeners(player) { _classCallCheck(this, Listeners); @@ -4609,7 +4581,7 @@ function () { case 75: // Space and K key if (!repeat) { - player.togglePlay(); + silencePromise(player.togglePlay()); } break; @@ -4659,19 +4631,6 @@ function () { // L key player.loop = !player.loop; break; - - /* case 73: - this.setLoop('start'); - break; - case 76: - this.setLoop(); - break; - case 79: - this.setLoop('end'); - break; */ - - default: - break; } // Escape is handle natively when in full screen // So we only need to worry about non native @@ -4802,15 +4761,7 @@ function () { timers.controls = setTimeout(function () { return ui.toggleControls.call(player, false); }, delay); - }); // Force edge to repaint on exit fullscreen - // TODO: Fix weird bug where Edge doesn't re-draw when exiting fullscreen - - /* if (browser.isEdge) { - on.call(player, elements.container, 'exitfullscreen', () => { - setTimeout(() => repaint(elements.container), 100); - }); - } */ - // Set a gutter for Vimeo + }); // Set a gutter for Vimeo var setGutter = function setGutter(ratio, padding, toggle) { if (!player.isVimeo) { @@ -4852,9 +4803,14 @@ function () { on.call(player, elements.container, 'enterfullscreen exitfullscreen', function (event) { var _player$fullscreen = player.fullscreen, target = _player$fullscreen.target, - usingNative = _player$fullscreen.usingNative; // Ignore for iOS native + usingNative = _player$fullscreen.usingNative; // Ignore events not from target - if (!player.isEmbed || target !== elements.container) { + if (target !== elements.container) { + return; + } // If it's not an embed and no ratio specified + + + if (!player.isEmbed && is$1.empty(player.config.ratio)) { return; } @@ -4891,19 +4847,15 @@ function () { on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) { return controls.durationUpdate.call(player, event); - }); // Check for audio tracks on load - // We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point - - on.call(player, player.media, 'canplay loadeddata', function () { - toggleHidden(elements.volume, !player.hasAudio); - toggleHidden(elements.buttons.mute, !player.hasAudio); }); // Handle the media finishing on.call(player, player.media, 'ended', function () { // Show poster on end if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) { // Restart - player.restart(); + player.restart(); // Call pause otherwise IE11 will start playing the video again + + player.pause(); } }); // Check for buffer progress @@ -4947,9 +4899,13 @@ function () { if (player.ended) { _this.proxy(event, player.restart, 'restart'); - _this.proxy(event, player.play, 'play'); + _this.proxy(event, function () { + silencePromise(player.play()); + }, 'play'); } else { - _this.proxy(event, player.togglePlay, 'play'); + _this.proxy(event, function () { + silencePromise(player.togglePlay()); + }, 'play'); } }); } // Disable right click @@ -5016,7 +4972,7 @@ function () { } // Only call default handler if not prevented in custom handler - if (returned && is$1.function(defaultHandler)) { + if (returned !== false && is$1.function(defaultHandler)) { defaultHandler.call(player, event); } } // Trigger custom and default handlers @@ -5047,7 +5003,9 @@ function () { if (elements.buttons.play) { Array.from(elements.buttons.play).forEach(function (button) { - _this3.bind(button, 'click', player.togglePlay, 'play'); + _this3.bind(button, 'click', function () { + silencePromise(player.togglePlay()); + }, 'play'); }); } // Pause @@ -5083,9 +5041,11 @@ function () { this.bind(elements.buttons.settings, 'click', function (event) { // Prevent the document click listener closing the menu event.stopPropagation(); + event.preventDefault(); controls.toggleMenu.call(player, event); - }); // Settings menu - keyboard toggle + }, null, false); // Can't be passive as we're preventing default + // Settings menu - keyboard toggle // We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus // https://bugzilla.mozilla.org/show_bug.cgi?id=1220143 @@ -5142,7 +5102,7 @@ function () { if (play && done) { seek.removeAttribute(attribute); - player.play(); + silencePromise(player.play()); } else if (!done && player.playing) { seek.setAttribute(attribute, ''); player.pause(); @@ -5187,7 +5147,7 @@ function () { } }); // Hide thumbnail preview - on mouse click, mouse leave, and video play/seek. All four are required, e.g., for buffering - this.bind(elements.progress, 'mouseleave click', function () { + this.bind(elements.progress, 'mouseleave touchend click', function () { var previewThumbnails = player.previewThumbnails; if (previewThumbnails && previewThumbnails.loaded) { @@ -5404,12 +5364,13 @@ var loadjs_umd = createCommonjsModule(function (module, exports) { async = args.async, maxTries = (args.numRetries || 0) + 1, beforeCallbackFn = args.before || devnull, + pathname = path.replace(/[\?|#].*$/, ''), pathStripped = path.replace(/^(css|img)!/, ''), isLegacyIECss, e; numTries = numTries || 0; - if (/(^css!|\.css$)/.test(path)) { + if (/(^css!|\.css$)/.test(pathname)) { // css e = doc.createElement('link'); e.rel = 'stylesheet'; @@ -5422,7 +5383,7 @@ var loadjs_umd = createCommonjsModule(function (module, exports) { e.rel = 'preload'; e.as = 'style'; } - } else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) { + } else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) { // image e = doc.createElement('img'); e.src = pathStripped; @@ -5632,26 +5593,27 @@ function assurePlaybackState(play) { var vimeo = { setup: function setup() { - var _this = this; + var player = this; // Add embed class for responsive - // Add embed class for responsive - toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set intial ratio + toggleClass(player.elements.wrapper, player.config.classNames.embed, true); // Set speed options from config - setAspectRatio.call(this); // Load the SDK if not already + player.options.speed = player.config.speed.options; // Set intial ratio + + setAspectRatio.call(player); // Load the SDK if not already if (!is$1.object(window.Vimeo)) { - loadScript(this.config.urls.vimeo.sdk).then(function () { - vimeo.ready.call(_this); + loadScript(player.config.urls.vimeo.sdk).then(function () { + vimeo.ready.call(player); }).catch(function (error) { - _this.debug.warn('Vimeo SDK (player.js) failed to load', error); + player.debug.warn('Vimeo SDK (player.js) failed to load', error); }); } else { - vimeo.ready.call(this); + vimeo.ready.call(player); } }, // API Ready ready: function ready() { - var _this2 = this; + var _this = this; var player = this; var config = player.config.vimeo; // Get Vimeo params for the iframe @@ -5677,7 +5639,12 @@ var vimeo = { iframe.setAttribute('src', src); iframe.setAttribute('allowfullscreen', ''); iframe.setAttribute('allowtransparency', ''); - iframe.setAttribute('allow', 'autoplay'); // Get poster, if already set + iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required + + if (!is$1.empty(config.referrerPolicy)) { + iframe.setAttribute('referrerPolicy', config.referrerPolicy); + } // Get poster, if already set + var poster = player.poster; // Inject the package @@ -5770,11 +5737,9 @@ var vimeo = { player.embed.setPlaybackRate(input).then(function () { speed = input; triggerEvent.call(player, player.media, 'ratechange'); - }).catch(function (error) { - // Hide menu item (and menu if empty) - if (error.name === 'Error') { - controls.setSpeedMenu.call(player, []); - } + }).catch(function () { + // Cannot set Playback Rate, Video is probably not on Pro account + player.options.speed = [1]; }); } }); // Volume @@ -5824,7 +5789,7 @@ var vimeo = { currentSrc = value; controls.setDownloadUrl.call(player); }).catch(function (error) { - _this2.debug.warn(error); + _this.debug.warn(error); }); Object.defineProperty(player.media, 'currentSrc', { get: function get() { @@ -5844,7 +5809,7 @@ var vimeo = { height = _dimensions[1]; player.embed.ratio = [width, height]; - setAspectRatio.call(_this2); + setAspectRatio.call(_this); }); // Set autopause player.embed.setAutopause(player.config.autopause).then(function (state) { @@ -5853,7 +5818,7 @@ var vimeo = { player.embed.getVideoTitle().then(function (title) { player.config.title = title; - ui.setTitle.call(_this2); + ui.setTitle.call(_this); }); // Get current time player.embed.getCurrentTime().then(function (value) { @@ -5895,6 +5860,12 @@ var vimeo = { frame.setAttribute('tabindex', -1); } }); + player.embed.on('bufferstart', function () { + triggerEvent.call(player, player.media, 'waiting'); + }); + player.embed.on('bufferend', function () { + triggerEvent.call(player, player.media, 'playing'); + }); player.embed.on('play', function () { assurePlaybackState.call(player, true); triggerEvent.call(player, player.media, 'playing'); @@ -6222,7 +6193,11 @@ var youtube = { } }); // Get available speeds - player.options.speed = instance.getAvailablePlaybackRates(); // Set the tabindex to avoid focus entering iframe + var speeds = instance.getAvailablePlaybackRates(); // Filter based on config + + player.options.speed = speeds.filter(function (s) { + return player.config.speed.options.includes(s); + }); // Set the tabindex to avoid focus entering iframe if (player.supported.ui) { player.media.setAttribute('tabindex', -1); @@ -6328,7 +6303,9 @@ var youtube = { assurePlaybackState$1.call(player, false); break; - default: + case 3: + // Trigger waiting event to add loading classes to container as the video buffers. + triggerEvent.call(player, player.media, 'waiting'); break; } @@ -6370,14 +6347,16 @@ var media = { wrap(this.media, this.elements.wrapper); // Faux poster container - this.elements.poster = createElement('div', { - class: this.config.classNames.poster - }); - this.elements.wrapper.appendChild(this.elements.poster); + if (this.isEmbed) { + this.elements.poster = createElement('div', { + class: this.config.classNames.poster + }); + this.elements.wrapper.appendChild(this.elements.poster); + } } if (this.isHTML5) { - html5.extend.call(this); + html5.setup.call(this); } else if (this.isYouTube) { youtube.setup.call(this); } else if (this.isVimeo) { @@ -6400,9 +6379,7 @@ var destroy = function destroy(instance) { instance.elements.container.remove(); }; -var Ads = -/*#__PURE__*/ -function () { +var Ads = /*#__PURE__*/function () { /** * Ads constructor. * @param {Object} player @@ -6502,6 +6479,8 @@ function () { * mobile devices, this initialization is done as the result of a user action. */ value: function setupIMA() { + var _this4 = this; + // Create the container for our advertisements this.elements.container = createElement('div', { class: this.player.config.classNames.ads @@ -6514,7 +6493,16 @@ function () { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.player.config.playsinline); // We assume the adContainer is the video container of the plyr element that will house the ads - this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container, this.player.media); // Request video ads to be pre-loaded + this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container, this.player.media); // Create ads loader + + this.loader = new google.ima.AdsLoader(this.elements.displayContainer); // Listen and respond to ads loaded and error events + + this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, function (event) { + return _this4.onAdsManagerLoaded(event); + }, false); + this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (error) { + return _this4.onAdError(error); + }, false); // Request video ads to be pre-loaded this.requestAds(); } @@ -6525,21 +6513,10 @@ function () { }, { key: "requestAds", value: function requestAds() { - var _this4 = this; - var container = this.player.elements.container; try { - // Create ads loader - this.loader = new google.ima.AdsLoader(this.elements.displayContainer); // Listen and respond to ads loaded and error events - - this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, function (event) { - return _this4.onAdsManagerLoaded(event); - }, false); - this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (error) { - return _this4.onAdError(error); - }, false); // Request video ads - + // Request video ads var request = new google.ima.AdsRequest(); request.adTagUrl = this.tagUrl; // Specify the linear and nonlinear slot sizes. This helps the SDK // to select the correct creative if multiple are returned @@ -6718,7 +6695,13 @@ function () { // }; // TODO: So there is still this thing where a video should only be allowed to start // playing when the IMA SDK is ready or has failed - this.loadAds(); + if (this.player.ended) { + this.loadAds(); + } else { + // The SDK won't allow new ads to be called without receiving a contentComplete() + this.loader.contentComplete(); + } + break; case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED: @@ -6743,9 +6726,6 @@ function () { } break; - - default: - break; } } /** @@ -6857,7 +6837,7 @@ function () { this.playing = false; // Play video - this.player.media.play(); + silencePromise(this.player.media.play()); } /** * Pause our video @@ -6914,7 +6894,9 @@ function () { _this11.on('loaded', resolve); _this11.player.debug.log(_this11.manager); - }); // Now request some new advertisements + }); // Now that the manager has been destroyed set it to also be un-initialized + + _this11.initialized = false; // Now request some new advertisements _this11.requestAds(); }).catch(function () {}); @@ -7017,7 +6999,7 @@ function () { cb: Date.now(), AV_WIDTH: 640, AV_HEIGHT: 480, - AV_CDIM2: this.publisherId + AV_CDIM2: config.publisherId }; var base = 'https://go.aniview.com/api/adserver6/vast/'; return "".concat(base, "?").concat(buildUrlParams(params)); @@ -7082,9 +7064,22 @@ var parseVtt = function parseVtt(vttDataString) { */ -var PreviewThumbnails = -/*#__PURE__*/ -function () { +var fitRatio = function fitRatio(ratio, outer) { + var targetRatio = outer.width / outer.height; + var result = {}; + + if (ratio > targetRatio) { + result.width = outer.width; + result.height = 1 / ratio * outer.width; + } else { + result.height = outer.height; + result.width = ratio * outer.height; + } + + return result; +}; + +var PreviewThumbnails = /*#__PURE__*/function () { /** * PreviewThumbnails constructor. * @param {Plyr} player @@ -7111,7 +7106,7 @@ function () { value: function load() { var _this = this; - // Togglethe regular seek tooltip + // Toggle the regular seek tooltip if (this.player.elements.display.seekTooltip) { this.player.elements.display.seekTooltip.hidden = this.enabled; } @@ -7121,7 +7116,11 @@ function () { } this.getThumbnails().then(function () { - // Render DOM elements + if (!_this.enabled) { + return; + } // Render DOM elements + + _this.render(); // Check to see if thumb container size was specified manually in CSS @@ -7141,15 +7140,10 @@ function () { if (is$1.empty(src)) { throw new Error('Missing previewThumbnails.src config attribute'); - } // If string, convert into single-element list - + } // Resolve promise - var urls = is$1.string(src) ? [src] : src; // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails - var promises = urls.map(function (u) { - return _this2.getThumbnail(u); - }); - Promise.all(promises).then(function () { + var sortAndResolve = function sortAndResolve() { // Sort smallest to biggest (e.g., [120p, 480p, 1080p]) _this2.thumbnails.sort(function (x, y) { return x.height - y.height; @@ -7158,7 +7152,25 @@ function () { _this2.player.debug.log('Preview thumbnails', _this2.thumbnails); resolve(); - }); + }; // Via callback() + + + if (is$1.function(src)) { + src(function (thumbnails) { + _this2.thumbnails = thumbnails; + sortAndResolve(); + }); + } // VTT urls + else { + // If string, convert into single-element list + var urls = is$1.string(src) ? [src] : src; // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails + + var promises = urls.map(function (u) { + return _this2.getThumbnail(u); + }); // Resolve + + Promise.all(promises).then(sortAndResolve); + } }); } // Process individual VTT file @@ -7248,8 +7260,8 @@ function () { }, { key: "startScrubbing", value: function startScrubbing(event) { - // Only act on left mouse button (0), or touch device (event.button is false) - if (event.button === false || event.button === 0) { + // Only act on left mouse button (0), or touch device (event.button does not exist or is false) + if (is$1.nullOrUndefined(event.button) || event.button === false || event.button === 0) { this.mouseDown = true; // Wait until media has a duration if (this.player.media.duration) { @@ -7335,6 +7347,17 @@ function () { this.player.elements.wrapper.appendChild(this.elements.scrubbing.container); } }, { + key: "destroy", + value: function destroy() { + if (this.elements.thumb.container) { + this.elements.thumb.container.remove(); + } + + if (this.elements.scrubbing.container) { + this.elements.scrubbing.container.remove(); + } + } + }, { key: "showImageAtCurrentTime", value: function showImageAtCurrentTime() { var _this6 = this; @@ -7579,7 +7602,7 @@ function () { }, { key: "determineContainerAutoSizing", value: function determineContainerAutoSizing() { - if (this.elements.thumb.imageContainer.clientHeight > 20) { + if (this.elements.thumb.imageContainer.clientHeight > 20 || this.elements.thumb.imageContainer.clientWidth > 20) { // This will prevent auto sizing in this.setThumbContainerSizeAndPos() this.sizeSpecifiedInCSS = true; } @@ -7592,6 +7615,13 @@ function () { var thumbWidth = Math.floor(this.thumbContainerHeight * this.thumbAspectRatio); this.elements.thumb.imageContainer.style.height = "".concat(this.thumbContainerHeight, "px"); this.elements.thumb.imageContainer.style.width = "".concat(thumbWidth, "px"); + } else if (this.elements.thumb.imageContainer.clientHeight > 20 && this.elements.thumb.imageContainer.clientWidth < 20) { + var _thumbWidth = Math.floor(this.elements.thumb.imageContainer.clientHeight * this.thumbAspectRatio); + + this.elements.thumb.imageContainer.style.width = "".concat(_thumbWidth, "px"); + } else if (this.elements.thumb.imageContainer.clientHeight < 20 && this.elements.thumb.imageContainer.clientWidth > 20) { + var thumbHeight = Math.floor(this.elements.thumb.imageContainer.clientWidth / this.thumbAspectRatio); + this.elements.thumb.imageContainer.style.height = "".concat(thumbHeight, "px"); } this.setThumbContainerPos(); @@ -7622,9 +7652,15 @@ function () { }, { key: "setScrubbingContainerSize", value: function setScrubbingContainerSize() { - this.elements.scrubbing.container.style.width = "".concat(this.player.media.clientWidth, "px"); // Can't use media.clientHeight - html5 video goes big and does black bars above and below - - this.elements.scrubbing.container.style.height = "".concat(this.player.media.clientWidth / this.thumbAspectRatio, "px"); + var _fitRatio = fitRatio(this.thumbAspectRatio, { + width: this.player.media.clientWidth, + height: this.player.media.clientHeight + }), + width = _fitRatio.width, + height = _fitRatio.height; + + this.elements.scrubbing.container.style.width = "".concat(width, "px"); + this.elements.scrubbing.container.style.height = "".concat(height, "px"); } // Sprites need to be offset to the correct location }, { @@ -7637,9 +7673,9 @@ function () { var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign - previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px"); // eslint-disable-next-line no-param-reassign + previewImage.style.height = "".concat(previewImage.naturalHeight * multiplier, "px"); // eslint-disable-next-line no-param-reassign - previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px"); // eslint-disable-next-line no-param-reassign + previewImage.style.width = "".concat(previewImage.naturalWidth * multiplier, "px"); // eslint-disable-next-line no-param-reassign previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign @@ -7677,8 +7713,18 @@ function () { key: "thumbContainerHeight", get: function get() { if (this.mouseDown) { - // Can't use media.clientHeight - HTML5 video goes big and does black bars above and below - return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio); + var _fitRatio2 = fitRatio(this.thumbAspectRatio, { + width: this.player.media.clientWidth, + height: this.player.media.clientHeight + }), + height = _fitRatio2.height; + + return height; + } // If css is used this needs to return the css height for sprites to work (see setImageSizeAndOffset) + + + if (this.sizeSpecifiedInCSS) { + return this.elements.thumb.imageContainer.clientHeight; } return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4); @@ -7828,11 +7874,22 @@ var source = { if (_this2.isHTML5) { _this2.media.load(); - } // Reload thumbnails + } // Update previewThumbnails config & reload plugin + + + if (!is$1.empty(input.previewThumbnails)) { + Object.assign(_this2.config.previewThumbnails, input.previewThumbnails); // Cleanup previewThumbnails plugin if it was loaded + if (_this2.previewThumbnails && _this2.previewThumbnails.loaded) { + _this2.previewThumbnails.destroy(); - if (_this2.previewThumbnails) { - _this2.previewThumbnails.load(); + _this2.previewThumbnails = null; + } // Create new instance if it is still enabled + + + if (_this2.config.previewThumbnails.enabled) { + _this2.previewThumbnails = new PreviewThumbnails(_this2); + } } // Update the fullscreen support @@ -7864,9 +7921,7 @@ function clamp() { // const globals = new WeakMap(); // Plyr instance -var Plyr = -/*#__PURE__*/ -function () { +var Plyr = /*#__PURE__*/function () { function Plyr(target, options) { var _this = this; @@ -8110,7 +8165,7 @@ function () { if (this.isHTML5 && this.config.autoplay) { setTimeout(function () { - return _this.play(); + return silencePromise(_this.play()); }, 10); } // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek @@ -8147,7 +8202,7 @@ function () { this.ads.managerPromise.then(function () { return _this2.ads.play(); }).catch(function () { - return _this2.media.play(); + return silencePromise(_this2.media.play()); }); } // Return the promise (for HTML5) @@ -8162,10 +8217,10 @@ function () { key: "pause", value: function pause() { if (!this.playing || !is$1.function(this.media.pause)) { - return; + return null; } - this.media.pause(); + return this.media.pause(); } /** * Get playing state @@ -8183,10 +8238,10 @@ function () { var toggle = is$1.boolean(input) ? input : !this.playing; if (toggle) { - this.play(); - } else { - this.pause(); + return this.play(); } + + return this.pause(); } /** * Stop playback @@ -8219,7 +8274,7 @@ function () { }, { key: "rewind", value: function rewind(seekTime) { - this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime); + this.currentTime -= is$1.number(seekTime) ? seekTime : this.config.seekTime; } /** * Fast forward @@ -8229,7 +8284,7 @@ function () { }, { key: "forward", value: function forward(seekTime) { - this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime); + this.currentTime += is$1.number(seekTime) ? seekTime : this.config.seekTime; } /** * Seek to a time |