Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 840e31a693 | |||
| 38f954ef17 | |||
| abd1182303 | |||
| 1ad76800b0 | |||
| cc97d7be6a | |||
| f951cb372c | |||
| 37a3ab202a | |||
| b148adc0af | |||
| 16828e975a | |||
| 7d26f41d64 | |||
| f37f465ce4 | |||
| b199215525 | |||
| 94699f3255 | |||
| d3e98eb27e | |||
| 41012a9843 | |||
| c83487a293 | |||
| 1fab4919c0 | |||
| 9dc0f28800 | |||
| b57784d1a5 | |||
| a80b31bf98 | |||
| 76bb299c68 | |||
| 0c03accd41 | |||
| b12eeb0eb7 | |||
| 8e634862ff | |||
| 84424f7f67 | |||
| c95d9923f7 |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# v3.3.11
|
# v3.3.12
|
||||||
|
|
||||||
- Fix synthetic event bubble/proxy loses detail (thanks @friday!)
|
- Fix synthetic event bubble/proxy loses detail (thanks @friday!)
|
||||||
- Make utils static (thanks @friday!)
|
- Make utils static (thanks @friday!)
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+348
-271
@@ -493,61 +493,63 @@ var utils = {
|
|||||||
// Check variable types
|
// Check variable types
|
||||||
is: {
|
is: {
|
||||||
object: function object(input) {
|
object: function object(input) {
|
||||||
return this.getConstructor(input) === Object;
|
return utils.getConstructor(input) === Object;
|
||||||
},
|
},
|
||||||
number: function number(input) {
|
number: function number(input) {
|
||||||
return this.getConstructor(input) === Number && !Number.isNaN(input);
|
return utils.getConstructor(input) === Number && !Number.isNaN(input);
|
||||||
},
|
},
|
||||||
string: function string(input) {
|
string: function string(input) {
|
||||||
return this.getConstructor(input) === String;
|
return utils.getConstructor(input) === String;
|
||||||
},
|
},
|
||||||
boolean: function boolean(input) {
|
boolean: function boolean(input) {
|
||||||
return this.getConstructor(input) === Boolean;
|
return utils.getConstructor(input) === Boolean;
|
||||||
},
|
},
|
||||||
function: function _function(input) {
|
function: function _function(input) {
|
||||||
return this.getConstructor(input) === Function;
|
return utils.getConstructor(input) === Function;
|
||||||
},
|
},
|
||||||
array: function array(input) {
|
array: function array(input) {
|
||||||
return !this.nullOrUndefined(input) && Array.isArray(input);
|
return !utils.is.nullOrUndefined(input) && Array.isArray(input);
|
||||||
},
|
},
|
||||||
weakMap: function weakMap(input) {
|
weakMap: function weakMap(input) {
|
||||||
return this.instanceof(input, WeakMap);
|
return utils.is.instanceof(input, WeakMap);
|
||||||
},
|
},
|
||||||
nodeList: function nodeList(input) {
|
nodeList: function nodeList(input) {
|
||||||
return this.instanceof(input, NodeList);
|
return utils.is.instanceof(input, NodeList);
|
||||||
},
|
},
|
||||||
element: function element(input) {
|
element: function element(input) {
|
||||||
return this.instanceof(input, Element);
|
return utils.is.instanceof(input, Element);
|
||||||
},
|
},
|
||||||
textNode: function textNode(input) {
|
textNode: function textNode(input) {
|
||||||
return this.getConstructor(input) === Text;
|
return utils.getConstructor(input) === Text;
|
||||||
},
|
},
|
||||||
event: function event(input) {
|
event: function event(input) {
|
||||||
return this.instanceof(input, Event);
|
return utils.is.instanceof(input, Event);
|
||||||
},
|
},
|
||||||
cue: function cue(input) {
|
cue: function cue(input) {
|
||||||
return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue);
|
return utils.is.instanceof(input, window.TextTrackCue) || utils.is.instanceof(input, window.VTTCue);
|
||||||
},
|
},
|
||||||
track: function track(input) {
|
track: function track(input) {
|
||||||
return this.instanceof(input, TextTrack) || !this.nullOrUndefined(input) && this.string(input.kind);
|
return utils.is.instanceof(input, TextTrack) || !utils.is.nullOrUndefined(input) && utils.is.string(input.kind);
|
||||||
},
|
},
|
||||||
url: function url(input) {
|
url: function url(input) {
|
||||||
return !this.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
return !utils.is.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
||||||
},
|
},
|
||||||
nullOrUndefined: function nullOrUndefined(input) {
|
nullOrUndefined: function nullOrUndefined(input) {
|
||||||
return input === null || typeof input === 'undefined';
|
return input === null || typeof input === 'undefined';
|
||||||
},
|
},
|
||||||
empty: function empty(input) {
|
empty: function empty(input) {
|
||||||
return this.nullOrUndefined(input) || (this.string(input) || this.array(input) || this.nodeList(input)) && !input.length || this.object(input) && !Object.keys(input).length;
|
return utils.is.nullOrUndefined(input) || (utils.is.string(input) || utils.is.array(input) || utils.is.nodeList(input)) && !input.length || utils.is.object(input) && !Object.keys(input).length;
|
||||||
},
|
},
|
||||||
instanceof: function _instanceof$$1(input, constructor) {
|
instanceof: function _instanceof$$1(input, constructor) {
|
||||||
return Boolean(input && constructor && input instanceof constructor);
|
return Boolean(input && constructor && input instanceof constructor);
|
||||||
},
|
|
||||||
getConstructor: function getConstructor(input) {
|
|
||||||
return !this.nullOrUndefined(input) ? input.constructor : null;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getConstructor: function getConstructor(input) {
|
||||||
|
return !utils.is.nullOrUndefined(input) ? input.constructor : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Unfortunately, due to mixed support, UA sniffing is required
|
// Unfortunately, due to mixed support, UA sniffing is required
|
||||||
getBrowser: function getBrowser() {
|
getBrowser: function getBrowser() {
|
||||||
return {
|
return {
|
||||||
@@ -637,26 +639,25 @@ var utils = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix = 'cache-';
|
var prefix = 'cache';
|
||||||
var hasId = utils.is.string(id);
|
var hasId = utils.is.string(id);
|
||||||
var isCached = false;
|
var isCached = false;
|
||||||
|
|
||||||
var exists = function exists() {
|
var exists = function exists() {
|
||||||
return document.querySelectorAll('#' + id).length;
|
return document.getElementById(id) !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function injectSprite(data) {
|
var update = function update(container, data) {
|
||||||
|
container.innerHTML = data;
|
||||||
|
|
||||||
// Check again incase of race condition
|
// Check again incase of race condition
|
||||||
if (hasId && exists()) {
|
if (hasId && exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject content
|
|
||||||
this.innerHTML = data;
|
|
||||||
|
|
||||||
// Inject the SVG to the body
|
// Inject the SVG to the body
|
||||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
document.body.insertAdjacentElement('afterbegin', container);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Only load once if ID set
|
// Only load once if ID set
|
||||||
if (!hasId || !exists()) {
|
if (!hasId || !exists()) {
|
||||||
@@ -672,13 +673,12 @@ var utils = {
|
|||||||
|
|
||||||
// Check in cache
|
// Check in cache
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
var cached = window.localStorage.getItem(prefix + id);
|
var cached = window.localStorage.getItem(prefix + '-' + id);
|
||||||
isCached = cached !== null;
|
isCached = cached !== null;
|
||||||
|
|
||||||
if (isCached) {
|
if (isCached) {
|
||||||
var data = JSON.parse(cached);
|
var data = JSON.parse(cached);
|
||||||
injectSprite.call(container, data.content);
|
update(container, data.content);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,12 +689,12 @@ var utils = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
window.localStorage.setItem(prefix + id, JSON.stringify({
|
window.localStorage.setItem(prefix + '-' + id, JSON.stringify({
|
||||||
content: result
|
content: result
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
injectSprite.call(container, result);
|
update(container, result);
|
||||||
}).catch(function () {});
|
}).catch(function () {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1169,7 +1169,7 @@ var utils = {
|
|||||||
|
|
||||||
// Bail if the value isn't a number
|
// Bail if the value isn't a number
|
||||||
if (!utils.is.number(time)) {
|
if (!utils.is.number(time)) {
|
||||||
return this.formatTime(null, displayHours, inverted);
|
return utils.formatTime(null, displayHours, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format time component to add leading zero
|
// Format time component to add leading zero
|
||||||
@@ -1178,9 +1178,9 @@ var utils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Breakdown to hours, mins, secs
|
// Breakdown to hours, mins, secs
|
||||||
var hours = this.getHours(time);
|
var hours = utils.getHours(time);
|
||||||
var mins = this.getMinutes(time);
|
var mins = utils.getMinutes(time);
|
||||||
var secs = this.getSeconds(time);
|
var secs = utils.getSeconds(time);
|
||||||
|
|
||||||
// Do we need to display hours?
|
// Do we need to display hours?
|
||||||
if (displayHours || hours > 0) {
|
if (displayHours || hours > 0) {
|
||||||
@@ -1376,12 +1376,12 @@ var utils = {
|
|||||||
|
|
||||||
// Parse URL if needed
|
// Parse URL if needed
|
||||||
if (input.startsWith('http://') || input.startsWith('https://')) {
|
if (input.startsWith('http://') || input.startsWith('https://')) {
|
||||||
var _parseUrl = this.parseUrl(input);
|
var _utils$parseUrl = utils.parseUrl(input);
|
||||||
|
|
||||||
search = _parseUrl.search;
|
search = _utils$parseUrl.search;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.is.empty(search)) {
|
if (utils.is.empty(search)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1420,6 +1420,14 @@ var utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Like outerHTML, but also works for DocumentFragment
|
||||||
|
getHTML: function getHTML(element) {
|
||||||
|
var wrapper = document.createElement('div');
|
||||||
|
wrapper.appendChild(element);
|
||||||
|
return wrapper.innerHTML;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Get aspect ratio for dimensions
|
// Get aspect ratio for dimensions
|
||||||
getAspectRatio: function getAspectRatio(width, height) {
|
getAspectRatio: function getAspectRatio(width, height) {
|
||||||
var getRatio = function getRatio(w, h) {
|
var getRatio = function getRatio(w, h) {
|
||||||
@@ -2161,9 +2169,15 @@ var controls = {
|
|||||||
|
|
||||||
|
|
||||||
// Create a settings menu item
|
// Create a settings menu item
|
||||||
createMenuItem: function createMenuItem(value, list, type, title) {
|
createMenuItem: function createMenuItem(_ref) {
|
||||||
var badge = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
|
var value = _ref.value,
|
||||||
var checked = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
list = _ref.list,
|
||||||
|
type = _ref.type,
|
||||||
|
title = _ref.title,
|
||||||
|
_ref$badge = _ref.badge,
|
||||||
|
badge = _ref$badge === undefined ? null : _ref$badge,
|
||||||
|
_ref$checked = _ref.checked,
|
||||||
|
checked = _ref$checked === undefined ? false : _ref$checked;
|
||||||
|
|
||||||
var item = utils.createElement('li');
|
var item = utils.createElement('li');
|
||||||
|
|
||||||
@@ -2479,8 +2493,13 @@ var controls = {
|
|||||||
var sorting = _this3.config.quality.options;
|
var sorting = _this3.config.quality.options;
|
||||||
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
||||||
}).forEach(function (quality) {
|
}).forEach(function (quality) {
|
||||||
var label = controls.getLabel.call(_this3, 'quality', quality);
|
controls.createMenuItem.call(_this3, {
|
||||||
controls.createMenuItem.call(_this3, quality, list, type, label, getBadge(quality));
|
value: quality,
|
||||||
|
list: list,
|
||||||
|
type: type,
|
||||||
|
title: controls.getLabel.call(_this3, 'quality', quality),
|
||||||
|
badge: getBadge(quality)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
@@ -2523,18 +2542,7 @@ var controls = {
|
|||||||
|
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case 'captions':
|
case 'captions':
|
||||||
if (this.captions.active) {
|
value = this.currentTrack;
|
||||||
if (this.options.captions.length > 2 || !this.options.captions.some(function (lang) {
|
|
||||||
return lang === 'enabled';
|
|
||||||
})) {
|
|
||||||
value = this.captions.language;
|
|
||||||
} else {
|
|
||||||
value = 'enabled';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2629,10 +2637,10 @@ var controls = {
|
|||||||
// TODO: Captions or language? Currently it's mixed
|
// TODO: Captions or language? Currently it's mixed
|
||||||
var type = 'captions';
|
var type = 'captions';
|
||||||
var list = this.elements.settings.panes.captions.querySelector('ul');
|
var list = this.elements.settings.panes.captions.querySelector('ul');
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
|
||||||
// Toggle the pane and tab
|
// Toggle the pane and tab
|
||||||
var toggle = captions.getTracks.call(this).length;
|
controls.toggleTab.call(this, type, tracks.length);
|
||||||
controls.toggleTab.call(this, type, toggle);
|
|
||||||
|
|
||||||
// Empty the menu
|
// Empty the menu
|
||||||
utils.emptyElement(list);
|
utils.emptyElement(list);
|
||||||
@@ -2641,28 +2649,33 @@ var controls = {
|
|||||||
controls.checkMenu.call(this);
|
controls.checkMenu.call(this);
|
||||||
|
|
||||||
// If there's no captions, bail
|
// If there's no captions, bail
|
||||||
if (!toggle) {
|
if (!tracks.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-map the tracks into just the data we need
|
// Generate options data
|
||||||
var tracks = captions.getTracks.call(this).map(function (track) {
|
var options = tracks.map(function (track, value) {
|
||||||
return {
|
return {
|
||||||
language: !utils.is.empty(track.language) ? track.language : 'enabled',
|
value: value,
|
||||||
label: captions.getLabel.call(_this4, track)
|
checked: _this4.captions.active && _this4.currentTrack === value,
|
||||||
|
title: captions.getLabel.call(_this4, track),
|
||||||
|
badge: track.language && controls.createBadge.call(_this4, track.language.toUpperCase()),
|
||||||
|
list: list,
|
||||||
|
type: 'language'
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the "Disabled" option to turn off captions
|
// Add the "Disabled" option to turn off captions
|
||||||
tracks.unshift({
|
options.unshift({
|
||||||
language: '',
|
value: -1,
|
||||||
label: i18n.get('disabled', this.config)
|
checked: !this.captions.active,
|
||||||
|
title: i18n.get('disabled', this.config),
|
||||||
|
list: list,
|
||||||
|
type: 'language'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate options
|
// Generate options
|
||||||
tracks.forEach(function (track) {
|
options.forEach(controls.createMenuItem.bind(this));
|
||||||
controls.createMenuItem.call(_this4, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this4, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this4.language);
|
|
||||||
});
|
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
},
|
},
|
||||||
@@ -2716,8 +2729,12 @@ var controls = {
|
|||||||
|
|
||||||
// Create items
|
// Create items
|
||||||
this.options.speed.forEach(function (speed) {
|
this.options.speed.forEach(function (speed) {
|
||||||
var label = controls.getLabel.call(_this5, 'speed', speed);
|
controls.createMenuItem.call(_this5, {
|
||||||
controls.createMenuItem.call(_this5, speed, list, type, label);
|
value: speed,
|
||||||
|
list: list,
|
||||||
|
type: type,
|
||||||
|
title: controls.getLabel.call(_this5, 'speed', speed)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
@@ -3191,10 +3208,10 @@ var controls = {
|
|||||||
var replace = function replace(input) {
|
var replace = function replace(input) {
|
||||||
var result = input;
|
var result = input;
|
||||||
|
|
||||||
Object.entries(props).forEach(function (_ref) {
|
Object.entries(props).forEach(function (_ref2) {
|
||||||
var _ref2 = slicedToArray(_ref, 2),
|
var _ref3 = slicedToArray(_ref2, 2),
|
||||||
key = _ref2[0],
|
key = _ref3[0],
|
||||||
value = _ref2[1];
|
value = _ref3[1];
|
||||||
|
|
||||||
result = utils.replaceAll(result, '{' + key + '}', value);
|
result = utils.replaceAll(result, '{' + key + '}', value);
|
||||||
});
|
});
|
||||||
@@ -3311,91 +3328,168 @@ var captions = {
|
|||||||
active = this.config.captions.active;
|
active = this.config.captions.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set toggled state
|
// Get language from storage, fallback to config
|
||||||
this.toggleCaptions(active);
|
var language = this.storage.get('language') || this.config.captions.language;
|
||||||
|
if (language === 'auto') {
|
||||||
|
var _split = (navigator.language || navigator.userLanguage).split('-');
|
||||||
|
|
||||||
|
var _split2 = slicedToArray(_split, 1);
|
||||||
|
|
||||||
|
language = _split2[0];
|
||||||
|
}
|
||||||
|
// Set language and show if active
|
||||||
|
captions.setLanguage.call(this, language, active);
|
||||||
|
|
||||||
// Watch changes to textTracks and update captions menu
|
// Watch changes to textTracks and update captions menu
|
||||||
if (this.config.captions.update) {
|
if (this.isHTML5) {
|
||||||
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
|
var trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
|
||||||
|
utils.on(this.media.textTracks, trackEvents, captions.update.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
||||||
setTimeout(captions.update.bind(this), 0);
|
setTimeout(captions.update.bind(this), 0);
|
||||||
},
|
},
|
||||||
update: function update() {
|
update: function update() {
|
||||||
// Update tracks
|
var _this = this;
|
||||||
var tracks = captions.getTracks.call(this);
|
|
||||||
this.options.captions = tracks.map(function (_ref) {
|
|
||||||
var language = _ref.language;
|
|
||||||
return language;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set language if it hasn't been set already
|
var tracks = captions.getTracks.call(this, true);
|
||||||
if (!this.language) {
|
// Get the wanted language
|
||||||
var language = this.config.captions.language;
|
var _captions = this.captions,
|
||||||
|
language = _captions.language,
|
||||||
|
meta = _captions.meta;
|
||||||
|
|
||||||
if (language === 'auto') {
|
// Handle tracks (add event listener and "pseudo"-default)
|
||||||
var _split = (navigator.language || navigator.userLanguage).split('-');
|
|
||||||
|
|
||||||
var _split2 = slicedToArray(_split, 1);
|
if (this.isHTML5 && this.isVideo) {
|
||||||
|
tracks.filter(function (track) {
|
||||||
|
return !meta.get(track);
|
||||||
|
}).forEach(function (track) {
|
||||||
|
_this.debug.log('Track added', track);
|
||||||
|
// Attempt to store if the original dom element was "default"
|
||||||
|
meta.set(track, {
|
||||||
|
default: track.mode === 'showing'
|
||||||
|
});
|
||||||
|
|
||||||
language = _split2[0];
|
// Turn off native caption rendering to avoid double captions
|
||||||
}
|
track.mode = 'hidden';
|
||||||
this.language = this.storage.get('language') || (language || '').toLowerCase();
|
|
||||||
|
// Add event listener for cue changes
|
||||||
|
utils.on(track, 'cuechange', function () {
|
||||||
|
return captions.updateCues.call(_this);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle the class hooks
|
var trackRemoved = !tracks.find(function (track) {
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
|
return track === _this.captions.currentTrackNode;
|
||||||
|
});
|
||||||
|
var firstMatch = this.language !== language && tracks.find(function (track) {
|
||||||
|
return track.language === language;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update language if removed or first matching track added
|
||||||
|
if (trackRemoved || firstMatch) {
|
||||||
|
captions.setLanguage.call(this, language, this.config.captions.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable captions based on track length
|
||||||
|
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(tracks));
|
||||||
|
|
||||||
// Update available languages in list
|
// Update available languages in list
|
||||||
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
||||||
controls.setCaptionsMenu.call(this);
|
controls.setCaptionsMenu.call(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
set: function set$$1(index) {
|
||||||
|
var setLanguage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
||||||
|
var show = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
||||||
|
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
|
||||||
// Set the captions language
|
// Disable captions if setting to -1
|
||||||
setLanguage: function setLanguage() {
|
if (index === -1) {
|
||||||
var _this = this;
|
this.toggleCaptions(false);
|
||||||
|
return;
|
||||||
// Setup HTML5 track rendering
|
|
||||||
if (this.isHTML5 && this.isVideo) {
|
|
||||||
captions.getTracks.call(this).forEach(function (track) {
|
|
||||||
// Show track
|
|
||||||
utils.on(track, 'cuechange', function (event) {
|
|
||||||
return captions.setCue.call(_this, event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Turn off native caption rendering to avoid double captions
|
|
||||||
// eslint-disable-next-line
|
|
||||||
track.mode = 'hidden';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get current track
|
|
||||||
var currentTrack = captions.getCurrentTrack.call(this);
|
|
||||||
|
|
||||||
// Check if suported kind
|
|
||||||
if (utils.is.track(currentTrack)) {
|
|
||||||
// If we change the active track while a cue is already displayed we need to update it
|
|
||||||
if (Array.from(currentTrack.activeCues || []).length) {
|
|
||||||
captions.setCue.call(this, currentTrack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.isVimeo && this.captions.active) {
|
|
||||||
this.embed.enableTextTrack(this.language);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!utils.is.number(index)) {
|
||||||
|
this.debug.warn('Invalid caption argument', index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(index in tracks)) {
|
||||||
|
this.debug.warn('Track not found', index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.captions.currentTrack !== index) {
|
||||||
|
this.captions.currentTrack = index;
|
||||||
|
var track = captions.getCurrentTrack.call(this);
|
||||||
|
|
||||||
|
var _ref = track || {},
|
||||||
|
language = _ref.language;
|
||||||
|
|
||||||
|
// Store reference to node for invalidation on remove
|
||||||
|
|
||||||
|
|
||||||
|
this.captions.currentTrackNode = track;
|
||||||
|
|
||||||
|
// Prevent setting language in some cases, since it can violate user's intentions
|
||||||
|
if (setLanguage) {
|
||||||
|
this.captions.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Vimeo captions
|
||||||
|
if (this.isVimeo) {
|
||||||
|
this.embed.enableTextTrack(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isHTML5 && this.isVideo) {
|
||||||
|
// If we change the active track while a cue is already displayed we need to update it
|
||||||
|
captions.updateCues.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show captions
|
||||||
|
if (show) {
|
||||||
|
this.toggleCaptions(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setLanguage: function setLanguage(language) {
|
||||||
|
var show = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
||||||
|
|
||||||
|
if (!utils.is.string(language)) {
|
||||||
|
this.debug.warn('Invalid language argument', language);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Normalize
|
||||||
|
this.captions.language = language.toLowerCase();
|
||||||
|
|
||||||
|
// Set currentTrack
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
var track = captions.getCurrentTrack.call(this, true);
|
||||||
|
captions.set.call(this, tracks.indexOf(track), false, show);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Get the tracks
|
// Get current valid caption tracks
|
||||||
|
// If update is false it will also ignore tracks without metadata
|
||||||
|
// This is used to "freeze" the language options when captions.update is false
|
||||||
getTracks: function getTracks() {
|
getTracks: function getTracks() {
|
||||||
// Return empty array at least
|
var _this2 = this;
|
||||||
if (utils.is.nullOrUndefined(this.media)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only get accepted kinds
|
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
return Array.from(this.media.textTracks || []).filter(function (track) {
|
|
||||||
|
// Handle media or textTracks missing or null
|
||||||
|
var tracks = Array.from((this.media || {}).textTracks || []);
|
||||||
|
// For HTML5, use cache instead of current tracks when it exists (if captions.update is false)
|
||||||
|
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
|
||||||
|
return tracks.filter(function (track) {
|
||||||
|
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
|
||||||
|
}).filter(function (track) {
|
||||||
return ['captions', 'subtitles'].includes(track.kind);
|
return ['captions', 'subtitles'].includes(track.kind);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -3403,32 +3497,20 @@ var captions = {
|
|||||||
|
|
||||||
// Get the current track for the current language
|
// Get the current track for the current language
|
||||||
getCurrentTrack: function getCurrentTrack() {
|
getCurrentTrack: function getCurrentTrack() {
|
||||||
var _this2 = this;
|
var _this3 = this;
|
||||||
|
|
||||||
|
var fromLanguage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
|
|
||||||
var tracks = captions.getTracks.call(this);
|
var tracks = captions.getTracks.call(this);
|
||||||
|
var sortIsDefault = function sortIsDefault(track) {
|
||||||
if (!tracks.length) {
|
return Number((_this3.captions.meta.get(track) || {}).default);
|
||||||
return null;
|
};
|
||||||
}
|
var sorted = Array.from(tracks).sort(function (a, b) {
|
||||||
|
return sortIsDefault(b) - sortIsDefault(a);
|
||||||
// Get track based on current language
|
|
||||||
var track = tracks.find(function (track) {
|
|
||||||
return track.language.toLowerCase() === _this2.language;
|
|
||||||
});
|
});
|
||||||
|
return !fromLanguage && tracks[this.currentTrack] || sorted.find(function (track) {
|
||||||
// Get the <track> with default attribute
|
return track.language === _this3.captions.language;
|
||||||
if (!track) {
|
}) || sorted[0];
|
||||||
track = utils.getElement.call(this, 'track[default]');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first track
|
|
||||||
if (!track) {
|
|
||||||
var _tracks = slicedToArray(tracks, 1);
|
|
||||||
|
|
||||||
track = _tracks[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return track;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@@ -3456,58 +3538,50 @@ var captions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Display active caption if it contains text
|
// Update captions using current track's active cues
|
||||||
setCue: function setCue(input) {
|
// Also optional array argument in case there isn't any track (ex: vimeo)
|
||||||
// Get the track from the event if needed
|
updateCues: function updateCues(input) {
|
||||||
var track = utils.is.event(input) ? input.target : input;
|
|
||||||
var activeCues = track.activeCues;
|
|
||||||
|
|
||||||
var active = activeCues.length && activeCues[0];
|
|
||||||
var currentTrack = captions.getCurrentTrack.call(this);
|
|
||||||
|
|
||||||
// Only display current track
|
|
||||||
if (track !== currentTrack) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display a cue, if there is one
|
|
||||||
if (utils.is.cue(active)) {
|
|
||||||
captions.setText.call(this, active.getCueAsHTML());
|
|
||||||
} else {
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// Set the current caption
|
|
||||||
setText: function setText(input) {
|
|
||||||
// Requires UI
|
// Requires UI
|
||||||
if (!this.supported.ui) {
|
if (!this.supported.ui) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.element(this.elements.captions)) {
|
if (!utils.is.element(this.elements.captions)) {
|
||||||
var content = utils.createElement('span');
|
|
||||||
|
|
||||||
// Empty the container
|
|
||||||
utils.emptyElement(this.elements.captions);
|
|
||||||
|
|
||||||
// Default to empty
|
|
||||||
var caption = !utils.is.nullOrUndefined(input) ? input : '';
|
|
||||||
|
|
||||||
// Set the span content
|
|
||||||
if (utils.is.string(caption)) {
|
|
||||||
content.innerText = caption.trim();
|
|
||||||
} else {
|
|
||||||
content.appendChild(caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new caption text
|
|
||||||
this.elements.captions.appendChild(content);
|
|
||||||
} else {
|
|
||||||
this.debug.warn('No captions element to render to');
|
this.debug.warn('No captions element to render to');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept array or empty input
|
||||||
|
if (!utils.is.nullOrUndefined(input) && !Array.isArray(input)) {
|
||||||
|
this.debug.warn('updateCues: Invalid input', input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cues = input;
|
||||||
|
|
||||||
|
// Get cues from track
|
||||||
|
if (!cues) {
|
||||||
|
var track = captions.getCurrentTrack.call(this);
|
||||||
|
cues = Array.from((track || {}).activeCues || []).map(function (cue) {
|
||||||
|
return cue.getCueAsHTML();
|
||||||
|
}).map(utils.getHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new caption text
|
||||||
|
var content = cues.map(function (cueText) {
|
||||||
|
return cueText.trim();
|
||||||
|
}).join('\n');
|
||||||
|
var changed = content !== this.elements.captions.innerHTML;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
// Empty the container and create a new child element
|
||||||
|
utils.emptyElement(this.elements.captions);
|
||||||
|
var caption = utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.caption));
|
||||||
|
caption.innerHTML = content;
|
||||||
|
this.elements.captions.appendChild(caption);
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3610,7 +3684,7 @@ var defaults$1 = {
|
|||||||
// Sprite (for icons)
|
// Sprite (for icons)
|
||||||
loadSprite: true,
|
loadSprite: true,
|
||||||
iconPrefix: 'plyr',
|
iconPrefix: 'plyr',
|
||||||
iconUrl: 'https://cdn.plyr.io/3.3.11/plyr.svg',
|
iconUrl: 'https://cdn.plyr.io/3.3.12/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',
|
||||||
@@ -3811,6 +3885,7 @@ var defaults$1 = {
|
|||||||
},
|
},
|
||||||
progress: '.plyr__progress',
|
progress: '.plyr__progress',
|
||||||
captions: '.plyr__captions',
|
captions: '.plyr__captions',
|
||||||
|
caption: '.plyr__caption',
|
||||||
menu: {
|
menu: {
|
||||||
quality: '.js-plyr__menu__list--quality'
|
quality: '.js-plyr__menu__list--quality'
|
||||||
}
|
}
|
||||||
@@ -4426,7 +4501,7 @@ var Listeners = function () {
|
|||||||
// and if the focused element is not editable (e.g. text input)
|
// and if the focused element is not editable (e.g. text input)
|
||||||
// and any that accept key input http://webaim.org/techniques/keyboard/
|
// and any that accept key input http://webaim.org/techniques/keyboard/
|
||||||
var focused = utils.getFocusElement();
|
var focused = utils.getFocusElement();
|
||||||
if (utils.is.element(focused) && utils.matches(focused, this.player.config.selectors.editable)) {
|
if (utils.is.element(focused) && focused !== this.player.elements.inputs.seek && utils.matches(focused, this.player.config.selectors.editable)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4789,9 +4864,11 @@ var Listeners = function () {
|
|||||||
// Proxy events to container
|
// Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
utils.on(this.player.media, this.player.config.events.concat(['keyup', 'keydown']).join(' '), function (event) {
|
utils.on(this.player.media, this.player.config.events.concat(['keyup', 'keydown']).join(' '), function (event) {
|
||||||
var detail = {};
|
var _event$detail = event.detail,
|
||||||
|
detail = _event$detail === undefined ? {} : _event$detail;
|
||||||
|
|
||||||
// Get error details from media
|
// Get error details from media
|
||||||
|
|
||||||
if (event.type === 'error') {
|
if (event.type === 'error') {
|
||||||
detail = _this3.player.media.error;
|
detail = _this3.player.media.error;
|
||||||
}
|
}
|
||||||
@@ -4890,7 +4967,7 @@ var Listeners = function () {
|
|||||||
// 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, function () {
|
proxy(event, function () {
|
||||||
_this4.player.language = event.target.value;
|
_this4.player.currentTrack = Number(event.target.value);
|
||||||
showHomeTab();
|
showHomeTab();
|
||||||
}, 'language');
|
}, '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)) {
|
||||||
@@ -4920,6 +4997,12 @@ var Listeners = function () {
|
|||||||
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
|
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
|
||||||
var seek = event.currentTarget;
|
var seek = event.currentTarget;
|
||||||
|
|
||||||
|
var code = event.keyCode ? event.keyCode : event.which;
|
||||||
|
var eventType = event.type;
|
||||||
|
|
||||||
|
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Was playing before?
|
// Was playing before?
|
||||||
var play = seek.hasAttribute('play-on-seeked');
|
var play = seek.hasAttribute('play-on-seeked');
|
||||||
|
|
||||||
@@ -5079,6 +5162,9 @@ var Listeners = function () {
|
|||||||
|
|
||||||
// Set playback state and trigger change (only on actual change)
|
// Set playback state and trigger change (only on actual change)
|
||||||
function assurePlaybackState(play) {
|
function assurePlaybackState(play) {
|
||||||
|
if (play && !this.embed.hasPlayed) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -5231,24 +5317,25 @@ var vimeo = {
|
|||||||
paused = player.paused,
|
paused = player.paused,
|
||||||
volume = player.volume;
|
volume = player.volume;
|
||||||
|
|
||||||
// Set seeking state and trigger event
|
var restorePause = paused && !embed.hasPlayed;
|
||||||
|
|
||||||
|
// Set seeking state and trigger event
|
||||||
media.seeking = true;
|
media.seeking = true;
|
||||||
utils.dispatchEvent.call(player, media, 'seeking');
|
utils.dispatchEvent.call(player, media, 'seeking');
|
||||||
|
|
||||||
// If paused, mute until seek is complete
|
// If paused, mute until seek is complete
|
||||||
Promise.resolve(paused && embed.setVolume(0))
|
Promise.resolve(restorePause && embed.setVolume(0))
|
||||||
// Seek
|
// Seek
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return embed.setCurrentTime(time);
|
return embed.setCurrentTime(time);
|
||||||
})
|
})
|
||||||
// Restore paused
|
// Restore paused
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return paused && embed.pause();
|
return restorePause && embed.pause();
|
||||||
})
|
})
|
||||||
// Restore volume
|
// Restore volume
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return paused && embed.setVolume(volume);
|
return restorePause && embed.setVolume(volume);
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
});
|
});
|
||||||
@@ -5378,17 +5465,25 @@ var vimeo = {
|
|||||||
captions.setup.call(player);
|
captions.setup.call(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('cuechange', function (data) {
|
player.embed.on('cuechange', function (_ref) {
|
||||||
var cue = null;
|
var _ref$cues = _ref.cues,
|
||||||
|
cues = _ref$cues === undefined ? [] : _ref$cues;
|
||||||
|
|
||||||
if (data.cues.length) {
|
var strippedCues = cues.map(function (cue) {
|
||||||
cue = utils.stripHTML(data.cues[0].text);
|
return utils.stripHTML(cue.text);
|
||||||
}
|
});
|
||||||
|
captions.updateCues.call(player, strippedCues);
|
||||||
captions.setText.call(player, cue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('loaded', function () {
|
player.embed.on('loaded', function () {
|
||||||
|
// Assure state and events are updated on autoplay
|
||||||
|
player.embed.getPaused().then(function (paused) {
|
||||||
|
assurePlaybackState.call(player, !paused);
|
||||||
|
if (!paused) {
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'playing');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
||||||
var frame = player.embed.element;
|
var frame = player.embed.element;
|
||||||
|
|
||||||
@@ -5518,6 +5613,9 @@ function mapQualityUnits(levels) {
|
|||||||
|
|
||||||
// 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) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -5931,7 +6029,7 @@ var youtube = {
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
||||||
if (player.media.paused) {
|
if (player.media.paused && !player.embed.hasPlayed) {
|
||||||
player.media.pause();
|
player.media.pause();
|
||||||
} else {
|
} else {
|
||||||
assurePlaybackState$1.call(player, true);
|
assurePlaybackState$1.call(player, true);
|
||||||
@@ -6922,7 +7020,8 @@ var Plyr = function () {
|
|||||||
// Captions
|
// Captions
|
||||||
this.captions = {
|
this.captions = {
|
||||||
active: null,
|
active: null,
|
||||||
currentTrack: null
|
currentTrack: -1,
|
||||||
|
meta: new WeakMap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fullscreen
|
// Fullscreen
|
||||||
@@ -6933,8 +7032,7 @@ var Plyr = function () {
|
|||||||
// Options
|
// Options
|
||||||
this.options = {
|
this.options = {
|
||||||
speed: [],
|
speed: [],
|
||||||
quality: [],
|
quality: []
|
||||||
captions: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
@@ -7310,8 +7408,8 @@ var Plyr = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the captions language
|
* Set the caption track by index
|
||||||
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
* @param {number} - Caption index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}, {
|
}, {
|
||||||
@@ -8002,60 +8100,41 @@ var Plyr = function () {
|
|||||||
return Boolean(this.config.autoplay);
|
return Boolean(this.config.autoplay);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
key: 'language',
|
key: 'currentTrack',
|
||||||
set: function set$$1(input) {
|
set: function set$$1(input) {
|
||||||
// Nothing specified
|
captions.set.call(this, input);
|
||||||
if (!utils.is.string(input)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If empty string is passed, assume disable captions
|
|
||||||
if (utils.is.empty(input)) {
|
|
||||||
this.toggleCaptions(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize
|
|
||||||
var language = input.toLowerCase();
|
|
||||||
|
|
||||||
// Check for support
|
|
||||||
if (!this.options.captions.includes(language)) {
|
|
||||||
this.debug.warn('Unsupported language option: ' + language);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure captions are enabled
|
|
||||||
this.toggleCaptions(true);
|
|
||||||
|
|
||||||
// Enabled only
|
|
||||||
if (language === 'enabled') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing to change, bail
|
|
||||||
if (this.language === language) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config
|
|
||||||
this.captions.language = language;
|
|
||||||
|
|
||||||
// Clear caption
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
|
|
||||||
// Update captions
|
|
||||||
captions.setLanguage.call(this);
|
|
||||||
|
|
||||||
// Trigger an event
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current captions language
|
* Get the current caption track index (-1 if disabled)
|
||||||
*/
|
*/
|
||||||
,
|
,
|
||||||
get: function get$$1() {
|
get: function get$$1() {
|
||||||
return this.captions.language;
|
var _captions = this.captions,
|
||||||
|
active = _captions.active,
|
||||||
|
currentTrack = _captions.currentTrack;
|
||||||
|
|
||||||
|
return active ? currentTrack : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the wanted language for captions
|
||||||
|
* Since tracks can be added later it won't update the actual caption track until there is a matching track
|
||||||
|
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
||||||
|
*/
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: 'language',
|
||||||
|
set: function set$$1(input) {
|
||||||
|
captions.setLanguage.call(this, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current track's language
|
||||||
|
*/
|
||||||
|
,
|
||||||
|
get: function get$$1() {
|
||||||
|
return (captions.getCurrentTrack.call(this) || {}).language;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8131,9 +8210,7 @@ var Plyr = function () {
|
|||||||
} else if (utils.is.nodeList(selector)) {
|
} else if (utils.is.nodeList(selector)) {
|
||||||
targets = Array.from(selector);
|
targets = Array.from(selector);
|
||||||
} else if (utils.is.array(selector)) {
|
} else if (utils.is.array(selector)) {
|
||||||
targets = selector.filter(function (i) {
|
targets = selector.filter(utils.is.element);
|
||||||
return utils.is.element(i);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.empty(targets)) {
|
if (utils.is.empty(targets)) {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+348
-271
@@ -5876,61 +5876,63 @@ var utils = {
|
|||||||
// Check variable types
|
// Check variable types
|
||||||
is: {
|
is: {
|
||||||
object: function object(input) {
|
object: function object(input) {
|
||||||
return this.getConstructor(input) === Object;
|
return utils.getConstructor(input) === Object;
|
||||||
},
|
},
|
||||||
number: function number(input) {
|
number: function number(input) {
|
||||||
return this.getConstructor(input) === Number && !Number.isNaN(input);
|
return utils.getConstructor(input) === Number && !Number.isNaN(input);
|
||||||
},
|
},
|
||||||
string: function string(input) {
|
string: function string(input) {
|
||||||
return this.getConstructor(input) === String;
|
return utils.getConstructor(input) === String;
|
||||||
},
|
},
|
||||||
boolean: function boolean(input) {
|
boolean: function boolean(input) {
|
||||||
return this.getConstructor(input) === Boolean;
|
return utils.getConstructor(input) === Boolean;
|
||||||
},
|
},
|
||||||
function: function _function(input) {
|
function: function _function(input) {
|
||||||
return this.getConstructor(input) === Function;
|
return utils.getConstructor(input) === Function;
|
||||||
},
|
},
|
||||||
array: function array(input) {
|
array: function array(input) {
|
||||||
return !this.nullOrUndefined(input) && Array.isArray(input);
|
return !utils.is.nullOrUndefined(input) && Array.isArray(input);
|
||||||
},
|
},
|
||||||
weakMap: function weakMap(input) {
|
weakMap: function weakMap(input) {
|
||||||
return this.instanceof(input, WeakMap);
|
return utils.is.instanceof(input, WeakMap);
|
||||||
},
|
},
|
||||||
nodeList: function nodeList(input) {
|
nodeList: function nodeList(input) {
|
||||||
return this.instanceof(input, NodeList);
|
return utils.is.instanceof(input, NodeList);
|
||||||
},
|
},
|
||||||
element: function element(input) {
|
element: function element(input) {
|
||||||
return this.instanceof(input, Element);
|
return utils.is.instanceof(input, Element);
|
||||||
},
|
},
|
||||||
textNode: function textNode(input) {
|
textNode: function textNode(input) {
|
||||||
return this.getConstructor(input) === Text;
|
return utils.getConstructor(input) === Text;
|
||||||
},
|
},
|
||||||
event: function event(input) {
|
event: function event(input) {
|
||||||
return this.instanceof(input, Event);
|
return utils.is.instanceof(input, Event);
|
||||||
},
|
},
|
||||||
cue: function cue(input) {
|
cue: function cue(input) {
|
||||||
return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue);
|
return utils.is.instanceof(input, window.TextTrackCue) || utils.is.instanceof(input, window.VTTCue);
|
||||||
},
|
},
|
||||||
track: function track(input) {
|
track: function track(input) {
|
||||||
return this.instanceof(input, TextTrack) || !this.nullOrUndefined(input) && this.string(input.kind);
|
return utils.is.instanceof(input, TextTrack) || !utils.is.nullOrUndefined(input) && utils.is.string(input.kind);
|
||||||
},
|
},
|
||||||
url: function url(input) {
|
url: function url(input) {
|
||||||
return !this.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
return !utils.is.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
||||||
},
|
},
|
||||||
nullOrUndefined: function nullOrUndefined(input) {
|
nullOrUndefined: function nullOrUndefined(input) {
|
||||||
return input === null || typeof input === 'undefined';
|
return input === null || typeof input === 'undefined';
|
||||||
},
|
},
|
||||||
empty: function empty(input) {
|
empty: function empty(input) {
|
||||||
return this.nullOrUndefined(input) || (this.string(input) || this.array(input) || this.nodeList(input)) && !input.length || this.object(input) && !Object.keys(input).length;
|
return utils.is.nullOrUndefined(input) || (utils.is.string(input) || utils.is.array(input) || utils.is.nodeList(input)) && !input.length || utils.is.object(input) && !Object.keys(input).length;
|
||||||
},
|
},
|
||||||
instanceof: function _instanceof$$1(input, constructor) {
|
instanceof: function _instanceof$$1(input, constructor) {
|
||||||
return Boolean(input && constructor && input instanceof constructor);
|
return Boolean(input && constructor && input instanceof constructor);
|
||||||
},
|
|
||||||
getConstructor: function getConstructor(input) {
|
|
||||||
return !this.nullOrUndefined(input) ? input.constructor : null;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getConstructor: function getConstructor(input) {
|
||||||
|
return !utils.is.nullOrUndefined(input) ? input.constructor : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Unfortunately, due to mixed support, UA sniffing is required
|
// Unfortunately, due to mixed support, UA sniffing is required
|
||||||
getBrowser: function getBrowser() {
|
getBrowser: function getBrowser() {
|
||||||
return {
|
return {
|
||||||
@@ -6020,26 +6022,25 @@ var utils = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix = 'cache-';
|
var prefix = 'cache';
|
||||||
var hasId = utils.is.string(id);
|
var hasId = utils.is.string(id);
|
||||||
var isCached = false;
|
var isCached = false;
|
||||||
|
|
||||||
var exists = function exists() {
|
var exists = function exists() {
|
||||||
return document.querySelectorAll('#' + id).length;
|
return document.getElementById(id) !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function injectSprite(data) {
|
var update = function update(container, data) {
|
||||||
|
container.innerHTML = data;
|
||||||
|
|
||||||
// Check again incase of race condition
|
// Check again incase of race condition
|
||||||
if (hasId && exists()) {
|
if (hasId && exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject content
|
|
||||||
this.innerHTML = data;
|
|
||||||
|
|
||||||
// Inject the SVG to the body
|
// Inject the SVG to the body
|
||||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
document.body.insertAdjacentElement('afterbegin', container);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Only load once if ID set
|
// Only load once if ID set
|
||||||
if (!hasId || !exists()) {
|
if (!hasId || !exists()) {
|
||||||
@@ -6055,13 +6056,12 @@ var utils = {
|
|||||||
|
|
||||||
// Check in cache
|
// Check in cache
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
var cached = window.localStorage.getItem(prefix + id);
|
var cached = window.localStorage.getItem(prefix + '-' + id);
|
||||||
isCached = cached !== null;
|
isCached = cached !== null;
|
||||||
|
|
||||||
if (isCached) {
|
if (isCached) {
|
||||||
var data = JSON.parse(cached);
|
var data = JSON.parse(cached);
|
||||||
injectSprite.call(container, data.content);
|
update(container, data.content);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6072,12 +6072,12 @@ var utils = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
window.localStorage.setItem(prefix + id, JSON.stringify({
|
window.localStorage.setItem(prefix + '-' + id, JSON.stringify({
|
||||||
content: result
|
content: result
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
injectSprite.call(container, result);
|
update(container, result);
|
||||||
}).catch(function () {});
|
}).catch(function () {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -6552,7 +6552,7 @@ var utils = {
|
|||||||
|
|
||||||
// Bail if the value isn't a number
|
// Bail if the value isn't a number
|
||||||
if (!utils.is.number(time)) {
|
if (!utils.is.number(time)) {
|
||||||
return this.formatTime(null, displayHours, inverted);
|
return utils.formatTime(null, displayHours, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format time component to add leading zero
|
// Format time component to add leading zero
|
||||||
@@ -6561,9 +6561,9 @@ var utils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Breakdown to hours, mins, secs
|
// Breakdown to hours, mins, secs
|
||||||
var hours = this.getHours(time);
|
var hours = utils.getHours(time);
|
||||||
var mins = this.getMinutes(time);
|
var mins = utils.getMinutes(time);
|
||||||
var secs = this.getSeconds(time);
|
var secs = utils.getSeconds(time);
|
||||||
|
|
||||||
// Do we need to display hours?
|
// Do we need to display hours?
|
||||||
if (displayHours || hours > 0) {
|
if (displayHours || hours > 0) {
|
||||||
@@ -6759,12 +6759,12 @@ var utils = {
|
|||||||
|
|
||||||
// Parse URL if needed
|
// Parse URL if needed
|
||||||
if (input.startsWith('http://') || input.startsWith('https://')) {
|
if (input.startsWith('http://') || input.startsWith('https://')) {
|
||||||
var _parseUrl = this.parseUrl(input);
|
var _utils$parseUrl = utils.parseUrl(input);
|
||||||
|
|
||||||
search = _parseUrl.search;
|
search = _utils$parseUrl.search;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.is.empty(search)) {
|
if (utils.is.empty(search)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6803,6 +6803,14 @@ var utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Like outerHTML, but also works for DocumentFragment
|
||||||
|
getHTML: function getHTML(element) {
|
||||||
|
var wrapper = document.createElement('div');
|
||||||
|
wrapper.appendChild(element);
|
||||||
|
return wrapper.innerHTML;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Get aspect ratio for dimensions
|
// Get aspect ratio for dimensions
|
||||||
getAspectRatio: function getAspectRatio(width, height) {
|
getAspectRatio: function getAspectRatio(width, height) {
|
||||||
var getRatio = function getRatio(w, h) {
|
var getRatio = function getRatio(w, h) {
|
||||||
@@ -7544,9 +7552,15 @@ var controls = {
|
|||||||
|
|
||||||
|
|
||||||
// Create a settings menu item
|
// Create a settings menu item
|
||||||
createMenuItem: function createMenuItem(value, list, type, title) {
|
createMenuItem: function createMenuItem(_ref) {
|
||||||
var badge = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
|
var value = _ref.value,
|
||||||
var checked = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
list = _ref.list,
|
||||||
|
type = _ref.type,
|
||||||
|
title = _ref.title,
|
||||||
|
_ref$badge = _ref.badge,
|
||||||
|
badge = _ref$badge === undefined ? null : _ref$badge,
|
||||||
|
_ref$checked = _ref.checked,
|
||||||
|
checked = _ref$checked === undefined ? false : _ref$checked;
|
||||||
|
|
||||||
var item = utils.createElement('li');
|
var item = utils.createElement('li');
|
||||||
|
|
||||||
@@ -7862,8 +7876,13 @@ var controls = {
|
|||||||
var sorting = _this3.config.quality.options;
|
var sorting = _this3.config.quality.options;
|
||||||
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
||||||
}).forEach(function (quality) {
|
}).forEach(function (quality) {
|
||||||
var label = controls.getLabel.call(_this3, 'quality', quality);
|
controls.createMenuItem.call(_this3, {
|
||||||
controls.createMenuItem.call(_this3, quality, list, type, label, getBadge(quality));
|
value: quality,
|
||||||
|
list: list,
|
||||||
|
type: type,
|
||||||
|
title: controls.getLabel.call(_this3, 'quality', quality),
|
||||||
|
badge: getBadge(quality)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
@@ -7906,18 +7925,7 @@ var controls = {
|
|||||||
|
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case 'captions':
|
case 'captions':
|
||||||
if (this.captions.active) {
|
value = this.currentTrack;
|
||||||
if (this.options.captions.length > 2 || !this.options.captions.some(function (lang) {
|
|
||||||
return lang === 'enabled';
|
|
||||||
})) {
|
|
||||||
value = this.captions.language;
|
|
||||||
} else {
|
|
||||||
value = 'enabled';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -8012,10 +8020,10 @@ var controls = {
|
|||||||
// TODO: Captions or language? Currently it's mixed
|
// TODO: Captions or language? Currently it's mixed
|
||||||
var type = 'captions';
|
var type = 'captions';
|
||||||
var list = this.elements.settings.panes.captions.querySelector('ul');
|
var list = this.elements.settings.panes.captions.querySelector('ul');
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
|
||||||
// Toggle the pane and tab
|
// Toggle the pane and tab
|
||||||
var toggle = captions.getTracks.call(this).length;
|
controls.toggleTab.call(this, type, tracks.length);
|
||||||
controls.toggleTab.call(this, type, toggle);
|
|
||||||
|
|
||||||
// Empty the menu
|
// Empty the menu
|
||||||
utils.emptyElement(list);
|
utils.emptyElement(list);
|
||||||
@@ -8024,28 +8032,33 @@ var controls = {
|
|||||||
controls.checkMenu.call(this);
|
controls.checkMenu.call(this);
|
||||||
|
|
||||||
// If there's no captions, bail
|
// If there's no captions, bail
|
||||||
if (!toggle) {
|
if (!tracks.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-map the tracks into just the data we need
|
// Generate options data
|
||||||
var tracks = captions.getTracks.call(this).map(function (track) {
|
var options = tracks.map(function (track, value) {
|
||||||
return {
|
return {
|
||||||
language: !utils.is.empty(track.language) ? track.language : 'enabled',
|
value: value,
|
||||||
label: captions.getLabel.call(_this4, track)
|
checked: _this4.captions.active && _this4.currentTrack === value,
|
||||||
|
title: captions.getLabel.call(_this4, track),
|
||||||
|
badge: track.language && controls.createBadge.call(_this4, track.language.toUpperCase()),
|
||||||
|
list: list,
|
||||||
|
type: 'language'
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the "Disabled" option to turn off captions
|
// Add the "Disabled" option to turn off captions
|
||||||
tracks.unshift({
|
options.unshift({
|
||||||
language: '',
|
value: -1,
|
||||||
label: i18n.get('disabled', this.config)
|
checked: !this.captions.active,
|
||||||
|
title: i18n.get('disabled', this.config),
|
||||||
|
list: list,
|
||||||
|
type: 'language'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate options
|
// Generate options
|
||||||
tracks.forEach(function (track) {
|
options.forEach(controls.createMenuItem.bind(this));
|
||||||
controls.createMenuItem.call(_this4, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this4, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this4.language);
|
|
||||||
});
|
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
},
|
},
|
||||||
@@ -8099,8 +8112,12 @@ var controls = {
|
|||||||
|
|
||||||
// Create items
|
// Create items
|
||||||
this.options.speed.forEach(function (speed) {
|
this.options.speed.forEach(function (speed) {
|
||||||
var label = controls.getLabel.call(_this5, 'speed', speed);
|
controls.createMenuItem.call(_this5, {
|
||||||
controls.createMenuItem.call(_this5, speed, list, type, label);
|
value: speed,
|
||||||
|
list: list,
|
||||||
|
type: type,
|
||||||
|
title: controls.getLabel.call(_this5, 'speed', speed)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
@@ -8574,10 +8591,10 @@ var controls = {
|
|||||||
var replace = function replace(input) {
|
var replace = function replace(input) {
|
||||||
var result = input;
|
var result = input;
|
||||||
|
|
||||||
Object.entries(props).forEach(function (_ref) {
|
Object.entries(props).forEach(function (_ref2) {
|
||||||
var _ref2 = slicedToArray(_ref, 2),
|
var _ref3 = slicedToArray(_ref2, 2),
|
||||||
key = _ref2[0],
|
key = _ref3[0],
|
||||||
value = _ref2[1];
|
value = _ref3[1];
|
||||||
|
|
||||||
result = utils.replaceAll(result, '{' + key + '}', value);
|
result = utils.replaceAll(result, '{' + key + '}', value);
|
||||||
});
|
});
|
||||||
@@ -8694,91 +8711,168 @@ var captions = {
|
|||||||
active = this.config.captions.active;
|
active = this.config.captions.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set toggled state
|
// Get language from storage, fallback to config
|
||||||
this.toggleCaptions(active);
|
var language = this.storage.get('language') || this.config.captions.language;
|
||||||
|
if (language === 'auto') {
|
||||||
|
var _split = (navigator.language || navigator.userLanguage).split('-');
|
||||||
|
|
||||||
|
var _split2 = slicedToArray(_split, 1);
|
||||||
|
|
||||||
|
language = _split2[0];
|
||||||
|
}
|
||||||
|
// Set language and show if active
|
||||||
|
captions.setLanguage.call(this, language, active);
|
||||||
|
|
||||||
// Watch changes to textTracks and update captions menu
|
// Watch changes to textTracks and update captions menu
|
||||||
if (this.config.captions.update) {
|
if (this.isHTML5) {
|
||||||
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
|
var trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
|
||||||
|
utils.on(this.media.textTracks, trackEvents, captions.update.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
||||||
setTimeout(captions.update.bind(this), 0);
|
setTimeout(captions.update.bind(this), 0);
|
||||||
},
|
},
|
||||||
update: function update() {
|
update: function update() {
|
||||||
// Update tracks
|
var _this = this;
|
||||||
var tracks = captions.getTracks.call(this);
|
|
||||||
this.options.captions = tracks.map(function (_ref) {
|
|
||||||
var language = _ref.language;
|
|
||||||
return language;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set language if it hasn't been set already
|
var tracks = captions.getTracks.call(this, true);
|
||||||
if (!this.language) {
|
// Get the wanted language
|
||||||
var language = this.config.captions.language;
|
var _captions = this.captions,
|
||||||
|
language = _captions.language,
|
||||||
|
meta = _captions.meta;
|
||||||
|
|
||||||
if (language === 'auto') {
|
// Handle tracks (add event listener and "pseudo"-default)
|
||||||
var _split = (navigator.language || navigator.userLanguage).split('-');
|
|
||||||
|
|
||||||
var _split2 = slicedToArray(_split, 1);
|
if (this.isHTML5 && this.isVideo) {
|
||||||
|
tracks.filter(function (track) {
|
||||||
|
return !meta.get(track);
|
||||||
|
}).forEach(function (track) {
|
||||||
|
_this.debug.log('Track added', track);
|
||||||
|
// Attempt to store if the original dom element was "default"
|
||||||
|
meta.set(track, {
|
||||||
|
default: track.mode === 'showing'
|
||||||
|
});
|
||||||
|
|
||||||
language = _split2[0];
|
// Turn off native caption rendering to avoid double captions
|
||||||
}
|
track.mode = 'hidden';
|
||||||
this.language = this.storage.get('language') || (language || '').toLowerCase();
|
|
||||||
|
// Add event listener for cue changes
|
||||||
|
utils.on(track, 'cuechange', function () {
|
||||||
|
return captions.updateCues.call(_this);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle the class hooks
|
var trackRemoved = !tracks.find(function (track) {
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
|
return track === _this.captions.currentTrackNode;
|
||||||
|
});
|
||||||
|
var firstMatch = this.language !== language && tracks.find(function (track) {
|
||||||
|
return track.language === language;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update language if removed or first matching track added
|
||||||
|
if (trackRemoved || firstMatch) {
|
||||||
|
captions.setLanguage.call(this, language, this.config.captions.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable captions based on track length
|
||||||
|
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(tracks));
|
||||||
|
|
||||||
// Update available languages in list
|
// Update available languages in list
|
||||||
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
||||||
controls.setCaptionsMenu.call(this);
|
controls.setCaptionsMenu.call(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
set: function set(index) {
|
||||||
|
var setLanguage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
||||||
|
var show = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
||||||
|
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
|
||||||
// Set the captions language
|
// Disable captions if setting to -1
|
||||||
setLanguage: function setLanguage() {
|
if (index === -1) {
|
||||||
var _this = this;
|
this.toggleCaptions(false);
|
||||||
|
return;
|
||||||
// Setup HTML5 track rendering
|
|
||||||
if (this.isHTML5 && this.isVideo) {
|
|
||||||
captions.getTracks.call(this).forEach(function (track) {
|
|
||||||
// Show track
|
|
||||||
utils.on(track, 'cuechange', function (event) {
|
|
||||||
return captions.setCue.call(_this, event);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Turn off native caption rendering to avoid double captions
|
|
||||||
// eslint-disable-next-line
|
|
||||||
track.mode = 'hidden';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get current track
|
|
||||||
var currentTrack = captions.getCurrentTrack.call(this);
|
|
||||||
|
|
||||||
// Check if suported kind
|
|
||||||
if (utils.is.track(currentTrack)) {
|
|
||||||
// If we change the active track while a cue is already displayed we need to update it
|
|
||||||
if (Array.from(currentTrack.activeCues || []).length) {
|
|
||||||
captions.setCue.call(this, currentTrack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.isVimeo && this.captions.active) {
|
|
||||||
this.embed.enableTextTrack(this.language);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!utils.is.number(index)) {
|
||||||
|
this.debug.warn('Invalid caption argument', index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(index in tracks)) {
|
||||||
|
this.debug.warn('Track not found', index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.captions.currentTrack !== index) {
|
||||||
|
this.captions.currentTrack = index;
|
||||||
|
var track = captions.getCurrentTrack.call(this);
|
||||||
|
|
||||||
|
var _ref = track || {},
|
||||||
|
language = _ref.language;
|
||||||
|
|
||||||
|
// Store reference to node for invalidation on remove
|
||||||
|
|
||||||
|
|
||||||
|
this.captions.currentTrackNode = track;
|
||||||
|
|
||||||
|
// Prevent setting language in some cases, since it can violate user's intentions
|
||||||
|
if (setLanguage) {
|
||||||
|
this.captions.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Vimeo captions
|
||||||
|
if (this.isVimeo) {
|
||||||
|
this.embed.enableTextTrack(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isHTML5 && this.isVideo) {
|
||||||
|
// If we change the active track while a cue is already displayed we need to update it
|
||||||
|
captions.updateCues.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show captions
|
||||||
|
if (show) {
|
||||||
|
this.toggleCaptions(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setLanguage: function setLanguage(language) {
|
||||||
|
var show = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
||||||
|
|
||||||
|
if (!utils.is.string(language)) {
|
||||||
|
this.debug.warn('Invalid language argument', language);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Normalize
|
||||||
|
this.captions.language = language.toLowerCase();
|
||||||
|
|
||||||
|
// Set currentTrack
|
||||||
|
var tracks = captions.getTracks.call(this);
|
||||||
|
var track = captions.getCurrentTrack.call(this, true);
|
||||||
|
captions.set.call(this, tracks.indexOf(track), false, show);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Get the tracks
|
// Get current valid caption tracks
|
||||||
|
// If update is false it will also ignore tracks without metadata
|
||||||
|
// This is used to "freeze" the language options when captions.update is false
|
||||||
getTracks: function getTracks() {
|
getTracks: function getTracks() {
|
||||||
// Return empty array at least
|
var _this2 = this;
|
||||||
if (utils.is.nullOrUndefined(this.media)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only get accepted kinds
|
var update = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
return Array.from(this.media.textTracks || []).filter(function (track) {
|
|
||||||
|
// Handle media or textTracks missing or null
|
||||||
|
var tracks = Array.from((this.media || {}).textTracks || []);
|
||||||
|
// For HTML5, use cache instead of current tracks when it exists (if captions.update is false)
|
||||||
|
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
|
||||||
|
return tracks.filter(function (track) {
|
||||||
|
return !_this2.isHTML5 || update || _this2.captions.meta.has(track);
|
||||||
|
}).filter(function (track) {
|
||||||
return ['captions', 'subtitles'].includes(track.kind);
|
return ['captions', 'subtitles'].includes(track.kind);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -8786,32 +8880,20 @@ var captions = {
|
|||||||
|
|
||||||
// Get the current track for the current language
|
// Get the current track for the current language
|
||||||
getCurrentTrack: function getCurrentTrack() {
|
getCurrentTrack: function getCurrentTrack() {
|
||||||
var _this2 = this;
|
var _this3 = this;
|
||||||
|
|
||||||
|
var fromLanguage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
||||||
|
|
||||||
var tracks = captions.getTracks.call(this);
|
var tracks = captions.getTracks.call(this);
|
||||||
|
var sortIsDefault = function sortIsDefault(track) {
|
||||||
if (!tracks.length) {
|
return Number((_this3.captions.meta.get(track) || {}).default);
|
||||||
return null;
|
};
|
||||||
}
|
var sorted = Array.from(tracks).sort(function (a, b) {
|
||||||
|
return sortIsDefault(b) - sortIsDefault(a);
|
||||||
// Get track based on current language
|
|
||||||
var track = tracks.find(function (track) {
|
|
||||||
return track.language.toLowerCase() === _this2.language;
|
|
||||||
});
|
});
|
||||||
|
return !fromLanguage && tracks[this.currentTrack] || sorted.find(function (track) {
|
||||||
// Get the <track> with default attribute
|
return track.language === _this3.captions.language;
|
||||||
if (!track) {
|
}) || sorted[0];
|
||||||
track = utils.getElement.call(this, 'track[default]');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first track
|
|
||||||
if (!track) {
|
|
||||||
var _tracks = slicedToArray(tracks, 1);
|
|
||||||
|
|
||||||
track = _tracks[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return track;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@@ -8839,58 +8921,50 @@ var captions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Display active caption if it contains text
|
// Update captions using current track's active cues
|
||||||
setCue: function setCue(input) {
|
// Also optional array argument in case there isn't any track (ex: vimeo)
|
||||||
// Get the track from the event if needed
|
updateCues: function updateCues(input) {
|
||||||
var track = utils.is.event(input) ? input.target : input;
|
|
||||||
var activeCues = track.activeCues;
|
|
||||||
|
|
||||||
var active = activeCues.length && activeCues[0];
|
|
||||||
var currentTrack = captions.getCurrentTrack.call(this);
|
|
||||||
|
|
||||||
// Only display current track
|
|
||||||
if (track !== currentTrack) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display a cue, if there is one
|
|
||||||
if (utils.is.cue(active)) {
|
|
||||||
captions.setText.call(this, active.getCueAsHTML());
|
|
||||||
} else {
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// Set the current caption
|
|
||||||
setText: function setText(input) {
|
|
||||||
// Requires UI
|
// Requires UI
|
||||||
if (!this.supported.ui) {
|
if (!this.supported.ui) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.element(this.elements.captions)) {
|
if (!utils.is.element(this.elements.captions)) {
|
||||||
var content = utils.createElement('span');
|
|
||||||
|
|
||||||
// Empty the container
|
|
||||||
utils.emptyElement(this.elements.captions);
|
|
||||||
|
|
||||||
// Default to empty
|
|
||||||
var caption = !utils.is.nullOrUndefined(input) ? input : '';
|
|
||||||
|
|
||||||
// Set the span content
|
|
||||||
if (utils.is.string(caption)) {
|
|
||||||
content.innerText = caption.trim();
|
|
||||||
} else {
|
|
||||||
content.appendChild(caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new caption text
|
|
||||||
this.elements.captions.appendChild(content);
|
|
||||||
} else {
|
|
||||||
this.debug.warn('No captions element to render to');
|
this.debug.warn('No captions element to render to');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept array or empty input
|
||||||
|
if (!utils.is.nullOrUndefined(input) && !Array.isArray(input)) {
|
||||||
|
this.debug.warn('updateCues: Invalid input', input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cues = input;
|
||||||
|
|
||||||
|
// Get cues from track
|
||||||
|
if (!cues) {
|
||||||
|
var track = captions.getCurrentTrack.call(this);
|
||||||
|
cues = Array.from((track || {}).activeCues || []).map(function (cue) {
|
||||||
|
return cue.getCueAsHTML();
|
||||||
|
}).map(utils.getHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new caption text
|
||||||
|
var content = cues.map(function (cueText) {
|
||||||
|
return cueText.trim();
|
||||||
|
}).join('\n');
|
||||||
|
var changed = content !== this.elements.captions.innerHTML;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
// Empty the container and create a new child element
|
||||||
|
utils.emptyElement(this.elements.captions);
|
||||||
|
var caption = utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.caption));
|
||||||
|
caption.innerHTML = content;
|
||||||
|
this.elements.captions.appendChild(caption);
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -8993,7 +9067,7 @@ var defaults$1 = {
|
|||||||
// Sprite (for icons)
|
// Sprite (for icons)
|
||||||
loadSprite: true,
|
loadSprite: true,
|
||||||
iconPrefix: 'plyr',
|
iconPrefix: 'plyr',
|
||||||
iconUrl: 'https://cdn.plyr.io/3.3.11/plyr.svg',
|
iconUrl: 'https://cdn.plyr.io/3.3.12/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',
|
||||||
@@ -9194,6 +9268,7 @@ var defaults$1 = {
|
|||||||
},
|
},
|
||||||
progress: '.plyr__progress',
|
progress: '.plyr__progress',
|
||||||
captions: '.plyr__captions',
|
captions: '.plyr__captions',
|
||||||
|
caption: '.plyr__caption',
|
||||||
menu: {
|
menu: {
|
||||||
quality: '.js-plyr__menu__list--quality'
|
quality: '.js-plyr__menu__list--quality'
|
||||||
}
|
}
|
||||||
@@ -9809,7 +9884,7 @@ var Listeners = function () {
|
|||||||
// and if the focused element is not editable (e.g. text input)
|
// and if the focused element is not editable (e.g. text input)
|
||||||
// and any that accept key input http://webaim.org/techniques/keyboard/
|
// and any that accept key input http://webaim.org/techniques/keyboard/
|
||||||
var focused = utils.getFocusElement();
|
var focused = utils.getFocusElement();
|
||||||
if (utils.is.element(focused) && utils.matches(focused, this.player.config.selectors.editable)) {
|
if (utils.is.element(focused) && focused !== this.player.elements.inputs.seek && utils.matches(focused, this.player.config.selectors.editable)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10172,9 +10247,11 @@ var Listeners = function () {
|
|||||||
// Proxy events to container
|
// Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
utils.on(this.player.media, this.player.config.events.concat(['keyup', 'keydown']).join(' '), function (event) {
|
utils.on(this.player.media, this.player.config.events.concat(['keyup', 'keydown']).join(' '), function (event) {
|
||||||
var detail = {};
|
var _event$detail = event.detail,
|
||||||
|
detail = _event$detail === undefined ? {} : _event$detail;
|
||||||
|
|
||||||
// Get error details from media
|
// Get error details from media
|
||||||
|
|
||||||
if (event.type === 'error') {
|
if (event.type === 'error') {
|
||||||
detail = _this3.player.media.error;
|
detail = _this3.player.media.error;
|
||||||
}
|
}
|
||||||
@@ -10273,7 +10350,7 @@ var Listeners = function () {
|
|||||||
// 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, function () {
|
proxy(event, function () {
|
||||||
_this4.player.language = event.target.value;
|
_this4.player.currentTrack = Number(event.target.value);
|
||||||
showHomeTab();
|
showHomeTab();
|
||||||
}, 'language');
|
}, '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)) {
|
||||||
@@ -10303,6 +10380,12 @@ var Listeners = function () {
|
|||||||
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
|
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
|
||||||
var seek = event.currentTarget;
|
var seek = event.currentTarget;
|
||||||
|
|
||||||
|
var code = event.keyCode ? event.keyCode : event.which;
|
||||||
|
var eventType = event.type;
|
||||||
|
|
||||||
|
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Was playing before?
|
// Was playing before?
|
||||||
var play = seek.hasAttribute('play-on-seeked');
|
var play = seek.hasAttribute('play-on-seeked');
|
||||||
|
|
||||||
@@ -10462,6 +10545,9 @@ var Listeners = function () {
|
|||||||
|
|
||||||
// Set playback state and trigger change (only on actual change)
|
// Set playback state and trigger change (only on actual change)
|
||||||
function assurePlaybackState(play) {
|
function assurePlaybackState(play) {
|
||||||
|
if (play && !this.embed.hasPlayed) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -10614,24 +10700,25 @@ var vimeo = {
|
|||||||
paused = player.paused,
|
paused = player.paused,
|
||||||
volume = player.volume;
|
volume = player.volume;
|
||||||
|
|
||||||
// Set seeking state and trigger event
|
var restorePause = paused && !embed.hasPlayed;
|
||||||
|
|
||||||
|
// Set seeking state and trigger event
|
||||||
media.seeking = true;
|
media.seeking = true;
|
||||||
utils.dispatchEvent.call(player, media, 'seeking');
|
utils.dispatchEvent.call(player, media, 'seeking');
|
||||||
|
|
||||||
// If paused, mute until seek is complete
|
// If paused, mute until seek is complete
|
||||||
Promise.resolve(paused && embed.setVolume(0))
|
Promise.resolve(restorePause && embed.setVolume(0))
|
||||||
// Seek
|
// Seek
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return embed.setCurrentTime(time);
|
return embed.setCurrentTime(time);
|
||||||
})
|
})
|
||||||
// Restore paused
|
// Restore paused
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return paused && embed.pause();
|
return restorePause && embed.pause();
|
||||||
})
|
})
|
||||||
// Restore volume
|
// Restore volume
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return paused && embed.setVolume(volume);
|
return restorePause && embed.setVolume(volume);
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
});
|
});
|
||||||
@@ -10761,17 +10848,25 @@ var vimeo = {
|
|||||||
captions.setup.call(player);
|
captions.setup.call(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('cuechange', function (data) {
|
player.embed.on('cuechange', function (_ref) {
|
||||||
var cue = null;
|
var _ref$cues = _ref.cues,
|
||||||
|
cues = _ref$cues === undefined ? [] : _ref$cues;
|
||||||
|
|
||||||
if (data.cues.length) {
|
var strippedCues = cues.map(function (cue) {
|
||||||
cue = utils.stripHTML(data.cues[0].text);
|
return utils.stripHTML(cue.text);
|
||||||
}
|
});
|
||||||
|
captions.updateCues.call(player, strippedCues);
|
||||||
captions.setText.call(player, cue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('loaded', function () {
|
player.embed.on('loaded', function () {
|
||||||
|
// Assure state and events are updated on autoplay
|
||||||
|
player.embed.getPaused().then(function (paused) {
|
||||||
|
assurePlaybackState.call(player, !paused);
|
||||||
|
if (!paused) {
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'playing');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
||||||
var frame = player.embed.element;
|
var frame = player.embed.element;
|
||||||
|
|
||||||
@@ -10901,6 +10996,9 @@ function mapQualityUnits(levels) {
|
|||||||
|
|
||||||
// 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) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -11314,7 +11412,7 @@ var youtube = {
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
||||||
if (player.media.paused) {
|
if (player.media.paused && !player.embed.hasPlayed) {
|
||||||
player.media.pause();
|
player.media.pause();
|
||||||
} else {
|
} else {
|
||||||
assurePlaybackState$1.call(player, true);
|
assurePlaybackState$1.call(player, true);
|
||||||
@@ -12305,7 +12403,8 @@ var Plyr = function () {
|
|||||||
// Captions
|
// Captions
|
||||||
this.captions = {
|
this.captions = {
|
||||||
active: null,
|
active: null,
|
||||||
currentTrack: null
|
currentTrack: -1,
|
||||||
|
meta: new WeakMap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fullscreen
|
// Fullscreen
|
||||||
@@ -12316,8 +12415,7 @@ var Plyr = function () {
|
|||||||
// Options
|
// Options
|
||||||
this.options = {
|
this.options = {
|
||||||
speed: [],
|
speed: [],
|
||||||
quality: [],
|
quality: []
|
||||||
captions: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
@@ -12693,8 +12791,8 @@ var Plyr = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the captions language
|
* Set the caption track by index
|
||||||
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
* @param {number} - Caption index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}, {
|
}, {
|
||||||
@@ -13385,60 +13483,41 @@ var Plyr = function () {
|
|||||||
return Boolean(this.config.autoplay);
|
return Boolean(this.config.autoplay);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
key: 'language',
|
key: 'currentTrack',
|
||||||
set: function set(input) {
|
set: function set(input) {
|
||||||
// Nothing specified
|
captions.set.call(this, input);
|
||||||
if (!utils.is.string(input)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If empty string is passed, assume disable captions
|
|
||||||
if (utils.is.empty(input)) {
|
|
||||||
this.toggleCaptions(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize
|
|
||||||
var language = input.toLowerCase();
|
|
||||||
|
|
||||||
// Check for support
|
|
||||||
if (!this.options.captions.includes(language)) {
|
|
||||||
this.debug.warn('Unsupported language option: ' + language);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure captions are enabled
|
|
||||||
this.toggleCaptions(true);
|
|
||||||
|
|
||||||
// Enabled only
|
|
||||||
if (language === 'enabled') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing to change, bail
|
|
||||||
if (this.language === language) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config
|
|
||||||
this.captions.language = language;
|
|
||||||
|
|
||||||
// Clear caption
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
|
|
||||||
// Update captions
|
|
||||||
captions.setLanguage.call(this);
|
|
||||||
|
|
||||||
// Trigger an event
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current captions language
|
* Get the current caption track index (-1 if disabled)
|
||||||
*/
|
*/
|
||||||
,
|
,
|
||||||
get: function get() {
|
get: function get() {
|
||||||
return this.captions.language;
|
var _captions = this.captions,
|
||||||
|
active = _captions.active,
|
||||||
|
currentTrack = _captions.currentTrack;
|
||||||
|
|
||||||
|
return active ? currentTrack : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the wanted language for captions
|
||||||
|
* Since tracks can be added later it won't update the actual caption track until there is a matching track
|
||||||
|
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
||||||
|
*/
|
||||||
|
|
||||||
|
}, {
|
||||||
|
key: 'language',
|
||||||
|
set: function set(input) {
|
||||||
|
captions.setLanguage.call(this, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current track's language
|
||||||
|
*/
|
||||||
|
,
|
||||||
|
get: function get() {
|
||||||
|
return (captions.getCurrentTrack.call(this) || {}).language;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13514,9 +13593,7 @@ var Plyr = function () {
|
|||||||
} else if (utils.is.nodeList(selector)) {
|
} else if (utils.is.nodeList(selector)) {
|
||||||
targets = Array.from(selector);
|
targets = Array.from(selector);
|
||||||
} else if (utils.is.array(selector)) {
|
} else if (utils.is.array(selector)) {
|
||||||
targets = selector.filter(function (i) {
|
targets = selector.filter(utils.is.element);
|
||||||
return utils.is.element(i);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.empty(targets)) {
|
if (utils.is.empty(targets)) {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plyr",
|
"name": "plyr",
|
||||||
"version": "3.3.11",
|
"version": "3.3.12",
|
||||||
"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",
|
||||||
|
|||||||
@@ -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.3.11/plyr.js"></script>
|
<script src="https://cdn.plyr.io/3.3.12/plyr.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
...or...
|
...or...
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.3.11/plyr.polyfilled.js"></script>
|
<script src="https://cdn.plyr.io/3.3.12/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.3.11/plyr.css">
|
<link rel="stylesheet" href="https://cdn.plyr.io/3.3.12/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.3.11/plyr.svg`.
|
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.3.12/plyr.svg`.
|
||||||
|
|
||||||
## Ads
|
## Ads
|
||||||
|
|
||||||
@@ -411,7 +411,8 @@ player.fullscreen.active; // false;
|
|||||||
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#source-setter) below for examples. |
|
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#source-setter) below for examples. |
|
||||||
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
|
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
|
||||||
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
||||||
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. |
|
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
|
||||||
|
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. |
|
||||||
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
|
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
|
||||||
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
|
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
|
||||||
| `pip` | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+. |
|
| `pip` | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+. |
|
||||||
|
|||||||
+151
-111
@@ -69,12 +69,18 @@ const captions = {
|
|||||||
({ active } = this.config.captions);
|
({ active } = this.config.captions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set toggled state
|
// Get language from storage, fallback to config
|
||||||
this.toggleCaptions(active);
|
let language = this.storage.get('language') || this.config.captions.language;
|
||||||
|
if (language === 'auto') {
|
||||||
|
[ language ] = (navigator.language || navigator.userLanguage).split('-');
|
||||||
|
}
|
||||||
|
// Set language and show if active
|
||||||
|
captions.setLanguage.call(this, language, active);
|
||||||
|
|
||||||
// Watch changes to textTracks and update captions menu
|
// Watch changes to textTracks and update captions menu
|
||||||
if (this.config.captions.update) {
|
if (this.isHTML5) {
|
||||||
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
|
const trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
|
||||||
|
utils.on(this.media.textTracks, trackEvents, captions.update.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
// Update available languages in list next tick (the event must not be triggered before the listeners)
|
||||||
@@ -82,21 +88,39 @@ const captions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
// Update tracks
|
const tracks = captions.getTracks.call(this, true);
|
||||||
const tracks = captions.getTracks.call(this);
|
// Get the wanted language
|
||||||
this.options.captions = tracks.map(({language}) => language);
|
const { language, meta } = this.captions;
|
||||||
|
|
||||||
// Set language if it hasn't been set already
|
// Handle tracks (add event listener and "pseudo"-default)
|
||||||
if (!this.language) {
|
if (this.isHTML5 && this.isVideo) {
|
||||||
let { language } = this.config.captions;
|
tracks
|
||||||
if (language === 'auto') {
|
.filter(track => !meta.get(track))
|
||||||
[ language ] = (navigator.language || navigator.userLanguage).split('-');
|
.forEach(track => {
|
||||||
}
|
this.debug.log('Track added', track);
|
||||||
this.language = this.storage.get('language') || (language || '').toLowerCase();
|
// Attempt to store if the original dom element was "default"
|
||||||
|
meta.set(track, {
|
||||||
|
default: track.mode === 'showing',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Turn off native caption rendering to avoid double captions
|
||||||
|
track.mode = 'hidden';
|
||||||
|
|
||||||
|
// Add event listener for cue changes
|
||||||
|
utils.on(track, 'cuechange', () => captions.updateCues.call(this));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle the class hooks
|
const trackRemoved = !tracks.find(track => track === this.captions.currentTrackNode);
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
|
const firstMatch = this.language !== language && tracks.find(track => track.language === language);
|
||||||
|
|
||||||
|
// Update language if removed or first matching track added
|
||||||
|
if (trackRemoved || firstMatch) {
|
||||||
|
captions.setLanguage.call(this, language, this.config.captions.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable captions based on track length
|
||||||
|
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(tracks));
|
||||||
|
|
||||||
// Update available languages in list
|
// Update available languages in list
|
||||||
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
|
||||||
@@ -104,70 +128,94 @@ const captions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set the captions language
|
set(index, setLanguage = true, show = true) {
|
||||||
setLanguage() {
|
const tracks = captions.getTracks.call(this);
|
||||||
// Setup HTML5 track rendering
|
|
||||||
if (this.isHTML5 && this.isVideo) {
|
|
||||||
captions.getTracks.call(this).forEach(track => {
|
|
||||||
// Show track
|
|
||||||
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
|
||||||
|
|
||||||
// Turn off native caption rendering to avoid double captions
|
// Disable captions if setting to -1
|
||||||
// eslint-disable-next-line
|
if (index === -1) {
|
||||||
track.mode = 'hidden';
|
this.toggleCaptions(false);
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get current track
|
if (!utils.is.number(index)) {
|
||||||
const currentTrack = captions.getCurrentTrack.call(this);
|
this.debug.warn('Invalid caption argument', index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if suported kind
|
if (!(index in tracks)) {
|
||||||
if (utils.is.track(currentTrack)) {
|
this.debug.warn('Track not found', index);
|
||||||
// If we change the active track while a cue is already displayed we need to update it
|
return;
|
||||||
if (Array.from(currentTrack.activeCues || []).length) {
|
}
|
||||||
captions.setCue.call(this, currentTrack);
|
|
||||||
}
|
if (this.captions.currentTrack !== index) {
|
||||||
|
this.captions.currentTrack = index;
|
||||||
|
const track = captions.getCurrentTrack.call(this);
|
||||||
|
const { language } = track || {};
|
||||||
|
|
||||||
|
// Store reference to node for invalidation on remove
|
||||||
|
this.captions.currentTrackNode = track;
|
||||||
|
|
||||||
|
// Prevent setting language in some cases, since it can violate user's intentions
|
||||||
|
if (setLanguage) {
|
||||||
|
this.captions.language = language;
|
||||||
}
|
}
|
||||||
} else if (this.isVimeo && this.captions.active) {
|
|
||||||
this.embed.enableTextTrack(this.language);
|
// Handle Vimeo captions
|
||||||
|
if (this.isVimeo) {
|
||||||
|
this.embed.enableTextTrack(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isHTML5 && this.isVideo) {
|
||||||
|
// If we change the active track while a cue is already displayed we need to update it
|
||||||
|
captions.updateCues.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show captions
|
||||||
|
if (show) {
|
||||||
|
this.toggleCaptions(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Get the tracks
|
setLanguage(language, show = true) {
|
||||||
getTracks() {
|
if (!utils.is.string(language)) {
|
||||||
// Return empty array at least
|
this.debug.warn('Invalid language argument', language);
|
||||||
if (utils.is.nullOrUndefined(this.media)) {
|
return;
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
// Normalize
|
||||||
|
this.captions.language = language.toLowerCase();
|
||||||
|
|
||||||
// Only get accepted kinds
|
// Set currentTrack
|
||||||
return Array.from(this.media.textTracks || []).filter(track => [
|
const tracks = captions.getTracks.call(this);
|
||||||
'captions',
|
const track = captions.getCurrentTrack.call(this, true);
|
||||||
'subtitles',
|
captions.set.call(this, tracks.indexOf(track), false, show);
|
||||||
].includes(track.kind));
|
},
|
||||||
|
|
||||||
|
// Get current valid caption tracks
|
||||||
|
// If update is false it will also ignore tracks without metadata
|
||||||
|
// This is used to "freeze" the language options when captions.update is false
|
||||||
|
getTracks(update = false) {
|
||||||
|
// Handle media or textTracks missing or null
|
||||||
|
const tracks = Array.from((this.media || {}).textTracks || []);
|
||||||
|
// For HTML5, use cache instead of current tracks when it exists (if captions.update is false)
|
||||||
|
// Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
|
||||||
|
return tracks
|
||||||
|
.filter(track => !this.isHTML5 || update || this.captions.meta.has(track))
|
||||||
|
.filter(track => [
|
||||||
|
'captions',
|
||||||
|
'subtitles',
|
||||||
|
].includes(track.kind));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Get the current track for the current language
|
// Get the current track for the current language
|
||||||
getCurrentTrack() {
|
getCurrentTrack(fromLanguage = false) {
|
||||||
const tracks = captions.getTracks.call(this);
|
const tracks = captions.getTracks.call(this);
|
||||||
|
const sortIsDefault = track => Number((this.captions.meta.get(track) || {}).default);
|
||||||
if (!tracks.length) {
|
const sorted = Array.from(tracks).sort((a, b) => sortIsDefault(b) - sortIsDefault(a));
|
||||||
return null;
|
return (!fromLanguage && tracks[this.currentTrack]) || sorted.find(track => track.language === this.captions.language) || sorted[0];
|
||||||
}
|
|
||||||
|
|
||||||
// Get track based on current language
|
|
||||||
let track = tracks.find(track => track.language.toLowerCase() === this.language);
|
|
||||||
|
|
||||||
// Get the <track> with default attribute
|
|
||||||
if (!track) {
|
|
||||||
track = utils.getElement.call(this, 'track[default]');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first track
|
|
||||||
if (!track) {
|
|
||||||
[track] = tracks;
|
|
||||||
}
|
|
||||||
|
|
||||||
return track;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Get UI label for track
|
// Get UI label for track
|
||||||
@@ -193,56 +241,48 @@ const captions = {
|
|||||||
return i18n.get('disabled', this.config);
|
return i18n.get('disabled', this.config);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Display active caption if it contains text
|
// Update captions using current track's active cues
|
||||||
setCue(input) {
|
// Also optional array argument in case there isn't any track (ex: vimeo)
|
||||||
// Get the track from the event if needed
|
updateCues(input) {
|
||||||
const track = utils.is.event(input) ? input.target : input;
|
|
||||||
const { activeCues } = track;
|
|
||||||
const active = activeCues.length && activeCues[0];
|
|
||||||
const currentTrack = captions.getCurrentTrack.call(this);
|
|
||||||
|
|
||||||
// Only display current track
|
|
||||||
if (track !== currentTrack) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display a cue, if there is one
|
|
||||||
if (utils.is.cue(active)) {
|
|
||||||
captions.setText.call(this, active.getCueAsHTML());
|
|
||||||
} else {
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set the current caption
|
|
||||||
setText(input) {
|
|
||||||
// Requires UI
|
// Requires UI
|
||||||
if (!this.supported.ui) {
|
if (!this.supported.ui) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.element(this.elements.captions)) {
|
if (!utils.is.element(this.elements.captions)) {
|
||||||
const content = utils.createElement('span');
|
|
||||||
|
|
||||||
// Empty the container
|
|
||||||
utils.emptyElement(this.elements.captions);
|
|
||||||
|
|
||||||
// Default to empty
|
|
||||||
const caption = !utils.is.nullOrUndefined(input) ? input : '';
|
|
||||||
|
|
||||||
// Set the span content
|
|
||||||
if (utils.is.string(caption)) {
|
|
||||||
content.innerText = caption.trim();
|
|
||||||
} else {
|
|
||||||
content.appendChild(caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new caption text
|
|
||||||
this.elements.captions.appendChild(content);
|
|
||||||
} else {
|
|
||||||
this.debug.warn('No captions element to render to');
|
this.debug.warn('No captions element to render to');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept array or empty input
|
||||||
|
if (!utils.is.nullOrUndefined(input) && !Array.isArray(input)) {
|
||||||
|
this.debug.warn('updateCues: Invalid input', input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cues = input;
|
||||||
|
|
||||||
|
// Get cues from track
|
||||||
|
if (!cues) {
|
||||||
|
const track = captions.getCurrentTrack.call(this);
|
||||||
|
cues = Array.from((track || {}).activeCues || [])
|
||||||
|
.map(cue => cue.getCueAsHTML())
|
||||||
|
.map(utils.getHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new caption text
|
||||||
|
const content = cues.map(cueText => cueText.trim()).join('\n');
|
||||||
|
const changed = content !== this.elements.captions.innerHTML;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
// Empty the container and create a new child element
|
||||||
|
utils.emptyElement(this.elements.captions);
|
||||||
|
const caption = utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.caption));
|
||||||
|
caption.innerHTML = content;
|
||||||
|
this.elements.captions.appendChild(caption);
|
||||||
|
|
||||||
|
// Trigger event
|
||||||
|
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+33
-36
@@ -376,7 +376,7 @@ const controls = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Create a settings menu item
|
// Create a settings menu item
|
||||||
createMenuItem(value, list, type, title, badge = null, checked = false) {
|
createMenuItem({value, list, type, title, badge = null, checked = false}) {
|
||||||
const item = utils.createElement('li');
|
const item = utils.createElement('li');
|
||||||
|
|
||||||
const label = utils.createElement('label', {
|
const label = utils.createElement('label', {
|
||||||
@@ -680,8 +680,13 @@ const controls = {
|
|||||||
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
|
||||||
})
|
})
|
||||||
.forEach(quality => {
|
.forEach(quality => {
|
||||||
const label = controls.getLabel.call(this, 'quality', quality);
|
controls.createMenuItem.call(this, {
|
||||||
controls.createMenuItem.call(this, quality, list, type, label, getBadge(quality));
|
value: quality,
|
||||||
|
list,
|
||||||
|
type,
|
||||||
|
title: controls.getLabel.call(this, 'quality', quality),
|
||||||
|
badge: getBadge(quality),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
@@ -722,16 +727,7 @@ const controls = {
|
|||||||
|
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case 'captions':
|
case 'captions':
|
||||||
if (this.captions.active) {
|
value = this.currentTrack;
|
||||||
if (this.options.captions.length > 2 || !this.options.captions.some(lang => lang === 'enabled')) {
|
|
||||||
value = this.captions.language;
|
|
||||||
} else {
|
|
||||||
value = 'enabled';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -831,10 +827,10 @@ const controls = {
|
|||||||
// TODO: Captions or language? Currently it's mixed
|
// TODO: Captions or language? Currently it's mixed
|
||||||
const type = 'captions';
|
const type = 'captions';
|
||||||
const list = this.elements.settings.panes.captions.querySelector('ul');
|
const list = this.elements.settings.panes.captions.querySelector('ul');
|
||||||
|
const tracks = captions.getTracks.call(this);
|
||||||
|
|
||||||
// Toggle the pane and tab
|
// Toggle the pane and tab
|
||||||
const toggle = captions.getTracks.call(this).length;
|
controls.toggleTab.call(this, type, tracks.length);
|
||||||
controls.toggleTab.call(this, type, toggle);
|
|
||||||
|
|
||||||
// Empty the menu
|
// Empty the menu
|
||||||
utils.emptyElement(list);
|
utils.emptyElement(list);
|
||||||
@@ -843,34 +839,31 @@ const controls = {
|
|||||||
controls.checkMenu.call(this);
|
controls.checkMenu.call(this);
|
||||||
|
|
||||||
// If there's no captions, bail
|
// If there's no captions, bail
|
||||||
if (!toggle) {
|
if (!tracks.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-map the tracks into just the data we need
|
// Generate options data
|
||||||
const tracks = captions.getTracks.call(this).map(track => ({
|
const options = tracks.map((track, value) => ({
|
||||||
language: !utils.is.empty(track.language) ? track.language : 'enabled',
|
value,
|
||||||
label: captions.getLabel.call(this, track),
|
checked: this.captions.active && this.currentTrack === value,
|
||||||
|
title: captions.getLabel.call(this, track),
|
||||||
|
badge: track.language && controls.createBadge.call(this, track.language.toUpperCase()),
|
||||||
|
list,
|
||||||
|
type: 'language',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Add the "Disabled" option to turn off captions
|
// Add the "Disabled" option to turn off captions
|
||||||
tracks.unshift({
|
options.unshift({
|
||||||
language: '',
|
value: -1,
|
||||||
label: i18n.get('disabled', this.config),
|
checked: !this.captions.active,
|
||||||
|
title: i18n.get('disabled', this.config),
|
||||||
|
list,
|
||||||
|
type: 'language',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate options
|
// Generate options
|
||||||
tracks.forEach(track => {
|
options.forEach(controls.createMenuItem.bind(this));
|
||||||
controls.createMenuItem.call(
|
|
||||||
this,
|
|
||||||
track.language,
|
|
||||||
list,
|
|
||||||
'language',
|
|
||||||
track.label,
|
|
||||||
track.language !== 'enabled' ? controls.createBadge.call(this, track.language.toUpperCase()) : null,
|
|
||||||
track.language.toLowerCase() === this.language,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
},
|
},
|
||||||
@@ -927,8 +920,12 @@ const controls = {
|
|||||||
|
|
||||||
// Create items
|
// Create items
|
||||||
this.options.speed.forEach(speed => {
|
this.options.speed.forEach(speed => {
|
||||||
const label = controls.getLabel.call(this, 'speed', speed);
|
controls.createMenuItem.call(this, {
|
||||||
controls.createMenuItem.call(this, speed, list, type, label);
|
value: speed,
|
||||||
|
list,
|
||||||
|
type,
|
||||||
|
title: controls.getLabel.call(this, 'speed', speed),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controls.updateSetting.call(this, type, list);
|
controls.updateSetting.call(this, type, list);
|
||||||
|
|||||||
+2
-1
@@ -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.3.11/plyr.svg',
|
iconUrl: 'https://cdn.plyr.io/3.3.12/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',
|
||||||
@@ -328,6 +328,7 @@ const defaults = {
|
|||||||
},
|
},
|
||||||
progress: '.plyr__progress',
|
progress: '.plyr__progress',
|
||||||
captions: '.plyr__captions',
|
captions: '.plyr__captions',
|
||||||
|
caption: '.plyr__caption',
|
||||||
menu: {
|
menu: {
|
||||||
quality: '.js-plyr__menu__list--quality',
|
quality: '.js-plyr__menu__list--quality',
|
||||||
},
|
},
|
||||||
|
|||||||
+12
-3
@@ -74,7 +74,10 @@ class Listeners {
|
|||||||
// and if the focused element is not editable (e.g. text input)
|
// and if the focused element is not editable (e.g. text input)
|
||||||
// and any that accept key input http://webaim.org/techniques/keyboard/
|
// and any that accept key input http://webaim.org/techniques/keyboard/
|
||||||
const focused = utils.getFocusElement();
|
const focused = utils.getFocusElement();
|
||||||
if (utils.is.element(focused) && utils.matches(focused, this.player.config.selectors.editable)) {
|
if (utils.is.element(focused) && (
|
||||||
|
focused !== this.player.elements.inputs.seek &&
|
||||||
|
utils.matches(focused, this.player.config.selectors.editable))
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +414,7 @@ class Listeners {
|
|||||||
'keyup',
|
'keyup',
|
||||||
'keydown',
|
'keydown',
|
||||||
]).join(' '), event => {
|
]).join(' '), event => {
|
||||||
let detail = {};
|
let {detail = {}} = event;
|
||||||
|
|
||||||
// Get error details from media
|
// Get error details from media
|
||||||
if (event.type === 'error') {
|
if (event.type === 'error') {
|
||||||
@@ -520,7 +523,7 @@ class Listeners {
|
|||||||
proxy(
|
proxy(
|
||||||
event,
|
event,
|
||||||
() => {
|
() => {
|
||||||
this.player.language = event.target.value;
|
this.player.currentTrack = Number(event.target.value);
|
||||||
showHomeTab();
|
showHomeTab();
|
||||||
},
|
},
|
||||||
'language',
|
'language',
|
||||||
@@ -560,6 +563,12 @@ class Listeners {
|
|||||||
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', event => {
|
on(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', event => {
|
||||||
const seek = event.currentTarget;
|
const seek = event.currentTarget;
|
||||||
|
|
||||||
|
const code = event.keyCode ? event.keyCode : event.which;
|
||||||
|
const eventType = event.type;
|
||||||
|
|
||||||
|
if ((eventType === 'keydown' || eventType === 'keyup') && (code !== 39 && code !== 37)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Was playing before?
|
// Was playing before?
|
||||||
const play = seek.hasAttribute('play-on-seeked');
|
const play = seek.hasAttribute('play-on-seeked');
|
||||||
|
|
||||||
|
|||||||
+18
-11
@@ -9,6 +9,9 @@ import utils from './../utils';
|
|||||||
|
|
||||||
// Set playback state and trigger change (only on actual change)
|
// Set playback state and trigger change (only on actual change)
|
||||||
function assurePlaybackState(play) {
|
function assurePlaybackState(play) {
|
||||||
|
if (play && !this.embed.hasPlayed) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -153,19 +156,20 @@ const vimeo = {
|
|||||||
|
|
||||||
// Get current paused state and volume etc
|
// Get current paused state and volume etc
|
||||||
const { embed, media, paused, volume } = player;
|
const { embed, media, paused, volume } = player;
|
||||||
|
const restorePause = paused && !embed.hasPlayed;
|
||||||
|
|
||||||
// Set seeking state and trigger event
|
// Set seeking state and trigger event
|
||||||
media.seeking = true;
|
media.seeking = true;
|
||||||
utils.dispatchEvent.call(player, media, 'seeking');
|
utils.dispatchEvent.call(player, media, 'seeking');
|
||||||
|
|
||||||
// If paused, mute until seek is complete
|
// If paused, mute until seek is complete
|
||||||
Promise.resolve(paused && embed.setVolume(0))
|
Promise.resolve(restorePause && embed.setVolume(0))
|
||||||
// Seek
|
// Seek
|
||||||
.then(() => embed.setCurrentTime(time))
|
.then(() => embed.setCurrentTime(time))
|
||||||
// Restore paused
|
// Restore paused
|
||||||
.then(() => paused && embed.pause())
|
.then(() => restorePause && embed.pause())
|
||||||
// Restore volume
|
// Restore volume
|
||||||
.then(() => paused && embed.setVolume(volume))
|
.then(() => restorePause && embed.setVolume(volume))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
});
|
});
|
||||||
@@ -301,17 +305,20 @@ const vimeo = {
|
|||||||
captions.setup.call(player);
|
captions.setup.call(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('cuechange', data => {
|
player.embed.on('cuechange', ({ cues = [] }) => {
|
||||||
let cue = null;
|
const strippedCues = cues.map(cue => utils.stripHTML(cue.text));
|
||||||
|
captions.updateCues.call(player, strippedCues);
|
||||||
if (data.cues.length) {
|
|
||||||
cue = utils.stripHTML(data.cues[0].text);
|
|
||||||
}
|
|
||||||
|
|
||||||
captions.setText.call(player, cue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
player.embed.on('loaded', () => {
|
player.embed.on('loaded', () => {
|
||||||
|
// Assure state and events are updated on autoplay
|
||||||
|
player.embed.getPaused().then(paused => {
|
||||||
|
assurePlaybackState.call(player, !paused);
|
||||||
|
if (!paused) {
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'playing');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
||||||
const frame = player.embed.element;
|
const frame = player.embed.element;
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ function mapQualityUnits(levels) {
|
|||||||
|
|
||||||
// Set playback state and trigger change (only on actual change)
|
// Set playback state and trigger change (only on actual change)
|
||||||
function assurePlaybackState(play) {
|
function assurePlaybackState(play) {
|
||||||
|
if (play && !this.embed.hasPlayed) {
|
||||||
|
this.embed.hasPlayed = true;
|
||||||
|
}
|
||||||
if (this.media.paused === play) {
|
if (this.media.paused === play) {
|
||||||
this.media.paused = !play;
|
this.media.paused = !play;
|
||||||
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause');
|
||||||
@@ -469,7 +472,7 @@ const youtube = {
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
||||||
if (player.media.paused) {
|
if (player.media.paused && !player.embed.hasPlayed) {
|
||||||
player.media.pause();
|
player.media.pause();
|
||||||
} else {
|
} else {
|
||||||
assurePlaybackState.call(player, true);
|
assurePlaybackState.call(player, true);
|
||||||
|
|||||||
+27
-53
@@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr
|
// Plyr
|
||||||
// plyr.js v3.3.11
|
// plyr.js v3.3.12
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
@@ -84,7 +84,8 @@ class Plyr {
|
|||||||
// Captions
|
// Captions
|
||||||
this.captions = {
|
this.captions = {
|
||||||
active: null,
|
active: null,
|
||||||
currentTrack: null,
|
currentTrack: -1,
|
||||||
|
meta: new WeakMap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fullscreen
|
// Fullscreen
|
||||||
@@ -96,7 +97,6 @@ class Plyr {
|
|||||||
this.options = {
|
this.options = {
|
||||||
speed: [],
|
speed: [],
|
||||||
quality: [],
|
quality: [],
|
||||||
captions: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
@@ -854,61 +854,35 @@ class Plyr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the captions language
|
* Set the caption track by index
|
||||||
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
* @param {number} - Caption index
|
||||||
*/
|
*/
|
||||||
set language(input) {
|
set currentTrack(input) {
|
||||||
// Nothing specified
|
captions.set.call(this, input);
|
||||||
if (!utils.is.string(input)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If empty string is passed, assume disable captions
|
|
||||||
if (utils.is.empty(input)) {
|
|
||||||
this.toggleCaptions(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize
|
|
||||||
const language = input.toLowerCase();
|
|
||||||
|
|
||||||
// Check for support
|
|
||||||
if (!this.options.captions.includes(language)) {
|
|
||||||
this.debug.warn(`Unsupported language option: ${language}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure captions are enabled
|
|
||||||
this.toggleCaptions(true);
|
|
||||||
|
|
||||||
// Enabled only
|
|
||||||
if (language === 'enabled') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing to change, bail
|
|
||||||
if (this.language === language) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config
|
|
||||||
this.captions.language = language;
|
|
||||||
|
|
||||||
// Clear caption
|
|
||||||
captions.setText.call(this, null);
|
|
||||||
|
|
||||||
// Update captions
|
|
||||||
captions.setLanguage.call(this);
|
|
||||||
|
|
||||||
// Trigger an event
|
|
||||||
utils.dispatchEvent.call(this, this.media, 'languagechange');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current captions language
|
* Get the current caption track index (-1 if disabled)
|
||||||
|
*/
|
||||||
|
get currentTrack() {
|
||||||
|
const { active, currentTrack } = this.captions;
|
||||||
|
return active ? currentTrack : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the wanted language for captions
|
||||||
|
* Since tracks can be added later it won't update the actual caption track until there is a matching track
|
||||||
|
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
|
||||||
|
*/
|
||||||
|
set language(input) {
|
||||||
|
captions.setLanguage.call(this, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current track's language
|
||||||
*/
|
*/
|
||||||
get language() {
|
get language() {
|
||||||
return this.captions.language;
|
return (captions.getCurrentTrack.call(this) || {}).language;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1159,7 +1133,7 @@ class Plyr {
|
|||||||
} else if (utils.is.nodeList(selector)) {
|
} else if (utils.is.nodeList(selector)) {
|
||||||
targets = Array.from(selector);
|
targets = Array.from(selector);
|
||||||
} else if (utils.is.array(selector)) {
|
} else if (utils.is.array(selector)) {
|
||||||
targets = selector.filter(i => utils.is.element(i));
|
targets = selector.filter(utils.is.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.is.empty(targets)) {
|
if (utils.is.empty(targets)) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr Polyfilled Build
|
// Plyr Polyfilled Build
|
||||||
// plyr.js v3.3.11
|
// plyr.js v3.3.12
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|||||||
+45
-39
@@ -11,63 +11,64 @@ const utils = {
|
|||||||
// Check variable types
|
// Check variable types
|
||||||
is: {
|
is: {
|
||||||
object(input) {
|
object(input) {
|
||||||
return this.getConstructor(input) === Object;
|
return utils.getConstructor(input) === Object;
|
||||||
},
|
},
|
||||||
number(input) {
|
number(input) {
|
||||||
return this.getConstructor(input) === Number && !Number.isNaN(input);
|
return utils.getConstructor(input) === Number && !Number.isNaN(input);
|
||||||
},
|
},
|
||||||
string(input) {
|
string(input) {
|
||||||
return this.getConstructor(input) === String;
|
return utils.getConstructor(input) === String;
|
||||||
},
|
},
|
||||||
boolean(input) {
|
boolean(input) {
|
||||||
return this.getConstructor(input) === Boolean;
|
return utils.getConstructor(input) === Boolean;
|
||||||
},
|
},
|
||||||
function(input) {
|
function(input) {
|
||||||
return this.getConstructor(input) === Function;
|
return utils.getConstructor(input) === Function;
|
||||||
},
|
},
|
||||||
array(input) {
|
array(input) {
|
||||||
return !this.nullOrUndefined(input) && Array.isArray(input);
|
return !utils.is.nullOrUndefined(input) && Array.isArray(input);
|
||||||
},
|
},
|
||||||
weakMap(input) {
|
weakMap(input) {
|
||||||
return this.instanceof(input, WeakMap);
|
return utils.is.instanceof(input, WeakMap);
|
||||||
},
|
},
|
||||||
nodeList(input) {
|
nodeList(input) {
|
||||||
return this.instanceof(input, NodeList);
|
return utils.is.instanceof(input, NodeList);
|
||||||
},
|
},
|
||||||
element(input) {
|
element(input) {
|
||||||
return this.instanceof(input, Element);
|
return utils.is.instanceof(input, Element);
|
||||||
},
|
},
|
||||||
textNode(input) {
|
textNode(input) {
|
||||||
return this.getConstructor(input) === Text;
|
return utils.getConstructor(input) === Text;
|
||||||
},
|
},
|
||||||
event(input) {
|
event(input) {
|
||||||
return this.instanceof(input, Event);
|
return utils.is.instanceof(input, Event);
|
||||||
},
|
},
|
||||||
cue(input) {
|
cue(input) {
|
||||||
return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue);
|
return utils.is.instanceof(input, window.TextTrackCue) || utils.is.instanceof(input, window.VTTCue);
|
||||||
},
|
},
|
||||||
track(input) {
|
track(input) {
|
||||||
return this.instanceof(input, TextTrack) || (!this.nullOrUndefined(input) && this.string(input.kind));
|
return utils.is.instanceof(input, TextTrack) || (!utils.is.nullOrUndefined(input) && utils.is.string(input.kind));
|
||||||
},
|
},
|
||||||
url(input) {
|
url(input) {
|
||||||
return !this.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
return !utils.is.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
|
||||||
},
|
},
|
||||||
nullOrUndefined(input) {
|
nullOrUndefined(input) {
|
||||||
return input === null || typeof input === 'undefined';
|
return input === null || typeof input === 'undefined';
|
||||||
},
|
},
|
||||||
empty(input) {
|
empty(input) {
|
||||||
return (
|
return (
|
||||||
this.nullOrUndefined(input) ||
|
utils.is.nullOrUndefined(input) ||
|
||||||
((this.string(input) || this.array(input) || this.nodeList(input)) && !input.length) ||
|
((utils.is.string(input) || utils.is.array(input) || utils.is.nodeList(input)) && !input.length) ||
|
||||||
(this.object(input) && !Object.keys(input).length)
|
(utils.is.object(input) && !Object.keys(input).length)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
instanceof(input, constructor) {
|
instanceof(input, constructor) {
|
||||||
return Boolean(input && constructor && input instanceof constructor);
|
return Boolean(input && constructor && input instanceof constructor);
|
||||||
},
|
},
|
||||||
getConstructor(input) {
|
},
|
||||||
return !this.nullOrUndefined(input) ? input.constructor : null;
|
|
||||||
},
|
getConstructor(input) {
|
||||||
|
return !utils.is.nullOrUndefined(input) ? input.constructor : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Unfortunately, due to mixed support, UA sniffing is required
|
// Unfortunately, due to mixed support, UA sniffing is required
|
||||||
@@ -151,24 +152,23 @@ const utils = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefix = 'cache-';
|
const prefix = 'cache';
|
||||||
const hasId = utils.is.string(id);
|
const hasId = utils.is.string(id);
|
||||||
let isCached = false;
|
let isCached = false;
|
||||||
|
|
||||||
const exists = () => document.querySelectorAll(`#${id}`).length;
|
const exists = () => document.getElementById(id) !== null;
|
||||||
|
|
||||||
|
const update = (container, data) => {
|
||||||
|
container.innerHTML = data;
|
||||||
|
|
||||||
function injectSprite(data) {
|
|
||||||
// Check again incase of race condition
|
// Check again incase of race condition
|
||||||
if (hasId && exists()) {
|
if (hasId && exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject content
|
|
||||||
this.innerHTML = data;
|
|
||||||
|
|
||||||
// Inject the SVG to the body
|
// Inject the SVG to the body
|
||||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
document.body.insertAdjacentElement('afterbegin', container);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Only load once if ID set
|
// Only load once if ID set
|
||||||
if (!hasId || !exists()) {
|
if (!hasId || !exists()) {
|
||||||
@@ -184,13 +184,12 @@ const utils = {
|
|||||||
|
|
||||||
// Check in cache
|
// Check in cache
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
const cached = window.localStorage.getItem(prefix + id);
|
const cached = window.localStorage.getItem(`${prefix}-${id}`);
|
||||||
isCached = cached !== null;
|
isCached = cached !== null;
|
||||||
|
|
||||||
if (isCached) {
|
if (isCached) {
|
||||||
const data = JSON.parse(cached);
|
const data = JSON.parse(cached);
|
||||||
injectSprite.call(container, data.content);
|
update(container, data.content);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,14 +203,14 @@ const utils = {
|
|||||||
|
|
||||||
if (useStorage) {
|
if (useStorage) {
|
||||||
window.localStorage.setItem(
|
window.localStorage.setItem(
|
||||||
prefix + id,
|
`${prefix}-${id}`,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
content: result,
|
content: result,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
injectSprite.call(container, result);
|
update(container, result);
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
@@ -627,16 +626,16 @@ const utils = {
|
|||||||
formatTime(time = 0, displayHours = false, inverted = false) {
|
formatTime(time = 0, displayHours = false, inverted = false) {
|
||||||
// Bail if the value isn't a number
|
// Bail if the value isn't a number
|
||||||
if (!utils.is.number(time)) {
|
if (!utils.is.number(time)) {
|
||||||
return this.formatTime(null, displayHours, inverted);
|
return utils.formatTime(null, displayHours, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format time component to add leading zero
|
// Format time component to add leading zero
|
||||||
const format = value => `0${value}`.slice(-2);
|
const format = value => `0${value}`.slice(-2);
|
||||||
|
|
||||||
// Breakdown to hours, mins, secs
|
// Breakdown to hours, mins, secs
|
||||||
let hours = this.getHours(time);
|
let hours = utils.getHours(time);
|
||||||
const mins = this.getMinutes(time);
|
const mins = utils.getMinutes(time);
|
||||||
const secs = this.getSeconds(time);
|
const secs = utils.getSeconds(time);
|
||||||
|
|
||||||
// Do we need to display hours?
|
// Do we need to display hours?
|
||||||
if (displayHours || hours > 0) {
|
if (displayHours || hours > 0) {
|
||||||
@@ -794,10 +793,10 @@ const utils = {
|
|||||||
|
|
||||||
// Parse URL if needed
|
// Parse URL if needed
|
||||||
if (input.startsWith('http://') || input.startsWith('https://')) {
|
if (input.startsWith('http://') || input.startsWith('https://')) {
|
||||||
({ search } = this.parseUrl(input));
|
({ search } = utils.parseUrl(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.is.empty(search)) {
|
if (utils.is.empty(search)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,6 +832,13 @@ const utils = {
|
|||||||
return fragment.firstChild.innerText;
|
return fragment.firstChild.innerText;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Like outerHTML, but also works for DocumentFragment
|
||||||
|
getHTML(element) {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.appendChild(element);
|
||||||
|
return wrapper.innerHTML;
|
||||||
|
},
|
||||||
|
|
||||||
// Get aspect ratio for dimensions
|
// Get aspect ratio for dimensions
|
||||||
getAspectRatio(width, height) {
|
getAspectRatio(width, height) {
|
||||||
const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));
|
const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
transition: transform 0.4s ease-in-out;
|
transition: transform 0.4s ease-in-out;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
span {
|
.plyr__caption {
|
||||||
background: $plyr-captions-bg;
|
background: $plyr-captions-bg;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-decoration-break: clone;
|
box-decoration-break: clone;
|
||||||
|
|||||||
Reference in New Issue
Block a user