Compare commits

...

10 Commits

Author SHA1 Message Date
Sam Potts 0976afe282 v3.0.4 2018-03-27 23:47:58 +11:00
Sam Potts 7b1e4abda7 Controls fixes 2018-03-27 23:43:38 +11:00
Sam Potts 0cf75eed3f Revert API method change 2018-03-27 21:15:11 +11:00
Sam Potts d96957d086 Allow fullscreen in iframe 2018-03-27 21:13:22 +11:00
Sam Potts 1a032ea498 Fix for seeking issue 2018-03-27 21:10:06 +11:00
Sam Potts 5d079da1b8 Use object.entries 2018-03-27 10:41:06 +11:00
Sam Potts 9c1bc6ab08 Fixes for fast forward and issues with event.preventDefault() 2018-03-27 10:36:08 +11:00
Sam Potts 3d2ba8c009 Update readme.md 2018-03-22 09:11:42 +11:00
Sam Potts e872ce3f77 Update readme.md 2018-03-22 09:10:50 +11:00
Sam Potts b77756da04 Typo 2018-03-22 01:15:10 +11:00
31 changed files with 945 additions and 564 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
* Vimeo offset tweak (fixes #826) * Vimeo offset tweak (fixes #826)
* Fix for .stop() method (fixes #819) * Fix for .stop() method (fixes #819)
* Check for array for speed options (fixes #252) * Check for array for speed options (fixes #817)
* Restore as float (fixes #828) * Restore as float (fixes #828)
* Fix for Firefox fullscreen oddness (Fixes #821) * Fix for Firefox fullscreen oddness (Fixes #821)
* Improve Sprite checking (fixes #827) * Improve Sprite checking (fixes #827)
+1 -1
View File
File diff suppressed because one or more lines are too long
+17 -1
View File
@@ -3820,6 +3820,22 @@ var singleton = Raven;
tooltips: { tooltips: {
controls: true controls: true
}, },
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
captions: { captions: {
active: true active: true
}, },
@@ -3827,7 +3843,7 @@ var singleton = Raven;
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c' google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
}, },
ads: { ads: {
enabled: true, // enabled: true,
publisherId: '918848828995742' publisherId: '918848828995742'
} }
}); });
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+17 -1
View File
@@ -57,6 +57,22 @@ import Raven from 'raven-js';
tooltips: { tooltips: {
controls: true, controls: true,
}, },
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
captions: { captions: {
active: true, active: true,
}, },
@@ -64,7 +80,7 @@ import Raven from 'raven-js';
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c', google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
}, },
ads: { ads: {
enabled: true, // enabled: true,
publisherId: '918848828995742', publisherId: '918848828995742',
}, },
}); });
+1 -1
View File
File diff suppressed because one or more lines are too long
+301 -187
View File
@@ -77,7 +77,7 @@ var defaults = {
// Sprite (for icons) // Sprite (for icons)
loadSprite: true, loadSprite: true,
iconPrefix: 'plyr', iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.0.2/plyr.svg', iconUrl: 'https://cdn.plyr.io/3.0.3/plyr.svg',
// Blank video (used to prevent errors on source change) // Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4', blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -133,7 +133,12 @@ var defaults = {
}, },
// Default controls // Default controls
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'], controls: ['play-large',
// 'restart',
// 'rewind',
'play',
// 'fast-forward',
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'],
settings: ['captions', 'quality', 'speed'], settings: ['captions', 'quality', 'speed'],
// Localisation // Localisation
@@ -142,7 +147,7 @@ var defaults = {
rewind: 'Rewind {seektime} secs', rewind: 'Rewind {seektime} secs',
play: 'Play', play: 'Play',
pause: 'Pause', pause: 'Pause',
forward: 'Forward {seektime} secs', fastForward: 'Forward {seektime} secs',
seek: 'Seek', seek: 'Seek',
played: 'Played', played: 'Played',
buffered: 'Buffered', buffered: 'Buffered',
@@ -165,7 +170,6 @@ var defaults = {
end: 'End', end: 'End',
all: 'All', all: 'All',
reset: 'Reset', reset: 'Reset',
none: 'None',
disabled: 'Disabled', disabled: 'Disabled',
advertisement: 'Ad' advertisement: 'Ad'
}, },
@@ -190,7 +194,7 @@ var defaults = {
pause: null, pause: null,
restart: null, restart: null,
rewind: null, rewind: null,
forward: null, fastForward: null,
mute: null, mute: null,
volume: null, volume: null,
captions: null, captions: null,
@@ -1073,16 +1077,6 @@ var utils = {
}, },
// Determine if we're in an iframe
inFrame: function inFrame() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
},
// Wrap an element // Wrap an element
wrap: function wrap(elements, wrapper) { wrap: function wrap(elements, wrapper) {
// Convert `elements` to an array, if necessary. // Convert `elements` to an array, if necessary.
@@ -1191,8 +1185,12 @@ var utils = {
return; return;
} }
Object.keys(attributes).forEach(function (key) { Object.entries(attributes).forEach(function (_ref) {
element.setAttribute(key, attributes[key]); var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
element.setAttribute(key, value);
}); });
}, },
@@ -1426,7 +1424,11 @@ var utils = {
// Toggle event listener // Toggle event listener
toggleListener: function toggleListener(elements, event, callback, toggle, passive, capture) { toggleListener: function toggleListener(elements, event, callback) {
var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
// Bail if no elemetns, event, or callback // Bail if no elemetns, event, or callback
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) { if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
return; return;
@@ -1448,16 +1450,16 @@ var utils = {
var events = event.split(' '); var events = event.split(' ');
// Build options // Build options
// Default to just capture boolean // Default to just the capture boolean for browsers with no passive listener support
var options = utils.is.boolean(capture) ? capture : false; var options = capture;
// If passive events listeners are supported // If passive events listeners are supported
if (support.passiveListeners) { if (support.passiveListeners) {
options = { options = {
// Whether the listener can be passive (i.e. default never prevented) // Whether the listener can be passive (i.e. default never prevented)
passive: utils.is.boolean(passive) ? passive : true, passive: passive,
// Whether the listener is a capturing listener or not // Whether the listener is a capturing listener or not
capture: utils.is.boolean(capture) ? capture : false capture: capture
}; };
} }
@@ -1469,13 +1471,23 @@ var utils = {
// Bind event handler // Bind event handler
on: function on(element, events, callback, passive, capture) { on: function on(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments[2];
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
utils.toggleListener(element, events, callback, true, passive, capture); utils.toggleListener(element, events, callback, true, passive, capture);
}, },
// Unbind event handler // Unbind event handler
off: function off(element, events, callback, passive, capture) { off: function off(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments[2];
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
utils.toggleListener(element, events, callback, false, passive, capture); utils.toggleListener(element, events, callback, false, passive, capture);
}, },
@@ -1580,6 +1592,60 @@ var utils = {
}, },
// Replace all occurances of a string in a string
replaceAll: function replaceAll() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var find = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
},
// Convert to title case
toTitleCase: function toTitleCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return input.toString().replace(/\w\S*/g, function (text) {
return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();
});
},
// Convert string to pascalCase
toPascalCase: function toPascalCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString();
// Convert kebab case
string = utils.replaceAll(string, '-', ' ');
// Convert snake case
string = utils.replaceAll(string, '_', ' ');
// Convert to title case
string = utils.toTitleCase(string);
// Convert to pascal case
return utils.replaceAll(string, ' ', '');
},
// Convert string to pascalCase
toCamelCase: function toCamelCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString();
// Convert to pascal case
string = utils.toPascalCase(string);
// Convert first character to lowercase
return string.charAt(0).toLowerCase() + string.slice(1);
},
// Deep extend destination object with N more objects // Deep extend destination object with N more objects
extend: function extend() { extend: function extend() {
var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -1898,7 +1964,7 @@ var support = {
}(), }(),
// Touch // Touch
// Remember a device can be moust + touch enabled // NOTE: Remember a device can be mouse + touch enabled so we check on first touch event
touch: 'ontouchstart' in document.documentElement, touch: 'ontouchstart' in document.documentElement,
// Detect transitions support // Detect transitions support
@@ -2064,6 +2130,8 @@ var Fullscreen = function () {
return; return;
} }
console.warn(this.prefix);
// iOS native fullscreen doesn't need the request step // iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) { if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) { if (this.player.playing) {
@@ -2072,7 +2140,7 @@ var Fullscreen = function () {
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullScreen(); this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) { } else if (!utils.is.empty(this.prefix)) {
this.target[this.prefix + 'Request' + this.name](); this.target[this.prefix + 'Request' + this.name]();
} }
@@ -2106,6 +2174,8 @@ var Fullscreen = function () {
}, { }, {
key: 'toggle', key: 'toggle',
value: function toggle() { value: function toggle() {
console.warn('TOGGLE');
if (!this.active) { if (!this.active) {
this.enter(); this.enter();
} else { } else {
@@ -2118,9 +2188,7 @@ var Fullscreen = function () {
// Determine if fullscreen is enabled // Determine if fullscreen is enabled
get: function get$$1() { get: function get$$1() {
var fallback = this.player.config.fullscreen.fallback && !utils.inFrame(); return (Fullscreen.native || this.player.config.fullscreen.fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;
return (Fullscreen.native || fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;
} }
// Get active state // Get active state
@@ -2423,6 +2491,38 @@ var captions = {
} }
}; };
// ==========================================================================
// Plyr internationalization
// ==========================================================================
var i18n = {
get: function get$$1() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ========================================================================== // ==========================================================================
// Plyr UI // Plyr UI
// ========================================================================== // ==========================================================================
@@ -2522,7 +2622,7 @@ var ui = {
// Setup aria attribute for play and iframe title // Setup aria attribute for play and iframe title
setTitle: function setTitle() { setTitle: function setTitle() {
// Find the current text // Find the current text
var label = this.config.i18n.play; var label = i18n.get('play', this.config);
// If there's a media title set, use that for the label // If there's a media title set, use that for the label
if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
@@ -2551,7 +2651,7 @@ var ui = {
// Default to media type // Default to media type
var title = !utils.is.empty(this.config.title) ? this.config.title : 'video'; var title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title)); iframe.setAttribute('title', i18n.get('frameTitle', this.config));
} }
}, },
@@ -2836,7 +2936,7 @@ var controls = {
// Create hidden text label // Create hidden text label
createLabel: function createLabel(type, attr) { createLabel: function createLabel(type, attr) {
var text = this.config.i18n[type]; var text = i18n.get(type, this.config);
var attributes = Object.assign({}, attr); var attributes = Object.assign({}, attr);
switch (type) { switch (type) {
@@ -2884,7 +2984,7 @@ var controls = {
createButton: function createButton(buttonType, attr) { createButton: function createButton(buttonType, attr) {
var button = utils.createElement('button'); var button = utils.createElement('button');
var attributes = Object.assign({}, attr); var attributes = Object.assign({}, attr);
var type = buttonType; var type = utils.toCamelCase(buttonType);
var toggle = false; var toggle = false;
var label = void 0; var label = void 0;
@@ -2905,7 +3005,7 @@ var controls = {
} }
// Large play button // Large play button
switch (type) { switch (buttonType) {
case 'play': case 'play':
toggle = true; toggle = true;
label = 'play'; label = 'play';
@@ -2947,7 +3047,7 @@ var controls = {
default: default:
label = type; label = type;
icon = type; icon = buttonType;
} }
// Setup toggle icon and labels // Setup toggle icon and labels
@@ -2962,7 +3062,7 @@ var controls = {
// Add aria attributes // Add aria attributes
attributes['aria-pressed'] = false; attributes['aria-pressed'] = false;
attributes['aria-label'] = this.config.i18n[label]; attributes['aria-label'] = i18n.get(label, this.config);
} else { } else {
button.appendChild(controls.createIcon.call(this, icon)); button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label)); button.appendChild(controls.createLabel.call(this, label));
@@ -2994,7 +3094,7 @@ var controls = {
var label = utils.createElement('label', { var label = utils.createElement('label', {
for: attributes.id, for: attributes.id,
class: this.config.classNames.hidden class: this.config.classNames.hidden
}, this.config.i18n[type]); }, i18n.get(type, this.config));
// Seek input // Seek input
var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(this.config.selectors.inputs[type]), { var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(this.config.selectors.inputs[type]), {
@@ -3033,11 +3133,11 @@ var controls = {
var suffix = ''; var suffix = '';
switch (type) { switch (type) {
case 'played': case 'played':
suffix = this.config.i18n.played; suffix = i18n.get('played', this.config);
break; break;
case 'buffer': case 'buffer':
suffix = this.config.i18n.buffered; suffix = i18n.get('buffered', this.config);
break; break;
default: default:
@@ -3061,7 +3161,7 @@ var controls = {
container.appendChild(utils.createElement('span', { container.appendChild(utils.createElement('span', {
class: this.config.classNames.hidden class: this.config.classNames.hidden
}, this.config.i18n[type])); }, i18n.get(type, this.config)));
container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00')); container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));
@@ -3107,6 +3207,8 @@ var controls = {
// Update hover tooltip for seeking // Update hover tooltip for seeking
updateSeekTooltip: function updateSeekTooltip(event) { updateSeekTooltip: function updateSeekTooltip(event) {
var _this = this;
// Bail if setting not true // Bail if setting not true
if (!this.config.tooltips.seek || !utils.is.element(this.elements.inputs.seek) || !utils.is.element(this.elements.display.seekTooltip) || this.duration === 0) { if (!this.config.tooltips.seek || !utils.is.element(this.elements.inputs.seek) || !utils.is.element(this.elements.display.seekTooltip) || this.duration === 0) {
return; return;
@@ -3117,6 +3219,16 @@ var controls = {
var clientRect = this.elements.inputs.seek.getBoundingClientRect(); var clientRect = this.elements.inputs.seek.getBoundingClientRect();
var visible = this.config.classNames.tooltip + '--visible'; var visible = this.config.classNames.tooltip + '--visible';
var toggle = function toggle(_toggle) {
utils.toggleClass(_this.elements.display.seekTooltip, visible, _toggle);
};
// Hide on touch
if (this.touch) {
toggle(false);
return;
}
// Determine percentage, if already visible // Determine percentage, if already visible
if (utils.is.event(event)) { if (utils.is.event(event)) {
percent = 100 / clientRect.width * (event.pageX - clientRect.left); percent = 100 / clientRect.width * (event.pageX - clientRect.left);
@@ -3142,7 +3254,7 @@ var controls = {
// Show/hide the tooltip // Show/hide the tooltip
// If the event is a moues in/out and percentage is inside bounds // If the event is a moues in/out and percentage is inside bounds
if (utils.is.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) { if (utils.is.event(event) && ['mouseenter', 'mouseleave'].includes(event.type)) {
utils.toggleClass(this.elements.display.seekTooltip, visible, event.type === 'mouseenter'); toggle(event.type === 'mouseenter');
} }
}, },
@@ -3160,7 +3272,7 @@ var controls = {
// Set the YouTube quality menu // Set the YouTube quality menu
// TODO: Support for HTML5 // TODO: Support for HTML5
setQualityMenu: function setQualityMenu(options) { setQualityMenu: function setQualityMenu(options) {
var _this = this; var _this2 = this;
// Menu required // Menu required
if (!utils.is.element(this.elements.settings.panes.quality)) { if (!utils.is.element(this.elements.settings.panes.quality)) {
@@ -3173,7 +3285,7 @@ var controls = {
// Set options if passed and filter based on config // Set options if passed and filter based on config
if (utils.is.array(options)) { if (utils.is.array(options)) {
this.options.quality = options.filter(function (quality) { this.options.quality = options.filter(function (quality) {
return _this.config.quality.options.includes(quality); return _this2.config.quality.options.includes(quality);
}); });
} else { } else {
this.options.quality = this.config.quality.options; this.options.quality = this.config.quality.options;
@@ -3220,11 +3332,11 @@ var controls = {
return null; return null;
} }
return controls.createBadge.call(_this, label); return controls.createBadge.call(_this2, label);
}; };
this.options.quality.forEach(function (quality) { this.options.quality.forEach(function (quality) {
return controls.createMenuItem.call(_this, quality, list, type, controls.getLabel.call(_this, 'quality', quality), getBadge(quality)); return controls.createMenuItem.call(_this2, quality, list, type, controls.getLabel.call(_this2, 'quality', quality), getBadge(quality));
}); });
controls.updateSetting.call(this, type, list); controls.updateSetting.call(this, type, list);
@@ -3279,7 +3391,7 @@ var controls = {
switch (setting) { switch (setting) {
case 'captions': case 'captions':
value = this.captions.active ? this.captions.language : ''; value = this.captions.active ? this.captions.language : i18n.get('disabled', this.config);
break; break;
default: default:
@@ -3351,7 +3463,7 @@ var controls = {
class: this.config.classNames.control, class: this.config.classNames.control,
'data-plyr-loop-action': option, 'data-plyr-loop-action': option,
}), }),
this.config.i18n[option] i18n.get(option, this.config)
); );
if (['start', 'end'].includes(option)) { if (['start', 'end'].includes(option)) {
const badge = controls.createBadge.call(this, '00:00'); const badge = controls.createBadge.call(this, '00:00');
@@ -3369,11 +3481,7 @@ var controls = {
return null; return null;
} }
if (!support.textTracks || !captions.getTracks.call(this).length) { if (support.textTracks && captions.getTracks.call(this).length && this.captions.active) {
return this.config.i18n.none;
}
if (this.captions.active) {
var currentTrack = captions.getCurrentTrack.call(this); var currentTrack = captions.getCurrentTrack.call(this);
if (utils.is.track(currentTrack)) { if (utils.is.track(currentTrack)) {
@@ -3381,13 +3489,13 @@ var controls = {
} }
} }
return this.config.i18n.disabled; return i18n.get('disabled', this.config);
}, },
// Set a list of available captions languages // Set a list of available captions languages
setCaptionsMenu: function setCaptionsMenu() { setCaptionsMenu: function setCaptionsMenu() {
var _this2 = this; var _this3 = this;
// TODO: Captions or language? Currently it's mixed // TODO: Captions or language? Currently it's mixed
var type = 'captions'; var type = 'captions';
@@ -3413,15 +3521,15 @@ var controls = {
}; };
}); });
// Add the "None" option to turn off captions // Add the "Disabled" option to turn off captions
tracks.unshift({ tracks.unshift({
language: '', language: '',
label: this.config.i18n.none label: i18n.get('disabled', this.config)
}); });
// Generate options // Generate options
tracks.forEach(function (track) { tracks.forEach(function (track) {
controls.createMenuItem.call(_this2, track.language, list, 'language', track.label || track.language, controls.createBadge.call(_this2, track.language.toUpperCase()), track.language.toLowerCase() === _this2.captions.language.toLowerCase()); controls.createMenuItem.call(_this3, track.language, list, 'language', track.label || track.language, controls.createBadge.call(_this3, track.language.toUpperCase()), track.language.toLowerCase() === _this3.captions.language.toLowerCase());
}); });
controls.updateSetting.call(this, type, list); controls.updateSetting.call(this, type, list);
@@ -3430,7 +3538,7 @@ var controls = {
// Set a list of available captions languages // Set a list of available captions languages
setSpeedMenu: function setSpeedMenu() { setSpeedMenu: function setSpeedMenu() {
var _this3 = this; var _this4 = this;
// Menu required // Menu required
if (!utils.is.element(this.elements.settings.panes.speed)) { if (!utils.is.element(this.elements.settings.panes.speed)) {
@@ -3446,7 +3554,7 @@ var controls = {
// Set options if passed and filter based on config // Set options if passed and filter based on config
this.options.speed = this.options.speed.filter(function (speed) { this.options.speed = this.options.speed.filter(function (speed) {
return _this3.config.speed.options.includes(speed); return _this4.config.speed.options.includes(speed);
}); });
// Toggle the pane and tab // Toggle the pane and tab
@@ -3470,7 +3578,7 @@ var controls = {
// Create items // Create items
this.options.speed.forEach(function (speed) { this.options.speed.forEach(function (speed) {
return controls.createMenuItem.call(_this3, speed, list, type, controls.getLabel.call(_this3, 'speed', speed)); return controls.createMenuItem.call(_this4, speed, list, type, controls.getLabel.call(_this4, 'speed', speed));
}); });
controls.updateSetting.call(this, type, list); controls.updateSetting.call(this, type, list);
@@ -3633,7 +3741,7 @@ var controls = {
// 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) {
var _this4 = this; var _this5 = this;
// Do nothing if we want no controls // Do nothing if we want no controls
if (utils.is.empty(this.config.controls)) { if (utils.is.empty(this.config.controls)) {
@@ -3660,7 +3768,7 @@ var controls = {
// Fast forward button // Fast forward button
if (this.config.controls.includes('fast-forward')) { if (this.config.controls.includes('fast-forward')) {
container.appendChild(controls.createButton.call(this, 'fastForward')); container.appendChild(controls.createButton.call(this, 'fast-forward'));
} }
// Progress // Progress
@@ -3782,17 +3890,17 @@ var controls = {
hidden: '' hidden: ''
}); });
var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(_this4.config.selectors.buttons.settings), { var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(_this5.config.selectors.buttons.settings), {
type: 'button', type: 'button',
class: _this4.config.classNames.control + ' ' + _this4.config.classNames.control + '--forward', class: _this5.config.classNames.control + ' ' + _this5.config.classNames.control + '--forward',
id: 'plyr-settings-' + data.id + '-' + type + '-tab', id: 'plyr-settings-' + data.id + '-' + type + '-tab',
'aria-haspopup': true, 'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-' + type, 'aria-controls': 'plyr-settings-' + data.id + '-' + type,
'aria-expanded': false 'aria-expanded': false
}), _this4.config.i18n[type]); }), i18n.get(type, _this5.config));
var value = utils.createElement('span', { var value = utils.createElement('span', {
class: _this4.config.classNames.menu.value class: _this5.config.classNames.menu.value
}); });
// Speed contains HTML entities // Speed contains HTML entities
@@ -3802,7 +3910,7 @@ var controls = {
tab.appendChild(button); tab.appendChild(button);
tabs.appendChild(tab); tabs.appendChild(tab);
_this4.elements.settings.tabs[type] = tab; _this5.elements.settings.tabs[type] = tab;
}); });
home.appendChild(tabs); home.appendChild(tabs);
@@ -3821,11 +3929,11 @@ var controls = {
var back = utils.createElement('button', { var back = utils.createElement('button', {
type: 'button', type: 'button',
class: _this4.config.classNames.control + ' ' + _this4.config.classNames.control + '--back', class: _this5.config.classNames.control + ' ' + _this5.config.classNames.control + '--back',
'aria-haspopup': true, 'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-home', 'aria-controls': 'plyr-settings-' + data.id + '-home',
'aria-expanded': false 'aria-expanded': false
}, _this4.config.i18n[type]); }, i18n.get(type, _this5.config));
pane.appendChild(back); pane.appendChild(back);
@@ -3834,7 +3942,7 @@ var controls = {
pane.appendChild(options); pane.appendChild(options);
inner.appendChild(pane); inner.appendChild(pane);
_this4.elements.settings.panes[type] = pane; _this5.elements.settings.panes[type] = pane;
}); });
form.appendChild(inner); form.appendChild(inner);
@@ -3877,7 +3985,7 @@ var controls = {
// Insert controls // Insert controls
inject: function inject() { inject: function inject() {
var _this5 = this; var _this6 = this;
// Sprite // Sprite
if (this.config.loadSprite) { if (this.config.loadSprite) {
@@ -3955,8 +4063,8 @@ var controls = {
var labels = utils.getElements.call(this, [this.config.selectors.controls.wrapper, ' ', this.config.selectors.labels, ' .', this.config.classNames.hidden].join('')); var labels = utils.getElements.call(this, [this.config.selectors.controls.wrapper, ' ', this.config.selectors.labels, ' .', this.config.classNames.hidden].join(''));
Array.from(labels).forEach(function (label) { Array.from(labels).forEach(function (label) {
utils.toggleClass(label, _this5.config.classNames.hidden, false); utils.toggleClass(label, _this6.config.classNames.hidden, false);
utils.toggleClass(label, _this5.config.classNames.tooltip, true); utils.toggleClass(label, _this6.config.classNames.tooltip, true);
label.setAttribute('role', 'tooltip'); label.setAttribute('role', 'tooltip');
}); });
} }
@@ -3979,6 +4087,7 @@ var Listeners = function () {
this.handleKey = this.handleKey.bind(this); this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this); this.toggleMenu = this.toggleMenu.bind(this);
this.firstTouch = this.firstTouch.bind(this);
} }
// Handle key presses // Handle key presses
@@ -4074,7 +4183,7 @@ var Listeners = function () {
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); this.player.fastForward();
break; break;
case 37: case 37:
@@ -4134,6 +4243,20 @@ var Listeners = function () {
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(this.player, event);
} }
// Device is touch enabled
}, {
key: 'firstTouch',
value: function firstTouch() {
this.player.touch = true;
// Add touch class
utils.toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
// Clean up
utils.off(document.body, 'touchstart', this.firstTouch);
}
// Global window & document listeners // Global window & document listeners
}, { }, {
@@ -4148,6 +4271,9 @@ var Listeners = function () {
// Click anywhere closes menu // Click anywhere closes menu
utils.toggleListener(document.body, 'click', this.toggleMenu, toggle); utils.toggleListener(document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
utils.on(document.body, 'touchstart', this.firstTouch);
} }
// Container listeners // Container listeners
@@ -4262,7 +4388,7 @@ var Listeners = function () {
// On click play, pause ore restart // On click play, pause ore restart
utils.on(wrapper, 'click', function () { utils.on(wrapper, 'click', function () {
// Touch devices will just show controls (if we're hiding controls) // Touch devices will just show controls (if we're hiding controls)
if (_this3.player.config.hideControls && support.touch && !_this3.player.paused) { if (_this3.player.config.hideControls && _this3.player.touch && !_this3.player.paused) {
return; return;
} }
@@ -4350,12 +4476,13 @@ var Listeners = function () {
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
var inputEvent = browser$1.isIE ? 'change' : 'input'; var inputEvent = browser$1.isIE ? 'change' : 'input';
// Trigger custom and default handlers // Run default and custom handlers
var proxy = function proxy(event, handlerKey, defaultHandler) { var proxy = function proxy(event, defaultHandler, customHandlerKey) {
var customHandler = _this4.player.config.listeners[handlerKey]; var customHandler = _this4.player.config.listeners[customHandlerKey];
var hasCustomHandler = utils.is.function(customHandler);
// Execute custom handler // Execute custom handler
if (utils.is.function(customHandler)) { if (hasCustomHandler) {
customHandler.call(_this4.player, event); customHandler.call(_this4.player, event);
} }
@@ -4365,107 +4492,87 @@ var Listeners = function () {
} }
}; };
// Trigger custom and default handlers
var on = function on(element, type, defaultHandler, customHandlerKey) {
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var customHandler = _this4.player.config.listeners[customHandlerKey];
var hasCustomHandler = utils.is.function(customHandler);
utils.on(element, type, function (event) {
return proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler);
};
// Play/pause toggle // Play/pause toggle
utils.on(this.player.elements.buttons.play, 'click', function (event) { on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play');
return proxy(event, 'play', function () {
_this4.player.togglePlay();
});
});
// Pause // Pause
utils.on(this.player.elements.buttons.restart, 'click', function (event) { on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
return proxy(event, 'restart', function () {
_this4.player.restart();
});
});
// Rewind // Rewind
utils.on(this.player.elements.buttons.rewind, 'click', function (event) { on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
return proxy(event, 'rewind', function () {
_this4.player.rewind();
});
});
// Rewind // Rewind
utils.on(this.player.elements.buttons.forward, 'click', function (event) { on(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward');
return proxy(event, 'forward', function () {
_this4.player.forward();
});
});
// Mute toggle // Mute toggle
utils.on(this.player.elements.buttons.mute, 'click', function (event) { on(this.player.elements.buttons.mute, 'click', function () {
return proxy(event, 'mute', function () { _this4.player.muted = !_this4.player.muted;
_this4.player.muted = !_this4.player.muted; }, 'mute');
});
});
// Captions toggle // Captions toggle
utils.on(this.player.elements.buttons.captions, 'click', function (event) { on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions);
return proxy(event, 'captions', function () {
_this4.player.toggleCaptions();
});
});
// Fullscreen toggle // Fullscreen toggle
utils.on(this.player.elements.buttons.fullscreen, 'click', function (event) { on(this.player.elements.buttons.fullscreen, 'click', function () {
return proxy(event, 'fullscreen', function () { _this4.player.fullscreen.toggle();
_this4.player.fullscreen.toggle(); }, 'fullscreen');
});
});
// Picture-in-Picture // Picture-in-Picture
utils.on(this.player.elements.buttons.pip, 'click', function (event) { on(this.player.elements.buttons.pip, 'click', function () {
return proxy(event, 'pip', function () { _this4.player.pip = 'toggle';
_this4.player.pip = 'toggle'; }, 'pip');
});
});
// Airplay // Airplay
utils.on(this.player.elements.buttons.airplay, 'click', function (event) { on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
return proxy(event, 'airplay', function () {
_this4.player.airplay();
});
});
// Settings menu // Settings menu
utils.on(this.player.elements.buttons.settings, 'click', function (event) { on(this.player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this4.player, event); controls.toggleMenu.call(_this4.player, event);
}); });
// Settings menu // Settings menu
utils.on(this.player.elements.settings.form, 'click', function (event) { on(this.player.elements.settings.form, 'click', function (event) {
event.stopPropagation(); event.stopPropagation();
// Settings menu items - use event delegation as items are added/removed // Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, _this4.player.config.selectors.inputs.language)) { if (utils.matches(event.target, _this4.player.config.selectors.inputs.language)) {
proxy(event, 'language', function () { proxy(event, function () {
_this4.player.language = event.target.value; _this4.player.language = event.target.value;
}); }, 'language');
} else if (utils.matches(event.target, _this4.player.config.selectors.inputs.quality)) { } else if (utils.matches(event.target, _this4.player.config.selectors.inputs.quality)) {
proxy(event, 'quality', function () { proxy(event, function () {
_this4.player.quality = event.target.value; _this4.player.quality = event.target.value;
}); }, 'quality');
} else if (utils.matches(event.target, _this4.player.config.selectors.inputs.speed)) { } else if (utils.matches(event.target, _this4.player.config.selectors.inputs.speed)) {
proxy(event, 'speed', function () { proxy(event, function () {
_this4.player.speed = parseFloat(event.target.value); _this4.player.speed = parseFloat(event.target.value);
}); }, 'speed');
} else { } else {
controls.showTab.call(_this4.player, event); controls.showTab.call(_this4.player, event);
} }
}); });
// Seek // Seek
utils.on(this.player.elements.inputs.seek, inputEvent, function (event) { on(this.player.elements.inputs.seek, inputEvent, function (event) {
return proxy(event, 'seek', function () { _this4.player.currentTime = event.target.value / event.target.max * _this4.player.duration;
_this4.player.currentTime = event.target.value / event.target.max * _this4.player.duration; }, 'seek');
});
});
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) { if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) {
utils.on(this.player.elements.display.currentTime, 'click', function () { on(this.player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start // Do nothing if we're at the start
if (_this4.player.currentTime === 0) { if (_this4.player.currentTime === 0) {
return; return;
@@ -4477,79 +4584,75 @@ var Listeners = function () {
} }
// Volume // Volume
utils.on(this.player.elements.inputs.volume, inputEvent, function (event) { on(this.player.elements.inputs.volume, inputEvent, function (event) {
return proxy(event, 'volume', function () { _this4.player.volume = event.target.value;
_this4.player.volume = event.target.value; }, 'volume');
});
});
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser$1.isWebkit) { if (browser$1.isWebkit) {
utils.on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', function (event) { on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', function (event) {
controls.updateRangeFill.call(_this4.player, event.target); controls.updateRangeFill.call(_this4.player, event.target);
}); });
} }
// Seek tooltip // Seek tooltip
utils.on(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) { on(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this4.player, event); return controls.updateSeekTooltip.call(_this4.player, event);
}); });
// Toggle controls visibility based on mouse movement // Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) { if (this.player.config.hideControls) {
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mouseenter mouseleave', function (event) { on(this.player.elements.controls, 'mouseenter mouseleave', function (event) {
_this4.player.elements.controls.hover = event.type === 'mouseenter'; _this4.player.elements.controls.hover = !_this4.player.touch && event.type === 'mouseenter';
}); });
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) { on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
_this4.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type); _this4.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
}); });
// Focus in/out on controls // Focus in/out on controls
utils.on(this.player.elements.controls, 'focusin focusout', function (event) { on(this.player.elements.controls, 'focusin focusout', function (event) {
_this4.player.toggleControls(event); _this4.player.toggleControls(event);
}); });
} }
// Mouse wheel for volume // Mouse wheel for volume
utils.on(this.player.elements.inputs.volume, 'wheel', function (event) { on(this.player.elements.inputs.volume, 'wheel', function (event) {
return proxy(event, 'volume', function () { // Detect "natural" scroll - suppored on OS X Safari only
// Detect "natural" scroll - suppored on OS X Safari only // Other browsers on OS X will be inverted until support improves
// Other browsers on OS X will be inverted until support improves var inverted = event.webkitDirectionInvertedFromDevice;
var inverted = event.webkitDirectionInvertedFromDevice; var step = 1 / 50;
var step = 1 / 50; var direction = 0;
var direction = 0;
// Scroll down (or up on natural) to decrease // Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) { if (event.deltaY < 0 || event.deltaX > 0) {
if (inverted) { if (inverted) {
_this4.player.decreaseVolume(step); _this4.player.decreaseVolume(step);
direction = -1; direction = -1;
} else { } else {
_this4.player.increaseVolume(step); _this4.player.increaseVolume(step);
direction = 1; direction = 1;
}
} }
}
// Scroll up (or down on natural) to increase // Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) { if (event.deltaY > 0 || event.deltaX < 0) {
if (inverted) { if (inverted) {
_this4.player.increaseVolume(step); _this4.player.increaseVolume(step);
direction = 1; direction = 1;
} else { } else {
_this4.player.decreaseVolume(step); _this4.player.decreaseVolume(step);
direction = -1; direction = -1;
}
} }
}
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
if (direction === 1 && _this4.player.media.volume < 1 || direction === -1 && _this4.player.media.volume > 0) { if (direction === 1 && _this4.player.media.volume < 1 || direction === -1 && _this4.player.media.volume > 0) {
event.preventDefault(); event.preventDefault();
} }
}); }, 'volume', false);
}, false);
} }
// Reset on destroy // Reset on destroy
@@ -4842,7 +4945,7 @@ var Ads = function () {
var update = function update() { var update = function update() {
var time = utils.formatTime(Math.max(_this5.manager.getRemainingTime(), 0)); var time = utils.formatTime(Math.max(_this5.manager.getRemainingTime(), 0));
var label = _this5.player.config.i18n.advertisement + ' - ' + time; var label = i18n.get('advertisement', _this5.player.config) + ' - ' + time;
_this5.elements.container.setAttribute('data-badge-text', label); _this5.elements.container.setAttribute('data-badge-text', label);
}; };
@@ -6109,7 +6212,7 @@ var media = {
utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser$3.isIos); utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser$3.isIos);
// Add touch class // Add touch class
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch); utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
} }
// Inject the player wrapper // Inject the player wrapper
@@ -6311,7 +6414,7 @@ var source = {
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.0.2 // plyr.js v3.0.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
@@ -6335,6 +6438,9 @@ var Plyr = function () {
this.loading = false; this.loading = false;
this.failed = false; this.failed = false;
// Touch device
this.touch = support.touch;
// Set the media element // Set the media element
this.media = target; this.media = target;
@@ -6815,16 +6921,22 @@ var Plyr = function () {
// Is the enter fullscreen event // Is the enter fullscreen event
isEnterFullscreen = toggle.type === 'enterfullscreen'; isEnterFullscreen = toggle.type === 'enterfullscreen';
// Events that show the controls
var showEvents = ['touchstart', 'touchmove', 'mouseenter', 'mousemove', 'focusin'];
// Events that delay hiding
var delayEvents = ['touchmove', 'touchend', 'mousemove'];
// Whether to show controls // Whether to show controls
show = ['mouseenter', 'mousemove', 'touchstart', 'touchmove', 'focusin'].includes(toggle.type); show = showEvents.includes(toggle.type);
// Delay hiding on move events // Delay hiding on move events
if (['mousemove', 'touchmove', 'touchend'].includes(toggle.type)) { if (delayEvents.includes(toggle.type)) {
delay = 2000; delay = 2000;
} }
// Delay a little more for keyboard users // Delay a little more for keyboard users
if (toggle.type === 'focusin') { if (!this.touch && toggle.type === 'focusin') {
delay = 3000; delay = 3000;
utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true); utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true);
} }
@@ -6852,7 +6964,7 @@ var Plyr = function () {
} }
// Delay for hiding on touch // Delay for hiding on touch
if (support.touch) { if (this.touch) {
delay = 3000; delay = 3000;
} }
} }
@@ -6861,6 +6973,8 @@ var Plyr = function () {
// then set the timer to hide the controls // then set the timer to hide the controls
if (!show || this.playing) { if (!show || this.playing) {
this.timers.controls = setTimeout(function () { this.timers.controls = setTimeout(function () {
console.warn(_this3.elements.controls.pressed, _this3.elements.controls.hover, delay);
// If the mouse is over the controls (and not entering fullscreen), bail // If the mouse is over the controls (and not entering fullscreen), bail
if ((_this3.elements.controls.pressed || _this3.elements.controls.hover) && !isEnterFullscreen) { if ((_this3.elements.controls.pressed || _this3.elements.controls.hover) && !isEnterFullscreen) {
return; return;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+302 -188
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "plyr", "name": "plyr",
"version": "3.0.3", "version": "3.0.4",
"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",
"main": "./dist/plyr.js", "main": "./dist/plyr.js",
+8 -4
View File
@@ -10,7 +10,7 @@ A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo medi
* **Accessible** - full support for VTT captions and screen readers * **Accessible** - full support for VTT captions and screen readers
* **[Customisable](#html)** - make the player look how you want with the markup you want * **[Customisable](#html)** - make the player look how you want with the markup you want
* **Semantic** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no * **Good HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
`<span>` or `<a href="#">` button hacks `<span>` or `<a href="#">` button hacks
* **Responsive** - works with any screen size * **Responsive** - works with any screen size
* **HTML Video & Audio** - support for both formats * **HTML Video & Audio** - support for both formats
@@ -21,6 +21,10 @@ A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo medi
* **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats * **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
* **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes * **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
* **[Shortcuts](#shortcuts)** - supports keyboard shortcuts * **[Shortcuts](#shortcuts)** - supports keyboard shortcuts
* **Picture-in-Picture** - supports Safari's picture-in-picture mode
* **Playsinline** - supports the `playsinline` attribute
* **Speed controls** - adjust speed on the fly
* **Multiple captions** - support for multiple caption tracks
* **i18n support** - support for internationalization of controls * **i18n support** - support for internationalization of controls
* **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required * **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required
* **SASS** - to include in your build processes * **SASS** - to include in your build processes
@@ -124,7 +128,7 @@ See [initialising](#initialising) for more information on advanced setups.
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the following: If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the following:
```html ```html
<script src="https://cdn.plyr.io/3.0.3/plyr.js"></script> <script src="https://cdn.plyr.io/3.0.4/plyr.js"></script>
``` ```
_Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility _Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility
@@ -140,13 +144,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.0.3/plyr.css"> <link rel="stylesheet" href="https://cdn.plyr.io/3.0.4/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.0.3/plyr.svg`. reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.4/plyr.svg`.
## Ads ## Ads
+30 -23
View File
@@ -5,6 +5,7 @@
import support from './support'; import support from './support';
import utils from './utils'; import utils from './utils';
import ui from './ui'; import ui from './ui';
import i18n from './i18n';
import captions from './captions'; import captions from './captions';
// Sniff out the browser // Sniff out the browser
@@ -74,7 +75,7 @@ const controls = {
// Create hidden text label // Create hidden text label
createLabel(type, attr) { createLabel(type, attr) {
let text = this.config.i18n[type]; let text = i18n.get(type, this.config);
const attributes = Object.assign({}, attr); const attributes = Object.assign({}, attr);
switch (type) { switch (type) {
@@ -126,7 +127,7 @@ const controls = {
createButton(buttonType, attr) { createButton(buttonType, attr) {
const button = utils.createElement('button'); const button = utils.createElement('button');
const attributes = Object.assign({}, attr); const attributes = Object.assign({}, attr);
let type = buttonType; let type = utils.toCamelCase(buttonType);
let toggle = false; let toggle = false;
let label; let label;
@@ -147,7 +148,7 @@ const controls = {
} }
// Large play button // Large play button
switch (type) { switch (buttonType) {
case 'play': case 'play':
toggle = true; toggle = true;
label = 'play'; label = 'play';
@@ -189,7 +190,7 @@ const controls = {
default: default:
label = type; label = type;
icon = type; icon = buttonType;
} }
// Setup toggle icon and labels // Setup toggle icon and labels
@@ -204,7 +205,7 @@ const controls = {
// Add aria attributes // Add aria attributes
attributes['aria-pressed'] = false; attributes['aria-pressed'] = false;
attributes['aria-label'] = this.config.i18n[label]; attributes['aria-label'] = i18n.get(label, this.config);
} else { } else {
button.appendChild(controls.createIcon.call(this, icon)); button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label)); button.appendChild(controls.createLabel.call(this, label));
@@ -238,7 +239,7 @@ const controls = {
for: attributes.id, for: attributes.id,
class: this.config.classNames.hidden, class: this.config.classNames.hidden,
}, },
this.config.i18n[type], i18n.get(type, this.config),
); );
// Seek input // Seek input
@@ -291,11 +292,11 @@ const controls = {
let suffix = ''; let suffix = '';
switch (type) { switch (type) {
case 'played': case 'played':
suffix = this.config.i18n.played; suffix = i18n.get('played', this.config);
break; break;
case 'buffer': case 'buffer':
suffix = this.config.i18n.buffered; suffix = i18n.get('buffered', this.config);
break; break;
default: default:
@@ -322,7 +323,7 @@ const controls = {
{ {
class: this.config.classNames.hidden, class: this.config.classNames.hidden,
}, },
this.config.i18n[type], i18n.get(type, this.config),
), ),
); );
@@ -383,6 +384,16 @@ const controls = {
const clientRect = this.elements.inputs.seek.getBoundingClientRect(); const clientRect = this.elements.inputs.seek.getBoundingClientRect();
const visible = `${this.config.classNames.tooltip}--visible`; const visible = `${this.config.classNames.tooltip}--visible`;
const toggle = toggle => {
utils.toggleClass(this.elements.display.seekTooltip, visible, toggle);
};
// Hide on touch
if (this.touch) {
toggle(false);
return;
}
// Determine percentage, if already visible // Determine percentage, if already visible
if (utils.is.event(event)) { if (utils.is.event(event)) {
percent = 100 / clientRect.width * (event.pageX - clientRect.left); percent = 100 / clientRect.width * (event.pageX - clientRect.left);
@@ -411,7 +422,7 @@ const controls = {
'mouseenter', 'mouseenter',
'mouseleave', 'mouseleave',
].includes(event.type)) { ].includes(event.type)) {
utils.toggleClass(this.elements.display.seekTooltip, visible, event.type === 'mouseenter'); toggle(event.type === 'mouseenter');
} }
}, },
@@ -540,7 +551,7 @@ const controls = {
switch (setting) { switch (setting) {
case 'captions': case 'captions':
value = this.captions.active ? this.captions.language : ''; value = this.captions.active ? this.captions.language : i18n.get('disabled', this.config);
break; break;
default: default:
@@ -617,7 +628,7 @@ const controls = {
class: this.config.classNames.control, class: this.config.classNames.control,
'data-plyr-loop-action': option, 'data-plyr-loop-action': option,
}), }),
this.config.i18n[option] i18n.get(option, this.config)
); );
if (['start', 'end'].includes(option)) { if (['start', 'end'].includes(option)) {
@@ -637,11 +648,7 @@ const controls = {
return null; return null;
} }
if (!support.textTracks || !captions.getTracks.call(this).length) { if (support.textTracks && captions.getTracks.call(this).length && this.captions.active) {
return this.config.i18n.none;
}
if (this.captions.active) {
const currentTrack = captions.getCurrentTrack.call(this); const currentTrack = captions.getCurrentTrack.call(this);
if (utils.is.track(currentTrack)) { if (utils.is.track(currentTrack)) {
@@ -649,7 +656,7 @@ const controls = {
} }
} }
return this.config.i18n.disabled; return i18n.get('disabled', this.config);
}, },
// Set a list of available captions languages // Set a list of available captions languages
@@ -676,10 +683,10 @@ const controls = {
label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase(), label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase(),
})); }));
// Add the "None" option to turn off captions // Add the "Disabled" option to turn off captions
tracks.unshift({ tracks.unshift({
language: '', language: '',
label: this.config.i18n.none, label: i18n.get('disabled', this.config),
}); });
// Generate options // Generate options
@@ -927,7 +934,7 @@ const controls = {
// Fast forward button // Fast forward button
if (this.config.controls.includes('fast-forward')) { if (this.config.controls.includes('fast-forward')) {
container.appendChild(controls.createButton.call(this, 'fastForward')); container.appendChild(controls.createButton.call(this, 'fast-forward'));
} }
// Progress // Progress
@@ -1069,7 +1076,7 @@ const controls = {
'aria-controls': `plyr-settings-${data.id}-${type}`, 'aria-controls': `plyr-settings-${data.id}-${type}`,
'aria-expanded': false, 'aria-expanded': false,
}), }),
this.config.i18n[type], i18n.get(type, this.config),
); );
const value = utils.createElement('span', { const value = utils.createElement('span', {
@@ -1109,7 +1116,7 @@ const controls = {
'aria-controls': `plyr-settings-${data.id}-home`, 'aria-controls': `plyr-settings-${data.id}-home`,
'aria-expanded': false, 'aria-expanded': false,
}, },
this.config.i18n[type], i18n.get(type, this.config),
); );
pane.appendChild(back); pane.appendChild(back);
+6 -4
View File
@@ -56,7 +56,7 @@ const defaults = {
// Sprite (for icons) // Sprite (for icons)
loadSprite: true, loadSprite: true,
iconPrefix: 'plyr', iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.0.3/plyr.svg', iconUrl: 'https://cdn.plyr.io/3.0.4/plyr.svg',
// Blank video (used to prevent errors on source change) // Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4', blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -132,7 +132,10 @@ const defaults = {
// Default controls // Default controls
controls: [ controls: [
'play-large', 'play-large',
// 'restart',
// 'rewind',
'play', 'play',
// 'fast-forward',
'progress', 'progress',
'current-time', 'current-time',
'mute', 'mute',
@@ -155,7 +158,7 @@ const defaults = {
rewind: 'Rewind {seektime} secs', rewind: 'Rewind {seektime} secs',
play: 'Play', play: 'Play',
pause: 'Pause', pause: 'Pause',
forward: 'Forward {seektime} secs', fastForward: 'Forward {seektime} secs',
seek: 'Seek', seek: 'Seek',
played: 'Played', played: 'Played',
buffered: 'Buffered', buffered: 'Buffered',
@@ -178,7 +181,6 @@ const defaults = {
end: 'End', end: 'End',
all: 'All', all: 'All',
reset: 'Reset', reset: 'Reset',
none: 'None',
disabled: 'Disabled', disabled: 'Disabled',
advertisement: 'Ad', advertisement: 'Ad',
}, },
@@ -203,7 +205,7 @@ const defaults = {
pause: null, pause: null,
restart: null, restart: null,
rewind: null, rewind: null,
forward: null, fastForward: null,
mute: null, mute: null,
volume: null, volume: null,
captions: null, captions: null,
+11 -4
View File
@@ -117,9 +117,12 @@ class Fullscreen {
// Determine if fullscreen is enabled // Determine if fullscreen is enabled
get enabled() { get enabled() {
const fallback = this.player.config.fullscreen.fallback && !utils.inFrame(); return (
(Fullscreen.native || this.player.config.fullscreen.fallback) &&
return (Fullscreen.native || fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo; this.player.config.fullscreen.enabled &&
this.player.supported.ui &&
this.player.isVideo
);
} }
// Get active state // Get active state
@@ -161,6 +164,8 @@ class Fullscreen {
return; return;
} }
console.warn(this.prefix);
// iOS native fullscreen doesn't need the request step // iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) { if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) { if (this.player.playing) {
@@ -169,7 +174,7 @@ class Fullscreen {
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullScreen(); this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) { } else if (!utils.is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.name}`](); this.target[`${this.prefix}Request${this.name}`]();
} }
@@ -197,6 +202,8 @@ class Fullscreen {
// Toggle state // Toggle state
toggle() { toggle() {
console.warn('TOGGLE');
if (!this.active) { if (!this.active) {
this.enter(); this.enter();
} else { } else {
+31
View File
@@ -0,0 +1,31 @@
// ==========================================================================
// Plyr internationalization
// ==========================================================================
import utils from './utils';
const i18n = {
get(key = '', config = {}) {
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
let string = config.i18n[key];
const replace = {
'{seektime}': config.seekTime,
'{title}': config.title,
};
Object.entries(replace).forEach(([
key,
value,
]) => {
string = utils.replaceAll(string, key, value);
});
return string;
},
};
export default i18n;
+127 -100
View File
@@ -17,6 +17,7 @@ class Listeners {
this.handleKey = this.handleKey.bind(this); this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this); this.toggleMenu = this.toggleMenu.bind(this);
this.firstTouch = this.firstTouch.bind(this);
} }
// Handle key presses // Handle key presses
@@ -128,7 +129,7 @@ class Listeners {
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); this.player.fastForward();
break; break;
case 37: case 37:
@@ -187,6 +188,17 @@ class Listeners {
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(this.player, event);
} }
// Device is touch enabled
firstTouch() {
this.player.touch = true;
// Add touch class
utils.toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
// Clean up
utils.off(document.body, 'touchstart', this.firstTouch);
}
// Global window & document listeners // Global window & document listeners
global(toggle = true) { global(toggle = true) {
// Keyboard shortcuts // Keyboard shortcuts
@@ -196,6 +208,9 @@ class Listeners {
// Click anywhere closes menu // Click anywhere closes menu
utils.toggleListener(document.body, 'click', this.toggleMenu, toggle); utils.toggleListener(document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
utils.on(document.body, 'touchstart', this.firstTouch);
} }
// Container listeners // Container listeners
@@ -288,7 +303,7 @@ class Listeners {
// On click play, pause ore restart // On click play, pause ore restart
utils.on(wrapper, 'click', () => { utils.on(wrapper, 'click', () => {
// Touch devices will just show controls (if we're hiding controls) // Touch devices will just show controls (if we're hiding controls)
if (this.player.config.hideControls && support.touch && !this.player.paused) { if (this.player.config.hideControls && this.player.touch && !this.player.paused) {
return; return;
} }
@@ -379,12 +394,13 @@ class Listeners {
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
const inputEvent = browser.isIE ? 'change' : 'input'; const inputEvent = browser.isIE ? 'change' : 'input';
// Trigger custom and default handlers // Run default and custom handlers
const proxy = (event, handlerKey, defaultHandler) => { const proxy = (event, defaultHandler, customHandlerKey) => {
const customHandler = this.player.config.listeners[handlerKey]; const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = utils.is.function(customHandler);
// Execute custom handler // Execute custom handler
if (utils.is.function(customHandler)) { if (hasCustomHandler) {
customHandler.call(this.player, event); customHandler.call(this.player, event);
} }
@@ -394,107 +410,115 @@ class Listeners {
} }
}; };
// Trigger custom and default handlers
const on = (element, type, defaultHandler, customHandlerKey, passive = true) => {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = utils.is.function(customHandler);
utils.on(element, type, event => proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler);
};
// Play/pause toggle // Play/pause toggle
utils.on(this.player.elements.buttons.play, 'click', event => on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play');
proxy(event, 'play', () => {
this.player.togglePlay();
}),
);
// Pause // Pause
utils.on(this.player.elements.buttons.restart, 'click', event => on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
proxy(event, 'restart', () => {
this.player.restart();
}),
);
// Rewind // Rewind
utils.on(this.player.elements.buttons.rewind, 'click', event => on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
proxy(event, 'rewind', () => {
this.player.rewind();
}),
);
// Rewind // Rewind
utils.on(this.player.elements.buttons.forward, 'click', event => on(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward');
proxy(event, 'forward', () => {
this.player.forward();
}),
);
// Mute toggle // Mute toggle
utils.on(this.player.elements.buttons.mute, 'click', event => on(
proxy(event, 'mute', () => { this.player.elements.buttons.mute,
'click',
() => {
this.player.muted = !this.player.muted; this.player.muted = !this.player.muted;
}), },
'mute',
); );
// Captions toggle // Captions toggle
utils.on(this.player.elements.buttons.captions, 'click', event => on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions);
proxy(event, 'captions', () => {
this.player.toggleCaptions();
}),
);
// Fullscreen toggle // Fullscreen toggle
utils.on(this.player.elements.buttons.fullscreen, 'click', event => on(
proxy(event, 'fullscreen', () => { this.player.elements.buttons.fullscreen,
'click',
() => {
this.player.fullscreen.toggle(); this.player.fullscreen.toggle();
}), },
'fullscreen',
); );
// Picture-in-Picture // Picture-in-Picture
utils.on(this.player.elements.buttons.pip, 'click', event => on(
proxy(event, 'pip', () => { this.player.elements.buttons.pip,
'click',
() => {
this.player.pip = 'toggle'; this.player.pip = 'toggle';
}), },
'pip',
); );
// Airplay // Airplay
utils.on(this.player.elements.buttons.airplay, 'click', event => on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
proxy(event, 'airplay', () => {
this.player.airplay();
}),
);
// Settings menu // Settings menu
utils.on(this.player.elements.buttons.settings, 'click', event => { on(this.player.elements.buttons.settings, 'click', event => {
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(this.player, event);
}); });
// Settings menu // Settings menu
utils.on(this.player.elements.settings.form, 'click', event => { on(this.player.elements.settings.form, 'click', event => {
event.stopPropagation(); event.stopPropagation();
// Settings menu items - use event delegation as items are added/removed // Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, this.player.config.selectors.inputs.language)) { if (utils.matches(event.target, this.player.config.selectors.inputs.language)) {
proxy(event, 'language', () => { proxy(
this.player.language = event.target.value; event,
}); () => {
this.player.language = event.target.value;
},
'language',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) { } else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) {
proxy(event, 'quality', () => { proxy(
this.player.quality = event.target.value; event,
}); () => {
this.player.quality = event.target.value;
},
'quality',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) { } else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) {
proxy(event, 'speed', () => { proxy(
this.player.speed = parseFloat(event.target.value); event,
}); () => {
this.player.speed = parseFloat(event.target.value);
},
'speed',
);
} else { } else {
controls.showTab.call(this.player, event); controls.showTab.call(this.player, event);
} }
}); });
// Seek // Seek
utils.on(this.player.elements.inputs.seek, inputEvent, event => on(
proxy(event, 'seek', () => { this.player.elements.inputs.seek,
inputEvent,
event => {
this.player.currentTime = event.target.value / event.target.max * this.player.duration; this.player.currentTime = event.target.value / event.target.max * this.player.duration;
}), },
'seek',
); );
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) { if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) {
utils.on(this.player.elements.display.currentTime, 'click', () => { on(this.player.elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start // Do nothing if we're at the start
if (this.player.currentTime === 0) { if (this.player.currentTime === 0) {
return; return;
@@ -506,31 +530,34 @@ class Listeners {
} }
// Volume // Volume
utils.on(this.player.elements.inputs.volume, inputEvent, event => on(
proxy(event, 'volume', () => { this.player.elements.inputs.volume,
inputEvent,
event => {
this.player.volume = event.target.value; this.player.volume = event.target.value;
}), },
'volume',
); );
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) { if (browser.isWebkit) {
utils.on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => { on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => {
controls.updateRangeFill.call(this.player, event.target); controls.updateRangeFill.call(this.player, event.target);
}); });
} }
// Seek tooltip // Seek tooltip
utils.on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event)); on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event));
// Toggle controls visibility based on mouse movement // Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) { if (this.player.config.hideControls) {
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mouseenter mouseleave', event => { on(this.player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls.hover = event.type === 'mouseenter'; this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
}); });
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => { on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
this.player.elements.controls.pressed = [ this.player.elements.controls.pressed = [
'mousedown', 'mousedown',
'touchstart', 'touchstart',
@@ -538,50 +565,50 @@ class Listeners {
}); });
// Focus in/out on controls // Focus in/out on controls
utils.on(this.player.elements.controls, 'focusin focusout', event => { on(this.player.elements.controls, 'focusin focusout', event => {
this.player.toggleControls(event); this.player.toggleControls(event);
}); });
} }
// Mouse wheel for volume // Mouse wheel for volume
utils.on( on(
this.player.elements.inputs.volume, this.player.elements.inputs.volume,
'wheel', 'wheel',
event => event => {
proxy(event, 'volume', () => { // Detect "natural" scroll - suppored on OS X Safari only
// Detect "natural" scroll - suppored on OS X Safari only // Other browsers on OS X will be inverted until support improves
// Other browsers on OS X will be inverted until support improves const inverted = event.webkitDirectionInvertedFromDevice;
const inverted = event.webkitDirectionInvertedFromDevice; const step = 1 / 50;
const step = 1 / 50; let direction = 0;
let direction = 0;
// Scroll down (or up on natural) to decrease // Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) { if (event.deltaY < 0 || event.deltaX > 0) {
if (inverted) { if (inverted) {
this.player.decreaseVolume(step); this.player.decreaseVolume(step);
direction = -1; direction = -1;
} else { } else {
this.player.increaseVolume(step); this.player.increaseVolume(step);
direction = 1; direction = 1;
}
} }
}
// Scroll up (or down on natural) to increase // Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) { if (event.deltaY > 0 || event.deltaX < 0) {
if (inverted) { if (inverted) {
this.player.increaseVolume(step); this.player.increaseVolume(step);
direction = 1; direction = 1;
} else { } else {
this.player.decreaseVolume(step); this.player.decreaseVolume(step);
direction = -1; direction = -1;
}
} }
}
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) { if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) {
event.preventDefault(); event.preventDefault();
} }
}), },
'volume',
false, false,
); );
} }
+1 -1
View File
@@ -46,7 +46,7 @@ const media = {
utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
// Add touch class // Add touch class
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch); utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
} }
// Inject the player wrapper // Inject the player wrapper
+2 -1
View File
@@ -7,6 +7,7 @@
/* global google */ /* global google */
import utils from '../utils'; import utils from '../utils';
import i18n from '../i18n';
class Ads { class Ads {
/** /**
@@ -178,7 +179,7 @@ class Ads {
const update = () => { const update = () => {
const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0)); const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0));
const label = `${this.player.config.i18n.advertisement} - ${time}`; const label = `${i18n.get('advertisement', this.player.config)} - ${time}`;
this.elements.container.setAttribute('data-badge-text', label); this.elements.container.setAttribute('data-badge-text', label);
}; };
+24 -13
View File
@@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.0.3 // plyr.js v3.0.4
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
@@ -36,6 +36,9 @@ class Plyr {
this.loading = false; this.loading = false;
this.failed = false; this.failed = false;
// Touch device
this.touch = support.touch;
// Set the media element // Set the media element
this.media = target; this.media = target;
@@ -954,26 +957,32 @@ class Plyr {
// Is the enter fullscreen event // Is the enter fullscreen event
isEnterFullscreen = toggle.type === 'enterfullscreen'; isEnterFullscreen = toggle.type === 'enterfullscreen';
// Whether to show controls // Events that show the controls
show = [ const showEvents = [
'mouseenter',
'mousemove',
'touchstart', 'touchstart',
'touchmove', 'touchmove',
'focusin', 'mouseenter',
].includes(toggle.type);
// Delay hiding on move events
if ([
'mousemove', 'mousemove',
'focusin',
];
// Events that delay hiding
const delayEvents = [
'touchmove', 'touchmove',
'touchend', 'touchend',
].includes(toggle.type)) { 'mousemove',
];
// Whether to show controls
show = showEvents.includes(toggle.type);
// Delay hiding on move events
if (delayEvents.includes(toggle.type)) {
delay = 2000; delay = 2000;
} }
// Delay a little more for keyboard users // Delay a little more for keyboard users
if (toggle.type === 'focusin') { if (!this.touch && toggle.type === 'focusin') {
delay = 3000; delay = 3000;
utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true); utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true);
} }
@@ -1001,7 +1010,7 @@ class Plyr {
} }
// Delay for hiding on touch // Delay for hiding on touch
if (support.touch) { if (this.touch) {
delay = 3000; delay = 3000;
} }
} }
@@ -1010,6 +1019,8 @@ class Plyr {
// then set the timer to hide the controls // then set the timer to hide the controls
if (!show || this.playing) { if (!show || this.playing) {
this.timers.controls = setTimeout(() => { this.timers.controls = setTimeout(() => {
console.warn(this.elements.controls.pressed, this.elements.controls.hover, delay);
// If the mouse is over the controls (and not entering fullscreen), bail // If the mouse is over the controls (and not entering fullscreen), bail
if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) { if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {
return; return;
+1 -1
View File
@@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr Polyfilled Build // Plyr Polyfilled Build
// plyr.js v3.0.3 // plyr.js v3.0.4
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
+1 -1
View File
@@ -143,7 +143,7 @@ const support = {
})(), })(),
// Touch // Touch
// Remember a device can be moust + touch enabled // NOTE: Remember a device can be mouse + touch enabled so we check on first touch event
touch: 'ontouchstart' in document.documentElement, touch: 'ontouchstart' in document.documentElement,
// Detect transitions support // Detect transitions support
+3 -2
View File
@@ -5,6 +5,7 @@
import utils from './utils'; import utils from './utils';
import captions from './captions'; import captions from './captions';
import controls from './controls'; import controls from './controls';
import i18n from './i18n';
const ui = { const ui = {
addStyleHook() { addStyleHook() {
@@ -94,7 +95,7 @@ const ui = {
// Setup aria attribute for play and iframe title // Setup aria attribute for play and iframe title
setTitle() { setTitle() {
// Find the current text // Find the current text
let label = this.config.i18n.play; let label = i18n.get('play', this.config);
// If there's a media title set, use that for the label // If there's a media title set, use that for the label
if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
@@ -123,7 +124,7 @@ const ui = {
// Default to media type // Default to media type
const title = !utils.is.empty(this.config.title) ? this.config.title : 'video'; const title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title)); iframe.setAttribute('title', i18n.get('frameTitle', this.config));
} }
}, },
+50 -18
View File
@@ -208,15 +208,6 @@ const utils = {
return `${prefix}-${Math.floor(Math.random() * 10000)}`; return `${prefix}-${Math.floor(Math.random() * 10000)}`;
}, },
// Determine if we're in an iframe
inFrame() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
},
// Wrap an element // Wrap an element
wrap(elements, wrapper) { wrap(elements, wrapper) {
// Convert `elements` to an array, if necessary. // Convert `elements` to an array, if necessary.
@@ -319,8 +310,11 @@ const utils = {
return; return;
} }
Object.keys(attributes).forEach(key => { Object.entries(attributes).forEach(([
element.setAttribute(key, attributes[key]); key,
value,
]) => {
element.setAttribute(key, value);
}); });
}, },
@@ -540,7 +534,7 @@ const utils = {
}, },
// Toggle event listener // Toggle event listener
toggleListener(elements, event, callback, toggle, passive, capture) { toggleListener(elements, event, callback, toggle = false, passive = true, capture = false) {
// Bail if no elemetns, event, or callback // Bail if no elemetns, event, or callback
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) { if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
return; return;
@@ -562,16 +556,16 @@ const utils = {
const events = event.split(' '); const events = event.split(' ');
// Build options // Build options
// Default to just capture boolean // Default to just the capture boolean for browsers with no passive listener support
let options = utils.is.boolean(capture) ? capture : false; let options = capture;
// If passive events listeners are supported // If passive events listeners are supported
if (support.passiveListeners) { if (support.passiveListeners) {
options = { options = {
// Whether the listener can be passive (i.e. default never prevented) // Whether the listener can be passive (i.e. default never prevented)
passive: utils.is.boolean(passive) ? passive : true, passive,
// Whether the listener is a capturing listener or not // Whether the listener is a capturing listener or not
capture: utils.is.boolean(capture) ? capture : false, capture,
}; };
} }
@@ -582,12 +576,12 @@ const utils = {
}, },
// Bind event handler // Bind event handler
on(element, events, callback, passive, capture) { on(element, events = '', callback, passive = true, capture = false) {
utils.toggleListener(element, events, callback, true, passive, capture); utils.toggleListener(element, events, callback, true, passive, capture);
}, },
// Unbind event handler // Unbind event handler
off(element, events, callback, passive, capture) { off(element, events = '', callback, passive = true, capture = false) {
utils.toggleListener(element, events, callback, false, passive, capture); utils.toggleListener(element, events, callback, false, passive, capture);
}, },
@@ -678,6 +672,44 @@ const utils = {
return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`; return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`;
}, },
// Replace all occurances of a string in a string
replaceAll(input = '', find = '', replace = '') {
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
},
// Convert to title case
toTitleCase(input = '') {
return input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase());
},
// Convert string to pascalCase
toPascalCase(input = '') {
let string = input.toString();
// Convert kebab case
string = utils.replaceAll(string, '-', ' ');
// Convert snake case
string = utils.replaceAll(string, '_', ' ');
// Convert to title case
string = utils.toTitleCase(string);
// Convert to pascal case
return utils.replaceAll(string, ' ', '');
},
// Convert string to pascalCase
toCamelCase(input = '') {
let string = input.toString();
// Convert to pascal case
string = utils.toPascalCase(string);
// Convert first character to lowercase
return string.charAt(0).toLowerCase() + string.slice(1);
},
// Deep extend destination object with N more objects // Deep extend destination object with N more objects
extend(target = {}, ...sources) { extend(target = {}, ...sources) {
if (!sources.length) { if (!sources.length) {
-2
View File
@@ -84,7 +84,6 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
transition: border-color 0.2s ease;
} }
&--forward { &--forward {
@@ -108,7 +107,6 @@
margin-bottom: floor($plyr-control-padding / 2); margin-bottom: floor($plyr-control-padding / 2);
padding-left: ceil($plyr-control-padding * 4); padding-left: ceil($plyr-control-padding * 4);
position: relative; position: relative;
width: calc(100% - #{$horizontal-padding}); width: calc(100% - #{$horizontal-padding});
&::after { &::after {