This commit is contained in:
Sam Potts
2018-10-24 23:04:18 +11:00
parent 779e45c11b
commit e49da6c13f
17 changed files with 349 additions and 172 deletions

240
dist/plyr.js vendored
View File

@ -178,6 +178,11 @@ typeof navigator === "object" && (function (global, factory) {
// Accept a URL object
if (instanceOf(input, window.URL)) {
return true;
} // Must be string from here
if (!isString(input)) {
return false;
} // Add the protocol if required
@ -1006,6 +1011,13 @@ typeof navigator === "object" && (function (global, factory) {
return wrapper.innerHTML;
}
var resources = {
pip: 'PIP',
airplay: 'AirPlay',
html5: 'HTML5',
vimeo: 'Vimeo',
youtube: 'YouTube'
};
var i18n = {
get: function get() {
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);
if (is.empty(string)) {
if (Object.keys(resources).includes(key)) {
return resources[key];
}
return '';
}
@ -1330,23 +1346,18 @@ typeof navigator === "object" && (function (global, factory) {
if ('href' in use) {
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
} else {
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
} // Add <use> to <svg>
} // 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>
icon.appendChild(use);
return icon;
},
// Create hidden text label
createLabel: function createLabel(type) {
createLabel: function createLabel(key) {
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Skip i18n for abbreviations and brand names
var universals = {
pip: 'PIP',
airplay: 'AirPlay'
};
var text = universals[type] || i18n.get(type, this.config);
var text = i18n.get(key, this.config);
var attributes = Object.assign({}, attr, {
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
});
@ -1368,20 +1379,29 @@ typeof navigator === "object" && (function (global, factory) {
},
// Create a <button>
createButton: function createButton(buttonType, attr) {
var button = createElement('button');
var attributes = Object.assign({}, attr);
var type = toCamelCase(buttonType);
var toggle = false;
var label;
var icon;
var labelPressed;
var iconPressed;
var props = {
element: 'button',
toggle: false,
label: null,
icon: null,
labelPressed: null,
iconPressed: null
};
['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 (!('type' in attributes)) {
if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
attributes.type = 'button';
}
} // Set class name
if ('class' in attributes) {
if (Object.keys(attributes).includes('class')) {
if (!attributes.class.includes(this.config.classNames.control)) {
attributes.class += " ".concat(this.config.classNames.control);
}
@ -1392,69 +1412,76 @@ typeof navigator === "object" && (function (global, factory) {
switch (buttonType) {
case 'play':
toggle = true;
label = 'play';
labelPressed = 'pause';
icon = 'play';
iconPressed = 'pause';
props.toggle = true;
props.label = 'play';
props.labelPressed = 'pause';
props.icon = 'play';
props.iconPressed = 'pause';
break;
case 'mute':
toggle = true;
label = 'mute';
labelPressed = 'unmute';
icon = 'volume';
iconPressed = 'muted';
props.toggle = true;
props.label = 'mute';
props.labelPressed = 'unmute';
props.icon = 'volume';
props.iconPressed = 'muted';
break;
case 'captions':
toggle = true;
label = 'enableCaptions';
labelPressed = 'disableCaptions';
icon = 'captions-off';
iconPressed = 'captions-on';
props.toggle = true;
props.label = 'enableCaptions';
props.labelPressed = 'disableCaptions';
props.icon = 'captions-off';
props.iconPressed = 'captions-on';
break;
case 'fullscreen':
toggle = true;
label = 'enterFullscreen';
labelPressed = 'exitFullscreen';
icon = 'enter-fullscreen';
iconPressed = 'exit-fullscreen';
props.toggle = true;
props.label = 'enterFullscreen';
props.labelPressed = 'exitFullscreen';
props.icon = 'enter-fullscreen';
props.iconPressed = 'exit-fullscreen';
break;
case 'play-large':
attributes.class += " ".concat(this.config.classNames.control, "--overlaid");
type = 'play';
label = 'play';
icon = 'play';
props.label = 'play';
props.icon = 'play';
break;
default:
label = type;
icon = buttonType;
} // Setup toggle icon and labels
if (is.empty(props.label)) {
props.label = type;
}
if (is.empty(props.icon)) {
props.icon = buttonType;
}
if (toggle) {
}
var button = createElement(props.element); // Setup toggle icon and labels
if (props.toggle) {
// Icon
button.appendChild(controls.createIcon.call(this, iconPressed, {
button.appendChild(controls.createIcon.call(this, props.iconPressed, {
class: 'icon--pressed'
}));
button.appendChild(controls.createIcon.call(this, icon, {
button.appendChild(controls.createIcon.call(this, props.icon, {
class: 'icon--not-pressed'
})); // Label/Tooltip
button.appendChild(controls.createLabel.call(this, labelPressed, {
button.appendChild(controls.createLabel.call(this, props.labelPressed, {
class: 'label--pressed'
}));
button.appendChild(controls.createLabel.call(this, label, {
button.appendChild(controls.createLabel.call(this, props.label, {
class: 'label--not-pressed'
}));
} else {
button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label));
} // Merge attributes
button.appendChild(controls.createIcon.call(this, props.icon));
button.appendChild(controls.createLabel.call(this, props.label));
} // Merge and set 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);
},
// 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
// TODO: Set order based on order in the config.controls array?
create: function create(data) {
@ -2512,6 +2550,25 @@ typeof navigator === "object" && (function (global, factory) {
if (this.config.controls.includes('airplay') && support.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
@ -3178,7 +3235,8 @@ typeof navigator === "object" && (function (global, factory) {
controls: ['play-large', // 'restart',
// 'rewind',
'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'],
// Localisation
i18n: {
@ -3198,6 +3256,7 @@ typeof navigator === "object" && (function (global, factory) {
unmute: 'Unmute',
enableCaptions: 'Enable captions',
disableCaptions: 'Disable captions',
download: 'Download',
enterFullscreen: 'Enter fullscreen',
exitFullscreen: 'Exit fullscreen',
frameTitle: 'Player for {title}',
@ -3226,6 +3285,7 @@ typeof navigator === "object" && (function (global, factory) {
},
// URLs
urls: {
download: null,
vimeo: {
sdk: 'https://player.vimeo.com/api/player.js',
iframe: 'https://player.vimeo.com/video/{0}?{1}',
@ -3250,6 +3310,7 @@ typeof navigator === "object" && (function (global, factory) {
mute: null,
volume: null,
captions: null,
download: null,
fullscreen: null,
pip: null,
airplay: null,
@ -3262,7 +3323,7 @@ typeof navigator === "object" && (function (global, factory) {
events: [// Events to watch on HTML5 media elements and bubble
// 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
'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
'download', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', // YouTube
'statechange', // Quality
'qualitychange', // Ads
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
@ -3284,6 +3345,7 @@ typeof navigator === "object" && (function (global, factory) {
fastForward: '[data-plyr="fast-forward"]',
mute: '[data-plyr="mute"]',
captions: '[data-plyr="captions"]',
download: '[data-plyr="download"]',
fullscreen: '[data-plyr="fullscreen"]',
pip: '[data-plyr="pip"]',
airplay: '[data-plyr="airplay"]',
@ -3396,7 +3458,7 @@ typeof navigator === "object" && (function (global, factory) {
};
/**
* Get provider by URL
* @param {string} url
* @param {String} url
*/
function getProviderByUrl(url) {
@ -3923,8 +3985,10 @@ typeof navigator === "object" && (function (global, factory) {
var controls$$1 = this.elements.controls;
if (controls$$1 && this.config.hideControls) {
// Show controls if force, loading, paused, or button interaction, otherwise hide
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover));
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
var recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
this.toggleControls(Boolean(force || this.loading || this.paused || controls$$1.pressed || controls$$1.hover || recentTouchSeek));
}
}
};
@ -4283,7 +4347,7 @@ typeof navigator === "object" && (function (global, factory) {
if (!is.element(wrapper)) {
return;
} // On click play, pause ore restart
} // On click play, pause or restart
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) {
// Update UI
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
// Bubble up key events for Edge
@ -4413,7 +4481,11 @@ typeof navigator === "object" && (function (global, factory) {
this.bind(elements.buttons.captions, 'click', function () {
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 () {
player.fullscreen.toggle();
@ -4476,9 +4548,11 @@ typeof navigator === "object" && (function (global, factory) {
if (is.keyboardEvent(event) && code !== 39 && code !== 37) {
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 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) {
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,
elements = player.elements,
timers = player.timers;
var isFocusIn = event.type === 'focusin'; // Skip transition to prevent focus from scrolling the parent element
timers = player.timers; // 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 () {
toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0); // Delay a little more for keyboard users
setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false);
}, 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 () {
return ui.toggleControls.call(player, false);
}, delay);
}
timers.controls = setTimeout(function () {
return ui.toggleControls.call(player, false);
}, delay);
}); // Mouse wheel for volume
this.bind(elements.inputs.volume, 'wheel', function (event) {
@ -5171,6 +5241,7 @@ typeof navigator === "object" && (function (global, factory) {
var currentSrc;
player.embed.getVideoUrl().then(function (value) {
currentSrc = value;
controls.setDownloadLink.call(player);
}).catch(function (error) {
_this2.debug.warn(error);
});
@ -6724,7 +6795,10 @@ typeof navigator === "object" && (function (global, factory) {
if (this.config.autoplay) {
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
// ---------------------------------------
@ -7448,6 +7522,16 @@ typeof navigator === "object" && (function (global, factory) {
get: function get() {
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
* @param {input} - the URL for the new poster image