diff options
Diffstat (limited to 'dist/plyr.polyfilled.js')
-rw-r--r-- | dist/plyr.polyfilled.js | 895 |
1 files changed, 507 insertions, 388 deletions
diff --git a/dist/plyr.polyfilled.js b/dist/plyr.polyfilled.js index edc31b1c..d61fb929 100644 --- a/dist/plyr.polyfilled.js +++ b/dist/plyr.polyfilled.js @@ -6343,6 +6343,47 @@ typeof navigator === "object" && (function (global, factory) { }).then(function () {}); } + function cloneDeep(object) { + return JSON.parse(JSON.stringify(object)); + } // Get a nested value in an object + + function getDeep(object, path) { + return path.split('.').reduce(function (obj, key) { + return obj && obj[key]; + }, object); + } // Deep extend destination object with N more objects + + function extend() { + var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + sources[_key - 1] = arguments[_key]; + } + + if (!sources.length) { + return target; + } + + var source = sources.shift(); + + if (!is$1.object(source)) { + return target; + } + + Object.keys(source).forEach(function (key) { + if (is$1.object(source[key])) { + if (!Object.keys(target).includes(key)) { + Object.assign(target, _defineProperty({}, key, {})); + } + + extend(target[key], source[key]); + } else { + Object.assign(target, _defineProperty({}, key, source[key])); + } + }); + return extend.apply(void 0, [target].concat(sources)); + } + function wrap$1(elements, wrapper) { // Convert `elements` to an array, if necessary. var targets = elements.length ? elements : [elements]; // Loops backwards to prevent having to clone the wrapper on the @@ -6466,7 +6507,7 @@ typeof navigator === "object" && (function (global, factory) { } var attributes = {}; - var existing = existingAttributes; + var existing = extend({}, existingAttributes); sel.split(',').forEach(function (s) { // Remove whitespace var selector = s.trim(); @@ -6474,7 +6515,10 @@ typeof navigator === "object" && (function (global, factory) { var stripped = selector.replace(/[[\]]/g, ''); // Get the parts and value var parts = stripped.split('='); - var key = parts[0]; + + var _parts = _slicedToArray(parts, 1), + key = _parts[0]; + var value = parts.length > 1 ? parts[1].replace(/["']/g, '') : ''; // Get the first character var start = selector.charAt(0); @@ -6482,11 +6526,12 @@ typeof navigator === "object" && (function (global, factory) { switch (start) { case '.': // Add to existing classname - if (is$1.object(existing) && is$1.string(existing.class)) { - existing.class += " ".concat(className); + if (is$1.string(existing.class)) { + attributes.class = "".concat(existing.class, " ").concat(className); + } else { + attributes.class = className; } - attributes.class = className; break; case '#': @@ -6503,7 +6548,7 @@ typeof navigator === "object" && (function (global, factory) { break; } }); - return attributes; + return extend(existing, attributes); } // Toggle hidden function toggleHidden(element, hidden) { @@ -6798,8 +6843,16 @@ typeof navigator === "object" && (function (global, factory) { } // Get from embed - if (ratio === null && !is$1.empty(this.embed) && is$1.string(this.embed.ratio)) { - ratio = parse(this.embed.ratio); + if (ratio === null && !is$1.empty(this.embed) && is$1.array(this.embed.ratio)) { + ratio = this.embed.ratio; + } // Get from HTML5 video + + + if (ratio === null && this.isHTML5) { + var _this$media = this.media, + videoWidth = _this$media.videoWidth, + videoHeight = _this$media.videoHeight; + ratio = reduceAspectRatio([videoWidth, videoHeight]); } return ratio; @@ -6963,47 +7016,6 @@ typeof navigator === "object" && (function (global, factory) { }); } - function cloneDeep(object) { - return JSON.parse(JSON.stringify(object)); - } // Get a nested value in an object - - function getDeep(object, path) { - return path.split('.').reduce(function (obj, key) { - return obj && obj[key]; - }, object); - } // Deep extend destination object with N more objects - - function extend() { - var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - - for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - sources[_key - 1] = arguments[_key]; - } - - if (!sources.length) { - return target; - } - - var source = sources.shift(); - - if (!is$1.object(source)) { - return target; - } - - Object.keys(source).forEach(function (key) { - if (is$1.object(source[key])) { - if (!Object.keys(target).includes(key)) { - Object.assign(target, _defineProperty({}, key, {})); - } - - extend(target[key], source[key]); - } else { - Object.assign(target, _defineProperty({}, key, source[key])); - } - }); - return extend.apply(void 0, [target].concat(sources)); - } - var MATCH$2 = wellKnownSymbol('match'); @@ -7514,7 +7526,9 @@ typeof navigator === "object" && (function (global, factory) { }, // Create a <button> createButton: function createButton(buttonType, attr) { - var attributes = Object.assign({}, attr); + var _this = this; + + var attributes = extend({}, attr); var type = toCamelCase(buttonType); var props = { element: 'button', @@ -7537,8 +7551,12 @@ typeof navigator === "object" && (function (global, factory) { if (Object.keys(attributes).includes('class')) { - if (!attributes.class.includes(this.config.classNames.control)) { - attributes.class += " ".concat(this.config.classNames.control); + if (!attributes.class.split(' ').some(function (c) { + return c === _this.config.classNames.control; + })) { + extend(attributes, { + class: "".concat(attributes.class, " ").concat(this.config.classNames.control) + }); } } else { attributes.class = this.config.classNames.control; @@ -7682,10 +7700,10 @@ typeof navigator === "object" && (function (global, factory) { return progress; }, // Create time display - createTime: function createTime(type) { - var attributes = getAttributesFromSelector(this.config.selectors.display[type]); + createTime: function createTime(type, attrs) { + var attributes = getAttributesFromSelector(this.config.selectors.display[type], attrs); var container = createElement('div', extend(attributes, { - class: "".concat(this.config.classNames.display.time, " ").concat(attributes.class ? attributes.class : '').trim(), + class: "".concat(attributes.class ? attributes.class : '', " ").concat(this.config.classNames.display.time, " ").trim(), 'aria-label': i18n.get(type, this.config) }), '00:00'); // Reference for updates @@ -7696,7 +7714,7 @@ typeof navigator === "object" && (function (global, factory) { // We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus // https://bugzilla.mozilla.org/show_bug.cgi?id=1220143 bindMenuItemShortcuts: function bindMenuItemShortcuts(menuItem, type) { - var _this = this; + var _this2 = this; // Navigate through menus via arrow keys and space on(menuItem, 'keydown keyup', function (event) { @@ -7716,7 +7734,7 @@ typeof navigator === "object" && (function (global, factory) { var isRadioButton = matches$1(menuItem, '[role="menuitemradio"]'); // Show the respective menu if (!isRadioButton && [32, 39].includes(event.which)) { - controls.showMenuPanel.call(_this, type, true); + controls.showMenuPanel.call(_this2, type, true); } else { var target; @@ -7735,7 +7753,7 @@ typeof navigator === "object" && (function (global, factory) { } } - setFocus.call(_this, target, true); + setFocus.call(_this2, target, true); } } }, false); // Enter will fire a `click` event but we still need to manage focus @@ -7746,12 +7764,12 @@ typeof navigator === "object" && (function (global, factory) { return; } - controls.focusFirstMenuItem.call(_this, null, true); + controls.focusFirstMenuItem.call(_this2, null, true); }); }, // Create a settings menu item createMenuItem: function createMenuItem(_ref) { - var _this2 = this; + var _this3 = this; var value = _ref.value, list = _ref.list, @@ -7808,22 +7826,22 @@ typeof navigator === "object" && (function (global, factory) { switch (type) { case 'language': - _this2.currentTrack = Number(value); + _this3.currentTrack = Number(value); break; case 'quality': - _this2.quality = value; + _this3.quality = value; break; case 'speed': - _this2.speed = parseFloat(value); + _this3.speed = parseFloat(value); break; default: break; } - controls.showMenuPanel.call(_this2, 'home', is$1.keyboardEvent(event)); + controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event)); }, type, false); controls.bindMenuItemShortcuts.call(this, menuItem, type); list.appendChild(menuItem); @@ -7887,7 +7905,7 @@ typeof navigator === "object" && (function (global, factory) { }, // Update <progress> elements updateProgress: function updateProgress(event) { - var _this3 = this; + var _this4 = this; if (!this.supported.ui || !is$1.event(event)) { return; @@ -7897,7 +7915,7 @@ typeof navigator === "object" && (function (global, factory) { var setProgress = function setProgress(target, input) { var value = is$1.number(input) ? input : 0; - var progress = is$1.element(target) ? target : _this3.elements.display.buffer; // Update value and label + var progress = is$1.element(target) ? target : _this4.elements.display.buffer; // Update value and label if (is$1.element(progress)) { progress.value = value; // Update text label inside @@ -7969,7 +7987,7 @@ typeof navigator === "object" && (function (global, factory) { }, // Update hover tooltip for seeking updateSeekTooltip: function updateSeekTooltip(event) { - var _this4 = this; + var _this5 = this; // Bail if setting not true if (!this.config.tooltips.seek || !is$1.element(this.elements.inputs.seek) || !is$1.element(this.elements.display.seekTooltip) || this.duration === 0) { @@ -7982,7 +8000,7 @@ typeof navigator === "object" && (function (global, factory) { var visible = "".concat(this.config.classNames.tooltip, "--visible"); var toggle = function toggle(_toggle) { - toggleClass(_this4.elements.display.seekTooltip, visible, _toggle); + toggleClass(_this5.elements.display.seekTooltip, visible, _toggle); }; // Hide on touch @@ -8148,7 +8166,7 @@ typeof navigator === "object" && (function (global, factory) { }, // Set the quality menu setQualityMenu: function setQualityMenu(options) { - var _this5 = this; + var _this6 = this; // Menu required if (!is$1.element(this.elements.settings.panels.quality)) { @@ -8160,7 +8178,7 @@ typeof navigator === "object" && (function (global, factory) { if (is$1.array(options)) { this.options.quality = dedupe(options).filter(function (quality) { - return _this5.config.quality.options.includes(quality); + return _this6.config.quality.options.includes(quality); }); } // Toggle the pane and tab @@ -8178,25 +8196,25 @@ typeof navigator === "object" && (function (global, factory) { var getBadge = function getBadge(quality) { - var label = i18n.get("qualityBadge.".concat(quality), _this5.config); + var label = i18n.get("qualityBadge.".concat(quality), _this6.config); if (!label.length) { return null; } - return controls.createBadge.call(_this5, label); + return controls.createBadge.call(_this6, label); }; // Sort options by the config and then render options this.options.quality.sort(function (a, b) { - var sorting = _this5.config.quality.options; + var sorting = _this6.config.quality.options; return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1; }).forEach(function (quality) { - controls.createMenuItem.call(_this5, { + controls.createMenuItem.call(_this6, { value: quality, list: list, type: type, - title: controls.getLabel.call(_this5, 'quality', quality), + title: controls.getLabel.call(_this6, 'quality', quality), badge: getBadge(quality) }); }); @@ -8242,7 +8260,7 @@ typeof navigator === "object" && (function (global, factory) { // TODO: rework this to user the getter in the API? // Set a list of available captions languages setCaptionsMenu: function setCaptionsMenu() { - var _this6 = this; + var _this7 = this; // Menu required if (!is$1.element(this.elements.settings.panels.captions)) { @@ -8269,9 +8287,9 @@ typeof navigator === "object" && (function (global, factory) { var options = tracks.map(function (track, value) { return { value: value, - checked: _this6.captions.toggled && _this6.currentTrack === value, - title: captions.getLabel.call(_this6, track), - badge: track.language && controls.createBadge.call(_this6, track.language.toUpperCase()), + checked: _this7.captions.toggled && _this7.currentTrack === value, + title: captions.getLabel.call(_this7, track), + badge: track.language && controls.createBadge.call(_this7, track.language.toUpperCase()), list: list, type: 'language' }; @@ -8290,7 +8308,7 @@ typeof navigator === "object" && (function (global, factory) { }, // Set a list of available captions languages setSpeedMenu: function setSpeedMenu(options) { - var _this7 = this; + var _this8 = this; // Menu required if (!is$1.element(this.elements.settings.panels.speed)) { @@ -8308,7 +8326,7 @@ typeof navigator === "object" && (function (global, factory) { this.options.speed = this.options.speed.filter(function (speed) { - return _this7.config.speed.options.includes(speed); + return _this8.config.speed.options.includes(speed); }); // Toggle the pane and tab var toggle = !is$1.empty(this.options.speed) && this.options.speed.length > 1; @@ -8324,11 +8342,11 @@ typeof navigator === "object" && (function (global, factory) { this.options.speed.forEach(function (speed) { - controls.createMenuItem.call(_this7, { + controls.createMenuItem.call(_this8, { value: speed, list: list, type: type, - title: controls.getLabel.call(_this7, 'speed', speed) + title: controls.getLabel.call(_this8, 'speed', speed) }); }); controls.updateSetting.call(this, type, list); @@ -8378,7 +8396,10 @@ typeof navigator === "object" && (function (global, factory) { } else if (is$1.keyboardEvent(input) && input.which === 27) { show = false; } else if (is$1.event(input)) { - var isMenuItem = popup.contains(input.target); // If the click was inside the menu or if the click + // If Plyr is in a shadowDOM, the event target is set to the component, instead of the + // Element in the shadowDOM. The path, if available, is complete. + var target = is$1.function(input.composedPath) ? input.composedPath()[0] : input.target; + var isMenuItem = popup.contains(target); // If the click was inside the menu or if the click // wasn't the button or menu item and we're trying to // show the menu (a doc click shouldn't show the menu) @@ -8421,11 +8442,11 @@ typeof navigator === "object" && (function (global, factory) { }, // Show a panel in the menu showMenuPanel: function showMenuPanel() { - var _this8 = this; + var _this9 = this; var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var target = document.getElementById("plyr-settings-".concat(this.id, "-").concat(type)); // Nothing to show, bail + var target = this.elements.container.querySelector("#plyr-settings-".concat(this.id, "-").concat(type)); // Nothing to show, bail if (!is$1.element(target)) { return; @@ -8454,7 +8475,7 @@ typeof navigator === "object" && (function (global, factory) { container.style.width = ''; container.style.height = ''; // Only listen once - off.call(_this8, container, transitionEndEvent, restore); + off.call(_this9, container, transitionEndEvent, restore); }; // Listen for the transition finishing and restore auto height/width @@ -8471,265 +8492,293 @@ typeof navigator === "object" && (function (global, factory) { controls.focusFirstMenuItem.call(this, target, tabFocus); }, - // Set the download link - setDownloadLink: function setDownloadLink() { + // Set the download URL + setDownloadUrl: function setDownloadUrl() { var button = this.elements.buttons.download; // Bail if no button if (!is$1.element(button)) { return; - } // Set download link + } // Set attribute button.setAttribute('href', this.download); }, // Build the default HTML - // TODO: Set order based on order in the config.controls array? create: function create(data) { - var _this9 = this; - - // Create the container - var container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper)); // Restart button - - if (this.config.controls.includes('restart')) { - container.appendChild(controls.createButton.call(this, 'restart')); - } // Rewind button - + var _this10 = this; - if (this.config.controls.includes('rewind')) { - container.appendChild(controls.createButton.call(this, 'rewind')); - } // Play/Pause button + var bindMenuItemShortcuts = controls.bindMenuItemShortcuts, + createButton = controls.createButton, + createProgress = controls.createProgress, + createRange = controls.createRange, + createTime = controls.createTime, + setQualityMenu = controls.setQualityMenu, + setSpeedMenu = controls.setSpeedMenu, + showMenuPanel = controls.showMenuPanel; + this.elements.controls = null; // Larger overlaid play button + if (this.config.controls.includes('play-large')) { + this.elements.container.appendChild(createButton.call(this, 'play-large')); + } // Create the container - if (this.config.controls.includes('play')) { - container.appendChild(controls.createButton.call(this, 'play')); - } // Fast forward button + var container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper)); + this.elements.controls = container; // Default item attributes - if (this.config.controls.includes('fast-forward')) { - container.appendChild(controls.createButton.call(this, 'fast-forward')); - } // Progress + var defaultAttributes = { + class: 'plyr__controls__item' + }; // Loop through controls in order + dedupe(this.config.controls).forEach(function (control) { + // Restart button + if (control === 'restart') { + container.appendChild(createButton.call(_this10, 'restart', defaultAttributes)); + } // Rewind button - if (this.config.controls.includes('progress')) { - var progress = createElement('div', getAttributesFromSelector(this.config.selectors.progress)); // Seek range slider - progress.appendChild(controls.createRange.call(this, 'seek', { - id: "plyr-seek-".concat(data.id) - })); // Buffer progress + if (control === 'rewind') { + container.appendChild(createButton.call(_this10, 'rewind', defaultAttributes)); + } // Play/Pause button - progress.appendChild(controls.createProgress.call(this, 'buffer')); // TODO: Add loop display indicator - // Seek tooltip - if (this.config.tooltips.seek) { - var tooltip = createElement('span', { - class: this.config.classNames.tooltip - }, '00:00'); - progress.appendChild(tooltip); - this.elements.display.seekTooltip = tooltip; - } + if (control === 'play') { + container.appendChild(createButton.call(_this10, 'play', defaultAttributes)); + } // Fast forward button - this.elements.progress = progress; - container.appendChild(this.elements.progress); - } // Media current time display + if (control === 'fast-forward') { + container.appendChild(createButton.call(_this10, 'fast-forward', defaultAttributes)); + } // Progress - if (this.config.controls.includes('current-time')) { - container.appendChild(controls.createTime.call(this, 'currentTime')); - } // Media duration display + if (control === 'progress') { + var progressContainer = createElement('div', { + class: "".concat(defaultAttributes.class, " plyr__progress__container") + }); + var progress = createElement('div', getAttributesFromSelector(_this10.config.selectors.progress)); // Seek range slider - if (this.config.controls.includes('duration')) { - container.appendChild(controls.createTime.call(this, 'duration')); - } // Volume controls + progress.appendChild(createRange.call(_this10, 'seek', { + id: "plyr-seek-".concat(data.id) + })); // Buffer progress + progress.appendChild(createProgress.call(_this10, 'buffer')); // TODO: Add loop display indicator + // Seek tooltip - if (this.config.controls.includes('mute') || this.config.controls.includes('volume')) { - var volume = createElement('div', { - class: 'plyr__volume' - }); // Toggle mute button + if (_this10.config.tooltips.seek) { + var tooltip = createElement('span', { + class: _this10.config.classNames.tooltip + }, '00:00'); + progress.appendChild(tooltip); + _this10.elements.display.seekTooltip = tooltip; + } - if (this.config.controls.includes('mute')) { - volume.appendChild(controls.createButton.call(this, 'mute')); - } // Volume range control + _this10.elements.progress = progress; + progressContainer.appendChild(_this10.elements.progress); + container.appendChild(progressContainer); + } // Media current time display - if (this.config.controls.includes('volume')) { - // Set the attributes - var attributes = { - max: 1, - step: 0.05, - value: this.config.volume - }; // Create the volume range slider + if (control === 'current-time') { + container.appendChild(createTime.call(_this10, 'currentTime', defaultAttributes)); + } // Media duration display - volume.appendChild(controls.createRange.call(this, 'volume', extend(attributes, { - id: "plyr-volume-".concat(data.id) - }))); - this.elements.volume = volume; - } - container.appendChild(volume); - } // Toggle captions button + if (control === 'duration') { + container.appendChild(createTime.call(_this10, 'duration', defaultAttributes)); + } // Volume controls - if (this.config.controls.includes('captions')) { - container.appendChild(controls.createButton.call(this, 'captions')); - } // Settings button / menu + if (control === 'mute' || control === 'volume') { + var volume = _this10.elements.volume; // Create the volume container if needed + if (!is$1.element(volume) || !container.contains(volume)) { + volume = createElement('div', extend({}, defaultAttributes, { + class: "".concat(defaultAttributes.class, " plyr__volume").trim() + })); + _this10.elements.volume = volume; + container.appendChild(volume); + } // Toggle mute button - if (this.config.controls.includes('settings') && !is$1.empty(this.config.settings)) { - var control = createElement('div', { - class: 'plyr__menu', - hidden: '' - }); - control.appendChild(controls.createButton.call(this, 'settings', { - 'aria-haspopup': true, - 'aria-controls': "plyr-settings-".concat(data.id), - 'aria-expanded': false - })); - var popup = createElement('div', { - class: 'plyr__menu__container', - id: "plyr-settings-".concat(data.id), - hidden: '' - }); - var inner = createElement('div'); - var home = createElement('div', { - id: "plyr-settings-".concat(data.id, "-home") - }); // Create the menu - var menu = createElement('div', { - role: 'menu' - }); - home.appendChild(menu); - inner.appendChild(home); - this.elements.settings.panels.home = home; // Build the menu items - - this.config.settings.forEach(function (type) { - // TODO: bundle this with the createMenuItem helper and bindings - var menuItem = createElement('button', extend(getAttributesFromSelector(_this9.config.selectors.buttons.settings), { - type: 'button', - class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--forward"), - role: 'menuitem', - 'aria-haspopup': true, - hidden: '' - })); // Bind menu shortcuts for keyboard users + if (control === 'mute') { + volume.appendChild(createButton.call(_this10, 'mute')); + } // Volume range control - controls.bindMenuItemShortcuts.call(_this9, menuItem, type); // Show menu on click - on(menuItem, 'click', function () { - controls.showMenuPanel.call(_this9, type, false); - }); - var flex = createElement('span', null, i18n.get(type, _this9.config)); - var value = createElement('span', { - class: _this9.config.classNames.menu.value - }); // Speed contains HTML entities - - value.innerHTML = data[type]; - flex.appendChild(value); - menuItem.appendChild(flex); - menu.appendChild(menuItem); // Build the panes - - var pane = createElement('div', { - id: "plyr-settings-".concat(data.id, "-").concat(type), - hidden: '' - }); // Back button + if (control === 'volume') { + // Set the attributes + var attributes = { + max: 1, + step: 0.05, + value: _this10.config.volume + }; // Create the volume range slider - var backButton = createElement('button', { - type: 'button', - class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--back") - }); // Visible label + volume.appendChild(createRange.call(_this10, 'volume', extend(attributes, { + id: "plyr-volume-".concat(data.id) + }))); + } + } // Toggle captions button - backButton.appendChild(createElement('span', { - 'aria-hidden': true - }, i18n.get(type, _this9.config))); // Screen reader label - backButton.appendChild(createElement('span', { - class: _this9.config.classNames.hidden - }, i18n.get('menuBack', _this9.config))); // Go back via keyboard + if (control === 'captions') { + container.appendChild(createButton.call(_this10, 'captions', defaultAttributes)); + } // Settings button / menu - on(pane, 'keydown', function (event) { - // We only care about <- - if (event.which !== 37) { - return; - } // Prevent seek + if (control === 'settings' && !is$1.empty(_this10.config.settings)) { + var _control = createElement('div', extend({}, defaultAttributes, { + class: "".concat(defaultAttributes.class, " plyr__menu").trim(), + hidden: '' + })); - event.preventDefault(); - event.stopPropagation(); // Show the respective menu + _control.appendChild(createButton.call(_this10, 'settings', { + 'aria-haspopup': true, + 'aria-controls': "plyr-settings-".concat(data.id), + 'aria-expanded': false + })); - controls.showMenuPanel.call(_this9, 'home', true); - }, false); // Go back via button click + var popup = createElement('div', { + class: 'plyr__menu__container', + id: "plyr-settings-".concat(data.id), + hidden: '' + }); + var inner = createElement('div'); + var home = createElement('div', { + id: "plyr-settings-".concat(data.id, "-home") + }); // Create the menu - on(backButton, 'click', function () { - controls.showMenuPanel.call(_this9, 'home', false); - }); // Add to pane + var menu = createElement('div', { + role: 'menu' + }); + home.appendChild(menu); + inner.appendChild(home); + _this10.elements.settings.panels.home = home; // Build the menu items + + _this10.config.settings.forEach(function (type) { + // TODO: bundle this with the createMenuItem helper and bindings + var menuItem = createElement('button', extend(getAttributesFromSelector(_this10.config.selectors.buttons.settings), { + type: 'button', + class: "".concat(_this10.config.classNames.control, " ").concat(_this10.config.classNames.control, "--forward"), + role: 'menuitem', + 'aria-haspopup': true, + hidden: '' + })); // Bind menu shortcuts for keyboard users + + bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click + + on(menuItem, 'click', function () { + showMenuPanel.call(_this10, type, false); + }); + var flex = createElement('span', null, i18n.get(type, _this10.config)); + var value = createElement('span', { + class: _this10.config.classNames.menu.value + }); // Speed contains HTML entities + + value.innerHTML = data[type]; + flex.appendChild(value); + menuItem.appendChild(flex); + menu.appendChild(menuItem); // Build the panes + + var pane = createElement('div', { + id: "plyr-settings-".concat(data.id, "-").concat(type), + hidden: '' + }); // Back button + + var backButton = createElement('button', { + type: 'button', + class: "".concat(_this10.config.classNames.control, " ").concat(_this10.config.classNames.control, "--back") + }); // Visible label + + backButton.appendChild(createElement('span', { + 'aria-hidden': true + }, i18n.get(type, _this10.config))); // Screen reader label + + backButton.appendChild(createElement('span', { + class: _this10.config.classNames.hidden + }, i18n.get('menuBack', _this10.config))); // Go back via keyboard + + on(pane, 'keydown', function (event) { + // We only care about <- + if (event.which !== 37) { + return; + } // Prevent seek + + + event.preventDefault(); + event.stopPropagation(); // Show the respective menu + + showMenuPanel.call(_this10, 'home', true); + }, false); // Go back via button click + + on(backButton, 'click', function () { + showMenuPanel.call(_this10, 'home', false); + }); // Add to pane + + pane.appendChild(backButton); // Menu + + pane.appendChild(createElement('div', { + role: 'menu' + })); + inner.appendChild(pane); + _this10.elements.settings.buttons[type] = menuItem; + _this10.elements.settings.panels[type] = pane; + }); - pane.appendChild(backButton); // Menu + popup.appendChild(inner); - pane.appendChild(createElement('div', { - role: 'menu' - })); - inner.appendChild(pane); - _this9.elements.settings.buttons[type] = menuItem; - _this9.elements.settings.panels[type] = pane; - }); - popup.appendChild(inner); - control.appendChild(popup); - container.appendChild(control); - this.elements.settings.popup = popup; - this.elements.settings.menu = control; - } // Picture in picture button + _control.appendChild(popup); + container.appendChild(_control); + _this10.elements.settings.popup = popup; + _this10.elements.settings.menu = _control; + } // Picture in picture button - if (this.config.controls.includes('pip') && support.pip) { - container.appendChild(controls.createButton.call(this, 'pip')); - } // Airplay button + if (control === 'pip' && support.pip) { + container.appendChild(createButton.call(_this10, 'pip', defaultAttributes)); + } // Airplay button - if (this.config.controls.includes('airplay') && support.airplay) { - container.appendChild(controls.createButton.call(this, 'airplay')); - } // Download button + if (control === 'airplay' && support.airplay) { + container.appendChild(createButton.call(_this10, 'airplay', defaultAttributes)); + } // Download button - if (this.config.controls.includes('download')) { - var _attributes = { - element: 'a', - href: this.download, - target: '_blank' - }; - var download = this.config.urls.download; - if (!is$1.url(download) && this.isEmbed) { - extend(_attributes, { - icon: "logo-".concat(this.provider), - label: this.provider + if (control === 'download') { + var _attributes = extend({}, defaultAttributes, { + element: 'a', + href: _this10.download, + target: '_blank' }); - } - - container.appendChild(controls.createButton.call(this, 'download', _attributes)); - } // Toggle fullscreen button + var download = _this10.config.urls.download; - if (this.config.controls.includes('fullscreen')) { - container.appendChild(controls.createButton.call(this, 'fullscreen')); - } // Larger overlaid play button + if (!is$1.url(download) && _this10.isEmbed) { + extend(_attributes, { + icon: "logo-".concat(_this10.provider), + label: _this10.provider + }); + } + container.appendChild(createButton.call(_this10, 'download', _attributes)); + } // Toggle fullscreen button - if (this.config.controls.includes('play-large')) { - this.elements.container.appendChild(controls.createButton.call(this, 'play-large')); - } - this.elements.controls = container; // Set available quality levels + if (control === 'fullscreen') { + container.appendChild(createButton.call(_this10, 'fullscreen', defaultAttributes)); + } + }); // Set available quality levels if (this.isHTML5) { - controls.setQualityMenu.call(this, html5.getQualityOptions.call(this)); + setQualityMenu.call(this, html5.getQualityOptions.call(this)); } - controls.setSpeedMenu.call(this); + setSpeedMenu.call(this); return container; }, // Insert controls inject: function inject() { - var _this10 = this; + var _this11 = this; // Sprite if (this.config.loadSprite) { @@ -8824,7 +8873,7 @@ typeof navigator === "object" && (function (global, factory) { if (!is$1.empty(this.elements.buttons)) { var addProperty = function addProperty(button) { - var className = _this10.config.classNames.controlPressed; + var className = _this11.config.classNames.controlPressed; Object.defineProperty(button, 'pressed', { enumerable: true, get: function get() { @@ -8860,8 +8909,8 @@ typeof navigator === "object" && (function (global, factory) { var selector = "".concat(selectors.controls.wrapper, " ").concat(selectors.labels, " .").concat(classNames.hidden); var labels = getElements.call(this, selector); Array.from(labels).forEach(function (label) { - toggleClass(label, _this10.config.classNames.hidden, false); - toggleClass(label, _this10.config.classNames.tooltip, true); + toggleClass(label, _this11.config.classNames.hidden, false); + toggleClass(label, _this11.config.classNames.tooltip, true); }); } } @@ -9318,7 +9367,7 @@ typeof navigator === "object" && (function (global, factory) { // Sprite (for icons) loadSprite: true, iconPrefix: 'plyr', - iconUrl: 'https://cdn.plyr.io/3.5.3/plyr.svg', + iconUrl: 'https://cdn.plyr.io/3.5.4/plyr.svg', // Blank video (used to prevent errors on source change) blankVideo: 'https://cdn.plyr.io/static/blank.mp4', // Quality default @@ -9431,7 +9480,8 @@ typeof navigator === "object" && (function (global, factory) { }, youtube: { sdk: 'https://www.youtube.com/iframe_api', - api: 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title))&part=snippet' + 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', + }, googleIMA: { sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js' @@ -9507,10 +9557,7 @@ typeof navigator === "object" && (function (global, factory) { }, progress: '.plyr__progress', captions: '.plyr__captions', - caption: '.plyr__caption', - menu: { - quality: '.js-plyr__menu__list--quality' - } + caption: '.plyr__caption' }, // Class hooks added to the player in different states classNames: { @@ -9581,10 +9628,6 @@ typeof navigator === "object" && (function (global, factory) { id: 'data-plyr-embed-id' } }, - // API keys - keys: { - google: null - }, // Advertisements plugin // Register for an account here: http://vi.ai/publisher-video-monetization/?aid=plyrio ads: { @@ -10044,13 +10087,13 @@ typeof navigator === "object" && (function (global, factory) { this.volume = null; // Reset mute state - this.muted = null; // Reset speed - - this.speed = null; // Reset loop state + this.muted = null; // Reset loop state this.loop = null; // Reset quality setting - this.quality = null; // Reset volume display + this.quality = null; // Reset speed + + this.speed = null; // Reset volume display controls.updateVolume.call(this); // Reset time display @@ -10457,8 +10500,6 @@ typeof navigator === "object" && (function (global, factory) { }, { key: "container", value: function container() { - var _this = this; - var player = this.player; var config = player.config, elements = player.elements, @@ -10513,7 +10554,7 @@ typeof navigator === "object" && (function (global, factory) { var _ratio = _slicedToArray(ratio, 2), y = _ratio[1]; - var _getAspectRatio$call = getAspectRatio.call(_this), + var _getAspectRatio$call = getAspectRatio.call(player), _getAspectRatio$call2 = _slicedToArray(_getAspectRatio$call, 2), videoX = _getAspectRatio$call2[0], videoY = _getAspectRatio$call2[1]; @@ -10571,7 +10612,7 @@ typeof navigator === "object" && (function (global, factory) { }, { key: "media", value: function media() { - var _this2 = this; + var _this = this; var player = this.player; var elements = player.elements; // Time change on media @@ -10636,11 +10677,11 @@ typeof navigator === "object" && (function (global, factory) { } if (player.ended) { - _this2.proxy(event, player.restart, 'restart'); + _this.proxy(event, player.restart, 'restart'); - _this2.proxy(event, player.play, 'play'); + _this.proxy(event, player.play, 'play'); } else { - _this2.proxy(event, player.togglePlay, 'play'); + _this.proxy(event, player.togglePlay, 'play'); } }); } // Disable right click @@ -10677,7 +10718,7 @@ typeof navigator === "object" && (function (global, factory) { }); // Update download link when ready and if quality changes on.call(player, player.media, 'ready qualitychange', function () { - controls.setDownloadLink.call(player); + controls.setDownloadUrl.call(player); }); // Proxy events to container // Bubble up key events for Edge @@ -10715,21 +10756,21 @@ typeof navigator === "object" && (function (global, factory) { }, { key: "bind", value: function bind(element, type, defaultHandler, customHandlerKey) { - var _this3 = this; + var _this2 = this; var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; var player = this.player; var customHandler = player.config.listeners[customHandlerKey]; var hasCustomHandler = is$1.function(customHandler); on.call(player, element, type, function (event) { - return _this3.proxy(event, defaultHandler, customHandlerKey); + return _this2.proxy(event, defaultHandler, customHandlerKey); }, passive && !hasCustomHandler); } // Listen for control events }, { key: "controls", value: function controls$1() { - var _this4 = this; + var _this3 = this; var player = this.player; var elements = player.elements; // IE doesn't support input event, so we fallback to change @@ -10738,7 +10779,7 @@ typeof navigator === "object" && (function (global, factory) { if (elements.buttons.play) { Array.from(elements.buttons.play).forEach(function (button) { - _this4.bind(button, 'click', player.togglePlay, 'play'); + _this3.bind(button, 'click', player.togglePlay, 'play'); }); } // Pause @@ -10845,7 +10886,7 @@ typeof navigator === "object" && (function (global, factory) { if (browser.isIos) { var inputs = getElements.call(player, 'input[type="range"]'); Array.from(inputs).forEach(function (input) { - return _this4.bind(input, inputEvent, function (event) { + return _this3.bind(input, inputEvent, function (event) { return repaint(event.target); }); }); @@ -10903,7 +10944,7 @@ typeof navigator === "object" && (function (global, factory) { if (browser.isWebkit) { Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) { - _this4.bind(element, 'input', function (event) { + _this3.bind(element, 'input', function (event) { return controls.updateRangeFill.call(player, event.target); }); }); @@ -10950,7 +10991,7 @@ typeof navigator === "object" && (function (global, factory) { toggleClass(elements.controls, config.classNames.noTransition, false); }, 0); // Delay a little more for mouse users - var delay = _this4.touch ? 3000 : 4000; // Clear timer + var delay = _this3.touch ? 3000 : 4000; // Clear timer clearTimeout(timers.controls); // Hide again after delay @@ -11402,13 +11443,13 @@ typeof navigator === "object" && (function (global, factory) { // Add embed class for responsive toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set intial ratio - setAspectRatio.call(this); // Load the API if not already + setAspectRatio.call(this); // Load the SDK if not already if (!is$1.object(window.Vimeo)) { loadScript(this.config.urls.vimeo.sdk).then(function () { vimeo.ready.call(_this); }).catch(function (error) { - _this.debug.warn('Vimeo API failed to load', error); + _this.debug.warn('Vimeo SDK (player.js) failed to load', error); }); } else { vimeo.ready.call(this); @@ -11587,7 +11628,7 @@ typeof navigator === "object" && (function (global, factory) { var currentSrc; player.embed.getVideoUrl().then(function (value) { currentSrc = value; - controls.setDownloadLink.call(player); + controls.setDownloadUrl.call(player); }).catch(function (error) { _this2.debug.warn(error); }); @@ -11608,7 +11649,7 @@ typeof navigator === "object" && (function (global, factory) { width = _dimensions[0], height = _dimensions[1]; - player.embed.ratio = "".concat(width, ":").concat(height); + player.embed.ratio = [width, height]; setAspectRatio.call(_this2); }); // Set autopause @@ -11747,9 +11788,7 @@ typeof navigator === "object" && (function (global, factory) { var _this = this; // Add embed class for responsive - toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio - - setAspectRatio.call(this); // Setup API + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Setup API if (is$1.object(window.YT) && is$1.function(window.YT.Player)) { youtube.ready.call(this); @@ -11777,32 +11816,24 @@ typeof navigator === "object" && (function (global, factory) { getTitle: function getTitle(videoId) { var _this2 = this; - // Try via undocumented API method first - // This method disappears now and then though... - // https://github.com/sampotts/plyr/issues/709 - if (is$1.function(this.embed.getVideoData)) { - var _this$embed$getVideoD = this.embed.getVideoData(), - title = _this$embed$getVideoD.title; - - if (is$1.empty(title)) { - this.config.title = title; - ui.setTitle.call(this); - return; - } - } // Or via Google API + var url = format(this.config.urls.youtube.api, videoId); + fetch(url).then(function (data) { + if (is$1.object(data)) { + var title = data.title, + height = data.height, + width = data.width; // Set title + _this2.config.title = title; + ui.setTitle.call(_this2); // Set aspect ratio - var key = this.config.keys.google; + _this2.embed.ratio = [width, height]; + } - if (is$1.string(key) && !is$1.empty(key)) { - var url = format(this.config.urls.youtube.api, videoId, key); - fetch(url).then(function (result) { - if (is$1.object(result)) { - _this2.config.title = result.items[0].snippet.title; - ui.setTitle.call(_this2); - } - }).catch(function () {}); - } + setAspectRatio.call(_this2); + }).catch(function () { + // Set aspect ratio + setAspectRatio.call(_this2); + }); }, // API ready ready: function ready() { @@ -12159,6 +12190,20 @@ typeof navigator === "object" && (function (global, factory) { } }; + var destroy = function destroy(instance) { + // Destroy our adsManager + if (instance.manager) { + instance.manager.destroy(); + } // Destroy our adsManager + + + if (instance.elements.displayContainer) { + instance.elements.displayContainer.destroy(); + } + + instance.elements.container.remove(); + }; + var Ads = /*#__PURE__*/ function () { @@ -12206,18 +12251,20 @@ typeof navigator === "object" && (function (global, factory) { value: function load() { var _this2 = this; - if (this.enabled) { - // Check if the Google IMA3 SDK is loaded or load it ourselves - if (!is$1.object(window.google) || !is$1.object(window.google.ima)) { - loadScript(this.player.config.urls.googleIMA.sdk).then(function () { - _this2.ready(); - }).catch(function () { - // Script failed to load or is blocked - _this2.trigger('error', new Error('Google IMA SDK failed to load')); - }); - } else { - this.ready(); - } + if (!this.enabled) { + return; + } // Check if the Google IMA3 SDK is loaded or load it ourselves + + + if (!is$1.object(window.google) || !is$1.object(window.google.ima)) { + loadScript(this.player.config.urls.googleIMA.sdk).then(function () { + _this2.ready(); + }).catch(function () { + // Script failed to load or is blocked + _this2.trigger('error', new Error('Google IMA SDK failed to load')); + }); + } else { + this.ready(); } } /** @@ -12229,8 +12276,13 @@ typeof navigator === "object" && (function (global, factory) { value: function ready() { var _this3 = this; - // Start ticking our safety timer. If the whole advertisement + // Double check we're enabled + if (!this.enabled) { + destroy(this); + } // Start ticking our safety timer. If the whole advertisement // thing doesn't resolve within our set time; we bail + + this.startSafetyTimer(12000, 'ready()'); // Clear the safety timer this.managerPromise.then(function () { @@ -12360,9 +12412,7 @@ typeof navigator === "object" && (function (global, factory) { this.manager = event.getAdsManager(this.player, settings); // Get the cue points for any mid-rolls by filtering out the pre- and post-roll - this.cuePoints = this.manager.getCuePoints(); // Set volume to match player - - this.manager.setVolume(this.player.volume); // Add listeners to the required events + this.cuePoints = this.manager.getCuePoints(); // Add listeners to the required events // Advertisement error events this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, function (error) { @@ -12421,15 +12471,16 @@ typeof navigator === "object" && (function (global, factory) { var dispatchEvent = function dispatchEvent(type) { var event = "ads".concat(type.replace(/_/g, '').toLowerCase()); triggerEvent.call(_this8.player, _this8.player.media, event); - }; + }; // Bubble the event + + + dispatchEvent(event.type); switch (event.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to determine whether the // ad is a video ad or an overlay - this.trigger('loaded'); // Bubble event - - dispatchEvent(event.type); // Start countdown + this.trigger('loaded'); // Start countdown this.pollCountdown(true); @@ -12443,11 +12494,15 @@ typeof navigator === "object" && (function (global, factory) { break; + case google.ima.AdEvent.Type.STARTED: + // Set volume to match player + this.manager.setVolume(this.player.volume); + break; + case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: // All ads for the current videos are done. We can now request new advertisements // in case the video is re-played - // Fire event - dispatchEvent(event.type); // TODO: Example for what happens when a next video in a playlist would be loaded. + // TODO: Example for what happens when a next video in a playlist would be loaded. // So here we load a new video when all ads are done. // Then we load new ads within a new adsManager. When the video // Is started - after - the ads are loaded, then we get ads. @@ -12468,7 +12523,6 @@ typeof navigator === "object" && (function (global, factory) { // }; // TODO: So there is still this thing where a video should only be allowed to start // playing when the IMA SDK is ready or has failed - this.loadAds(); break; @@ -12476,7 +12530,6 @@ typeof navigator === "object" && (function (global, factory) { // This event indicates the ad has started - the video player can adjust the UI, // for example display a pause button and remaining time. Fired when content should // be paused. This usually happens right before an ad is about to cover the content - dispatchEvent(event.type); this.pauseContent(); break; @@ -12485,19 +12538,10 @@ typeof navigator === "object" && (function (global, factory) { // appropriate UI actions, such as removing the timer for remaining time detection. // Fired when content should be resumed. This usually happens when an ad finishes // or collapses - dispatchEvent(event.type); this.pollCountdown(); this.resumeContent(); break; - case google.ima.AdEvent.Type.STARTED: - case google.ima.AdEvent.Type.MIDPOINT: - case google.ima.AdEvent.Type.COMPLETE: - case google.ima.AdEvent.Type.IMPRESSION: - case google.ima.AdEvent.Type.CLICK: - dispatchEvent(event.type); - break; - case google.ima.AdEvent.Type.LOG: if (adData.adError) { this.player.debug.warn("Non-fatal ad error: ".concat(adData.adError.getMessage())); @@ -12582,7 +12626,10 @@ typeof navigator === "object" && (function (global, factory) { this.managerPromise.then(function () { - // Initialize the container. Must be done via a user action on mobile devices + // Set volume to match player + _this10.manager.setVolume(_this10.player.volume); // Initialize the container. Must be done via a user action on mobile devices + + _this10.elements.displayContainer.initialize(); try { @@ -13100,7 +13147,10 @@ typeof navigator === "object" && (function (global, factory) { timeContainer.appendChild(this.elements.thumb.time); this.elements.thumb.container.appendChild(timeContainer); // Inject the whole thumb - this.player.elements.progress.appendChild(this.elements.thumb.container); // Create HTML element: plyr__preview-scrubbing-container + if (is$1.element(this.player.elements.progress)) { + this.player.elements.progress.appendChild(this.elements.thumb.container); + } // Create HTML element: plyr__preview-scrubbing-container + this.elements.scrubbing.container = createElement('div', { class: this.player.config.classNames.previewThumbnails.scrubbingContainer @@ -13609,6 +13659,25 @@ typeof navigator === "object" && (function (global, factory) { } }; + /** + * Returns a number whose value is limited to the given range. + * + * Example: limit the output of this computation to between 0 and 255 + * (x * 255).clamp(0, 255) + * + * @param {Number} input + * @param {Number} min The lower boundary of the output range + * @param {Number} max The upper boundary of the output range + * @returns A number in the range [min, max] + * @type Number + */ + function clamp() { + var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 255; + return Math.min(Math.max(input, min), max); + } + // TODO: Use a WeakMap for private globals // const globals = new WeakMap(); // Plyr instance @@ -14455,6 +14524,8 @@ typeof navigator === "object" && (function (global, factory) { }, { key: "speed", set: function set(input) { + var _this4 = this; + var speed = null; if (is$1.number(input)) { @@ -14467,26 +14538,18 @@ typeof navigator === "object" && (function (global, factory) { if (!is$1.number(speed)) { speed = this.config.speed.selected; - } // Set min/max - - - if (speed < 0.1) { - speed = 0.1; - } - - if (speed > 2.0) { - speed = 2.0; - } + } // Clamp to min/max - if (!this.config.speed.options.includes(speed)) { - this.debug.warn("Unsupported speed (".concat(speed, ")")); - return; - } // Update config + var min = this.minimumSpeed, + max = this.maximumSpeed; + speed = clamp(speed, min, max); // Update config this.config.speed.selected = speed; // Set media speed - this.media.playbackRate = speed; + setTimeout(function () { + _this4.media.playbackRate = speed; + }, 0); } /** * Get current playback speed @@ -14496,6 +14559,46 @@ typeof navigator === "object" && (function (global, factory) { return Number(this.media.playbackRate); } /** + * Get the minimum allowed speed + */ + + }, { + key: "minimumSpeed", + get: function get() { + if (this.isYouTube) { + // https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + return Math.min.apply(Math, _toConsumableArray(this.options.speed)); + } + + if (this.isVimeo) { + // https://github.com/vimeo/player.js/#setplaybackrateplaybackrate-number-promisenumber-rangeerrorerror + return 0.5; + } // https://stackoverflow.com/a/32320020/1191319 + + + return 0.0625; + } + /** + * Get the maximum allowed speed + */ + + }, { + key: "maximumSpeed", + get: function get() { + if (this.isYouTube) { + // https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + return Math.max.apply(Math, _toConsumableArray(this.options.speed)); + } + + if (this.isVimeo) { + // https://github.com/vimeo/player.js/#setplaybackrateplaybackrate-number-promisenumber-rangeerrorerror + return 2; + } // https://stackoverflow.com/a/32320020/1191319 + + + return 16; + } + /** * Set playback quality * Currently HTML5 & YouTube only * @param {Number} input - Quality level @@ -14625,6 +14728,18 @@ typeof navigator === "object" && (function (global, factory) { return is$1.url(download) ? download : this.source; } /** + * Set the download URL + */ + , + set: function set(input) { + if (!is$1.url(input)) { + return; + } + + this.config.urls.download = input; + controls.setDownloadUrl.call(this); + } + /** * Set the poster image for a video * @param {String} input - the URL for the new poster image */ @@ -14657,6 +14772,10 @@ typeof navigator === "object" && (function (global, factory) { }, { key: "ratio", get: function get() { + if (!this.isVideo) { + return null; + } + var ratio = reduceAspectRatio(getAspectRatio.call(this)); return is$1.array(ratio) ? ratio.join(':') : ratio; } |