This commit is contained in:
Sam Potts 2020-04-24 01:05:58 +10:00
parent e48b1d11ce
commit 66c5780616
19 changed files with 998 additions and 346 deletions

View File

@ -1,6 +1,22 @@
## v3.6.0
- You can now easily change colors using CSS Custom Properties.
- You can now easily change colors using CSS Custom Properties. See the [README](README.md#customizing-the-css).
- Bug fix for Vimeo fullscreen.
- Various typos (thanks @likev)
- Preload TextTracks as per default video element (thanks @theprojectsomething)
- Features/fullscreen container (thanks @theprojectsomething)
- Ignore internal play promises (thanks @ydylla)
- Ads plugin fixes to allow multiple VAST requests (thanks @Steejo)
- Fix shadowroot (thanks @jnoordsij)
- Add financial contributors for Open Collective (thanks @monkeywithacupcake)
- Update the gitpod setup description to be more precise. (thanks @nisarhassan12)
- Completely hide SVG icons to screen readers (thanks @LeBenLeBen)
- Preview thumbnails via src:callback() (thanks @doublex)
- Add missing Typescripts types and options (thanks @hug963)
- Use number instead of string in TS quality definitions (thanks @mogzol)
- Fix Vimeo playback rate (thanks @hug963)
- Fix issue when controls config is string or element (thanks @CzBiX)
- Simplify contributions by fully automating the dev setup with gitpod (thanks @nisarhassan12)
### v3.5.10

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

262
demo/dist/demo.js vendored
View File

@ -4380,6 +4380,42 @@ typeof navigator === "object" && (function () {
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
@ -14612,7 +14648,7 @@ typeof navigator === "object" && (function () {
(module.exports = function (key, value) {
return sharedStore$1[key] || (sharedStore$1[key] = value !== undefined ? value : {});
})('versions', []).push({
version: '3.6.4',
version: '3.6.5',
mode: 'global',
copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
});
@ -17622,7 +17658,7 @@ typeof navigator === "object" && (function () {
var INVALID_PORT$1 = 'Invalid port';
var ALPHA$1 = /[A-Za-z]/;
var ALPHANUMERIC$1 = /[\d+\-.A-Za-z]/;
var ALPHANUMERIC$1 = /[\d+-.A-Za-z]/;
var DIGIT$1 = /\d/;
var HEX_START$1 = /^(0x|0X)/;
var OCT$1 = /^[0-7]+$/;
@ -19629,7 +19665,13 @@ typeof navigator === "object" && (function () {
defer$1 = functionBindContext$1(port$1.postMessage, port$1, 1);
// Browsers with postMessage, skip WebWorkers
// IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
} else if (global_1$1.addEventListener && typeof postMessage == 'function' && !global_1$1.importScripts && !fails$1(post$1)) {
} else if (
global_1$1.addEventListener &&
typeof postMessage == 'function' &&
!global_1$1.importScripts &&
!fails$1(post$1) &&
location$1.protocol !== 'file:'
) {
defer$1 = post$1;
global_1$1.addEventListener('message', listener$1, false);
// IE8-
@ -20639,6 +20681,25 @@ typeof navigator === "object" && (function () {
var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return method.call(element, selector);
} // Closest ancestor element matching selector (also tests element itself)
function closest(element, selector) {
var _Element2 = Element,
prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
function closestElement() {
var el = this;
do {
if (matches$2.matches(el, selector)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
}
var method = prototype.closest || closestElement;
return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@ -20990,8 +21051,8 @@ typeof navigator === "object" && (function () {
var padding = 100 / w * h;
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;
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) {
@ -21144,7 +21205,7 @@ typeof navigator === "object" && (function () {
});
} // Get the closest value in an array
function closest(array, value) {
function closest$1(array, value) {
if (!is$2.array(array) || !array.length) {
return null;
}
@ -23252,9 +23313,15 @@ typeof navigator === "object" && (function () {
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
// Note: mode='hidden' forces a track to download. To ensure every track
// isn't downloaded at once, only 'showing' tracks should be reassigned
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden'; // Add event listener for cue changes
if (track.mode === 'showing') {
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden';
} // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
@ -23278,6 +23345,8 @@ typeof navigator === "object" && (function () {
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
@ -23324,7 +23393,15 @@ typeof navigator === "object" && (function () {
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
} // Wait for the call stack to clear before setting mode='hidden'
// on the active track - forcing the browser to download it
setTimeout(function () {
if (active && _this2.captions.toggled) {
_this2.captions.currentTrackNode.mode = 'hidden';
}
});
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
@ -23405,7 +23482,7 @@ typeof navigator === "object" && (function () {
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var _this3 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
@ -23413,20 +23490,20 @@ typeof navigator === "object" && (function () {
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
return !_this3.isHTML5 || update || _this3.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var _this4 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
return Number((_this4.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
@ -23607,6 +23684,9 @@ typeof navigator === "object" && (function () {
fallback: true,
// Fallback using full viewport/window
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
// Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode
// Non-ancestors of the player element will be ignored
// container: null, // defaults to the player element
},
// Local storage
@ -23844,16 +23924,16 @@ typeof navigator === "object" && (function () {
title: false,
speed: true,
transparent: false,
// These settings require a pro or premium account to work
sidedock: false,
controls: false,
// Whether the owner of the video has a Pro or Business account
// (which allows us to properly hide controls without CSS hacks, etc)
premium: false,
// Custom settings from Plyr
referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy
},
// YouTube plugin
youtube: {
noCookie: false,
noCookie: true,
// Whether to use an alternative version of YouTube without cookies
rel: 0,
// No related vids
@ -23963,7 +24043,10 @@ typeof navigator === "object" && (function () {
y: 0
}; // Force the use of 'full window/browser' rather than fullscreen
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element
// Checks container is an ancestor, defaults to null
this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
@ -24189,7 +24272,7 @@ typeof navigator === "object" && (function () {
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container;
}
}], [{
key: "native",
@ -24398,12 +24481,7 @@ typeof navigator === "object" && (function () {
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute
if (this.isHTML5) {
return Promise.resolve(poster);
} // Wait until ui is ready
this.media.setAttribute('data-poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
@ -24479,6 +24557,26 @@ typeof navigator === "object" && (function () {
this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
},
// Migrate any custom properties from the media to the parent
migrateStyles: function migrateStyles() {
var _this5 = this;
// Loop through values (as they are the keys when the object is spread 🤔)
Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties
.filter(function (key) {
return key.startsWith('--plyr');
}).forEach(function (key) {
// Set on the container
_this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element
_this5.media.style.removeProperty(key);
}); // Remove attribute if empty
if (is$2.empty(this.media.style)) {
this.media.removeAttribute('style');
}
}
};
@ -24687,15 +24785,17 @@ typeof navigator === "object" && (function () {
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (event.type !== 'focusout') {
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
} // Global window & document listeners
}, {
@ -24713,7 +24813,7 @@ typeof navigator === "object" && (function () {
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
@ -24756,7 +24856,7 @@ typeof navigator === "object" && (function () {
}); // Set a gutter for Vimeo
var setGutter = function setGutter(ratio, padding, toggle) {
if (!player.isVimeo) {
if (!player.isVimeo || player.config.vimeo.premium) {
return;
}
@ -24813,7 +24913,7 @@ typeof navigator === "object" && (function () {
ratio = _setPlayerSize.ratio; // Set Vimeo gutter
setGutter(ratio, padding, isEnter); // If not using native fullscreen, we need to check for resizes of viewport
setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport
if (!usingNative) {
if (isEnter) {
@ -25192,7 +25292,18 @@ typeof navigator === "object" && (function () {
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
}); // Also update controls.hover state for any non-player children of fullscreen element (as above)
if (elements.fullscreen) {
Array.from(elements.fullscreen.children).filter(function (c) {
return !c.contains(elements.container);
}).forEach(function (child) {
_this3.bind(child, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
});
} // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
@ -25661,15 +25772,28 @@ typeof navigator === "object" && (function () {
var _this = this;
var player = this;
var config = player.config.vimeo; // Get Vimeo params for the iframe
var config = player.config.vimeo;
var params = buildUrlParams(extend$1({}, {
var premium = config.premium,
referrerPolicy = config.referrerPolicy,
frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc
if (premium) {
Object.assign(frameParams, {
controls: false,
sidedock: false
});
} // Get Vimeo params for the iframe
var params = buildUrlParams(_objectSpread2({
loop: player.config.loop.active,
autoplay: player.autoplay,
muted: player.muted,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
}, config)); // Get the source URL or ID
}, frameParams)); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
@ -25683,22 +25807,27 @@ typeof navigator === "object" && (function () {
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required
iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required
if (!is$2.empty(config.referrerPolicy)) {
iframe.setAttribute('referrerPolicy', config.referrerPolicy);
} // Get poster, if already set
if (!is$2.empty(referrerPolicy)) {
iframe.setAttribute('referrerPolicy', referrerPolicy);
} // Inject the package
var poster = player.poster; // Inject the package
var poster = player.poster;
if (premium) {
iframe.setAttribute('data-poster', poster);
player.media = replaceElement(iframe, player.media);
} else {
var wrapper = createElement$1('div', {
class: player.config.classNames.embedContainer,
'data-poster': poster
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media);
} // Get poster image
var wrapper = createElement$1('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$2.empty(response)) {
@ -26069,7 +26198,7 @@ typeof navigator === "object" && (function () {
var container = createElement$1('div', {
id: id,
poster: poster
'data-poster': poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
@ -26387,14 +26516,12 @@ typeof navigator === "object" && (function () {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap$4(this.media, this.elements.wrapper); // Faux poster container
wrap$4(this.media, this.elements.wrapper); // Poster image container
if (this.isEmbed) {
this.elements.poster = createElement$1('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
this.elements.poster = createElement$1('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
@ -28051,6 +28178,7 @@ typeof navigator === "object" && (function () {
this.elements = {
container: null,
fullscreen: null,
captions: null,
buttons: {},
display: {},
@ -28225,9 +28353,11 @@ typeof navigator === "object" && (function () {
tabindex: 0
});
wrap$4(this.media, this.elements.container);
} // Add style hook
} // Migrate custom properties from media to container (so they work 😉)
ui.migrateStyles.call(this); // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
@ -28236,10 +28366,12 @@ typeof navigator === "object" && (function () {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
} // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
@ -28247,9 +28379,7 @@ typeof navigator === "object" && (function () {
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
this.listeners.global(); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
@ -28948,7 +29078,7 @@ typeof navigator === "object" && (function () {
var updateStorage = true;
if (!options.includes(quality)) {
var value = closest(options, quality);
var value = closest$1(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value; // Don't update storage if quality is not supported
@ -29093,7 +29223,7 @@ typeof navigator === "object" && (function () {
return null;
}
return this.media.getAttribute('poster');
return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
}
/**
* Get the current aspect ratio in use

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

251
dist/plyr.js vendored
View File

@ -75,6 +75,42 @@ typeof navigator === "object" && (function (global, factory) {
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
@ -788,6 +824,25 @@ typeof navigator === "object" && (function (global, factory) {
var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return method.call(element, selector);
} // Closest ancestor element matching selector (also tests element itself)
function closest(element, selector) {
var _Element2 = Element,
prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
function closestElement() {
var el = this;
do {
if (matches$1.matches(el, selector)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
}
var method = prototype.closest || closestElement;
return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@ -1139,8 +1194,8 @@ typeof navigator === "object" && (function (global, factory) {
var padding = 100 / w * h;
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;
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) {
@ -1296,7 +1351,7 @@ typeof navigator === "object" && (function (global, factory) {
});
} // Get the closest value in an array
function closest(array, value) {
function closest$1(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
@ -3278,9 +3333,15 @@ typeof navigator === "object" && (function (global, factory) {
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
// Note: mode='hidden' forces a track to download. To ensure every track
// isn't downloaded at once, only 'showing' tracks should be reassigned
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden'; // Add event listener for cue changes
if (track.mode === 'showing') {
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden';
} // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
@ -3304,6 +3365,8 @@ typeof navigator === "object" && (function (global, factory) {
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
@ -3350,7 +3413,15 @@ typeof navigator === "object" && (function (global, factory) {
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
} // Wait for the call stack to clear before setting mode='hidden'
// on the active track - forcing the browser to download it
setTimeout(function () {
if (active && _this2.captions.toggled) {
_this2.captions.currentTrackNode.mode = 'hidden';
}
});
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
@ -3431,7 +3502,7 @@ typeof navigator === "object" && (function (global, factory) {
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var _this3 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
@ -3439,20 +3510,20 @@ typeof navigator === "object" && (function (global, factory) {
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
return !_this3.isHTML5 || update || _this3.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var _this4 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
return Number((_this4.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
@ -3633,6 +3704,9 @@ typeof navigator === "object" && (function (global, factory) {
fallback: true,
// Fallback using full viewport/window
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
// Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode
// Non-ancestors of the player element will be ignored
// container: null, // defaults to the player element
},
// Local storage
@ -3870,16 +3944,16 @@ typeof navigator === "object" && (function (global, factory) {
title: false,
speed: true,
transparent: false,
// These settings require a pro or premium account to work
sidedock: false,
controls: false,
// Whether the owner of the video has a Pro or Business account
// (which allows us to properly hide controls without CSS hacks, etc)
premium: false,
// Custom settings from Plyr
referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy
},
// YouTube plugin
youtube: {
noCookie: false,
noCookie: true,
// Whether to use an alternative version of YouTube without cookies
rel: 0,
// No related vids
@ -3989,7 +4063,10 @@ typeof navigator === "object" && (function (global, factory) {
y: 0
}; // Force the use of 'full window/browser' rather than fullscreen
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element
// Checks container is an ancestor, defaults to null
this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
@ -4215,7 +4292,7 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container;
}
}], [{
key: "native",
@ -4277,7 +4354,6 @@ typeof navigator === "object" && (function (global, factory) {
});
}
// ==========================================================================
var ui = {
addStyleHook: function addStyleHook() {
toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
@ -4412,12 +4488,7 @@ typeof navigator === "object" && (function (global, factory) {
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute
if (this.isHTML5) {
return Promise.resolve(poster);
} // Wait until ui is ready
this.media.setAttribute('data-poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
@ -4493,6 +4564,26 @@ typeof navigator === "object" && (function (global, factory) {
this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
},
// Migrate any custom properties from the media to the parent
migrateStyles: function migrateStyles() {
var _this5 = this;
// Loop through values (as they are the keys when the object is spread 🤔)
Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties
.filter(function (key) {
return key.startsWith('--plyr');
}).forEach(function (key) {
// Set on the container
_this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element
_this5.media.style.removeProperty(key);
}); // Remove attribute if empty
if (is$1.empty(this.media.style)) {
this.media.removeAttribute('style');
}
}
};
@ -4701,15 +4792,17 @@ typeof navigator === "object" && (function (global, factory) {
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (event.type !== 'focusout') {
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
} // Global window & document listeners
}, {
@ -4727,7 +4820,7 @@ typeof navigator === "object" && (function (global, factory) {
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
@ -4770,7 +4863,7 @@ typeof navigator === "object" && (function (global, factory) {
}); // Set a gutter for Vimeo
var setGutter = function setGutter(ratio, padding, toggle) {
if (!player.isVimeo) {
if (!player.isVimeo || player.config.vimeo.premium) {
return;
}
@ -4827,7 +4920,7 @@ typeof navigator === "object" && (function (global, factory) {
ratio = _setPlayerSize.ratio; // Set Vimeo gutter
setGutter(ratio, padding, isEnter); // If not using native fullscreen, we need to check for resizes of viewport
setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport
if (!usingNative) {
if (isEnter) {
@ -5206,7 +5299,18 @@ typeof navigator === "object" && (function (global, factory) {
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
}); // Also update controls.hover state for any non-player children of fullscreen element (as above)
if (elements.fullscreen) {
Array.from(elements.fullscreen.children).filter(function (c) {
return !c.contains(elements.container);
}).forEach(function (child) {
_this3.bind(child, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
});
} // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
@ -5622,15 +5726,28 @@ typeof navigator === "object" && (function (global, factory) {
var _this = this;
var player = this;
var config = player.config.vimeo; // Get Vimeo params for the iframe
var config = player.config.vimeo;
var params = buildUrlParams(extend({}, {
var premium = config.premium,
referrerPolicy = config.referrerPolicy,
frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc
if (premium) {
Object.assign(frameParams, {
controls: false,
sidedock: false
});
} // Get Vimeo params for the iframe
var params = buildUrlParams(_objectSpread2({
loop: player.config.loop.active,
autoplay: player.autoplay,
muted: player.muted,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
}, config)); // Get the source URL or ID
}, frameParams)); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
@ -5644,22 +5761,27 @@ typeof navigator === "object" && (function (global, factory) {
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required
iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required
if (!is$1.empty(config.referrerPolicy)) {
iframe.setAttribute('referrerPolicy', config.referrerPolicy);
} // Get poster, if already set
if (!is$1.empty(referrerPolicy)) {
iframe.setAttribute('referrerPolicy', referrerPolicy);
} // Inject the package
var poster = player.poster; // Inject the package
var poster = player.poster;
if (premium) {
iframe.setAttribute('data-poster', poster);
player.media = replaceElement(iframe, player.media);
} else {
var wrapper = createElement('div', {
class: player.config.classNames.embedContainer,
'data-poster': poster
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media);
} // Get poster image
var wrapper = createElement('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$1.empty(response)) {
@ -6032,7 +6154,7 @@ typeof navigator === "object" && (function (global, factory) {
var container = createElement('div', {
id: id,
poster: poster
'data-poster': poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
@ -6351,14 +6473,12 @@ typeof navigator === "object" && (function (global, factory) {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap(this.media, this.elements.wrapper); // Faux poster container
wrap(this.media, this.elements.wrapper); // Poster image container
if (this.isEmbed) {
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
@ -7964,6 +8084,7 @@ typeof navigator === "object" && (function (global, factory) {
this.elements = {
container: null,
fullscreen: null,
captions: null,
buttons: {},
display: {},
@ -8138,9 +8259,11 @@ typeof navigator === "object" && (function (global, factory) {
tabindex: 0
});
wrap(this.media, this.elements.container);
} // Add style hook
} // Migrate custom properties from media to container (so they work 😉)
ui.migrateStyles.call(this); // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
@ -8149,10 +8272,12 @@ typeof navigator === "object" && (function (global, factory) {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
} // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
@ -8160,9 +8285,7 @@ typeof navigator === "object" && (function (global, factory) {
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
this.listeners.global(); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
@ -8861,7 +8984,7 @@ typeof navigator === "object" && (function (global, factory) {
var updateStorage = true;
if (!options.includes(quality)) {
var value = closest(options, quality);
var value = closest$1(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value; // Don't update storage if quality is not supported
@ -9006,7 +9129,7 @@ typeof navigator === "object" && (function (global, factory) {
return null;
}
return this.media.getAttribute('poster');
return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
}
/**
* Get the current aspect ratio in use

4
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/plyr.min.mjs vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

251
dist/plyr.mjs vendored
View File

@ -69,6 +69,42 @@ function _objectSpread2(target) {
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
@ -782,6 +818,25 @@ function matches$1(element, selector) {
var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return method.call(element, selector);
} // Closest ancestor element matching selector (also tests element itself)
function closest(element, selector) {
var _Element2 = Element,
prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
function closestElement() {
var el = this;
do {
if (matches$1.matches(el, selector)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
}
var method = prototype.closest || closestElement;
return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@ -1133,8 +1188,8 @@ function setAspectRatio(input) {
var padding = 100 / w * h;
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;
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) {
@ -1290,7 +1345,7 @@ function dedupe(array) {
});
} // Get the closest value in an array
function closest(array, value) {
function closest$1(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
@ -3272,9 +3327,15 @@ var captions = {
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
// Note: mode='hidden' forces a track to download. To ensure every track
// isn't downloaded at once, only 'showing' tracks should be reassigned
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden'; // Add event listener for cue changes
if (track.mode === 'showing') {
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden';
} // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
@ -3298,6 +3359,8 @@ var captions = {
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
@ -3344,7 +3407,15 @@ var captions = {
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
} // Wait for the call stack to clear before setting mode='hidden'
// on the active track - forcing the browser to download it
setTimeout(function () {
if (active && _this2.captions.toggled) {
_this2.captions.currentTrackNode.mode = 'hidden';
}
});
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
@ -3425,7 +3496,7 @@ var captions = {
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var _this3 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
@ -3433,20 +3504,20 @@ var captions = {
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
return !_this3.isHTML5 || update || _this3.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var _this4 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
return Number((_this4.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
@ -3627,6 +3698,9 @@ var defaults$1 = {
fallback: true,
// Fallback using full viewport/window
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
// Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode
// Non-ancestors of the player element will be ignored
// container: null, // defaults to the player element
},
// Local storage
@ -3864,16 +3938,16 @@ var defaults$1 = {
title: false,
speed: true,
transparent: false,
// These settings require a pro or premium account to work
sidedock: false,
controls: false,
// Whether the owner of the video has a Pro or Business account
// (which allows us to properly hide controls without CSS hacks, etc)
premium: false,
// Custom settings from Plyr
referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy
},
// YouTube plugin
youtube: {
noCookie: false,
noCookie: true,
// Whether to use an alternative version of YouTube without cookies
rel: 0,
// No related vids
@ -3983,7 +4057,10 @@ var Fullscreen = /*#__PURE__*/function () {
y: 0
}; // Force the use of 'full window/browser' rather than fullscreen
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element
// Checks container is an ancestor, defaults to null
this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
@ -4209,7 +4286,7 @@ var Fullscreen = /*#__PURE__*/function () {
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container;
}
}], [{
key: "native",
@ -4271,7 +4348,6 @@ function loadImage(src) {
});
}
// ==========================================================================
var ui = {
addStyleHook: function addStyleHook() {
toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
@ -4406,12 +4482,7 @@ var ui = {
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute
if (this.isHTML5) {
return Promise.resolve(poster);
} // Wait until ui is ready
this.media.setAttribute('data-poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
@ -4487,6 +4558,26 @@ var ui = {
this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
},
// Migrate any custom properties from the media to the parent
migrateStyles: function migrateStyles() {
var _this5 = this;
// Loop through values (as they are the keys when the object is spread 🤔)
Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties
.filter(function (key) {
return key.startsWith('--plyr');
}).forEach(function (key) {
// Set on the container
_this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element
_this5.media.style.removeProperty(key);
}); // Remove attribute if empty
if (is$1.empty(this.media.style)) {
this.media.removeAttribute('style');
}
}
};
@ -4695,15 +4786,17 @@ var Listeners = /*#__PURE__*/function () {
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (event.type !== 'focusout') {
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
} // Global window & document listeners
}, {
@ -4721,7 +4814,7 @@ var Listeners = /*#__PURE__*/function () {
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
@ -4764,7 +4857,7 @@ var Listeners = /*#__PURE__*/function () {
}); // Set a gutter for Vimeo
var setGutter = function setGutter(ratio, padding, toggle) {
if (!player.isVimeo) {
if (!player.isVimeo || player.config.vimeo.premium) {
return;
}
@ -4821,7 +4914,7 @@ var Listeners = /*#__PURE__*/function () {
ratio = _setPlayerSize.ratio; // Set Vimeo gutter
setGutter(ratio, padding, isEnter); // If not using native fullscreen, we need to check for resizes of viewport
setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport
if (!usingNative) {
if (isEnter) {
@ -5200,7 +5293,18 @@ var Listeners = /*#__PURE__*/function () {
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
}); // Also update controls.hover state for any non-player children of fullscreen element (as above)
if (elements.fullscreen) {
Array.from(elements.fullscreen.children).filter(function (c) {
return !c.contains(elements.container);
}).forEach(function (child) {
_this3.bind(child, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
});
} // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
@ -5616,15 +5720,28 @@ var vimeo = {
var _this = this;
var player = this;
var config = player.config.vimeo; // Get Vimeo params for the iframe
var config = player.config.vimeo;
var params = buildUrlParams(extend({}, {
var premium = config.premium,
referrerPolicy = config.referrerPolicy,
frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc
if (premium) {
Object.assign(frameParams, {
controls: false,
sidedock: false
});
} // Get Vimeo params for the iframe
var params = buildUrlParams(_objectSpread2({
loop: player.config.loop.active,
autoplay: player.autoplay,
muted: player.muted,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
}, config)); // Get the source URL or ID
}, frameParams)); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
@ -5638,22 +5755,27 @@ var vimeo = {
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required
iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required
if (!is$1.empty(config.referrerPolicy)) {
iframe.setAttribute('referrerPolicy', config.referrerPolicy);
} // Get poster, if already set
if (!is$1.empty(referrerPolicy)) {
iframe.setAttribute('referrerPolicy', referrerPolicy);
} // Inject the package
var poster = player.poster; // Inject the package
var poster = player.poster;
if (premium) {
iframe.setAttribute('data-poster', poster);
player.media = replaceElement(iframe, player.media);
} else {
var wrapper = createElement('div', {
class: player.config.classNames.embedContainer,
'data-poster': poster
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media);
} // Get poster image
var wrapper = createElement('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$1.empty(response)) {
@ -6026,7 +6148,7 @@ var youtube = {
var container = createElement('div', {
id: id,
poster: poster
'data-poster': poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
@ -6345,14 +6467,12 @@ var media = {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap(this.media, this.elements.wrapper); // Faux poster container
wrap(this.media, this.elements.wrapper); // Poster image container
if (this.isEmbed) {
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
@ -7958,6 +8078,7 @@ var Plyr = /*#__PURE__*/function () {
this.elements = {
container: null,
fullscreen: null,
captions: null,
buttons: {},
display: {},
@ -8132,9 +8253,11 @@ var Plyr = /*#__PURE__*/function () {
tabindex: 0
});
wrap(this.media, this.elements.container);
} // Add style hook
} // Migrate custom properties from media to container (so they work 😉)
ui.migrateStyles.call(this); // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
@ -8143,10 +8266,12 @@ var Plyr = /*#__PURE__*/function () {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
} // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
@ -8154,9 +8279,7 @@ var Plyr = /*#__PURE__*/function () {
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
this.listeners.global(); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
@ -8855,7 +8978,7 @@ var Plyr = /*#__PURE__*/function () {
var updateStorage = true;
if (!options.includes(quality)) {
var value = closest(options, quality);
var value = closest$1(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value; // Don't update storage if quality is not supported
@ -9000,7 +9123,7 @@ var Plyr = /*#__PURE__*/function () {
return null;
}
return this.media.getAttribute('poster');
return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
}
/**
* Get the current aspect ratio in use

View File

@ -269,7 +269,7 @@ typeof navigator === "object" && (function (global, factory) {
(module.exports = function (key, value) {
return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
})('versions', []).push({
version: '3.6.4',
version: '3.6.5',
mode: 'global',
copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
});
@ -3061,7 +3061,7 @@ typeof navigator === "object" && (function (global, factory) {
var INVALID_PORT = 'Invalid port';
var ALPHA = /[A-Za-z]/;
var ALPHANUMERIC = /[\d+\-.A-Za-z]/;
var ALPHANUMERIC = /[\d+-.A-Za-z]/;
var DIGIT = /\d/;
var HEX_START = /^(0x|0X)/;
var OCT = /^[0-7]+$/;
@ -4123,6 +4123,42 @@ typeof navigator === "object" && (function (global, factory) {
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
@ -6008,7 +6044,13 @@ typeof navigator === "object" && (function (global, factory) {
defer = functionBindContext(port.postMessage, port, 1);
// Browsers with postMessage, skip WebWorkers
// IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
} else if (global_1.addEventListener && typeof postMessage == 'function' && !global_1.importScripts && !fails(post)) {
} else if (
global_1.addEventListener &&
typeof postMessage == 'function' &&
!global_1.importScripts &&
!fails(post) &&
location.protocol !== 'file:'
) {
defer = post;
global_1.addEventListener('message', listener, false);
// IE8-
@ -7018,6 +7060,25 @@ typeof navigator === "object" && (function (global, factory) {
var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return method.call(element, selector);
} // Closest ancestor element matching selector (also tests element itself)
function closest(element, selector) {
var _Element2 = Element,
prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
function closestElement() {
var el = this;
do {
if (matches$1.matches(el, selector)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
}
var method = prototype.closest || closestElement;
return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@ -7369,8 +7430,8 @@ typeof navigator === "object" && (function (global, factory) {
var padding = 100 / w * h;
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;
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) {
@ -7523,7 +7584,7 @@ typeof navigator === "object" && (function (global, factory) {
});
} // Get the closest value in an array
function closest(array, value) {
function closest$1(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
@ -9593,9 +9654,15 @@ typeof navigator === "object" && (function (global, factory) {
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
// Note: mode='hidden' forces a track to download. To ensure every track
// isn't downloaded at once, only 'showing' tracks should be reassigned
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden'; // Add event listener for cue changes
if (track.mode === 'showing') {
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden';
} // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
@ -9619,6 +9686,8 @@ typeof navigator === "object" && (function (global, factory) {
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
@ -9665,7 +9734,15 @@ typeof navigator === "object" && (function (global, factory) {
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
} // Wait for the call stack to clear before setting mode='hidden'
// on the active track - forcing the browser to download it
setTimeout(function () {
if (active && _this2.captions.toggled) {
_this2.captions.currentTrackNode.mode = 'hidden';
}
});
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
@ -9746,7 +9823,7 @@ typeof navigator === "object" && (function (global, factory) {
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var _this3 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
@ -9754,20 +9831,20 @@ typeof navigator === "object" && (function (global, factory) {
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
return !_this3.isHTML5 || update || _this3.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var _this4 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
return Number((_this4.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
@ -9948,6 +10025,9 @@ typeof navigator === "object" && (function (global, factory) {
fallback: true,
// Fallback using full viewport/window
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
// Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode
// Non-ancestors of the player element will be ignored
// container: null, // defaults to the player element
},
// Local storage
@ -10185,16 +10265,16 @@ typeof navigator === "object" && (function (global, factory) {
title: false,
speed: true,
transparent: false,
// These settings require a pro or premium account to work
sidedock: false,
controls: false,
// Whether the owner of the video has a Pro or Business account
// (which allows us to properly hide controls without CSS hacks, etc)
premium: false,
// Custom settings from Plyr
referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy
},
// YouTube plugin
youtube: {
noCookie: false,
noCookie: true,
// Whether to use an alternative version of YouTube without cookies
rel: 0,
// No related vids
@ -10304,7 +10384,10 @@ typeof navigator === "object" && (function (global, factory) {
y: 0
}; // Force the use of 'full window/browser' rather than fullscreen
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element
// Checks container is an ancestor, defaults to null
this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
@ -10530,7 +10613,7 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container;
}
}], [{
key: "native",
@ -10739,12 +10822,7 @@ typeof navigator === "object" && (function (global, factory) {
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute
if (this.isHTML5) {
return Promise.resolve(poster);
} // Wait until ui is ready
this.media.setAttribute('data-poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
@ -10820,6 +10898,26 @@ typeof navigator === "object" && (function (global, factory) {
this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
},
// Migrate any custom properties from the media to the parent
migrateStyles: function migrateStyles() {
var _this5 = this;
// Loop through values (as they are the keys when the object is spread 🤔)
Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties
.filter(function (key) {
return key.startsWith('--plyr');
}).forEach(function (key) {
// Set on the container
_this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element
_this5.media.style.removeProperty(key);
}); // Remove attribute if empty
if (is$1.empty(this.media.style)) {
this.media.removeAttribute('style');
}
}
};
@ -11028,15 +11126,17 @@ typeof navigator === "object" && (function (global, factory) {
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (event.type !== 'focusout') {
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
} // Global window & document listeners
}, {
@ -11054,7 +11154,7 @@ typeof navigator === "object" && (function (global, factory) {
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
@ -11097,7 +11197,7 @@ typeof navigator === "object" && (function (global, factory) {
}); // Set a gutter for Vimeo
var setGutter = function setGutter(ratio, padding, toggle) {
if (!player.isVimeo) {
if (!player.isVimeo || player.config.vimeo.premium) {
return;
}
@ -11154,7 +11254,7 @@ typeof navigator === "object" && (function (global, factory) {
ratio = _setPlayerSize.ratio; // Set Vimeo gutter
setGutter(ratio, padding, isEnter); // If not using native fullscreen, we need to check for resizes of viewport
setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport
if (!usingNative) {
if (isEnter) {
@ -11533,7 +11633,18 @@ typeof navigator === "object" && (function (global, factory) {
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
}); // Also update controls.hover state for any non-player children of fullscreen element (as above)
if (elements.fullscreen) {
Array.from(elements.fullscreen.children).filter(function (c) {
return !c.contains(elements.container);
}).forEach(function (child) {
_this3.bind(child, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
});
} // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
@ -12002,15 +12113,28 @@ typeof navigator === "object" && (function (global, factory) {
var _this = this;
var player = this;
var config = player.config.vimeo; // Get Vimeo params for the iframe
var config = player.config.vimeo;
var params = buildUrlParams(extend({}, {
var premium = config.premium,
referrerPolicy = config.referrerPolicy,
frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc
if (premium) {
Object.assign(frameParams, {
controls: false,
sidedock: false
});
} // Get Vimeo params for the iframe
var params = buildUrlParams(_objectSpread2({
loop: player.config.loop.active,
autoplay: player.autoplay,
muted: player.muted,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
}, config)); // Get the source URL or ID
}, frameParams)); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
@ -12024,22 +12148,27 @@ typeof navigator === "object" && (function (global, factory) {
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required
iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required
if (!is$1.empty(config.referrerPolicy)) {
iframe.setAttribute('referrerPolicy', config.referrerPolicy);
} // Get poster, if already set
if (!is$1.empty(referrerPolicy)) {
iframe.setAttribute('referrerPolicy', referrerPolicy);
} // Inject the package
var poster = player.poster; // Inject the package
var poster = player.poster;
if (premium) {
iframe.setAttribute('data-poster', poster);
player.media = replaceElement(iframe, player.media);
} else {
var wrapper = createElement('div', {
class: player.config.classNames.embedContainer,
'data-poster': poster
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media);
} // Get poster image
var wrapper = createElement('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$1.empty(response)) {
@ -12410,7 +12539,7 @@ typeof navigator === "object" && (function (global, factory) {
var container = createElement('div', {
id: id,
poster: poster
'data-poster': poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
@ -12728,14 +12857,12 @@ typeof navigator === "object" && (function (global, factory) {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap$1(this.media, this.elements.wrapper); // Faux poster container
wrap$1(this.media, this.elements.wrapper); // Poster image container
if (this.isEmbed) {
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
@ -14392,6 +14519,7 @@ typeof navigator === "object" && (function (global, factory) {
this.elements = {
container: null,
fullscreen: null,
captions: null,
buttons: {},
display: {},
@ -14566,9 +14694,11 @@ typeof navigator === "object" && (function (global, factory) {
tabindex: 0
});
wrap$1(this.media, this.elements.container);
} // Add style hook
} // Migrate custom properties from media to container (so they work 😉)
ui.migrateStyles.call(this); // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
@ -14577,10 +14707,12 @@ typeof navigator === "object" && (function (global, factory) {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
} // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
@ -14588,9 +14720,7 @@ typeof navigator === "object" && (function (global, factory) {
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
this.listeners.global(); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
@ -15289,7 +15419,7 @@ typeof navigator === "object" && (function (global, factory) {
var updateStorage = true;
if (!options.includes(quality)) {
var value = closest(options, quality);
var value = closest$1(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value; // Don't update storage if quality is not supported
@ -15434,7 +15564,7 @@ typeof navigator === "object" && (function (global, factory) {
return null;
}
return this.media.getAttribute('poster');
return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
}
/**
* Get the current aspect ratio in use

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -263,7 +263,7 @@ var shared = createCommonjsModule(function (module) {
(module.exports = function (key, value) {
return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
})('versions', []).push({
version: '3.6.4',
version: '3.6.5',
mode: 'global',
copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
});
@ -3055,7 +3055,7 @@ var INVALID_HOST = 'Invalid host';
var INVALID_PORT = 'Invalid port';
var ALPHA = /[A-Za-z]/;
var ALPHANUMERIC = /[\d+\-.A-Za-z]/;
var ALPHANUMERIC = /[\d+-.A-Za-z]/;
var DIGIT = /\d/;
var HEX_START = /^(0x|0X)/;
var OCT = /^[0-7]+$/;
@ -4117,6 +4117,42 @@ function _objectSpread2(target) {
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
@ -6002,7 +6038,13 @@ if (!set$1 || !clear) {
defer = functionBindContext(port.postMessage, port, 1);
// Browsers with postMessage, skip WebWorkers
// IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
} else if (global_1.addEventListener && typeof postMessage == 'function' && !global_1.importScripts && !fails(post)) {
} else if (
global_1.addEventListener &&
typeof postMessage == 'function' &&
!global_1.importScripts &&
!fails(post) &&
location.protocol !== 'file:'
) {
defer = post;
global_1.addEventListener('message', listener, false);
// IE8-
@ -7012,6 +7054,25 @@ function matches$1(element, selector) {
var method = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
return method.call(element, selector);
} // Closest ancestor element matching selector (also tests element itself)
function closest(element, selector) {
var _Element2 = Element,
prototype = _Element2.prototype; // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
function closestElement() {
var el = this;
do {
if (matches$1.matches(el, selector)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
}
var method = prototype.closest || closestElement;
return method.call(element, selector);
} // Find all elements
function getElements(selector) {
@ -7363,8 +7424,8 @@ function setAspectRatio(input) {
var padding = 100 / w * h;
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;
if (this.isVimeo && !this.config.vimeo.premium && this.supported.ui) {
var height = 100 / this.media.offsetWidth * parseInt(window.getComputedStyle(this.media).paddingBottom, 10);
var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) {
@ -7517,7 +7578,7 @@ function dedupe(array) {
});
} // Get the closest value in an array
function closest(array, value) {
function closest$1(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
@ -9587,9 +9648,15 @@ var captions = {
meta.set(track, {
default: track.mode === 'showing'
}); // Turn off native caption rendering to avoid double captions
// Note: mode='hidden' forces a track to download. To ensure every track
// isn't downloaded at once, only 'showing' tracks should be reassigned
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden'; // Add event listener for cue changes
if (track.mode === 'showing') {
// eslint-disable-next-line no-param-reassign
track.mode = 'hidden';
} // Add event listener for cue changes
on.call(_this, track, 'cuechange', function () {
return captions.updateCues.call(_this);
@ -9613,6 +9680,8 @@ var captions = {
// Toggle captions display
// Used internally for the toggleCaptions method, with the passive option forced to false
toggle: function toggle(input) {
var _this2 = this;
var passive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// If there's no full support
@ -9659,7 +9728,15 @@ var captions = {
controls.updateSetting.call(this, 'captions'); // Trigger event (not used internally)
triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
}
} // Wait for the call stack to clear before setting mode='hidden'
// on the active track - forcing the browser to download it
setTimeout(function () {
if (active && _this2.captions.toggled) {
_this2.captions.currentTrackNode.mode = 'hidden';
}
});
},
// Set captions by track index
// Used internally for the currentTrack setter with the passive option forced to false
@ -9740,7 +9817,7 @@ var captions = {
// If update is false it will also ignore tracks without metadata
// This is used to "freeze" the language options when captions.update is false
getTracks: function getTracks() {
var _this2 = this;
var _this3 = this;
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// Handle media or textTracks missing or null
@ -9748,20 +9825,20 @@ var captions = {
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
return tracks.filter(function (track) {
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
return !_this3.isHTML5 || update || _this3.captions.meta.has(track);
}).filter(function (track) {
return ['captions', 'subtitles'].includes(track.kind);
});
},
// Match tracks based on languages and get the first
findTrack: function findTrack(languages) {
var _this3 = this;
var _this4 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var tracks = captions.getTracks.call(this);
var sortIsDefault = function sortIsDefault(track) {
return Number((_this3.captions.meta.get(track) || {}).default);
return Number((_this4.captions.meta.get(track) || {}).default);
};
var sorted = Array.from(tracks).sort(function (a, b) {
@ -9942,6 +10019,9 @@ var defaults$1 = {
fallback: true,
// Fallback using full viewport/window
iosNative: false // Use the native fullscreen in iOS (disables custom controls)
// Selector for the fullscreen container so contextual / non-player content can remain visible in fullscreen mode
// Non-ancestors of the player element will be ignored
// container: null, // defaults to the player element
},
// Local storage
@ -10179,16 +10259,16 @@ var defaults$1 = {
title: false,
speed: true,
transparent: false,
// These settings require a pro or premium account to work
sidedock: false,
controls: false,
// Whether the owner of the video has a Pro or Business account
// (which allows us to properly hide controls without CSS hacks, etc)
premium: false,
// Custom settings from Plyr
referrerPolicy: null // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy
},
// YouTube plugin
youtube: {
noCookie: false,
noCookie: true,
// Whether to use an alternative version of YouTube without cookies
rel: 0,
// No related vids
@ -10298,7 +10378,10 @@ var Fullscreen = /*#__PURE__*/function () {
y: 0
}; // Force the use of 'full window/browser' rather than fullscreen
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Register event listeners
this.forceFallback = player.config.fullscreen.fallback === 'force'; // Get the fullscreen element
// Checks container is an ancestor, defaults to null
this.player.elements.fullscreen = player.config.fullscreen.container && closest(this.player.elements.container, player.config.fullscreen.container); // Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : "".concat(this.prefix, "fullscreenchange"), function () {
@ -10524,7 +10607,7 @@ var Fullscreen = /*#__PURE__*/function () {
}, {
key: "target",
get: function get() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.fullscreen || this.player.elements.container;
}
}], [{
key: "native",
@ -10733,12 +10816,7 @@ var ui = {
} // Set property synchronously to respect the call order
this.media.setAttribute('poster', poster); // HTML5 uses native poster attribute
if (this.isHTML5) {
return Promise.resolve(poster);
} // Wait until ui is ready
this.media.setAttribute('data-poster', poster); // Wait until ui is ready
return ready.call(this) // Load image
.then(function () {
@ -10814,6 +10892,26 @@ var ui = {
this.toggleControls(Boolean(force || this.loading || this.paused || controlsElement.pressed || controlsElement.hover || recentTouchSeek));
}
},
// Migrate any custom properties from the media to the parent
migrateStyles: function migrateStyles() {
var _this5 = this;
// Loop through values (as they are the keys when the object is spread 🤔)
Object.values(_objectSpread2({}, this.media.style)) // We're only fussed about Plyr specific properties
.filter(function (key) {
return key.startsWith('--plyr');
}).forEach(function (key) {
// Set on the container
_this5.elements.container.style.setProperty(key, _this5.media.style.getPropertyValue(key)); // Clean up from media element
_this5.media.style.removeProperty(key);
}); // Remove attribute if empty
if (is$1.empty(this.media.style)) {
this.media.removeAttribute('style');
}
}
};
@ -11022,15 +11120,17 @@ var Listeners = /*#__PURE__*/function () {
removeCurrent(); // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (event.type !== 'focusout') {
this.focusTimer = setTimeout(function () {
var focused = document.activeElement; // Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
} // Global window & document listeners
}, {
@ -11048,7 +11148,7 @@ var Listeners = /*#__PURE__*/function () {
once.call(player, document.body, 'touchstart', this.firstTouch); // Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
toggleListener.call(player, document.body, 'keydown focus blur focusout', this.setTabFocus, toggle, false, true);
} // Container listeners
}, {
@ -11091,7 +11191,7 @@ var Listeners = /*#__PURE__*/function () {
}); // Set a gutter for Vimeo
var setGutter = function setGutter(ratio, padding, toggle) {
if (!player.isVimeo) {
if (!player.isVimeo || player.config.vimeo.premium) {
return;
}
@ -11148,7 +11248,7 @@ var Listeners = /*#__PURE__*/function () {
ratio = _setPlayerSize.ratio; // Set Vimeo gutter
setGutter(ratio, padding, isEnter); // If not using native fullscreen, we need to check for resizes of viewport
setGutter(ratio, padding, isEnter); // If not using native browser fullscreen API, we need to check for resizes of viewport
if (!usingNative) {
if (isEnter) {
@ -11527,7 +11627,18 @@ var Listeners = /*#__PURE__*/function () {
this.bind(elements.controls, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
}); // Also update controls.hover state for any non-player children of fullscreen element (as above)
if (elements.fullscreen) {
Array.from(elements.fullscreen.children).filter(function (c) {
return !c.contains(elements.container);
}).forEach(function (child) {
_this3.bind(child, 'mouseenter mouseleave', function (event) {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
});
} // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
@ -11996,15 +12107,28 @@ var vimeo = {
var _this = this;
var player = this;
var config = player.config.vimeo; // Get Vimeo params for the iframe
var config = player.config.vimeo;
var params = buildUrlParams(extend({}, {
var premium = config.premium,
referrerPolicy = config.referrerPolicy,
frameParams = _objectWithoutProperties(config, ["premium", "referrerPolicy"]); // If the owner has a pro or premium account then we can hide controls etc
if (premium) {
Object.assign(frameParams, {
controls: false,
sidedock: false
});
} // Get Vimeo params for the iframe
var params = buildUrlParams(_objectSpread2({
loop: player.config.loop.active,
autoplay: player.autoplay,
muted: player.muted,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
}, config)); // Get the source URL or ID
}, frameParams)); // Get the source URL or ID
var source = player.media.getAttribute('src'); // Get from <div> if needed
@ -12018,22 +12142,27 @@ var vimeo = {
var src = format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay'); // Set the referrer policy if required
iframe.setAttribute('allow', 'autoplay,fullscreen,picture-in-picture'); // Set the referrer policy if required
if (!is$1.empty(config.referrerPolicy)) {
iframe.setAttribute('referrerPolicy', config.referrerPolicy);
} // Get poster, if already set
if (!is$1.empty(referrerPolicy)) {
iframe.setAttribute('referrerPolicy', referrerPolicy);
} // Inject the package
var poster = player.poster; // Inject the package
var poster = player.poster;
if (premium) {
iframe.setAttribute('data-poster', poster);
player.media = replaceElement(iframe, player.media);
} else {
var wrapper = createElement('div', {
class: player.config.classNames.embedContainer,
'data-poster': poster
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media);
} // Get poster image
var wrapper = createElement('div', {
poster: poster,
class: player.config.classNames.embedContainer
});
wrapper.appendChild(iframe);
player.media = replaceElement(wrapper, player.media); // Get poster image
fetch(format(player.config.urls.vimeo.api, id), 'json').then(function (response) {
if (is$1.empty(response)) {
@ -12404,7 +12533,7 @@ var youtube = {
var container = createElement('div', {
id: id,
poster: poster
'data-poster': poster
});
player.media = replaceElement(container, player.media); // Id to poster wrapper
@ -12722,14 +12851,12 @@ var media = {
class: this.config.classNames.video
}); // Wrap the video in a container
wrap$1(this.media, this.elements.wrapper); // Faux poster container
wrap$1(this.media, this.elements.wrapper); // Poster image container
if (this.isEmbed) {
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
this.elements.poster = createElement('div', {
class: this.config.classNames.poster
});
this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isHTML5) {
@ -14386,6 +14513,7 @@ var Plyr = /*#__PURE__*/function () {
this.elements = {
container: null,
fullscreen: null,
captions: null,
buttons: {},
display: {},
@ -14560,9 +14688,11 @@ var Plyr = /*#__PURE__*/function () {
tabindex: 0
});
wrap$1(this.media, this.elements.container);
} // Add style hook
} // Migrate custom properties from media to container (so they work 😉)
ui.migrateStyles.call(this); // Add style hook
ui.addStyleHook.call(this); // Setup media
media.setup.call(this); // Listen for events if debugging
@ -14571,10 +14701,12 @@ var Plyr = /*#__PURE__*/function () {
on.call(this, this.elements.container, this.config.events.join(' '), function (event) {
_this.debug.log("event: ".concat(event.type));
});
} // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
} // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup interface
// If embed but not fully supported, build interface now to avoid flash of controls
if (this.isHTML5 || this.isEmbed && !this.supported.ui) {
ui.build.call(this);
} // Container listeners
@ -14582,9 +14714,7 @@ var Plyr = /*#__PURE__*/function () {
this.listeners.container(); // Global listeners
this.listeners.global(); // Setup fullscreen
this.fullscreen = new Fullscreen(this); // Setup ads if provided
this.listeners.global(); // Setup ads if provided
if (this.config.ads.enabled) {
this.ads = new Ads(this);
@ -15283,7 +15413,7 @@ var Plyr = /*#__PURE__*/function () {
var updateStorage = true;
if (!options.includes(quality)) {
var value = closest(options, quality);
var value = closest$1(options, quality);
this.debug.warn("Unsupported quality option: ".concat(quality, ", using ").concat(value, " instead"));
quality = value; // Don't update storage if quality is not supported
@ -15428,7 +15558,7 @@ var Plyr = /*#__PURE__*/function () {
return null;
}
return this.media.getAttribute('poster');
return this.media.getAttribute('poster') || this.media.getAttribute('data-poster');
}
/**
* Get the current aspect ratio in use

View File

@ -1,6 +1,6 @@
{
"name": "plyr",
"version": "3.5.10",
"version": "3.6.0",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>",