aboutsummaryrefslogtreecommitdiffstats
path: root/libretube-theme/static/modules/plyr/plyr.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'libretube-theme/static/modules/plyr/plyr.mjs')
-rw-r--r--libretube-theme/static/modules/plyr/plyr.mjs1459
1 files changed, 849 insertions, 610 deletions
diff --git a/libretube-theme/static/modules/plyr/plyr.mjs b/libretube-theme/static/modules/plyr/plyr.mjs
index 765699e..f3bb7bd 100644
--- a/libretube-theme/static/modules/plyr/plyr.mjs
+++ b/libretube-theme/static/modules/plyr/plyr.mjs
@@ -109,7 +109,7 @@ function matches(element, selector) {
return Array.from(document.querySelectorAll(selector)).includes(this);
}
- var matches = match;
+ var matches = match;
return matches.call(element, selector);
}
@@ -535,6 +535,49 @@ var is$1 = {
};
// ==========================================================================
+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, delay) {
+ setTimeout(function () {
+ try {
+ // eslint-disable-next-line no-param-reassign
+ element.hidden = true; // eslint-disable-next-line no-unused-expressions
+
+ element.offsetHeight; // eslint-disable-next-line no-param-reassign
+
+ element.hidden = false;
+ } catch (e) {// Do nothing
+ }
+ }, 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)
+};
+
+// ==========================================================================
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g
@@ -680,6 +723,47 @@ function ready() {
}).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(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
@@ -803,7 +887,7 @@ function getAttributesFromSelector(sel, existingAttributes) {
}
var attributes = {};
- var existing = existingAttributes;
+ var existing = extend({}, existingAttributes);
sel.split(',').forEach(function (s) {
// Remove whitespace
var selector = s.trim();
@@ -811,7 +895,10 @@ function getAttributesFromSelector(sel, existingAttributes) {
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);
@@ -819,11 +906,12 @@ function getAttributesFromSelector(sel, existingAttributes) {
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 '#':
@@ -840,7 +928,7 @@ function getAttributesFromSelector(sel, existingAttributes) {
break;
}
});
- return attributes;
+ return extend(existing, attributes);
} // Toggle hidden
function toggleHidden(element, hidden) {
@@ -852,13 +940,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) {
@@ -892,8 +977,8 @@ function matches$1(element, selector) {
return Array.from(document.querySelectorAll(selector)).includes(this);
}
- var matches = match;
- return matches.call(element, selector);
+ var method = match;
+ return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@@ -957,47 +1042,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',
@@ -1095,6 +1139,87 @@ var support = {
reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches
};
+function validateRatio(input) {
+ if (!is$1.array(input) && (!is$1.string(input) || !input.includes(':'))) {
+ return false;
+ }
+
+ var ratio = is$1.array(input) ? input : input.split(':');
+ return ratio.map(Number).every(is$1.number);
+}
+function reduceAspectRatio(ratio) {
+ if (!is$1.array(ratio) || !ratio.every(is$1.number)) {
+ return null;
+ }
+
+ var _ratio = _slicedToArray(ratio, 2),
+ width = _ratio[0],
+ height = _ratio[1];
+
+ var getDivider = function getDivider(w, h) {
+ return h === 0 ? w : getDivider(h, w % h);
+ };
+
+ var divider = getDivider(width, height);
+ return [width / divider, height / divider];
+}
+function getAspectRatio(input) {
+ var parse = function parse(ratio) {
+ return validateRatio(ratio) ? ratio.split(':').map(Number) : null;
+ }; // Try provided ratio
+
+
+ var ratio = parse(input); // Get from config
+
+ if (ratio === null) {
+ ratio = parse(this.config.ratio);
+ } // Get from embed
+
+
+ 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;
+} // Set aspect ratio for responsive container
+
+function setAspectRatio(input) {
+ if (!this.isVideo) {
+ return {};
+ }
+
+ var ratio = getAspectRatio.call(this, input);
+
+ var _ref = is$1.array(ratio) ? ratio : [0, 0],
+ _ref2 = _slicedToArray(_ref, 2),
+ w = _ref2[0],
+ 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
+
+ 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);
+ }
+
+ return {
+ padding: padding,
+ ratio: ratio
+ };
+}
+
// ==========================================================================
var html5 = {
getSources: function getSources() {
@@ -1128,14 +1253,19 @@ var html5 = {
return;
}
- var player = this; // Quality
+ var player = this; // Set aspect ratio if fixed
+
+ if (!is$1.empty(this.config.ratio)) {
+ setAspectRatio.call(player);
+ } // Quality
+
Object.defineProperty(player.media, 'quality', {
get: function get() {
// Get sources
var sources = html5.getSources.call(player);
- var source = sources.find(function (source) {
- return source.getAttribute('src') === player.source;
+ var source = sources.find(function (s) {
+ return s.getAttribute('src') === player.source;
}); // Return size, if match is found
return source && Number(source.getAttribute('data-res'));
@@ -1144,8 +1274,8 @@ var html5 = {
// Get sources
var sources = html5.getSources.call(player); // Get first match for requested size
- var source = sources.find(function (source) {
- return Number(source.getAttribute('data-res')) === input;
+ var source = sources.find(function (s) {
+ return Number(s.getAttribute('data-res')) === input;
}); // No matching source found
if (!source) {
@@ -1225,47 +1355,6 @@ function closest(array, value) {
});
}
-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 generateId(prefix) {
@@ -1376,10 +1465,10 @@ var i18n = {
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
- key = _ref2[0],
- value = _ref2[1];
+ k = _ref2[0],
+ v = _ref2[1];
- string = replaceAll(string, key, value);
+ string = replaceAll(string, k, v);
});
return string;
}
@@ -1514,6 +1603,7 @@ function loadSprite(url, id) {
};
var update = function update(container, data) {
+ // eslint-disable-next-line no-param-reassign
container.innerHTML = data; // Check again incase of race condition
if (hasId && exists()) {
@@ -1712,7 +1802,9 @@ var controls = {
},
// 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',
@@ -1735,8 +1827,12 @@ var controls = {
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;
@@ -1880,10 +1976,10 @@ var controls = {
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
@@ -1894,7 +1990,7 @@ var controls = {
// 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) {
@@ -1914,7 +2010,7 @@ var controls = {
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;
@@ -1933,7 +2029,7 @@ var controls = {
}
}
- setFocus.call(_this, target, true);
+ setFocus.call(_this2, target, true);
}
}
}, false); // Enter will fire a `click` event but we still need to manage focus
@@ -1944,12 +2040,12 @@ var controls = {
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,
@@ -1982,9 +2078,9 @@ var controls = {
get: function get() {
return menuItem.getAttribute('aria-checked') === 'true';
},
- set: function set(checked) {
+ set: function set(check) {
// Ensure exclusivity
- if (checked) {
+ if (check) {
Array.from(menuItem.parentNode.children).filter(function (node) {
return matches$1(node, '[role="menuitemradio"]');
}).forEach(function (node) {
@@ -1992,7 +2088,7 @@ var controls = {
});
}
- menuItem.setAttribute('aria-checked', checked ? 'true' : 'false');
+ menuItem.setAttribute('aria-checked', check ? 'true' : 'false');
}
});
this.listeners.bind(menuItem, 'click keyup', function (event) {
@@ -2006,28 +2102,28 @@ var controls = {
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);
},
// Format a time for display
- formatTime: function formatTime$$1() {
+ formatTime: function formatTime$1() {
var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var inverted = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
@@ -2085,7 +2181,7 @@ var controls = {
},
// Update <progress> elements
updateProgress: function updateProgress(event) {
- var _this3 = this;
+ var _this4 = this;
if (!this.supported.ui || !is$1.event(event)) {
return;
@@ -2094,16 +2190,16 @@ var controls = {
var value = 0;
var setProgress = function setProgress(target, input) {
- var value = is$1.number(input) ? input : 0;
- var progress = is$1.element(target) ? target : _this3.elements.display.buffer; // Update value and label
+ var val = is$1.number(input) ? input : 0;
+ 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
+ progress.value = val; // Update text label inside
var label = progress.getElementsByTagName('span')[0];
if (is$1.element(label)) {
- label.childNodes[0].nodeValue = value;
+ label.childNodes[0].nodeValue = val;
}
}
};
@@ -2147,8 +2243,8 @@ var controls = {
range.setAttribute('aria-valuenow', this.currentTime);
var currentTime = controls.formatTime(this.currentTime);
var duration = controls.formatTime(this.duration);
- var format$$1 = i18n.get('seekLabel', this.config);
- range.setAttribute('aria-valuetext', format$$1.replace('{currentTime}', currentTime).replace('{duration}', duration));
+ var format = i18n.get('seekLabel', this.config);
+ range.setAttribute('aria-valuetext', format.replace('{currentTime}', currentTime).replace('{duration}', duration));
} else if (matches$1(range, this.config.selectors.inputs.volume)) {
var percent = range.value * 100;
range.setAttribute('aria-valuenow', percent);
@@ -2167,20 +2263,17 @@ var controls = {
},
// 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) {
return;
- } // Calculate percentage
-
+ }
- var percent = 0;
- var clientRect = this.elements.progress.getBoundingClientRect();
var visible = "".concat(this.config.classNames.tooltip, "--visible");
- var toggle = function toggle(_toggle) {
- toggleClass(_this4.elements.display.seekTooltip, visible, _toggle);
+ var toggle = function toggle(show) {
+ return toggleClass(_this5.elements.display.seekTooltip, visible, show);
}; // Hide on touch
@@ -2190,6 +2283,9 @@ var controls = {
} // Determine percentage, if already visible
+ var percent = 0;
+ var clientRect = this.elements.progress.getBoundingClientRect();
+
if (is$1.event(event)) {
percent = 100 / clientRect.width * (event.pageX - clientRect.left);
} else if (hasClass(this.elements.display.seekTooltip, visible)) {
@@ -2346,7 +2442,7 @@ var controls = {
},
// 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)) {
@@ -2358,7 +2454,7 @@ var controls = {
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
@@ -2376,25 +2472,25 @@ var controls = {
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)
});
});
@@ -2440,7 +2536,7 @@ var controls = {
// 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)) {
@@ -2467,9 +2563,9 @@ var controls = {
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'
};
@@ -2488,7 +2584,7 @@ var controls = {
},
// 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)) {
@@ -2506,7 +2602,7 @@ var controls = {
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;
@@ -2522,11 +2618,11 @@ var controls = {
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);
@@ -2550,8 +2646,8 @@ var controls = {
var target = pane;
if (!is$1.element(target)) {
- target = Object.values(this.elements.settings.panels).find(function (pane) {
- return !pane.hidden;
+ target = Object.values(this.elements.settings.panels).find(function (p) {
+ return !p.hidden;
});
}
@@ -2576,7 +2672,10 @@ var controls = {
} 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)
@@ -2619,11 +2718,11 @@ var controls = {
},
// 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;
@@ -2652,7 +2751,7 @@ var controls = {
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
@@ -2669,265 +2768,289 @@ var controls = {
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
-
-
- if (this.config.controls.includes('rewind')) {
- container.appendChild(controls.createButton.call(this, 'rewind'));
- } // Play/Pause button
-
+ var _this10 = this;
- if (this.config.controls.includes('play')) {
- container.appendChild(controls.createButton.call(this, 'play'));
- } // Fast forward 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('fast-forward')) {
- container.appendChild(controls.createButton.call(this, 'fast-forward'));
- } // Progress
+ var container = createElement('div', getAttributesFromSelector(this.config.selectors.controls.wrapper));
+ this.elements.controls = container; // Default item attributes
- if (this.config.controls.includes('progress')) {
- var progress = createElement('div', getAttributesFromSelector(this.config.selectors.progress)); // Seek range slider
+ var defaultAttributes = {
+ class: 'plyr__controls__item'
+ }; // Loop through controls in order
- progress.appendChild(controls.createRange.call(this, 'seek', {
- id: "plyr-seek-".concat(data.id)
- })); // Buffer progress
+ dedupe(this.config.controls).forEach(function (control) {
+ // Restart button
+ if (control === 'restart') {
+ container.appendChild(createButton.call(_this10, 'restart', defaultAttributes));
+ } // Rewind 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 === 'rewind') {
+ container.appendChild(createButton.call(_this10, 'rewind', defaultAttributes));
+ } // Play/Pause button
- this.elements.progress = progress;
- container.appendChild(this.elements.progress);
- } // Media current time display
+ if (control === 'play') {
+ container.appendChild(createButton.call(_this10, 'play', defaultAttributes));
+ } // Fast forward button
- if (this.config.controls.includes('current-time')) {
- container.appendChild(controls.createTime.call(this, 'currentTime'));
- } // Media duration display
+ if (control === 'fast-forward') {
+ container.appendChild(createButton.call(_this10, 'fast-forward', defaultAttributes));
+ } // Progress
- if (this.config.controls.includes('duration')) {
- container.appendChild(controls.createTime.call(this, 'duration'));
- } // Volume controls
+ 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('mute') || this.config.controls.includes('volume')) {
- var volume = createElement('div', {
- class: 'plyr__volume'
- }); // Toggle mute button
+ progress.appendChild(createRange.call(_this10, 'seek', {
+ id: "plyr-seek-".concat(data.id)
+ })); // Buffer progress
- if (this.config.controls.includes('mute')) {
- volume.appendChild(controls.createButton.call(this, 'mute'));
- } // Volume range control
+ progress.appendChild(createProgress.call(_this10, 'buffer')); // TODO: Add loop display indicator
+ // Seek tooltip
+ 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('volume')) {
- // Set the attributes
- var attributes = {
- max: 1,
- step: 0.05,
- value: this.config.volume
- }; // Create the volume range slider
+ _this10.elements.progress = progress;
+ progressContainer.appendChild(_this10.elements.progress);
+ container.appendChild(progressContainer);
+ } // Media current time 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 === 'current-time') {
+ container.appendChild(createTime.call(_this10, 'currentTime', defaultAttributes));
+ } // Media duration display
- if (this.config.controls.includes('captions')) {
- container.appendChild(controls.createButton.call(this, 'captions'));
- } // Settings button / menu
+ if (control === 'duration') {
+ container.appendChild(createTime.call(_this10, 'duration', defaultAttributes));
+ } // Volume controls
- 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
+ if (control === 'mute' || control === 'volume') {
+ var volume = _this10.elements.volume; // Create the volume container if needed
- var menu = createElement('div', {
- role: 'menu'
- });
- home.appendChild(menu);
- inner.appendChild(home);
- this.elements.settings.panels.home = home; // Build the menu items
-
- this.config.settings.forEach(function (type) {
- // TODO: bundle this with the createMenuItem helper and bindings
- var menuItem = createElement('button', extend(getAttributesFromSelector(_this9.config.selectors.buttons.settings), {
- type: 'button',
- class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--forward"),
- role: 'menuitem',
- 'aria-haspopup': true,
- hidden: ''
- })); // Bind menu shortcuts for keyboard users
-
- controls.bindMenuItemShortcuts.call(_this9, menuItem, type); // Show menu on click
-
- on(menuItem, 'click', function () {
- controls.showMenuPanel.call(_this9, type, false);
- });
- var flex = createElement('span', null, i18n.get(type, _this9.config));
- var value = createElement('span', {
- class: _this9.config.classNames.menu.value
- }); // Speed contains HTML entities
-
- value.innerHTML = data[type];
- flex.appendChild(value);
- menuItem.appendChild(flex);
- menu.appendChild(menuItem); // Build the panes
-
- var pane = createElement('div', {
- id: "plyr-settings-".concat(data.id, "-").concat(type),
- hidden: ''
- }); // Back button
+ 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
- var backButton = createElement('button', {
- type: 'button',
- class: "".concat(_this9.config.classNames.control, " ").concat(_this9.config.classNames.control, "--back")
- }); // Visible label
- backButton.appendChild(createElement('span', {
- 'aria-hidden': true
- }, i18n.get(type, _this9.config))); // Screen reader label
+ if (control === 'mute') {
+ volume.appendChild(createButton.call(_this10, 'mute'));
+ } // Volume range control
- backButton.appendChild(createElement('span', {
- class: _this9.config.classNames.hidden
- }, i18n.get('menuBack', _this9.config))); // Go back via keyboard
- on(pane, 'keydown', function (event) {
- // We only care about <-
- if (event.which !== 37) {
- return;
- } // Prevent seek
+ if (control === 'volume') {
+ // Set the attributes
+ var attributes = {
+ max: 1,
+ step: 0.05,
+ value: _this10.config.volume
+ }; // Create the volume range slider
+ volume.appendChild(createRange.call(_this10, 'volume', extend(attributes, {
+ id: "plyr-volume-".concat(data.id)
+ })));
+ }
+ } // Toggle captions button
- event.preventDefault();
- event.stopPropagation(); // Show the respective menu
- controls.showMenuPanel.call(_this9, 'home', true);
- }, false); // Go back via button click
+ if (control === 'captions') {
+ container.appendChild(createButton.call(_this10, 'captions', defaultAttributes));
+ } // Settings button / menu
- on(backButton, 'click', function () {
- controls.showMenuPanel.call(_this9, 'home', false);
- }); // Add to pane
- pane.appendChild(backButton); // Menu
+ if (control === 'settings' && !is$1.empty(_this10.config.settings)) {
+ var wrapper = createElement('div', extend({}, defaultAttributes, {
+ class: "".concat(defaultAttributes.class, " plyr__menu").trim(),
+ hidden: ''
+ }));
+ wrapper.appendChild(createButton.call(_this10, '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
- pane.appendChild(createElement('div', {
+ var menu = 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
+ });
+ 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;
+ });
+ popup.appendChild(inner);
+ wrapper.appendChild(popup);
+ container.appendChild(wrapper);
+ _this10.elements.settings.popup = popup;
+ _this10.elements.settings.menu = wrapper;
+ } // 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) {
@@ -3022,7 +3145,7 @@ var controls = {
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() {
@@ -3058,8 +3181,8 @@ var controls = {
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);
});
}
}
@@ -3210,6 +3333,7 @@ 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
track.mode = 'hidden'; // Add event listener for cue changes
@@ -3391,8 +3515,8 @@ var captions = {
});
var track;
languages.every(function (language) {
- track = sorted.find(function (track) {
- return track.language === language;
+ track = sorted.find(function (t) {
+ return t.language === language;
});
return !track; // Break iteration if there is a match
}); // If no match is found but is required, get first
@@ -3502,8 +3626,9 @@ var defaults$1 = {
invertTime: true,
// Clicking the currentTime inverts it's value to show time left rather than elapsed
toggleInvert: true,
- // Aspect ratio (for embeds)
- ratio: '16:9',
+ // Force an aspect ratio
+ // The format must be `'w:h'` (e.g. `'16:9'`)
+ ratio: null,
// Click video container to play/pause
clickToPlay: true,
// Auto hide the controls
@@ -3515,9 +3640,9 @@ var defaults$1 = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
- iconUrl: '../vendor/plyr/plyr.svg',
+ iconUrl: '/theme/modules/plyr/plyr.svg',
// Blank video (used to prevent errors on source change)
- blankVideo: '../vendor/plyr/blank.mp4',
+ blankVideo: '/theme/modules/plyr/blank.mp4',
// Quality default
quality: {
default: 576,
@@ -3570,7 +3695,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
@@ -3628,7 +3754,7 @@ var defaults$1 = {
},
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}'
},
googleIMA: {
sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js'
@@ -3704,10 +3830,7 @@ var defaults$1 = {
},
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: {
@@ -3715,6 +3838,7 @@ var defaults$1 = {
provider: 'plyr--{0}',
video: 'plyr__video-wrapper',
embed: 'plyr__video-embed',
+ videoFixedRatio: 'plyr__video-wrapper--fixed-ratio',
embedContainer: 'plyr__video-embed__container',
poster: 'plyr__poster',
posterEnabled: 'plyr__poster-enabled',
@@ -3777,10 +3901,6 @@ var defaults$1 = {
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: {
@@ -3919,8 +4039,6 @@ function onChange() {
}
function toggleFallback() {
- var _this = this;
-
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Store or restore scroll position
@@ -3960,12 +4078,7 @@ function toggleFallback() {
viewport.content = viewport.content.split(',').filter(function (part) {
return part.trim() !== property;
}).join(',');
- } // Force a repaint as sometimes Safari doesn't want to fill the screen
-
-
- setTimeout(function () {
- return repaint(_this.target);
- }, 100);
+ }
} // Toggle button and fire events
@@ -3976,7 +4089,7 @@ var Fullscreen =
/*#__PURE__*/
function () {
function Fullscreen(player) {
- var _this2 = this;
+ var _this = this;
_classCallCheck(this, Fullscreen);
@@ -3996,16 +4109,16 @@ function () {
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
// TODO: Filter for target??
- onChange.call(_this2);
+ onChange.call(_this);
}); // Fullscreen toggle on double click
on.call(this.player, this.player.elements.container, 'dblclick', function (event) {
// Ignore double click in controls
- if (is$1.element(_this2.player.elements.controls) && _this2.player.elements.controls.contains(event.target)) {
+ if (is$1.element(_this.player.elements.controls) && _this.player.elements.controls.contains(event.target)) {
return;
}
- _this2.toggle();
+ _this.toggle();
}); // Update the UI
this.update();
@@ -4230,13 +4343,13 @@ var ui = {
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
@@ -4353,7 +4466,9 @@ var ui = {
toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); // Set state
Array.from(this.elements.buttons.play || []).forEach(function (target) {
- target.pressed = _this3.playing;
+ Object.assign(target, {
+ pressed: _this3.playing
+ });
}); // Only update controls on non timeupdate events
if (is$1.event(event) && event.type === 'timeupdate') {
@@ -4380,55 +4495,17 @@ var ui = {
},
// Toggle controls based on state and `force` argument
toggleControls: function toggleControls(force) {
- var controls$$1 = this.elements.controls;
+ var controlsElement = this.elements.controls;
- if (controls$$1 && this.config.hideControls) {
+ if (controlsElement && this.config.hideControls) {
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
- this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover || recentTouchSeek));
+ this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
}
};
-/* function reduceAspectRatio(width, height) {
- const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));
- const ratio = getRatio(width, height);
- return `${width / ratio}:${height / ratio}`;
-} */
-// Set aspect ratio for responsive container
-
-function setAspectRatio(input) {
- var ratio = input;
-
- if (!is$1.string(ratio) && !is$1.nullOrUndefined(this.embed)) {
- ratio = this.embed.ratio;
- }
-
- if (!is$1.string(ratio)) {
- ratio = this.config.ratio;
- }
-
- var _ratio$split$map = ratio.split(':').map(Number),
- _ratio$split$map2 = _slicedToArray(_ratio$split$map, 2),
- x = _ratio$split$map2[0],
- y = _ratio$split$map2[1];
-
- var padding = 100 / x * y;
- this.elements.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, "%)");
- }
-
- return {
- padding: padding,
- ratio: ratio
- };
-}
-
var Listeners =
/*#__PURE__*/
function () {
@@ -4573,6 +4650,16 @@ function () {
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
@@ -4682,11 +4769,11 @@ function () {
on.call(player, elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
- var controls$$1 = elements.controls; // Remove button states for fullscreen
+ var controlsElement = elements.controls; // Remove button states for fullscreen
- if (controls$$1 && event.type === 'enterfullscreen') {
- controls$$1.pressed = false;
- controls$$1.hover = false;
+ if (controlsElement && event.type === 'enterfullscreen') {
+ controlsElement.pressed = false;
+ controlsElement.hover = false;
} // Show, then hide after a timeout unless another control event occurs
@@ -4705,15 +4792,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) {
@@ -4722,16 +4801,15 @@ function () {
var target = player.elements.wrapper.firstChild;
- var _ratio$split$map = ratio.split(':').map(Number),
- _ratio$split$map2 = _slicedToArray(_ratio$split$map, 2),
- height = _ratio$split$map2[1];
+ var _ratio = _slicedToArray(ratio, 2),
+ y = _ratio[1];
- var _player$embed$ratio$s = player.embed.ratio.split(':').map(Number),
- _player$embed$ratio$s2 = _slicedToArray(_player$embed$ratio$s, 2),
- videoWidth = _player$embed$ratio$s2[0],
- videoHeight = _player$embed$ratio$s2[1];
+ var _getAspectRatio$call = getAspectRatio.call(player),
+ _getAspectRatio$call2 = _slicedToArray(_getAspectRatio$call, 2),
+ videoX = _getAspectRatio$call2[0],
+ videoY = _getAspectRatio$call2[1];
- target.style.maxWidth = toggle ? "".concat(height / videoHeight * videoWidth, "px") : null;
+ target.style.maxWidth = toggle ? "".concat(y / videoY * videoX, "px") : null;
target.style.margin = toggle ? '0 auto' : null;
}; // Resize on fullscreen change
@@ -4749,16 +4827,21 @@ function () {
};
var resized = function resized() {
- window.clearTimeout(timers.resized);
- timers.resized = window.setTimeout(setPlayerSize, 50);
+ clearTimeout(timers.resized);
+ timers.resized = setTimeout(setPlayerSize, 50);
};
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;
}
@@ -4890,7 +4973,7 @@ function () {
}); // 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
@@ -4941,7 +5024,7 @@ function () {
}, {
key: "controls",
- value: function controls$$1() {
+ value: function controls$1() {
var _this3 = this;
var player = this.player;
@@ -5152,7 +5235,6 @@ function () {
this.bind(elements.controls, 'focusin', function () {
var config = player.config,
- elements = player.elements,
timers = player.timers; // Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, true); // Toggle
@@ -5201,7 +5283,7 @@ function () {
return Listeners;
}();
-var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
@@ -5310,16 +5392,23 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
maxTries = (args.numRetries || 0) + 1,
beforeCallbackFn = args.before || devnull,
pathStripped = path.replace(/^(css|img)!/, ''),
- isCss,
+ isLegacyIECss,
e;
numTries = numTries || 0;
if (/(^css!|\.css$)/.test(path)) {
- isCss = true; // css
-
+ // css
e = doc.createElement('link');
e.rel = 'stylesheet';
- e.href = pathStripped; //.replace(/^css!/, ''); // remove "css!" prefix
+ e.href = pathStripped; // tag IE9+
+
+ isLegacyIECss = 'hideFocus' in e; // use preload in IE Edge (to detect load errors)
+
+ if (isLegacyIECss && e.relList) {
+ isLegacyIECss = 0;
+ e.rel = 'preload';
+ e.as = 'style';
+ }
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) {
// image
e = doc.createElement('img');
@@ -5332,10 +5421,10 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
}
e.onload = e.onerror = e.onbeforeload = function (ev) {
- var result = ev.type[0]; // Note: The following code isolates IE using `hideFocus` and treats empty
- // stylesheets as failures to get around lack of onerror support
+ var result = ev.type[0]; // treat empty stylesheets as failures to get around lack of onerror
+ // support in IE9-11
- if (isCss && 'hideFocus' in e) {
+ if (isLegacyIECss) {
try {
if (!e.sheet.cssText.length) result = 'e';
} catch (x) {
@@ -5353,6 +5442,9 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
if (numTries < maxTries) {
return loadFile(path, callbackFn, args, numTries);
}
+ } else if (e.rel == 'preload' && e.as == 'style') {
+ // activate preloaded stylesheets
+ return e.rel = 'stylesheet'; // jshint ignore:line
} // execute callback
@@ -5399,9 +5491,11 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
/**
* Initiate script load and register bundle.
* @param {(string|string[])} paths - The file paths
- * @param {(string|Function)} [arg1] - The bundleId or success callback
- * @param {Function} [arg2] - The success or error callback
- * @param {Function} [arg3] - The error callback
+ * @param {(string|Function|Object)} [arg1] - The (1) bundleId or (2) success
+ * callback or (3) object literal with success/error arguments, numRetries,
+ * etc.
+ * @param {(Function|Object)} [arg2] - The (1) success callback or (2) object
+ * literal with success/error arguments, numRetries, etc.
*/
@@ -5418,15 +5512,26 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
} else {
bundleIdCache[bundleId] = true;
}
- } // load scripts
+ }
+ function loadFn(resolve, reject) {
+ loadFiles(paths, function (pathsNotFound) {
+ // execute callbacks
+ executeCallbacks(args, pathsNotFound); // resolve Promise
- loadFiles(paths, function (pathsNotFound) {
- // execute callbacks
- executeCallbacks(args, pathsNotFound); // publish bundle load event
+ if (resolve) {
+ executeCallbacks({
+ success: resolve,
+ error: reject
+ }, pathsNotFound);
+ } // publish bundle load event
+
+
+ publish(bundleId, pathsNotFound);
+ }, args);
+ }
- publish(bundleId, pathsNotFound);
- }, args);
+ if (args.returnPromise) return new Promise(loadFn);else loadFn();
}
/**
* Execute callbacks when dependencies have been satisfied.
@@ -5519,20 +5624,20 @@ var vimeo = {
// 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);
}
},
// API Ready
- ready: function ready$$1() {
+ ready: function ready() {
var _this2 = this;
var player = this;
@@ -5704,7 +5809,7 @@ var vimeo = {
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);
});
@@ -5725,8 +5830,8 @@ var vimeo = {
width = _dimensions[0],
height = _dimensions[1];
- player.embed.ratio = "".concat(width, ":").concat(height);
- setAspectRatio.call(_this2, player.embed.ratio);
+ player.embed.ratio = [width, height];
+ setAspectRatio.call(_this2);
}); // Set autopause
player.embed.setAutopause(player.config.autopause).then(function (state) {
@@ -5848,73 +5953,75 @@ function assurePlaybackState$1(play) {
}
}
+function getHost(config) {
+ if (config.noCookie) {
+ return 'https://www.youtube-nocookie.com';
+ }
+
+ if (window.location.protocol === 'http:') {
+ return 'http://www.youtube.com';
+ } // Use YouTube's default
+
+
+ return undefined;
+}
+
var youtube = {
setup: function setup() {
var _this = this;
// Add embed class for responsive
- toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio
-
- 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);
} else {
- // Load the API
- loadScript(this.config.urls.youtube.sdk).catch(function (error) {
- _this.debug.warn('YouTube API failed to load', error);
- }); // Setup callback for the API
- // YouTube has it's own system of course...
+ // Reference current global callback
+ var callback = window.onYouTubeIframeAPIReady; // Set callback to process queue
- window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || []; // Add to queue
+ window.onYouTubeIframeAPIReady = function () {
+ // Call global callback if set
+ if (is$1.function(callback)) {
+ callback();
+ }
- window.onYouTubeReadyCallbacks.push(function () {
youtube.ready.call(_this);
- }); // Set callback to process queue
+ }; // Load the SDK
- window.onYouTubeIframeAPIReady = function () {
- window.onYouTubeReadyCallbacks.forEach(function (callback) {
- callback();
- });
- };
+
+ loadScript(this.config.urls.youtube.sdk).catch(function (error) {
+ _this.debug.warn('YouTube API failed to load', error);
+ });
}
},
// Get the media title
getTitle: function getTitle(videoId) {
var _this2 = this;
- // Try via undocumented API method first
- // This method disappears now and then though...
- // https://github.com/sampotts/plyr/issues/709
- if (is$1.function(this.embed.getVideoData)) {
- var _this$embed$getVideoD = this.embed.getVideoData(),
- title = _this$embed$getVideoD.title;
-
- if (is$1.empty(title)) {
- this.config.title = title;
- ui.setTitle.call(this);
- return;
- }
- } // Or via Google API
+ var 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$$1() {
+ ready: function ready() {
var player = this; // Ignore already setup (race condition)
- var currentId = player.media.getAttribute('id');
+ var currentId = player.media && player.media.getAttribute('id');
if (!is$1.empty(currentId) && currentId.startsWith('youtube-')) {
return;
@@ -5939,8 +6046,8 @@ var youtube = {
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
- var posterSrc = function posterSrc(format$$1) {
- return "https://img.youtube.com/vi/".concat(videoId, "/").concat(format$$1, "default.jpg");
+ var posterSrc = function posterSrc(s) {
+ return "https://i.ytimg.com/vi/".concat(videoId, "/").concat(s, "default.jpg");
}; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)
@@ -5953,9 +6060,9 @@ var youtube = {
}) // 360p padded 4:3. Always exists
.then(function (image) {
return ui.setPoster.call(player, image.src);
- }).then(function (posterSrc) {
+ }).then(function (src) {
// If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
- if (!posterSrc.includes('maxres')) {
+ if (!src.includes('maxres')) {
player.elements.poster.style.backgroundSize = 'cover';
}
}).catch(function () {});
@@ -5964,7 +6071,7 @@ var youtube = {
player.embed = new window.YT.Player(id, {
videoId: videoId,
- host: config.noCookie ? 'https://www.youtube-nocookie.com' : undefined,
+ host: getHost(config),
playerVars: extend({}, {
autoplay: player.config.autoplay ? 1 : 0,
// Autoplay
@@ -6179,7 +6286,7 @@ var youtube = {
case 1:
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
- if (player.media.paused && !player.embed.hasPlayed) {
+ if (!player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
player.media.pause();
} else {
assurePlaybackState$1.call(player, true);
@@ -6266,6 +6373,20 @@ var media = {
}
};
+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 () {
@@ -6313,18 +6434,20 @@ function () {
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();
}
}
/**
@@ -6333,11 +6456,16 @@ function () {
}, {
key: "ready",
- value: function ready$$1() {
+ 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 () {
@@ -6467,9 +6595,7 @@ function () {
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) {
@@ -6477,8 +6603,8 @@ function () {
}); // Advertisement regular events
Object.keys(google.ima.AdEvent.Type).forEach(function (type) {
- _this6.manager.addEventListener(google.ima.AdEvent.Type[type], function (event) {
- return _this6.onAdEvent(event);
+ _this6.manager.addEventListener(google.ima.AdEvent.Type[type], function (e) {
+ return _this6.onAdEvent(e);
});
}); // Resolve our adsManager
@@ -6526,17 +6652,17 @@ function () {
var adData = event.getAdData(); // Proxy event
var dispatchEvent = function dispatchEvent(type) {
- var event = "ads".concat(type.replace(/_/g, '').toLowerCase());
- triggerEvent.call(_this8.player, _this8.player.media, event);
- };
+ triggerEvent.call(_this8.player, _this8.player.media, "ads".concat(type.replace(/_/g, '').toLowerCase()));
+ }; // 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);
@@ -6550,11 +6676,15 @@ function () {
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.
@@ -6575,7 +6705,6 @@ 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();
break;
@@ -6583,7 +6712,6 @@ function () {
// 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;
@@ -6592,19 +6720,10 @@ function () {
// 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()));
@@ -6689,7 +6808,10 @@ function () {
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 {
@@ -6817,7 +6939,7 @@ function () {
}, {
key: "on",
- value: function on$$1(event, callback) {
+ value: function on(event, callback) {
if (!is$1.array(this.events[event])) {
this.events[event] = [];
}
@@ -6901,11 +7023,11 @@ var parseVtt = function parseVtt(vttDataString) {
lines.forEach(function (line) {
if (!is$1.number(result.startTime)) {
// The line with start and end times on it is the first line of interest
- var matchTimes = line.match(/([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})/); // Note that this currently ignores caption formatting directives that are optionally on the end of this line - fine for non-captions VTT
+ var matchTimes = line.match(/([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})/); // Note that this currently ignores caption formatting directives that are optionally on the end of this line - fine for non-captions VTT
if (matchTimes) {
- result.startTime = Number(matchTimes[1]) * 60 * 60 + Number(matchTimes[2]) * 60 + Number(matchTimes[3]) + Number("0.".concat(matchTimes[4]));
- result.endTime = Number(matchTimes[6]) * 60 * 60 + Number(matchTimes[7]) * 60 + Number(matchTimes[8]) + Number("0.".concat(matchTimes[9]));
+ result.startTime = Number(matchTimes[1] || 0) * 60 * 60 + Number(matchTimes[2]) * 60 + Number(matchTimes[3]) + Number("0.".concat(matchTimes[4]));
+ result.endTime = Number(matchTimes[6] || 0) * 60 * 60 + Number(matchTimes[7]) * 60 + Number(matchTimes[8]) + Number("0.".concat(matchTimes[9]));
}
} else if (!is$1.empty(line.trim()) && is$1.empty(result.text)) {
// If we already have the startTime, then we're definitely up to the text line(s)
@@ -6986,7 +7108,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
@@ -7040,8 +7166,9 @@ function () {
urlPrefix: ''
}; // If the URLs don't start with '/', then we need to set their relative path to be the location of the VTT file
// If the URLs do start with '/', then they obviously don't need a prefix, so it will remain blank
+ // If the thumbnail URLs start with with none of '/', 'http://' or 'https://', then we need to set their relative path to be the location of the VTT file
- if (!thumbnail.frames[0].text.startsWith('/')) {
+ if (!thumbnail.frames[0].text.startsWith('/') && !thumbnail.frames[0].text.startsWith('http://') && !thumbnail.frames[0].text.startsWith('https://')) {
thumbnail.urlPrefix = url.substring(0, url.lastIndexOf('/') + 1);
} // Download the first frame, so that we can determine/set the height of this thumbnailsDef
@@ -7188,7 +7315,10 @@ function () {
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
@@ -7203,7 +7333,6 @@ function () {
if (this.mouseDown) {
this.setScrubbingContainerSize();
} else {
- this.toggleThumbContainer(true);
this.setThumbContainerSizeAndPos();
} // Find the desired thumbnail index
// TODO: Handle a video longer than the thumbs where thumbNum is null
@@ -7213,8 +7342,12 @@ function () {
return _this6.seekTime >= frame.startTime && _this6.seekTime <= frame.endTime;
});
var hasThumb = thumbNum >= 0;
- var qualityIndex = 0;
- this.toggleThumbContainer(hasThumb); // No matching thumb found
+ var qualityIndex = 0; // Show the thumb container if we're not scrubbing
+
+ if (!this.mouseDown) {
+ this.toggleThumbContainer(hasThumb);
+ } // No matching thumb found
+
if (!hasThumb) {
return;
@@ -7314,6 +7447,7 @@ function () {
if (image.dataset.index !== currentImage.dataset.index && !image.dataset.deleting) {
// Wait 200ms, as the new image can take some time to show on certain browsers (even though it was downloaded before showing). This will prevent flicker, and show some generosity towards slower clients
// First set attribute 'deleting' to prevent multi-handling of this on repeat firing of this function
+ // eslint-disable-next-line no-param-reassign
image.dataset.deleting = true; // This has to be set before the timeout - to prevent issues switching between hover and scrub
var currentImageContainer = _this8.currentImageContainer;
@@ -7492,10 +7626,14 @@ function () {
} // Find difference between height and preview container height
- var multiplier = this.thumbContainerHeight / frame.h;
- previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px");
- previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px");
- previewImage.style.left = "-".concat(frame.x * multiplier, "px");
+ 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.width = "".concat(Math.floor(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
+
previewImage.style.top = "-".concat(frame.y * multiplier, "px");
}
}, {
@@ -7694,6 +7832,25 @@ var source = {
}
};
+/**
+ * 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
@@ -7942,8 +8099,10 @@ function () {
} // Autoplay if required
- if (this.config.autoplay) {
- this.play();
+ if (this.isHTML5 && this.config.autoplay) {
+ setTimeout(function () {
+ return _this.play();
+ }, 10);
} // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
@@ -8162,7 +8321,7 @@ function () {
}, {
key: "on",
- value: function on$$1(event, callback) {
+ value: function on$1(event, callback) {
on.call(this, this.elements.container, event, callback);
}
/**
@@ -8173,7 +8332,7 @@ function () {
}, {
key: "once",
- value: function once$$1(event, callback) {
+ value: function once$1(event, callback) {
once.call(this, this.elements.container, event, callback);
}
/**
@@ -8184,7 +8343,7 @@ function () {
}, {
key: "off",
- value: function off$$1(event, callback) {
+ value: function off$1(event, callback) {
off(this.elements.container, event, callback);
}
/**
@@ -8253,12 +8412,14 @@ function () {
}; // Stop playback
- this.stop(); // Provider specific stuff
+ this.stop(); // Clear timeouts
- if (this.isHTML5) {
- // Clear timeout
- clearTimeout(this.timers.loading); // Restore native video controls
+ clearTimeout(this.timers.loading);
+ clearTimeout(this.timers.controls);
+ clearTimeout(this.timers.resized); // Provider specific stuff
+ if (this.isHTML5) {
+ // Restore native video controls
ui.toggleNativeControls.call(this, true); // Clean up
done();
@@ -8304,32 +8465,32 @@ function () {
}, {
key: "isHTML5",
get: function get() {
- return Boolean(this.provider === providers.html5);
+ return this.provider === providers.html5;
}
}, {
key: "isEmbed",
get: function get() {
- return Boolean(this.isYouTube || this.isVimeo);
+ return this.isYouTube || this.isVimeo;
}
}, {
key: "isYouTube",
get: function get() {
- return Boolean(this.provider === providers.youtube);
+ return this.provider === providers.youtube;
}
}, {
key: "isVimeo",
get: function get() {
- return Boolean(this.provider === providers.vimeo);
+ return this.provider === providers.vimeo;
}
}, {
key: "isVideo",
get: function get() {
- return Boolean(this.type === types.video);
+ return this.type === types.video;
}
}, {
key: "isAudio",
get: function get() {
- return Boolean(this.type === types.audio);
+ return this.type === types.audio;
}
}, {
key: "playing",
@@ -8536,6 +8697,8 @@ function () {
}, {
key: "speed",
set: function set(input) {
+ var _this4 = this;
+
var speed = null;
if (is$1.number(input)) {
@@ -8548,26 +8711,18 @@ function () {
if (!is$1.number(speed)) {
speed = this.config.speed.selected;
- } // Set min/max
+ } // Clamp to min/max
- if (speed < 0.1) {
- speed = 0.1;
- }
-
- if (speed > 2.0) {
- speed = 2.0;
- }
-
- if (!this.config.speed.options.includes(speed)) {
- this.debug.warn("Unsupported speed (".concat(speed, ")"));
- return;
- } // Update config
-
+ 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
@@ -8577,6 +8732,46 @@ function () {
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
@@ -8706,6 +8901,18 @@ function () {
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
*/
@@ -8732,6 +8939,38 @@ function () {
return this.media.getAttribute('poster');
}
/**
+ * Get the current aspect ratio in use
+ */
+
+ }, {
+ key: "ratio",
+ get: function get() {
+ if (!this.isVideo) {
+ return null;
+ }
+
+ var ratio = reduceAspectRatio(getAspectRatio.call(this));
+ return is$1.array(ratio) ? ratio.join(':') : ratio;
+ }
+ /**
+ * Set video aspect ratio
+ */
+ ,
+ set: function set(input) {
+ if (!this.isVideo) {
+ this.debug.warn('Aspect ratio can only be set for video');
+ return;
+ }
+
+ if (!is$1.string(input) || !validateRatio(input)) {
+ this.debug.error("Invalid aspect ratio specified (".concat(input, ")"));
+ return;
+ }
+
+ this.config.ratio = input;
+ setAspectRatio.call(this);
+ }
+ /**
* Set the autoplay state
* @param {Boolean} input - Whether to autoplay or not
*/
@@ -8843,7 +9082,7 @@ function () {
}, {
key: "loadSprite",
- value: function loadSprite$$1(url, id) {
+ value: function loadSprite$1(url, id) {
return loadSprite(url, id);
}
/**