v3.4.5
This commit is contained in:
parent
779e45c11b
commit
e49da6c13f
@ -1,3 +1,10 @@
|
|||||||
|
# v3.4.5
|
||||||
|
|
||||||
|
- Added download button option to download either current source or a custom URL you specify in options
|
||||||
|
- Prevent immediate hiding of controls on mobile (thanks @jamesoflol)
|
||||||
|
- Don't hide controls on focusout event (fixes #1122) (thanks @jamesoflol)
|
||||||
|
- Fix HTML5 quality settings being incorrectly set in local storage (thanks @TechGuard)
|
||||||
|
|
||||||
# v3.4.4
|
# v3.4.4
|
||||||
|
|
||||||
- Fixed issue with double binding for `click` and `touchstart` for `clickToPlay` option
|
- Fixed issue with double binding for `click` and `touchstart` for `clickToPlay` option
|
||||||
|
@ -28,6 +28,7 @@ controls: [
|
|||||||
'settings', // Settings menu
|
'settings', // Settings menu
|
||||||
'pip', // Picture-in-picture (currently Safari only)
|
'pip', // Picture-in-picture (currently Safari only)
|
||||||
'airplay', // Airplay (currently Safari only)
|
'airplay', // Airplay (currently Safari only)
|
||||||
|
'download', // Show a download button with a link to either the current source or a custom URL you specify in your options
|
||||||
'fullscreen', // Toggle fullscreen
|
'fullscreen', // Toggle fullscreen
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
230
dist/plyr.js
vendored
230
dist/plyr.js
vendored
@ -178,6 +178,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
// Accept a URL object
|
// Accept a URL object
|
||||||
if (instanceOf(input, window.URL)) {
|
if (instanceOf(input, window.URL)) {
|
||||||
return true;
|
return true;
|
||||||
|
} // Must be string from here
|
||||||
|
|
||||||
|
|
||||||
|
if (!isString(input)) {
|
||||||
|
return false;
|
||||||
} // Add the protocol if required
|
} // Add the protocol if required
|
||||||
|
|
||||||
|
|
||||||
@ -1006,6 +1011,13 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
return wrapper.innerHTML;
|
return wrapper.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resources = {
|
||||||
|
pip: 'PIP',
|
||||||
|
airplay: 'AirPlay',
|
||||||
|
html5: 'HTML5',
|
||||||
|
vimeo: 'Vimeo',
|
||||||
|
youtube: 'YouTube'
|
||||||
|
};
|
||||||
var i18n = {
|
var i18n = {
|
||||||
get: function get() {
|
get: function get() {
|
||||||
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
||||||
@ -1018,6 +1030,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var string = getDeep(config.i18n, key);
|
var string = getDeep(config.i18n, key);
|
||||||
|
|
||||||
if (is.empty(string)) {
|
if (is.empty(string)) {
|
||||||
|
if (Object.keys(resources).includes(key)) {
|
||||||
|
return resources[key];
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1330,23 +1346,18 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if ('href' in use) {
|
if ('href' in use) {
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
||||||
} else {
|
} // Always set the older attribute even though it's "deprecated" (it'll be around for ages)
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
|
||||||
} // Add <use> to <svg>
|
|
||||||
|
|
||||||
|
|
||||||
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path); // Add <use> to <svg>
|
||||||
|
|
||||||
icon.appendChild(use);
|
icon.appendChild(use);
|
||||||
return icon;
|
return icon;
|
||||||
},
|
},
|
||||||
// Create hidden text label
|
// Create hidden text label
|
||||||
createLabel: function createLabel(type) {
|
createLabel: function createLabel(key) {
|
||||||
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
// Skip i18n for abbreviations and brand names
|
var text = i18n.get(key, this.config);
|
||||||
var universals = {
|
|
||||||
pip: 'PIP',
|
|
||||||
airplay: 'AirPlay'
|
|
||||||
};
|
|
||||||
var text = universals[type] || i18n.get(type, this.config);
|
|
||||||
var attributes = Object.assign({}, attr, {
|
var attributes = Object.assign({}, attr, {
|
||||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
||||||
});
|
});
|
||||||
@ -1368,20 +1379,29 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
},
|
},
|
||||||
// Create a <button>
|
// Create a <button>
|
||||||
createButton: function createButton(buttonType, attr) {
|
createButton: function createButton(buttonType, attr) {
|
||||||
var button = createElement('button');
|
|
||||||
var attributes = Object.assign({}, attr);
|
var attributes = Object.assign({}, attr);
|
||||||
var type = toCamelCase(buttonType);
|
var type = toCamelCase(buttonType);
|
||||||
var toggle = false;
|
var props = {
|
||||||
var label;
|
element: 'button',
|
||||||
var icon;
|
toggle: false,
|
||||||
var labelPressed;
|
label: null,
|
||||||
var iconPressed;
|
icon: null,
|
||||||
|
labelPressed: null,
|
||||||
if (!('type' in attributes)) {
|
iconPressed: null
|
||||||
attributes.type = 'button';
|
};
|
||||||
|
['element', 'icon', 'label'].forEach(function (key) {
|
||||||
|
if (Object.keys(attributes).includes(key)) {
|
||||||
|
props[key] = attributes[key];
|
||||||
|
delete attributes[key];
|
||||||
}
|
}
|
||||||
|
}); // Default to 'button' type to prevent form submission
|
||||||
|
|
||||||
if ('class' in attributes) {
|
if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
|
||||||
|
attributes.type = 'button';
|
||||||
|
} // Set class name
|
||||||
|
|
||||||
|
|
||||||
|
if (Object.keys(attributes).includes('class')) {
|
||||||
if (!attributes.class.includes(this.config.classNames.control)) {
|
if (!attributes.class.includes(this.config.classNames.control)) {
|
||||||
attributes.class += " ".concat(this.config.classNames.control);
|
attributes.class += " ".concat(this.config.classNames.control);
|
||||||
}
|
}
|
||||||
@ -1392,69 +1412,76 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
switch (buttonType) {
|
switch (buttonType) {
|
||||||
case 'play':
|
case 'play':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
labelPressed = 'pause';
|
props.labelPressed = 'pause';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
iconPressed = 'pause';
|
props.iconPressed = 'pause';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mute':
|
case 'mute':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'mute';
|
props.label = 'mute';
|
||||||
labelPressed = 'unmute';
|
props.labelPressed = 'unmute';
|
||||||
icon = 'volume';
|
props.icon = 'volume';
|
||||||
iconPressed = 'muted';
|
props.iconPressed = 'muted';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'captions':
|
case 'captions':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enableCaptions';
|
props.label = 'enableCaptions';
|
||||||
labelPressed = 'disableCaptions';
|
props.labelPressed = 'disableCaptions';
|
||||||
icon = 'captions-off';
|
props.icon = 'captions-off';
|
||||||
iconPressed = 'captions-on';
|
props.iconPressed = 'captions-on';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fullscreen':
|
case 'fullscreen':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enterFullscreen';
|
props.label = 'enterFullscreen';
|
||||||
labelPressed = 'exitFullscreen';
|
props.labelPressed = 'exitFullscreen';
|
||||||
icon = 'enter-fullscreen';
|
props.icon = 'enter-fullscreen';
|
||||||
iconPressed = 'exit-fullscreen';
|
props.iconPressed = 'exit-fullscreen';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'play-large':
|
case 'play-large':
|
||||||
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
|
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
|
||||||
type = 'play';
|
type = 'play';
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
label = type;
|
if (is.empty(props.label)) {
|
||||||
icon = buttonType;
|
props.label = type;
|
||||||
} // Setup toggle icon and labels
|
}
|
||||||
|
|
||||||
|
if (is.empty(props.icon)) {
|
||||||
|
props.icon = buttonType;
|
||||||
|
}
|
||||||
|
|
||||||
if (toggle) {
|
}
|
||||||
|
|
||||||
|
var button = createElement(props.element); // Setup toggle icon and labels
|
||||||
|
|
||||||
|
if (props.toggle) {
|
||||||
// Icon
|
// Icon
|
||||||
button.appendChild(controls.createIcon.call(this, iconPressed, {
|
button.appendChild(controls.createIcon.call(this, props.iconPressed, {
|
||||||
class: 'icon--pressed'
|
class: 'icon--pressed'
|
||||||
}));
|
}));
|
||||||
button.appendChild(controls.createIcon.call(this, icon, {
|
button.appendChild(controls.createIcon.call(this, props.icon, {
|
||||||
class: 'icon--not-pressed'
|
class: 'icon--not-pressed'
|
||||||
})); // Label/Tooltip
|
})); // Label/Tooltip
|
||||||
|
|
||||||
button.appendChild(controls.createLabel.call(this, labelPressed, {
|
button.appendChild(controls.createLabel.call(this, props.labelPressed, {
|
||||||
class: 'label--pressed'
|
class: 'label--pressed'
|
||||||
}));
|
}));
|
||||||
button.appendChild(controls.createLabel.call(this, label, {
|
button.appendChild(controls.createLabel.call(this, props.label, {
|
||||||
class: 'label--not-pressed'
|
class: 'label--not-pressed'
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
button.appendChild(controls.createIcon.call(this, icon));
|
button.appendChild(controls.createIcon.call(this, props.icon));
|
||||||
button.appendChild(controls.createLabel.call(this, label));
|
button.appendChild(controls.createLabel.call(this, props.label));
|
||||||
} // Merge attributes
|
} // Merge and set attributes
|
||||||
|
|
||||||
|
|
||||||
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
||||||
@ -2307,6 +2334,17 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
||||||
},
|
},
|
||||||
|
// Set the download link
|
||||||
|
setDownloadLink: function setDownloadLink() {
|
||||||
|
var button = this.elements.buttons.download; // Bail if no button
|
||||||
|
|
||||||
|
if (!is.element(button)) {
|
||||||
|
return;
|
||||||
|
} // Set download link
|
||||||
|
|
||||||
|
|
||||||
|
button.setAttribute('href', this.download);
|
||||||
|
},
|
||||||
// Build the default HTML
|
// Build the default HTML
|
||||||
// TODO: Set order based on order in the config.controls array?
|
// TODO: Set order based on order in the config.controls array?
|
||||||
create: function create(data) {
|
create: function create(data) {
|
||||||
@ -2512,6 +2550,25 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (this.config.controls.includes('airplay') && support.airplay) {
|
if (this.config.controls.includes('airplay') && support.airplay) {
|
||||||
container.appendChild(controls.createButton.call(this, 'airplay'));
|
container.appendChild(controls.createButton.call(this, 'airplay'));
|
||||||
|
} // Download button
|
||||||
|
|
||||||
|
|
||||||
|
if (this.config.controls.includes('download')) {
|
||||||
|
var _attributes = {
|
||||||
|
element: 'a',
|
||||||
|
href: this.download,
|
||||||
|
target: '_blank'
|
||||||
|
};
|
||||||
|
var download = this.config.urls.download;
|
||||||
|
|
||||||
|
if (!is.url(download) && this.isEmbed) {
|
||||||
|
extend(_attributes, {
|
||||||
|
icon: "logo-".concat(this.provider),
|
||||||
|
label: this.provider
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(controls.createButton.call(this, 'download', _attributes));
|
||||||
} // Toggle fullscreen button
|
} // Toggle fullscreen button
|
||||||
|
|
||||||
|
|
||||||
@ -3178,7 +3235,8 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
controls: ['play-large', // 'restart',
|
controls: ['play-large', // 'restart',
|
||||||
// 'rewind',
|
// 'rewind',
|
||||||
'play', // 'fast-forward',
|
'play', // 'fast-forward',
|
||||||
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'],
|
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download',
|
||||||
|
'fullscreen'],
|
||||||
settings: ['captions', 'quality', 'speed'],
|
settings: ['captions', 'quality', 'speed'],
|
||||||
// Localisation
|
// Localisation
|
||||||
i18n: {
|
i18n: {
|
||||||
@ -3198,6 +3256,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
unmute: 'Unmute',
|
unmute: 'Unmute',
|
||||||
enableCaptions: 'Enable captions',
|
enableCaptions: 'Enable captions',
|
||||||
disableCaptions: 'Disable captions',
|
disableCaptions: 'Disable captions',
|
||||||
|
download: 'Download',
|
||||||
enterFullscreen: 'Enter fullscreen',
|
enterFullscreen: 'Enter fullscreen',
|
||||||
exitFullscreen: 'Exit fullscreen',
|
exitFullscreen: 'Exit fullscreen',
|
||||||
frameTitle: 'Player for {title}',
|
frameTitle: 'Player for {title}',
|
||||||
@ -3226,6 +3285,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
},
|
},
|
||||||
// URLs
|
// URLs
|
||||||
urls: {
|
urls: {
|
||||||
|
download: null,
|
||||||
vimeo: {
|
vimeo: {
|
||||||
sdk: 'https://player.vimeo.com/api/player.js',
|
sdk: 'https://player.vimeo.com/api/player.js',
|
||||||
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
||||||
@ -3250,6 +3310,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
mute: null,
|
mute: null,
|
||||||
volume: null,
|
volume: null,
|
||||||
captions: null,
|
captions: null,
|
||||||
|
download: null,
|
||||||
fullscreen: null,
|
fullscreen: null,
|
||||||
pip: null,
|
pip: null,
|
||||||
airplay: null,
|
airplay: null,
|
||||||
@ -3262,7 +3323,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
events: [// Events to watch on HTML5 media elements and bubble
|
events: [// Events to watch on HTML5 media elements and bubble
|
||||||
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
|
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
|
||||||
'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events
|
'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events
|
||||||
'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
|
'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
|
||||||
'statechange', // Quality
|
'statechange', // Quality
|
||||||
'qualitychange', // Ads
|
'qualitychange', // Ads
|
||||||
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
|
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
|
||||||
@ -3284,6 +3345,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
fastForward: '[data-plyr="fast-forward"]',
|
fastForward: '[data-plyr="fast-forward"]',
|
||||||
mute: '[data-plyr="mute"]',
|
mute: '[data-plyr="mute"]',
|
||||||
captions: '[data-plyr="captions"]',
|
captions: '[data-plyr="captions"]',
|
||||||
|
download: '[data-plyr="download"]',
|
||||||
fullscreen: '[data-plyr="fullscreen"]',
|
fullscreen: '[data-plyr="fullscreen"]',
|
||||||
pip: '[data-plyr="pip"]',
|
pip: '[data-plyr="pip"]',
|
||||||
airplay: '[data-plyr="airplay"]',
|
airplay: '[data-plyr="airplay"]',
|
||||||
@ -3396,7 +3458,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Get provider by URL
|
* Get provider by URL
|
||||||
* @param {string} url
|
* @param {String} url
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getProviderByUrl(url) {
|
function getProviderByUrl(url) {
|
||||||
@ -3923,8 +3985,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var controls$$1 = this.elements.controls;
|
var controls$$1 = this.elements.controls;
|
||||||
|
|
||||||
if (controls$$1 && this.config.hideControls) {
|
if (controls$$1 && this.config.hideControls) {
|
||||||
// Show controls if force, loading, paused, or button interaction, otherwise hide
|
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
|
||||||
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover));
|
var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
|
||||||
|
|
||||||
|
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover || recentTouchSeek));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -4283,7 +4347,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (!is.element(wrapper)) {
|
if (!is.element(wrapper)) {
|
||||||
return;
|
return;
|
||||||
} // On click play, pause ore restart
|
} // On click play, pause or restart
|
||||||
|
|
||||||
|
|
||||||
on.call(player, elements.container, 'click', function (event) {
|
on.call(player, elements.container, 'click', function (event) {
|
||||||
@ -4336,6 +4400,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
on.call(player, player.media, 'qualitychange', function (event) {
|
on.call(player, player.media, 'qualitychange', function (event) {
|
||||||
// Update UI
|
// Update UI
|
||||||
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
||||||
|
}); // Update download link when ready and if quality changes
|
||||||
|
|
||||||
|
on.call(player, player.media, 'ready qualitychange', function () {
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
}); // Proxy events to container
|
}); // Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
|
|
||||||
@ -4413,7 +4481,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
this.bind(elements.buttons.captions, 'click', function () {
|
this.bind(elements.buttons.captions, 'click', function () {
|
||||||
return player.toggleCaptions();
|
return player.toggleCaptions();
|
||||||
}); // Fullscreen toggle
|
}); // Download
|
||||||
|
|
||||||
|
this.bind(elements.buttons.download, 'click', function () {
|
||||||
|
triggerEvent.call(player, player.media, 'download');
|
||||||
|
}, 'download'); // Fullscreen toggle
|
||||||
|
|
||||||
this.bind(elements.buttons.fullscreen, 'click', function () {
|
this.bind(elements.buttons.fullscreen, 'click', function () {
|
||||||
player.fullscreen.toggle();
|
player.fullscreen.toggle();
|
||||||
@ -4476,9 +4548,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (is.keyboardEvent(event) && code !== 39 && code !== 37) {
|
if (is.keyboardEvent(event) && code !== 39 && code !== 37) {
|
||||||
return;
|
return;
|
||||||
} // Was playing before?
|
} // Record seek time so we can prevent hiding controls for a few seconds after seek
|
||||||
|
|
||||||
|
|
||||||
|
player.lastSeekTime = Date.now(); // Was playing before?
|
||||||
|
|
||||||
var play = seek.hasAttribute(attribute); // Done seeking
|
var play = seek.hasAttribute(attribute); // Done seeking
|
||||||
|
|
||||||
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback
|
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback
|
||||||
@ -4555,32 +4629,28 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
|
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
|
||||||
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
||||||
}); // Focus in/out on controls
|
}); // Show controls when they receive focus (e.g., when using keyboard tab key)
|
||||||
|
|
||||||
this.bind(elements.controls, 'focusin focusout', function (event) {
|
this.bind(elements.controls, 'focusin', function () {
|
||||||
var config = player.config,
|
var config = player.config,
|
||||||
elements = player.elements,
|
elements = player.elements,
|
||||||
timers = player.timers;
|
timers = player.timers; // Skip transition to prevent focus from scrolling the parent element
|
||||||
var isFocusIn = event.type === 'focusin'; // Skip transition to prevent focus from scrolling the parent element
|
|
||||||
|
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn); // Toggle
|
toggleClass(elements.controls, config.classNames.noTransition, true); // Toggle
|
||||||
|
|
||||||
ui.toggleControls.call(player, isFocusIn); // If focusin, hide again after delay
|
ui.toggleControls.call(player, true); // Restore transition
|
||||||
|
|
||||||
if (isFocusIn) {
|
|
||||||
// Restore transition
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, false);
|
toggleClass(elements.controls, config.classNames.noTransition, false);
|
||||||
}, 0); // Delay a little more for keyboard users
|
}, 0); // Delay a little more for mouse users
|
||||||
|
|
||||||
var delay = _this2.touch ? 3000 : 4000; // Clear timer
|
var delay = _this2.touch ? 3000 : 4000; // Clear timer
|
||||||
|
|
||||||
clearTimeout(timers.controls); // Hide
|
clearTimeout(timers.controls); // Hide again after delay
|
||||||
|
|
||||||
timers.controls = setTimeout(function () {
|
timers.controls = setTimeout(function () {
|
||||||
return ui.toggleControls.call(player, false);
|
return ui.toggleControls.call(player, false);
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
|
||||||
}); // Mouse wheel for volume
|
}); // Mouse wheel for volume
|
||||||
|
|
||||||
this.bind(elements.inputs.volume, 'wheel', function (event) {
|
this.bind(elements.inputs.volume, 'wheel', function (event) {
|
||||||
@ -5171,6 +5241,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var currentSrc;
|
var currentSrc;
|
||||||
player.embed.getVideoUrl().then(function (value) {
|
player.embed.getVideoUrl().then(function (value) {
|
||||||
currentSrc = value;
|
currentSrc = value;
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
_this2.debug.warn(error);
|
_this2.debug.warn(error);
|
||||||
});
|
});
|
||||||
@ -6724,7 +6795,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (this.config.autoplay) {
|
if (this.config.autoplay) {
|
||||||
this.play();
|
this.play();
|
||||||
}
|
} // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
|
||||||
|
|
||||||
|
|
||||||
|
this.lastSeekTime = 0;
|
||||||
} // ---------------------------------------
|
} // ---------------------------------------
|
||||||
// API
|
// API
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@ -7448,6 +7522,16 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
get: function get() {
|
get: function get() {
|
||||||
return this.media.currentSrc;
|
return this.media.currentSrc;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get a download URL (either source or custom)
|
||||||
|
*/
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: "download",
|
||||||
|
get: function get() {
|
||||||
|
var download = this.config.urls.download;
|
||||||
|
return is.url(download) ? download : this.source;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Set the poster image for a video
|
* Set the poster image for a video
|
||||||
* @param {input} - the URL for the new poster image
|
* @param {input} - the URL for the new poster image
|
||||||
|
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.min.js
vendored
2
dist/plyr.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.min.js.map
vendored
2
dist/plyr.min.js.map
vendored
File diff suppressed because one or more lines are too long
230
dist/plyr.polyfilled.js
vendored
230
dist/plyr.polyfilled.js
vendored
@ -2804,6 +2804,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
// Accept a URL object
|
// Accept a URL object
|
||||||
if (instanceOf(input, window.URL)) {
|
if (instanceOf(input, window.URL)) {
|
||||||
return true;
|
return true;
|
||||||
|
} // Must be string from here
|
||||||
|
|
||||||
|
|
||||||
|
if (!isString(input)) {
|
||||||
|
return false;
|
||||||
} // Add the protocol if required
|
} // Add the protocol if required
|
||||||
|
|
||||||
|
|
||||||
@ -3669,6 +3674,13 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
return wrapper.innerHTML;
|
return wrapper.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resources = {
|
||||||
|
pip: 'PIP',
|
||||||
|
airplay: 'AirPlay',
|
||||||
|
html5: 'HTML5',
|
||||||
|
vimeo: 'Vimeo',
|
||||||
|
youtube: 'YouTube'
|
||||||
|
};
|
||||||
var i18n = {
|
var i18n = {
|
||||||
get: function get() {
|
get: function get() {
|
||||||
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
||||||
@ -3681,6 +3693,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var string = getDeep(config.i18n, key);
|
var string = getDeep(config.i18n, key);
|
||||||
|
|
||||||
if (is$1.empty(string)) {
|
if (is$1.empty(string)) {
|
||||||
|
if (Object.keys(resources).includes(key)) {
|
||||||
|
return resources[key];
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3993,23 +4009,18 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if ('href' in use) {
|
if ('href' in use) {
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
||||||
} else {
|
} // Always set the older attribute even though it's "deprecated" (it'll be around for ages)
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
|
||||||
} // Add <use> to <svg>
|
|
||||||
|
|
||||||
|
|
||||||
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path); // Add <use> to <svg>
|
||||||
|
|
||||||
icon.appendChild(use);
|
icon.appendChild(use);
|
||||||
return icon;
|
return icon;
|
||||||
},
|
},
|
||||||
// Create hidden text label
|
// Create hidden text label
|
||||||
createLabel: function createLabel(type) {
|
createLabel: function createLabel(key) {
|
||||||
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
// Skip i18n for abbreviations and brand names
|
var text = i18n.get(key, this.config);
|
||||||
var universals = {
|
|
||||||
pip: 'PIP',
|
|
||||||
airplay: 'AirPlay'
|
|
||||||
};
|
|
||||||
var text = universals[type] || i18n.get(type, this.config);
|
|
||||||
var attributes = Object.assign({}, attr, {
|
var attributes = Object.assign({}, attr, {
|
||||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
||||||
});
|
});
|
||||||
@ -4031,20 +4042,29 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
},
|
},
|
||||||
// Create a <button>
|
// Create a <button>
|
||||||
createButton: function createButton(buttonType, attr) {
|
createButton: function createButton(buttonType, attr) {
|
||||||
var button = createElement('button');
|
|
||||||
var attributes = Object.assign({}, attr);
|
var attributes = Object.assign({}, attr);
|
||||||
var type = toCamelCase(buttonType);
|
var type = toCamelCase(buttonType);
|
||||||
var toggle = false;
|
var props = {
|
||||||
var label;
|
element: 'button',
|
||||||
var icon;
|
toggle: false,
|
||||||
var labelPressed;
|
label: null,
|
||||||
var iconPressed;
|
icon: null,
|
||||||
|
labelPressed: null,
|
||||||
if (!('type' in attributes)) {
|
iconPressed: null
|
||||||
attributes.type = 'button';
|
};
|
||||||
|
['element', 'icon', 'label'].forEach(function (key) {
|
||||||
|
if (Object.keys(attributes).includes(key)) {
|
||||||
|
props[key] = attributes[key];
|
||||||
|
delete attributes[key];
|
||||||
}
|
}
|
||||||
|
}); // Default to 'button' type to prevent form submission
|
||||||
|
|
||||||
if ('class' in attributes) {
|
if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
|
||||||
|
attributes.type = 'button';
|
||||||
|
} // Set class name
|
||||||
|
|
||||||
|
|
||||||
|
if (Object.keys(attributes).includes('class')) {
|
||||||
if (!attributes.class.includes(this.config.classNames.control)) {
|
if (!attributes.class.includes(this.config.classNames.control)) {
|
||||||
attributes.class += " ".concat(this.config.classNames.control);
|
attributes.class += " ".concat(this.config.classNames.control);
|
||||||
}
|
}
|
||||||
@ -4055,69 +4075,76 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
switch (buttonType) {
|
switch (buttonType) {
|
||||||
case 'play':
|
case 'play':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
labelPressed = 'pause';
|
props.labelPressed = 'pause';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
iconPressed = 'pause';
|
props.iconPressed = 'pause';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mute':
|
case 'mute':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'mute';
|
props.label = 'mute';
|
||||||
labelPressed = 'unmute';
|
props.labelPressed = 'unmute';
|
||||||
icon = 'volume';
|
props.icon = 'volume';
|
||||||
iconPressed = 'muted';
|
props.iconPressed = 'muted';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'captions':
|
case 'captions':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enableCaptions';
|
props.label = 'enableCaptions';
|
||||||
labelPressed = 'disableCaptions';
|
props.labelPressed = 'disableCaptions';
|
||||||
icon = 'captions-off';
|
props.icon = 'captions-off';
|
||||||
iconPressed = 'captions-on';
|
props.iconPressed = 'captions-on';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fullscreen':
|
case 'fullscreen':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enterFullscreen';
|
props.label = 'enterFullscreen';
|
||||||
labelPressed = 'exitFullscreen';
|
props.labelPressed = 'exitFullscreen';
|
||||||
icon = 'enter-fullscreen';
|
props.icon = 'enter-fullscreen';
|
||||||
iconPressed = 'exit-fullscreen';
|
props.iconPressed = 'exit-fullscreen';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'play-large':
|
case 'play-large':
|
||||||
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
|
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
|
||||||
type = 'play';
|
type = 'play';
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
label = type;
|
if (is$1.empty(props.label)) {
|
||||||
icon = buttonType;
|
props.label = type;
|
||||||
} // Setup toggle icon and labels
|
}
|
||||||
|
|
||||||
|
if (is$1.empty(props.icon)) {
|
||||||
|
props.icon = buttonType;
|
||||||
|
}
|
||||||
|
|
||||||
if (toggle) {
|
}
|
||||||
|
|
||||||
|
var button = createElement(props.element); // Setup toggle icon and labels
|
||||||
|
|
||||||
|
if (props.toggle) {
|
||||||
// Icon
|
// Icon
|
||||||
button.appendChild(controls.createIcon.call(this, iconPressed, {
|
button.appendChild(controls.createIcon.call(this, props.iconPressed, {
|
||||||
class: 'icon--pressed'
|
class: 'icon--pressed'
|
||||||
}));
|
}));
|
||||||
button.appendChild(controls.createIcon.call(this, icon, {
|
button.appendChild(controls.createIcon.call(this, props.icon, {
|
||||||
class: 'icon--not-pressed'
|
class: 'icon--not-pressed'
|
||||||
})); // Label/Tooltip
|
})); // Label/Tooltip
|
||||||
|
|
||||||
button.appendChild(controls.createLabel.call(this, labelPressed, {
|
button.appendChild(controls.createLabel.call(this, props.labelPressed, {
|
||||||
class: 'label--pressed'
|
class: 'label--pressed'
|
||||||
}));
|
}));
|
||||||
button.appendChild(controls.createLabel.call(this, label, {
|
button.appendChild(controls.createLabel.call(this, props.label, {
|
||||||
class: 'label--not-pressed'
|
class: 'label--not-pressed'
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
button.appendChild(controls.createIcon.call(this, icon));
|
button.appendChild(controls.createIcon.call(this, props.icon));
|
||||||
button.appendChild(controls.createLabel.call(this, label));
|
button.appendChild(controls.createLabel.call(this, props.label));
|
||||||
} // Merge attributes
|
} // Merge and set attributes
|
||||||
|
|
||||||
|
|
||||||
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
||||||
@ -4970,6 +4997,17 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
||||||
},
|
},
|
||||||
|
// Set the download link
|
||||||
|
setDownloadLink: function setDownloadLink() {
|
||||||
|
var button = this.elements.buttons.download; // Bail if no button
|
||||||
|
|
||||||
|
if (!is$1.element(button)) {
|
||||||
|
return;
|
||||||
|
} // Set download link
|
||||||
|
|
||||||
|
|
||||||
|
button.setAttribute('href', this.download);
|
||||||
|
},
|
||||||
// Build the default HTML
|
// Build the default HTML
|
||||||
// TODO: Set order based on order in the config.controls array?
|
// TODO: Set order based on order in the config.controls array?
|
||||||
create: function create(data) {
|
create: function create(data) {
|
||||||
@ -5175,6 +5213,25 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (this.config.controls.includes('airplay') && support.airplay) {
|
if (this.config.controls.includes('airplay') && support.airplay) {
|
||||||
container.appendChild(controls.createButton.call(this, 'airplay'));
|
container.appendChild(controls.createButton.call(this, 'airplay'));
|
||||||
|
} // Download button
|
||||||
|
|
||||||
|
|
||||||
|
if (this.config.controls.includes('download')) {
|
||||||
|
var _attributes = {
|
||||||
|
element: 'a',
|
||||||
|
href: this.download,
|
||||||
|
target: '_blank'
|
||||||
|
};
|
||||||
|
var download = this.config.urls.download;
|
||||||
|
|
||||||
|
if (!is$1.url(download) && this.isEmbed) {
|
||||||
|
extend(_attributes, {
|
||||||
|
icon: "logo-".concat(this.provider),
|
||||||
|
label: this.provider
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(controls.createButton.call(this, 'download', _attributes));
|
||||||
} // Toggle fullscreen button
|
} // Toggle fullscreen button
|
||||||
|
|
||||||
|
|
||||||
@ -5841,7 +5898,8 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
controls: ['play-large', // 'restart',
|
controls: ['play-large', // 'restart',
|
||||||
// 'rewind',
|
// 'rewind',
|
||||||
'play', // 'fast-forward',
|
'play', // 'fast-forward',
|
||||||
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'],
|
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', // 'download',
|
||||||
|
'fullscreen'],
|
||||||
settings: ['captions', 'quality', 'speed'],
|
settings: ['captions', 'quality', 'speed'],
|
||||||
// Localisation
|
// Localisation
|
||||||
i18n: {
|
i18n: {
|
||||||
@ -5861,6 +5919,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
unmute: 'Unmute',
|
unmute: 'Unmute',
|
||||||
enableCaptions: 'Enable captions',
|
enableCaptions: 'Enable captions',
|
||||||
disableCaptions: 'Disable captions',
|
disableCaptions: 'Disable captions',
|
||||||
|
download: 'Download',
|
||||||
enterFullscreen: 'Enter fullscreen',
|
enterFullscreen: 'Enter fullscreen',
|
||||||
exitFullscreen: 'Exit fullscreen',
|
exitFullscreen: 'Exit fullscreen',
|
||||||
frameTitle: 'Player for {title}',
|
frameTitle: 'Player for {title}',
|
||||||
@ -5889,6 +5948,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
},
|
},
|
||||||
// URLs
|
// URLs
|
||||||
urls: {
|
urls: {
|
||||||
|
download: null,
|
||||||
vimeo: {
|
vimeo: {
|
||||||
sdk: 'https://player.vimeo.com/api/player.js',
|
sdk: 'https://player.vimeo.com/api/player.js',
|
||||||
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
||||||
@ -5913,6 +5973,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
mute: null,
|
mute: null,
|
||||||
volume: null,
|
volume: null,
|
||||||
captions: null,
|
captions: null,
|
||||||
|
download: null,
|
||||||
fullscreen: null,
|
fullscreen: null,
|
||||||
pip: null,
|
pip: null,
|
||||||
airplay: null,
|
airplay: null,
|
||||||
@ -5925,7 +5986,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
events: [// Events to watch on HTML5 media elements and bubble
|
events: [// Events to watch on HTML5 media elements and bubble
|
||||||
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
|
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
|
||||||
'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events
|
'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied', 'ratechange', 'cuechange', // Custom events
|
||||||
'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
|
'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
|
||||||
'statechange', // Quality
|
'statechange', // Quality
|
||||||
'qualitychange', // Ads
|
'qualitychange', // Ads
|
||||||
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
|
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
|
||||||
@ -5947,6 +6008,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
fastForward: '[data-plyr="fast-forward"]',
|
fastForward: '[data-plyr="fast-forward"]',
|
||||||
mute: '[data-plyr="mute"]',
|
mute: '[data-plyr="mute"]',
|
||||||
captions: '[data-plyr="captions"]',
|
captions: '[data-plyr="captions"]',
|
||||||
|
download: '[data-plyr="download"]',
|
||||||
fullscreen: '[data-plyr="fullscreen"]',
|
fullscreen: '[data-plyr="fullscreen"]',
|
||||||
pip: '[data-plyr="pip"]',
|
pip: '[data-plyr="pip"]',
|
||||||
airplay: '[data-plyr="airplay"]',
|
airplay: '[data-plyr="airplay"]',
|
||||||
@ -6059,7 +6121,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Get provider by URL
|
* Get provider by URL
|
||||||
* @param {string} url
|
* @param {String} url
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getProviderByUrl(url) {
|
function getProviderByUrl(url) {
|
||||||
@ -6596,8 +6658,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var controls$$1 = this.elements.controls;
|
var controls$$1 = this.elements.controls;
|
||||||
|
|
||||||
if (controls$$1 && this.config.hideControls) {
|
if (controls$$1 && this.config.hideControls) {
|
||||||
// Show controls if force, loading, paused, or button interaction, otherwise hide
|
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
|
||||||
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover));
|
var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
|
||||||
|
|
||||||
|
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover || recentTouchSeek));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -6956,7 +7020,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (!is$1.element(wrapper)) {
|
if (!is$1.element(wrapper)) {
|
||||||
return;
|
return;
|
||||||
} // On click play, pause ore restart
|
} // On click play, pause or restart
|
||||||
|
|
||||||
|
|
||||||
on.call(player, elements.container, 'click', function (event) {
|
on.call(player, elements.container, 'click', function (event) {
|
||||||
@ -7009,6 +7073,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
on.call(player, player.media, 'qualitychange', function (event) {
|
on.call(player, player.media, 'qualitychange', function (event) {
|
||||||
// Update UI
|
// Update UI
|
||||||
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
||||||
|
}); // Update download link when ready and if quality changes
|
||||||
|
|
||||||
|
on.call(player, player.media, 'ready qualitychange', function () {
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
}); // Proxy events to container
|
}); // Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
|
|
||||||
@ -7086,7 +7154,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
this.bind(elements.buttons.captions, 'click', function () {
|
this.bind(elements.buttons.captions, 'click', function () {
|
||||||
return player.toggleCaptions();
|
return player.toggleCaptions();
|
||||||
}); // Fullscreen toggle
|
}); // Download
|
||||||
|
|
||||||
|
this.bind(elements.buttons.download, 'click', function () {
|
||||||
|
triggerEvent.call(player, player.media, 'download');
|
||||||
|
}, 'download'); // Fullscreen toggle
|
||||||
|
|
||||||
this.bind(elements.buttons.fullscreen, 'click', function () {
|
this.bind(elements.buttons.fullscreen, 'click', function () {
|
||||||
player.fullscreen.toggle();
|
player.fullscreen.toggle();
|
||||||
@ -7149,9 +7221,11 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (is$1.keyboardEvent(event) && code !== 39 && code !== 37) {
|
if (is$1.keyboardEvent(event) && code !== 39 && code !== 37) {
|
||||||
return;
|
return;
|
||||||
} // Was playing before?
|
} // Record seek time so we can prevent hiding controls for a few seconds after seek
|
||||||
|
|
||||||
|
|
||||||
|
player.lastSeekTime = Date.now(); // Was playing before?
|
||||||
|
|
||||||
var play = seek.hasAttribute(attribute); // Done seeking
|
var play = seek.hasAttribute(attribute); // Done seeking
|
||||||
|
|
||||||
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback
|
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); // If we're done seeking and it was playing, resume playback
|
||||||
@ -7228,32 +7302,28 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
|
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
|
||||||
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
||||||
}); // Focus in/out on controls
|
}); // Show controls when they receive focus (e.g., when using keyboard tab key)
|
||||||
|
|
||||||
this.bind(elements.controls, 'focusin focusout', function (event) {
|
this.bind(elements.controls, 'focusin', function () {
|
||||||
var config = player.config,
|
var config = player.config,
|
||||||
elements = player.elements,
|
elements = player.elements,
|
||||||
timers = player.timers;
|
timers = player.timers; // Skip transition to prevent focus from scrolling the parent element
|
||||||
var isFocusIn = event.type === 'focusin'; // Skip transition to prevent focus from scrolling the parent element
|
|
||||||
|
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn); // Toggle
|
toggleClass(elements.controls, config.classNames.noTransition, true); // Toggle
|
||||||
|
|
||||||
ui.toggleControls.call(player, isFocusIn); // If focusin, hide again after delay
|
ui.toggleControls.call(player, true); // Restore transition
|
||||||
|
|
||||||
if (isFocusIn) {
|
|
||||||
// Restore transition
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, false);
|
toggleClass(elements.controls, config.classNames.noTransition, false);
|
||||||
}, 0); // Delay a little more for keyboard users
|
}, 0); // Delay a little more for mouse users
|
||||||
|
|
||||||
var delay = _this2.touch ? 3000 : 4000; // Clear timer
|
var delay = _this2.touch ? 3000 : 4000; // Clear timer
|
||||||
|
|
||||||
clearTimeout(timers.controls); // Hide
|
clearTimeout(timers.controls); // Hide again after delay
|
||||||
|
|
||||||
timers.controls = setTimeout(function () {
|
timers.controls = setTimeout(function () {
|
||||||
return ui.toggleControls.call(player, false);
|
return ui.toggleControls.call(player, false);
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
|
||||||
}); // Mouse wheel for volume
|
}); // Mouse wheel for volume
|
||||||
|
|
||||||
this.bind(elements.inputs.volume, 'wheel', function (event) {
|
this.bind(elements.inputs.volume, 'wheel', function (event) {
|
||||||
@ -7864,6 +7934,7 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
var currentSrc;
|
var currentSrc;
|
||||||
player.embed.getVideoUrl().then(function (value) {
|
player.embed.getVideoUrl().then(function (value) {
|
||||||
currentSrc = value;
|
currentSrc = value;
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
_this2.debug.warn(error);
|
_this2.debug.warn(error);
|
||||||
});
|
});
|
||||||
@ -9414,7 +9485,10 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
|
|
||||||
if (this.config.autoplay) {
|
if (this.config.autoplay) {
|
||||||
this.play();
|
this.play();
|
||||||
}
|
} // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
|
||||||
|
|
||||||
|
|
||||||
|
this.lastSeekTime = 0;
|
||||||
} // ---------------------------------------
|
} // ---------------------------------------
|
||||||
// API
|
// API
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@ -10138,6 +10212,16 @@ typeof navigator === "object" && (function (global, factory) {
|
|||||||
get: function get() {
|
get: function get() {
|
||||||
return this.media.currentSrc;
|
return this.media.currentSrc;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get a download URL (either source or custom)
|
||||||
|
*/
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: "download",
|
||||||
|
get: function get() {
|
||||||
|
var download = this.config.urls.download;
|
||||||
|
return is$1.url(download) ? download : this.source;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Set the poster image for a video
|
* Set the poster image for a video
|
||||||
* @param {input} - the URL for the new poster image
|
* @param {input} - the URL for the new poster image
|
||||||
|
2
dist/plyr.polyfilled.js.map
vendored
2
dist/plyr.polyfilled.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.polyfilled.min.js
vendored
2
dist/plyr.polyfilled.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.polyfilled.min.js.map
vendored
2
dist/plyr.polyfilled.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.svg
vendored
2
dist/plyr.svg
vendored
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 5.3 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plyr",
|
"name": "plyr",
|
||||||
"version": "3.4.4",
|
"version": "3.4.5",
|
||||||
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
||||||
"homepage": "https://plyr.io",
|
"homepage": "https://plyr.io",
|
||||||
"author": "Sam Potts <sam@potts.es>",
|
"author": "Sam Potts <sam@potts.es>",
|
||||||
|
@ -132,13 +132,13 @@ See [initialising](#initialising) for more information on advanced setups.
|
|||||||
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
|
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.4.4/plyr.js"></script>
|
<script src="https://cdn.plyr.io/3.4.5/plyr.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
...or...
|
...or...
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.4.4/plyr.polyfilled.js"></script>
|
<script src="https://cdn.plyr.io/3.4.5/plyr.polyfilled.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSS
|
### CSS
|
||||||
@ -152,13 +152,13 @@ Include the `plyr.css` stylsheet into your `<head>`
|
|||||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.4/plyr.css">
|
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.5/plyr.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
### SVG Sprite
|
### SVG Sprite
|
||||||
|
|
||||||
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
||||||
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.4/plyr.svg`.
|
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.5/plyr.svg`.
|
||||||
|
|
||||||
## Ads
|
## Ads
|
||||||
|
|
||||||
@ -315,6 +315,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
|
|||||||
| `quality` | Object | `{ default: 'default', options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'] }` | Currently only supported by YouTube. `default` is the default quality level, determined by YouTube. `options` are the options to display. |
|
| `quality` | Object | `{ default: 'default', options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'] }` | Currently only supported by YouTube. `default` is the default quality level, determined by YouTube. `options` are the options to display. |
|
||||||
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
|
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
|
||||||
| `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable vi.ai ads. `publisherId`: Your unique vi.ai publisher ID. |
|
| `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable vi.ai ads. `publisherId`: Your unique vi.ai publisher ID. |
|
||||||
|
| `urls` | Object | See source. | If you wish to override any API URLs then you can do so here. You can also set a custom download URL for the download button. |
|
||||||
|
|
||||||
1. Vimeo only
|
1. Vimeo only
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr
|
// Plyr
|
||||||
// plyr.js v3.4.4
|
// plyr.js v3.4.5
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr Polyfilled Build
|
// Plyr Polyfilled Build
|
||||||
// plyr.js v3.4.4
|
// plyr.js v3.4.5
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
Loading…
x
Reference in New Issue
Block a user