-   Accessibility improvements (see #905)
-   Improvements to the way the controls work on iOS
-   Demo code clean up
-   YouTube quality selection removed due to their poor support for it. As a result, the `qualityrequested` event has been removed
-   Controls spacing improvements
-   Fix for pressed property missing with custom controls (Fixes #1062)
-   Fix #1153: Captions language fallback (thanks @friday)
-   Fix for setting pressed property of undefined (Fixes #1102)
This commit is contained in:
Sam Potts 2018-08-14 00:02:01 +10:00
parent 48bf368316
commit b57b7b2153
18 changed files with 242 additions and 317 deletions

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

5
demo/dist/demo.js vendored
View File

@ -4118,9 +4118,10 @@ typeof navigator === "object" && (function () {
// Remove class on blur // Remove class on blur
document.addEventListener('focusout', function (event) { document.addEventListener('focusout', function (event) {
if (container.contains(event.target)) { if (!event.target.classList || container.contains(event.target)) {
return; return;
} }
event.target.classList.remove(tabClassName); event.target.classList.remove(tabClassName);
}); });
@ -4135,7 +4136,7 @@ typeof navigator === "object" && (function () {
setTimeout(function () { setTimeout(function () {
var focused = document.activeElement; var focused = document.activeElement;
if (!focused || container.contains(focused)) { if (!focused || !focused.classList || container.contains(focused)) {
return; return;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

185
dist/plyr.js vendored
View File

@ -886,6 +886,9 @@ typeof navigator === "object" && (function (global, factory) {
triggerEvent.call(player, player.media, 'qualitychange', false, { triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: input quality: input
}); });
// Save to storage
player.storage.set({ quality: input });
} }
}); });
}, },
@ -918,6 +921,30 @@ typeof navigator === "object" && (function (global, factory) {
// ========================================================================== // ==========================================================================
// Remove duplicates in an array
function dedupe(array) {
if (!is.array(array)) {
return array;
}
return array.filter(function (item, index) {
return array.indexOf(item) === index;
});
}
// Get the closest value in an array
function closest(array, value) {
if (!is.array(array) || !array.length) {
return null;
}
return array.reduce(function (prev, curr) {
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
});
}
// ==========================================================================
// Clone nested objects // Clone nested objects
function cloneDeep(object) { function cloneDeep(object) {
return JSON.parse(JSON.stringify(object)); return JSON.parse(JSON.stringify(object));
@ -1096,30 +1123,6 @@ typeof navigator === "object" && (function (global, factory) {
// ========================================================================== // ==========================================================================
// Remove duplicates in an array
function dedupe(array) {
if (!is.array(array)) {
return array;
}
return array.filter(function (item, index) {
return array.indexOf(item) === index;
});
}
// Get the closest value in an array
function closest(array, value) {
if (!is.array(array) || !array.length) {
return null;
}
return array.reduce(function (prev, curr) {
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
});
}
// ==========================================================================
var Storage = function () { var Storage = function () {
function Storage(player) { function Storage(player) {
classCallCheck(this, Storage); classCallCheck(this, Storage);
@ -1604,20 +1607,6 @@ typeof navigator === "object" && (function (global, factory) {
this.elements.buttons[type] = button; this.elements.buttons[type] = button;
} }
// Toggle classname when pressed property is set
var className = this.config.classNames.controlPressed;
Object.defineProperty(button, 'pressed', {
enumerable: true,
get: function get$$1() {
return hasClass(button, className);
},
set: function set$$1() {
var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
toggleClass(button, className, pressed);
}
});
return button; return button;
}, },
@ -2490,9 +2479,8 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item if key interaction // Focus the first item if key interaction
if (show && is.keyboardEvent(input)) { if (show && is.keyboardEvent(input)) {
controls.focusFirstMenuItem.call(this, null, true); controls.focusFirstMenuItem.call(this, null, true);
} } else if (!show && !hidden) {
// If closing, re-focus the button // If closing, re-focus the button
else if (!show && !hidden) {
setFocus.call(this, button, is.keyboardEvent(input)); setFocus.call(this, button, is.keyboardEvent(input));
} }
}, },
@ -2651,17 +2639,19 @@ typeof navigator === "object" && (function (global, factory) {
container.appendChild(controls.createTime.call(this, 'duration')); container.appendChild(controls.createTime.call(this, 'duration'));
} }
// Toggle mute button // Volume controls
if (this.config.controls.includes('mute')) { if (this.config.controls.includes('mute') || this.config.controls.includes('volume')) {
container.appendChild(controls.createButton.call(this, 'mute'));
}
// Volume range control
if (this.config.controls.includes('volume')) {
var volume = createElement('div', { var volume = createElement('div', {
class: 'plyr__volume' class: 'plyr__volume'
}); });
// Toggle mute button
if (this.config.controls.includes('mute')) {
volume.appendChild(controls.createButton.call(this, 'mute'));
}
// Volume range control
if (this.config.controls.includes('volume')) {
// Set the attributes // Set the attributes
var attributes = { var attributes = {
max: 1, max: 1,
@ -2675,6 +2665,7 @@ typeof navigator === "object" && (function (global, factory) {
}))); })));
this.elements.volume = volume; this.elements.volume = volume;
}
container.appendChild(volume); container.appendChild(volume);
} }
@ -2836,6 +2827,7 @@ typeof navigator === "object" && (function (global, factory) {
this.elements.controls = container; this.elements.controls = container;
// Set available quality levels
if (this.isHTML5) { if (this.isHTML5) {
controls.setQualityMenu.call(this, html5.getQualityOptions.call(this)); controls.setQualityMenu.call(this, html5.getQualityOptions.call(this));
} }
@ -2948,6 +2940,25 @@ typeof navigator === "object" && (function (global, factory) {
controls.findElements.call(this); controls.findElements.call(this);
} }
// Add pressed property to buttons
if (!is.empty(this.elements.buttons)) {
// Toggle classname when pressed property is set
Object.values(this.elements.buttons).forEach(function (button) {
var className = _this10.config.classNames.controlPressed;
Object.defineProperty(button, 'pressed', {
enumerable: true,
get: function get$$1() {
return hasClass(button, className);
},
set: function set$$1() {
var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
toggleClass(button, className, pressed);
}
});
});
}
// Edge sometimes doesn't finish the paint so force a redraw // Edge sometimes doesn't finish the paint so force a redraw
if (window.navigator.userAgent.includes('Edge')) { if (window.navigator.userAgent.includes('Edge')) {
repaint(target); repaint(target);
@ -3065,7 +3076,8 @@ typeof navigator === "object" && (function (global, factory) {
// * active: The state preferred by user settings or config // * active: The state preferred by user settings or config
// * toggled: The real captions state // * toggled: The real captions state
var languages = dedupe(Array.from(navigator.languages || navigator.language || navigator.userLanguage).map(function (language) { var browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
var languages = dedupe(browserLanguages.map(function (language) {
return language.split('-')[0]; return language.split('-')[0];
})); }));
@ -3488,7 +3500,7 @@ typeof navigator === "object" && (function (global, factory) {
// Quality default // Quality default
quality: { quality: {
default: 576, default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240, 'default'] options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240]
}, },
// Set loops // Set loops
@ -3638,7 +3650,10 @@ typeof navigator === "object" && (function (global, factory) {
'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready',
// YouTube // YouTube
'statechange', 'qualitychange', 'qualityrequested', 'statechange',
// Quality
'qualitychange',
// Ads // Ads
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'], 'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
@ -3947,9 +3962,7 @@ typeof navigator === "object" && (function (global, factory) {
// 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) {
this.target.webkitEnterFullscreen(); this.target.webkitEnterFullscreen();
}
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
@ -4636,7 +4649,7 @@ typeof navigator === "object" && (function (global, factory) {
// Remove button states for fullscreen // Remove button states for fullscreen
if (event.type === 'enterfullscreen') { if (controls$$1 && event.type === 'enterfullscreen') {
controls$$1.pressed = false; controls$$1.pressed = false;
controls$$1.hover = false; controls$$1.hover = false;
} }
@ -4794,12 +4807,6 @@ typeof navigator === "object" && (function (global, factory) {
player.storage.set({ speed: player.speed }); player.storage.set({ speed: player.speed });
}); });
// Quality request
on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage
player.storage.set({ quality: event.detail.quality });
});
// Quality change // Quality change
on.call(player, player.media, 'qualitychange', function (event) { on.call(player, player.media, 'qualitychange', function (event) {
// Update UI // Update UI
@ -5879,43 +5886,6 @@ typeof navigator === "object" && (function (global, factory) {
return url.match(regex) ? RegExp.$2 : url; return url.match(regex) ? RegExp.$2 : url;
} }
// Standardise YouTube quality unit
function mapQualityUnit(input) {
var qualities = {
hd2160: 2160,
hd1440: 1440,
hd1080: 1080,
hd720: 720,
large: 480,
medium: 360,
small: 240,
tiny: 144
};
var entry = Object.entries(qualities).find(function (entry) {
return entry.includes(input);
});
if (entry) {
// Get the match corresponding to the input
return entry.find(function (value) {
return value !== input;
});
}
return 'default';
}
function mapQualityUnits(levels) {
if (is.empty(levels)) {
return levels;
}
return dedupe(levels.map(function (level) {
return mapQualityUnit(level);
}));
}
// Set playback state and trigger change (only on actual change) // Set playback state and trigger change (only on actual change)
function assurePlaybackState$1(play) { function assurePlaybackState$1(play) {
if (play && !this.embed.hasPlayed) { if (play && !this.embed.hasPlayed) {
@ -6099,11 +6069,6 @@ typeof navigator === "object" && (function (global, factory) {
triggerEvent.call(player, player.media, 'error'); triggerEvent.call(player, player.media, 'error');
} }
}, },
onPlaybackQualityChange: function onPlaybackQualityChange() {
triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: player.media.quality
});
},
onPlaybackRateChange: function onPlaybackRateChange(event) { onPlaybackRateChange: function onPlaybackRateChange(event) {
// Get the instance // Get the instance
var instance = event.target; var instance = event.target;
@ -6173,16 +6138,6 @@ typeof navigator === "object" && (function (global, factory) {
} }
}); });
// Quality
Object.defineProperty(player.media, 'quality', {
get: function get() {
return mapQualityUnit(instance.getPlaybackQuality());
},
set: function set(input) {
instance.setPlaybackQuality(mapQualityUnit(input));
}
});
// Volume // Volume
var volume = player.config.volume; var volume = player.config.volume;
@ -6335,9 +6290,6 @@ typeof navigator === "object" && (function (global, factory) {
player.media.duration = instance.getDuration(); player.media.duration = instance.getDuration();
triggerEvent.call(player, player.media, 'durationchange'); triggerEvent.call(player, player.media, 'durationchange');
} }
// Get quality
controls.setQualityMenu.call(player, mapQualityUnits(instance.getAvailableQualityLevels()));
} }
break; break;
@ -8203,11 +8155,6 @@ typeof navigator === "object" && (function (global, factory) {
quality = value; quality = value;
} }
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config // Update config
config.selected = quality; config.selected = quality;

2
dist/plyr.js.map vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -20,7 +20,7 @@ typeof navigator === "object" && (function (global, factory) {
}); });
var _core = createCommonjsModule(function (module) { var _core = createCommonjsModule(function (module) {
var core = module.exports = { version: '2.5.3' }; var core = module.exports = { version: '2.5.7' };
if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
}); });
var _core_1 = _core.version; var _core_1 = _core.version;
@ -333,11 +333,18 @@ typeof navigator === "object" && (function (global, factory) {
}; };
}; };
var _shared = createCommonjsModule(function (module) {
var SHARED = '__core-js_shared__'; var SHARED = '__core-js_shared__';
var store = _global[SHARED] || (_global[SHARED] = {}); var store = _global[SHARED] || (_global[SHARED] = {});
var _shared = function (key) {
return store[key] || (store[key] = {}); (module.exports = function (key, value) {
}; return store[key] || (store[key] = value !== undefined ? value : {});
})('versions', []).push({
version: _core.version,
mode: 'global',
copyright: '© 2018 Denis Pushkarev (zloirock.ru)'
});
});
var shared = _shared('keys'); var shared = _shared('keys');
@ -741,12 +748,12 @@ typeof navigator === "object" && (function (global, factory) {
if ($slice !== undefined && end === undefined) return $slice.call(_anObject(this), start); // FF fix if ($slice !== undefined && end === undefined) return $slice.call(_anObject(this), start); // FF fix
var len = _anObject(this).byteLength; var len = _anObject(this).byteLength;
var first = _toAbsoluteIndex(start, len); var first = _toAbsoluteIndex(start, len);
var final = _toAbsoluteIndex(end === undefined ? len : end, len); var fin = _toAbsoluteIndex(end === undefined ? len : end, len);
var result = new (_speciesConstructor(this, $ArrayBuffer))(_toLength(final - first)); var result = new (_speciesConstructor(this, $ArrayBuffer))(_toLength(fin - first));
var viewS = new $DataView(this); var viewS = new $DataView(this);
var viewT = new $DataView(result); var viewT = new $DataView(result);
var index = 0; var index = 0;
while (first < final) { while (first < fin) {
viewT.setUint8(index++, viewS.getUint8(first++)); viewT.setUint8(index++, viewS.getUint8(first++));
} return result; } return result;
} }
@ -991,7 +998,7 @@ typeof navigator === "object" && (function (global, factory) {
var VALUES_BUG = false; var VALUES_BUG = false;
var proto = Base.prototype; var proto = Base.prototype;
var $native = proto[ITERATOR$2] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; var $native = proto[ITERATOR$2] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
var $default = (!BUGGY && $native) || getMethod(DEFAULT); var $default = $native || getMethod(DEFAULT);
var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
var methods, key, IteratorPrototype; var methods, key, IteratorPrototype;
@ -1002,7 +1009,7 @@ typeof navigator === "object" && (function (global, factory) {
// Set @@toStringTag to native iterators // Set @@toStringTag to native iterators
_setToStringTag(IteratorPrototype, TAG, true); _setToStringTag(IteratorPrototype, TAG, true);
// fix for some old engines // fix for some old engines
if (!_has(IteratorPrototype, ITERATOR$2)) _hide(IteratorPrototype, ITERATOR$2, returnThis); if (typeof IteratorPrototype[ITERATOR$2] != 'function') _hide(IteratorPrototype, ITERATOR$2, returnThis);
} }
} }
// fix Array#{values, @@iterator}.name in V8 / FF // fix Array#{values, @@iterator}.name in V8 / FF
@ -2495,9 +2502,11 @@ typeof navigator === "object" && (function (global, factory) {
} }
if (_has(ownDesc, 'value')) { if (_has(ownDesc, 'value')) {
if (ownDesc.writable === false || !_isObject(receiver)) return false; if (ownDesc.writable === false || !_isObject(receiver)) return false;
existingDescriptor = _objectGopd.f(receiver, propertyKey) || _propertyDesc(0); if (existingDescriptor = _objectGopd.f(receiver, propertyKey)) {
if (existingDescriptor.get || existingDescriptor.set || existingDescriptor.writable === false) return false;
existingDescriptor.value = V; existingDescriptor.value = V;
_objectDp.f(receiver, propertyKey, existingDescriptor); _objectDp.f(receiver, propertyKey, existingDescriptor);
} else _objectDp.f(receiver, propertyKey, _propertyDesc(0, V));
return true; return true;
} }
return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true); return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true);
@ -2642,7 +2651,8 @@ typeof navigator === "object" && (function (global, factory) {
}; };
// environments with maybe non-completely correct, but existent Promise // environments with maybe non-completely correct, but existent Promise
} else if (Promise$1 && Promise$1.resolve) { } else if (Promise$1 && Promise$1.resolve) {
var promise = Promise$1.resolve(); // Promise.resolve without an argument throws an error in LG WebOS 2
var promise = Promise$1.resolve(undefined);
notify = function () { notify = function () {
promise.then(flush); promise.then(flush);
}; };
@ -2699,6 +2709,10 @@ typeof navigator === "object" && (function (global, factory) {
} }
}; };
var navigator$1 = _global.navigator;
var _userAgent = navigator$1 && navigator$1.userAgent || '';
var _promiseResolve = function (C, x) { var _promiseResolve = function (C, x) {
_anObject(C); _anObject(C);
if (_isObject(x) && x.constructor === C) return x; if (_isObject(x) && x.constructor === C) return x;
@ -2713,9 +2727,12 @@ typeof navigator === "object" && (function (global, factory) {
var PROMISE = 'Promise'; var PROMISE = 'Promise';
var TypeError$1 = _global.TypeError; var TypeError$1 = _global.TypeError;
var process$2 = _global.process; var process$2 = _global.process;
var versions = process$2 && process$2.versions;
var v8 = versions && versions.v8 || '';
var $Promise = _global[PROMISE]; var $Promise = _global[PROMISE];
var isNode$1 = _classof(process$2) == 'process'; var isNode$1 = _classof(process$2) == 'process';
var empty = function () { /* empty */ }; var empty = function () { /* empty */ };
@ -2730,7 +2747,13 @@ typeof navigator === "object" && (function (global, factory) {
exec(empty, empty); exec(empty, empty);
}; };
// unhandled rejections tracking support, NodeJS Promise without it fails @@species test // unhandled rejections tracking support, NodeJS Promise without it fails @@species test
return (isNode$1 || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise; return (isNode$1 || typeof PromiseRejectionEvent == 'function')
&& promise.then(empty) instanceof FakePromise
// v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
// https://bugs.chromium.org/p/chromium/issues/detail?id=830565
// we can't detect it synchronously, so just check versions
&& v8.indexOf('6.6') !== 0
&& _userAgent.indexOf('Chrome/66') === -1;
} catch (e) { /* empty */ } } catch (e) { /* empty */ }
}(); }();
@ -2752,7 +2775,7 @@ typeof navigator === "object" && (function (global, factory) {
var resolve = reaction.resolve; var resolve = reaction.resolve;
var reject = reaction.reject; var reject = reaction.reject;
var domain = reaction.domain; var domain = reaction.domain;
var result, then; var result, then, exited;
try { try {
if (handler) { if (handler) {
if (!ok) { if (!ok) {
@ -2762,8 +2785,11 @@ typeof navigator === "object" && (function (global, factory) {
if (handler === true) result = value; if (handler === true) result = value;
else { else {
if (domain) domain.enter(); if (domain) domain.enter();
result = handler(value); result = handler(value); // may throw
if (domain) domain.exit(); if (domain) {
domain.exit();
exited = true;
}
} }
if (result === reaction.promise) { if (result === reaction.promise) {
reject(TypeError$1('Promise-chain cycle')); reject(TypeError$1('Promise-chain cycle'));
@ -2772,6 +2798,7 @@ typeof navigator === "object" && (function (global, factory) {
} else resolve(result); } else resolve(result);
} else reject(value); } else reject(value);
} catch (e) { } catch (e) {
if (domain && !exited) domain.exit();
reject(e); reject(e);
} }
}; };
@ -4154,10 +4181,6 @@ typeof navigator === "object" && (function (global, factory) {
return left ? stringFiller + S : S + stringFiller; return left ? stringFiller + S : S + stringFiller;
}; };
var navigator$1 = _global.navigator;
var _userAgent = navigator$1 && navigator$1.userAgent || '';
// https://github.com/tc39/proposal-string-pad-start-end // https://github.com/tc39/proposal-string-pad-start-end
@ -5190,12 +5213,11 @@ typeof navigator === "object" && (function (global, factory) {
} }
proto.toString = function() { proto.toString = function() {
var searchString = ''; var searchArray = [];
this.forEach(function(value, name) { this.forEach(function(value, name) {
if(searchString.length > 0) searchString+= '&'; searchArray.push(serializeParam(name) + '=' + serializeParam(value));
searchString += serializeParam(name) + '=' + serializeParam(value);
}); });
return searchString; return searchArray.join("&");
}; };
global.URLSearchParams = URLSearchParams; global.URLSearchParams = URLSearchParams;
@ -5237,18 +5259,26 @@ typeof navigator === "object" && (function (global, factory) {
var URL = function(url, base) { var URL = function(url, base) {
if(typeof url !== 'string') url = String(url); if(typeof url !== 'string') url = String(url);
var doc = document.implementation.createHTMLDocument(''); // Only create another document if the base is different from current location.
window.doc = doc; var doc = document, baseElement;
if(base) { if(base && (global.location === void 0 || base !== global.location.href)) {
var baseElement = doc.createElement('base'); doc = document.implementation.createHTMLDocument('');
baseElement = doc.createElement('base');
baseElement.href = base; baseElement.href = base;
doc.head.appendChild(baseElement); doc.head.appendChild(baseElement);
try {
if(baseElement.href.indexOf(base) !== 0) throw new Error(baseElement.href);
} catch (err) {
throw new Error("URL unable to set base " + base + " due to " + err);
}
} }
var anchorElement = doc.createElement('a'); var anchorElement = doc.createElement('a');
anchorElement.href = url; anchorElement.href = url;
if (baseElement) {
doc.body.appendChild(anchorElement); doc.body.appendChild(anchorElement);
anchorElement.href = anchorElement.href; // force href to refresh anchorElement.href = anchorElement.href; // force href to refresh
}
if(anchorElement.protocol === ':' || !/:/.test(anchorElement.href)) { if(anchorElement.protocol === ':' || !/:/.test(anchorElement.href)) {
throw new TypeError('Invalid URL'); throw new TypeError('Invalid URL');
@ -6283,6 +6313,9 @@ typeof navigator === "object" && (function (global, factory) {
triggerEvent.call(player, player.media, 'qualitychange', false, { triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: input quality: input
}); });
// Save to storage
player.storage.set({ quality: input });
} }
}); });
}, },
@ -6315,6 +6348,30 @@ typeof navigator === "object" && (function (global, factory) {
// ========================================================================== // ==========================================================================
// Remove duplicates in an array
function dedupe(array) {
if (!is$1.array(array)) {
return array;
}
return array.filter(function (item, index) {
return array.indexOf(item) === index;
});
}
// Get the closest value in an array
function closest(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
return array.reduce(function (prev, curr) {
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
});
}
// ==========================================================================
// Clone nested objects // Clone nested objects
function cloneDeep(object) { function cloneDeep(object) {
return JSON.parse(JSON.stringify(object)); return JSON.parse(JSON.stringify(object));
@ -6493,30 +6550,6 @@ typeof navigator === "object" && (function (global, factory) {
// ========================================================================== // ==========================================================================
// Remove duplicates in an array
function dedupe(array) {
if (!is$1.array(array)) {
return array;
}
return array.filter(function (item, index) {
return array.indexOf(item) === index;
});
}
// Get the closest value in an array
function closest(array, value) {
if (!is$1.array(array) || !array.length) {
return null;
}
return array.reduce(function (prev, curr) {
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
});
}
// ==========================================================================
var Storage = function () { var Storage = function () {
function Storage(player) { function Storage(player) {
classCallCheck(this, Storage); classCallCheck(this, Storage);
@ -7001,20 +7034,6 @@ typeof navigator === "object" && (function (global, factory) {
this.elements.buttons[type] = button; this.elements.buttons[type] = button;
} }
// Toggle classname when pressed property is set
var className = this.config.classNames.controlPressed;
Object.defineProperty(button, 'pressed', {
enumerable: true,
get: function get() {
return hasClass(button, className);
},
set: function set() {
var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
toggleClass(button, className, pressed);
}
});
return button; return button;
}, },
@ -7887,9 +7906,8 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item if key interaction // Focus the first item if key interaction
if (show && is$1.keyboardEvent(input)) { if (show && is$1.keyboardEvent(input)) {
controls.focusFirstMenuItem.call(this, null, true); controls.focusFirstMenuItem.call(this, null, true);
} } else if (!show && !hidden) {
// If closing, re-focus the button // If closing, re-focus the button
else if (!show && !hidden) {
setFocus.call(this, button, is$1.keyboardEvent(input)); setFocus.call(this, button, is$1.keyboardEvent(input));
} }
}, },
@ -8048,17 +8066,19 @@ typeof navigator === "object" && (function (global, factory) {
container.appendChild(controls.createTime.call(this, 'duration')); container.appendChild(controls.createTime.call(this, 'duration'));
} }
// Toggle mute button // Volume controls
if (this.config.controls.includes('mute')) { if (this.config.controls.includes('mute') || this.config.controls.includes('volume')) {
container.appendChild(controls.createButton.call(this, 'mute'));
}
// Volume range control
if (this.config.controls.includes('volume')) {
var volume = createElement('div', { var volume = createElement('div', {
class: 'plyr__volume' class: 'plyr__volume'
}); });
// Toggle mute button
if (this.config.controls.includes('mute')) {
volume.appendChild(controls.createButton.call(this, 'mute'));
}
// Volume range control
if (this.config.controls.includes('volume')) {
// Set the attributes // Set the attributes
var attributes = { var attributes = {
max: 1, max: 1,
@ -8072,6 +8092,7 @@ typeof navigator === "object" && (function (global, factory) {
}))); })));
this.elements.volume = volume; this.elements.volume = volume;
}
container.appendChild(volume); container.appendChild(volume);
} }
@ -8233,6 +8254,7 @@ typeof navigator === "object" && (function (global, factory) {
this.elements.controls = container; this.elements.controls = container;
// Set available quality levels
if (this.isHTML5) { if (this.isHTML5) {
controls.setQualityMenu.call(this, html5.getQualityOptions.call(this)); controls.setQualityMenu.call(this, html5.getQualityOptions.call(this));
} }
@ -8345,6 +8367,25 @@ typeof navigator === "object" && (function (global, factory) {
controls.findElements.call(this); controls.findElements.call(this);
} }
// Add pressed property to buttons
if (!is$1.empty(this.elements.buttons)) {
// Toggle classname when pressed property is set
Object.values(this.elements.buttons).forEach(function (button) {
var className = _this10.config.classNames.controlPressed;
Object.defineProperty(button, 'pressed', {
enumerable: true,
get: function get() {
return hasClass(button, className);
},
set: function set() {
var pressed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
toggleClass(button, className, pressed);
}
});
});
}
// Edge sometimes doesn't finish the paint so force a redraw // Edge sometimes doesn't finish the paint so force a redraw
if (window.navigator.userAgent.includes('Edge')) { if (window.navigator.userAgent.includes('Edge')) {
repaint(target); repaint(target);
@ -8462,7 +8503,8 @@ typeof navigator === "object" && (function (global, factory) {
// * active: The state preferred by user settings or config // * active: The state preferred by user settings or config
// * toggled: The real captions state // * toggled: The real captions state
var languages = dedupe(Array.from(navigator.languages || navigator.language || navigator.userLanguage).map(function (language) { var browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
var languages = dedupe(browserLanguages.map(function (language) {
return language.split('-')[0]; return language.split('-')[0];
})); }));
@ -8885,7 +8927,7 @@ typeof navigator === "object" && (function (global, factory) {
// Quality default // Quality default
quality: { quality: {
default: 576, default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240, 'default'] options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240]
}, },
// Set loops // Set loops
@ -9035,7 +9077,10 @@ typeof navigator === "object" && (function (global, factory) {
'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled', 'languagechange', 'controlshidden', 'controlsshown', 'ready',
// YouTube // YouTube
'statechange', 'qualitychange', 'qualityrequested', 'statechange',
// Quality
'qualitychange',
// Ads // Ads
'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'], 'adsloaded', 'adscontentpause', 'adscontentresume', 'adstarted', 'adsmidpoint', 'adscomplete', 'adsallcomplete', 'adsimpression', 'adsclick'],
@ -9344,9 +9389,7 @@ typeof navigator === "object" && (function (global, factory) {
// 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) {
this.target.webkitEnterFullscreen(); this.target.webkitEnterFullscreen();
}
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
@ -10033,7 +10076,7 @@ typeof navigator === "object" && (function (global, factory) {
// Remove button states for fullscreen // Remove button states for fullscreen
if (event.type === 'enterfullscreen') { if (controls$$1 && event.type === 'enterfullscreen') {
controls$$1.pressed = false; controls$$1.pressed = false;
controls$$1.hover = false; controls$$1.hover = false;
} }
@ -10191,12 +10234,6 @@ typeof navigator === "object" && (function (global, factory) {
player.storage.set({ speed: player.speed }); player.storage.set({ speed: player.speed });
}); });
// Quality request
on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage
player.storage.set({ quality: event.detail.quality });
});
// Quality change // Quality change
on.call(player, player.media, 'qualitychange', function (event) { on.call(player, player.media, 'qualitychange', function (event) {
// Update UI // Update UI
@ -11270,43 +11307,6 @@ typeof navigator === "object" && (function (global, factory) {
return url.match(regex) ? RegExp.$2 : url; return url.match(regex) ? RegExp.$2 : url;
} }
// Standardise YouTube quality unit
function mapQualityUnit(input) {
var qualities = {
hd2160: 2160,
hd1440: 1440,
hd1080: 1080,
hd720: 720,
large: 480,
medium: 360,
small: 240,
tiny: 144
};
var entry = Object.entries(qualities).find(function (entry) {
return entry.includes(input);
});
if (entry) {
// Get the match corresponding to the input
return entry.find(function (value) {
return value !== input;
});
}
return 'default';
}
function mapQualityUnits(levels) {
if (is$1.empty(levels)) {
return levels;
}
return dedupe(levels.map(function (level) {
return mapQualityUnit(level);
}));
}
// Set playback state and trigger change (only on actual change) // Set playback state and trigger change (only on actual change)
function assurePlaybackState$1(play) { function assurePlaybackState$1(play) {
if (play && !this.embed.hasPlayed) { if (play && !this.embed.hasPlayed) {
@ -11490,11 +11490,6 @@ typeof navigator === "object" && (function (global, factory) {
triggerEvent.call(player, player.media, 'error'); triggerEvent.call(player, player.media, 'error');
} }
}, },
onPlaybackQualityChange: function onPlaybackQualityChange() {
triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: player.media.quality
});
},
onPlaybackRateChange: function onPlaybackRateChange(event) { onPlaybackRateChange: function onPlaybackRateChange(event) {
// Get the instance // Get the instance
var instance = event.target; var instance = event.target;
@ -11564,16 +11559,6 @@ typeof navigator === "object" && (function (global, factory) {
} }
}); });
// Quality
Object.defineProperty(player.media, 'quality', {
get: function get() {
return mapQualityUnit(instance.getPlaybackQuality());
},
set: function set(input) {
instance.setPlaybackQuality(mapQualityUnit(input));
}
});
// Volume // Volume
var volume = player.config.volume; var volume = player.config.volume;
@ -11726,9 +11711,6 @@ typeof navigator === "object" && (function (global, factory) {
player.media.duration = instance.getDuration(); player.media.duration = instance.getDuration();
triggerEvent.call(player, player.media, 'durationchange'); triggerEvent.call(player, player.media, 'durationchange');
} }
// Get quality
controls.setQualityMenu.call(player, mapQualityUnits(instance.getAvailableQualityLevels()));
} }
break; break;
@ -13594,11 +13576,6 @@ typeof navigator === "object" && (function (global, factory) {
quality = value; quality = value;
} }
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config // Update config
config.selected = quality; config.selected = quality;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@ -132,13 +132,13 @@ See [initialising](#initialising) for more information on advanced setups.
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build. You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
```html ```html
<script src="https://cdn.plyr.io/3.4.0-beta.2/plyr.js"></script> <script src="https://cdn.plyr.io/3.4.0/plyr.js"></script>
``` ```
...or... ...or...
```html ```html
<script src="https://cdn.plyr.io/3.4.0-beta.2/plyr.polyfilled.js"></script> <script src="https://cdn.plyr.io/3.4.0/plyr.polyfilled.js"></script>
``` ```
### CSS ### CSS
@ -152,13 +152,13 @@ Include the `plyr.css` stylsheet into your `<head>`
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following: If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html ```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.0-beta.2/plyr.css"> <link rel="stylesheet" href="https://cdn.plyr.io/3.4.0/plyr.css">
``` ```
### SVG Sprite ### SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.0-beta.2/plyr.svg`. reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.0/plyr.svg`.
## Ads ## Ads

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.4.0-beta.2 // plyr.js v3.4.0
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr Polyfilled Build // Plyr Polyfilled Build
// plyr.js v3.4.0-beta.2 // plyr.js v3.4.0
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================