This commit is contained in:
Sam Potts 2018-06-11 16:45:40 +10:00
parent 1ad76800b0
commit abd1182303
14 changed files with 764 additions and 538 deletions

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

110
demo/dist/demo.js vendored
View File

@ -767,11 +767,12 @@ function getLocationOrigin() {
// Oh dear IE10... // Oh dear IE10...
if (!document.location.origin) { if (!document.location.origin) {
document.location.origin = return (
document.location.protocol + document.location.protocol +
'//' + '//' +
document.location.hostname + document.location.hostname +
(document.location.port ? ':' + document.location.port : ''); (document.location.port ? ':' + document.location.port : '')
);
} }
return document.location.origin; return document.location.origin;
@ -1866,7 +1867,7 @@ Raven.prototype = {
// webpack (using a build step causes webpack #1617). Grunt verifies that // webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build. // this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465 // See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.25.2', VERSION: '3.26.1',
debug: false, debug: false,
@ -2032,7 +2033,7 @@ Raven.prototype = {
if (isFunction$1(options)) { if (isFunction$1(options)) {
args = func || []; args = func || [];
func = options; func = options;
options = undefined; options = {};
} }
return this.wrap(options, func).apply(this, args); return this.wrap(options, func).apply(this, args);
@ -2043,7 +2044,7 @@ Raven.prototype = {
* *
* @param {object} options A specific set of options for this context [optional] * @param {object} options A specific set of options for this context [optional]
* @param {function} func The function to be wrapped in a new context * @param {function} func The function to be wrapped in a new context
* @param {function} func A function to call before the try/catch wrapper [optional, private] * @param {function} _before A function to call before the try/catch wrapper [optional, private]
* @return {function} The newly wrapped functions with a context * @return {function} The newly wrapped functions with a context
*/ */
wrap: function(options, func, _before) { wrap: function(options, func, _before) {
@ -2156,8 +2157,9 @@ Raven.prototype = {
_promiseRejectionHandler: function(event) { _promiseRejectionHandler: function(event) {
this._logDebug('debug', 'Raven caught unhandled promise rejection:', event); this._logDebug('debug', 'Raven caught unhandled promise rejection:', event);
this.captureException(event.reason, { this.captureException(event.reason, {
extra: { mechanism: {
unhandledPromiseRejection: true type: 'onunhandledrejection',
handled: false
} }
}); });
}, },
@ -2834,7 +2836,15 @@ Raven.prototype = {
} }
var originalCallback = args[0]; var originalCallback = args[0];
if (isFunction$1(originalCallback)) { if (isFunction$1(originalCallback)) {
args[0] = self.wrap(originalCallback); args[0] = self.wrap(
{
mechanism: {
type: 'instrument',
data: {function: orig.name}
}
},
originalCallback
);
} }
// IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
@ -2861,7 +2871,15 @@ Raven.prototype = {
// preserve arity // preserve arity
try { try {
if (fn && fn.handleEvent) { if (fn && fn.handleEvent) {
fn.handleEvent = self.wrap(fn.handleEvent); fn.handleEvent = self.wrap(
{
mechanism: {
type: 'instrument',
data: {target: global, function: 'handleEvent', handler: fn.name}
}
},
fn.handleEvent
);
} }
} catch (err) { } catch (err) {
// can sometimes get 'Permission denied to access property "handle Event' // can sometimes get 'Permission denied to access property "handle Event'
@ -2901,7 +2919,20 @@ Raven.prototype = {
return orig.call( return orig.call(
this, this,
evtName, evtName,
self.wrap(fn, undefined, before), self.wrap(
{
mechanism: {
type: 'instrument',
data: {
target: global,
function: 'addEventListener',
handler: fn.name
}
}
},
fn,
before
),
capture, capture,
secure secure
); );
@ -2935,7 +2966,17 @@ Raven.prototype = {
'requestAnimationFrame', 'requestAnimationFrame',
function(orig) { function(orig) {
return function(cb) { return function(cb) {
return orig(self.wrap(cb)); return orig(
self.wrap(
{
mechanism: {
type: 'instrument',
data: {function: 'requestAnimationFrame', handler: orig.name}
}
},
cb
)
);
}; };
}, },
wrappedBuiltIns wrappedBuiltIns
@ -2998,7 +3039,15 @@ Raven.prototype = {
function wrapProp(prop, xhr) { function wrapProp(prop, xhr) {
if (prop in xhr && isFunction$1(xhr[prop])) { if (prop in xhr && isFunction$1(xhr[prop])) {
fill$1(xhr, prop, function(orig) { fill$1(xhr, prop, function(orig) {
return self.wrap(orig); return self.wrap(
{
mechanism: {
type: 'instrument',
data: {function: prop, handler: orig.name}
}
},
orig
);
}); // intentionally don't track filled methods on XHR instances }); // intentionally don't track filled methods on XHR instances
} }
} }
@ -3063,7 +3112,19 @@ Raven.prototype = {
xhr, xhr,
'onreadystatechange', 'onreadystatechange',
function(orig) { function(orig) {
return self.wrap(orig, undefined, onreadystatechangeHandler); return self.wrap(
{
mechanism: {
type: 'instrument',
data: {
function: 'onreadystatechange',
handler: orig.name
}
}
},
orig,
onreadystatechangeHandler
);
} /* intentionally don't track this instrumentation */ } /* intentionally don't track this instrumentation */
); );
} else { } else {
@ -3287,10 +3348,16 @@ Raven.prototype = {
return globalServer; return globalServer;
}, },
_handleOnErrorStackInfo: function() { _handleOnErrorStackInfo: function(stackInfo, options) {
options = options || {};
options.mechanism = options.mechanism || {
type: 'onerror',
handled: false
};
// if we are intentionally ignoring errors via onerror, bail out // if we are intentionally ignoring errors via onerror, bail out
if (!this._ignoreOnError) { if (!this._ignoreOnError) {
this._handleStackInfo.apply(this, arguments); this._handleStackInfo(stackInfo, options);
} }
}, },
@ -3427,6 +3494,19 @@ Raven.prototype = {
options options
); );
// Move mechanism from options to exception interface
// We do this, as requiring user to pass `{exception:{mechanism:{ ... }}}` would be
// too much
if (!data.exception.mechanism && data.mechanism) {
data.exception.mechanism = data.mechanism;
delete data.mechanism;
}
data.exception.mechanism = objectMerge$1(data.exception.mechanism || {}, {
type: 'generic',
handled: true
});
// Fire away! // Fire away!
this._send(data); this._send(data);
}, },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

585
dist/plyr.js vendored
View File

@ -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 {
@ -1167,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
@ -1176,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) {
@ -1374,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;
} }
@ -1418,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) {
@ -2159,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');
@ -2477,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);
@ -2521,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:
@ -2627,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);
@ -2639,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);
}, },
@ -2714,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);
@ -3189,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);
}); });
@ -3309,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);
}); });
}, },
@ -3401,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;
}, },
@ -3454,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');
} }
} }
}; };
@ -3809,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'
} }
@ -4787,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;
} }
@ -4888,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)) {
@ -5083,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');
@ -5235,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
}); });
@ -5382,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;
@ -5522,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');
@ -5935,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);
@ -6926,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
@ -6937,8 +7032,7 @@ var Plyr = function () {
// Options // Options
this.options = { this.options = {
speed: [], speed: [],
quality: [], quality: []
captions: []
}; };
// Debugging // Debugging
@ -7314,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
*/ */
}, { }, {
@ -8006,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;
} }
/** /**
@ -8135,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)) {

2
dist/plyr.js.map vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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 {
@ -6550,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
@ -6559,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) {
@ -6757,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;
} }
@ -6801,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) {
@ -7542,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');
@ -7860,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);
@ -7904,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:
@ -8010,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);
@ -8022,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);
}, },
@ -8097,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);
@ -8572,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);
}); });
@ -8692,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);
}); });
}, },
@ -8784,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;
}, },
@ -8837,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');
} }
} }
}; };
@ -9192,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'
} }
@ -10170,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;
} }
@ -10271,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)) {
@ -10466,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');
@ -10618,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
}); });
@ -10765,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;
@ -10905,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');
@ -11318,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);
@ -12309,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
@ -12320,8 +12415,7 @@ var Plyr = function () {
// Options // Options
this.options = { this.options = {
speed: [], speed: [],
quality: [], quality: []
captions: []
}; };
// Debugging // Debugging
@ -12697,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
*/ */
}, { }, {
@ -13389,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;
} }
/** /**
@ -13518,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)) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long