Fixes for fast forward and issues with event.preventDefault()

This commit is contained in:
Sam Potts 2018-03-27 10:36:08 +11:00
parent 3d2ba8c009
commit 9c1bc6ab08
22 changed files with 2553 additions and 1464 deletions

242
demo/dist/demo.js vendored
View File

@ -88,6 +88,17 @@ function serializer(replacer, cycleReplacer) {
} }
}); });
var stringify_2 = stringify_1.getSerialize;
var stringify = Object.freeze({
default: stringify_1,
__moduleExports: stringify_1,
getSerialize: stringify_2
});
var stringify$1 = ( stringify && stringify_1 ) || stringify;
var _window$3 = var _window$3 =
typeof window !== 'undefined' typeof window !== 'undefined'
? window ? window
@ -594,7 +605,7 @@ function serializeException$1(ex, depth, maxSize) {
var serialized = serializeObject(ex, depth); var serialized = serializeObject(ex, depth);
if (jsonSize(stringify_1(serialized)) > maxSize) { if (jsonSize(stringify$1(serialized)) > maxSize) {
return serializeException$1(ex, depth - 1); return serializeException$1(ex, depth - 1);
} }
@ -657,15 +668,76 @@ var utils = {
serializeKeysForMessage: serializeKeysForMessage$1 serializeKeysForMessage: serializeKeysForMessage$1
}; };
/* var utils_1 = utils.isObject;
TraceKit - Cross brower stack traces var utils_2 = utils.isError;
var utils_3 = utils.isErrorEvent;
var utils_4 = utils.isUndefined;
var utils_5 = utils.isFunction;
var utils_6 = utils.isPlainObject;
var utils_7 = utils.isString;
var utils_8 = utils.isArray;
var utils_9 = utils.isEmptyObject;
var utils_10 = utils.supportsErrorEvent;
var utils_11 = utils.supportsFetch;
var utils_12 = utils.supportsReferrerPolicy;
var utils_13 = utils.supportsPromiseRejectionEvent;
var utils_14 = utils.wrappedCallback;
var utils_15 = utils.each;
var utils_16 = utils.objectMerge;
var utils_17 = utils.truncate;
var utils_18 = utils.objectFrozen;
var utils_19 = utils.hasKey;
var utils_20 = utils.joinRegExp;
var utils_21 = utils.urlencode;
var utils_22 = utils.uuid4;
var utils_23 = utils.htmlTreeAsString;
var utils_24 = utils.htmlElementAsString;
var utils_25 = utils.isSameException;
var utils_26 = utils.isSameStacktrace;
var utils_27 = utils.parseUrl;
var utils_28 = utils.fill;
var utils_29 = utils.safeJoin;
var utils_30 = utils.serializeException;
var utils_31 = utils.serializeKeysForMessage;
This was originally forked from github.com/occ/TraceKit, but has since been
largely re-written and is now maintained as part of raven-js. Tests for
this are in test/vendor.
MIT license var utils$2 = Object.freeze({
*/ default: utils,
__moduleExports: utils,
isObject: utils_1,
isError: utils_2,
isErrorEvent: utils_3,
isUndefined: utils_4,
isFunction: utils_5,
isPlainObject: utils_6,
isString: utils_7,
isArray: utils_8,
isEmptyObject: utils_9,
supportsErrorEvent: utils_10,
supportsFetch: utils_11,
supportsReferrerPolicy: utils_12,
supportsPromiseRejectionEvent: utils_13,
wrappedCallback: utils_14,
each: utils_15,
objectMerge: utils_16,
truncate: utils_17,
objectFrozen: utils_18,
hasKey: utils_19,
joinRegExp: utils_20,
urlencode: utils_21,
uuid4: utils_22,
htmlTreeAsString: utils_23,
htmlElementAsString: utils_24,
isSameException: utils_25,
isSameStacktrace: utils_26,
parseUrl: utils_27,
fill: utils_28,
safeJoin: utils_29,
serializeException: utils_30,
serializeKeysForMessage: utils_31
});
var utils$3 = ( utils$2 && utils ) || utils$2;
var TraceKit = { var TraceKit = {
collectWindowErrors: true, collectWindowErrors: true,
@ -805,9 +877,9 @@ TraceKit.report = (function reportModuleWrapper() {
function traceKitWindowOnError(msg, url, lineNo, colNo, ex) { function traceKitWindowOnError(msg, url, lineNo, colNo, ex) {
var stack = null; var stack = null;
// If 'ex' is ErrorEvent, get real Error from inside // If 'ex' is ErrorEvent, get real Error from inside
var exception = utils.isErrorEvent(ex) ? ex.error : ex; var exception = utils$3.isErrorEvent(ex) ? ex.error : ex;
// If 'msg' is ErrorEvent, get real message from inside // If 'msg' is ErrorEvent, get real message from inside
var message = utils.isErrorEvent(msg) ? msg.message : msg; var message = utils$3.isErrorEvent(msg) ? msg.message : msg;
if (lastExceptionStack) { if (lastExceptionStack) {
TraceKit.computeStackTrace.augmentStackTraceWithInitialElement( TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(
@ -817,7 +889,7 @@ TraceKit.report = (function reportModuleWrapper() {
message message
); );
processLastException(); processLastException();
} else if (exception && utils.isError(exception)) { } else if (exception && utils$3.isError(exception)) {
// non-string `exception` arg; attempt to extract stack trace // non-string `exception` arg; attempt to extract stack trace
// New chrome and blink send along a real error object // New chrome and blink send along a real error object
@ -1284,6 +1356,13 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
var tracekit = TraceKit; var tracekit = TraceKit;
var tracekit$2 = Object.freeze({
default: tracekit,
__moduleExports: tracekit
});
/* /*
* JavaScript MD5 * JavaScript MD5
* https://github.com/blueimp/JavaScript-MD5 * https://github.com/blueimp/JavaScript-MD5
@ -1551,6 +1630,13 @@ function md5(string, key, raw) {
var md5_1 = md5; var md5_1 = md5;
var md5$1 = Object.freeze({
default: md5_1,
__moduleExports: md5_1
});
function RavenConfigError(message) { function RavenConfigError(message) {
this.name = 'RavenConfigError'; this.name = 'RavenConfigError';
this.message = message; this.message = message;
@ -1560,6 +1646,13 @@ RavenConfigError.prototype.constructor = RavenConfigError;
var configError = RavenConfigError; var configError = RavenConfigError;
var configError$2 = Object.freeze({
default: configError,
__moduleExports: configError
});
var wrapMethod = function(console, level, callback) { var wrapMethod = function(console, level, callback) {
var originalConsoleLevel = console[level]; var originalConsoleLevel = console[level];
var originalConsole = console; var originalConsole = console;
@ -1573,14 +1666,14 @@ var wrapMethod = function(console, level, callback) {
console[level] = function() { console[level] = function() {
var args = [].slice.call(arguments); var args = [].slice.call(arguments);
var msg = utils.safeJoin(args, ' '); var msg = utils$3.safeJoin(args, ' ');
var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}}; var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}};
if (level === 'assert') { if (level === 'assert') {
if (args[0] === false) { if (args[0] === false) {
// Default browsers message // Default browsers message
msg = msg =
'Assertion failed: ' + (utils.safeJoin(args.slice(1), ' ') || 'console.assert'); 'Assertion failed: ' + (utils$3.safeJoin(args.slice(1), ' ') || 'console.assert');
data.extra.arguments = args.slice(1); data.extra.arguments = args.slice(1);
callback && callback(msg, data); callback && callback(msg, data);
} }
@ -1601,42 +1694,51 @@ var console$1 = {
wrapMethod: wrapMethod wrapMethod: wrapMethod
}; };
/*global XDomainRequest:false */ var console_1 = console$1.wrapMethod;
var console$3 = Object.freeze({
default: console$1,
__moduleExports: console$1,
wrapMethod: console_1
});
var TraceKit$1 = ( tracekit$2 && tracekit ) || tracekit$2;
var md5$2 = ( md5$1 && md5_1 ) || md5$1;
var RavenConfigError$1 = ( configError$2 && configError ) || configError$2;
var require$$0 = ( console$3 && console$1 ) || console$3;
var isError = utils.isError; var isError = utils$3.isError;
var isObject = utils.isObject; var isObject = utils$3.isObject;
var isPlainObject = utils.isPlainObject; var isPlainObject = utils$3.isPlainObject;
var isErrorEvent = utils.isErrorEvent; var isErrorEvent = utils$3.isErrorEvent;
var isUndefined = utils.isUndefined; var isUndefined = utils$3.isUndefined;
var isFunction = utils.isFunction; var isFunction = utils$3.isFunction;
var isString = utils.isString; var isString = utils$3.isString;
var isArray = utils.isArray; var isArray = utils$3.isArray;
var isEmptyObject = utils.isEmptyObject; var isEmptyObject = utils$3.isEmptyObject;
var each = utils.each; var each = utils$3.each;
var objectMerge = utils.objectMerge; var objectMerge = utils$3.objectMerge;
var truncate = utils.truncate; var truncate = utils$3.truncate;
var objectFrozen = utils.objectFrozen; var objectFrozen = utils$3.objectFrozen;
var hasKey = utils.hasKey; var hasKey = utils$3.hasKey;
var joinRegExp = utils.joinRegExp; var joinRegExp = utils$3.joinRegExp;
var urlencode = utils.urlencode; var urlencode = utils$3.urlencode;
var uuid4 = utils.uuid4; var uuid4 = utils$3.uuid4;
var htmlTreeAsString = utils.htmlTreeAsString; var htmlTreeAsString = utils$3.htmlTreeAsString;
var isSameException = utils.isSameException; var isSameException = utils$3.isSameException;
var isSameStacktrace = utils.isSameStacktrace; var isSameStacktrace = utils$3.isSameStacktrace;
var parseUrl = utils.parseUrl; var parseUrl = utils$3.parseUrl;
var fill = utils.fill; var fill = utils$3.fill;
var supportsFetch = utils.supportsFetch; var supportsFetch = utils$3.supportsFetch;
var supportsReferrerPolicy = utils.supportsReferrerPolicy; var supportsReferrerPolicy = utils$3.supportsReferrerPolicy;
var serializeKeysForMessage = utils.serializeKeysForMessage; var serializeKeysForMessage = utils$3.serializeKeysForMessage;
var serializeException = utils.serializeException; var serializeException = utils$3.serializeException;
var wrapConsoleMethod = console$1.wrapMethod; var wrapConsoleMethod = require$$0.wrapMethod;
var dsnKeys = 'source protocol user pass host port path'.split(' '); var dsnKeys = 'source protocol user pass host port path'.split(' ');
var dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; var dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
@ -1743,7 +1845,7 @@ Raven$2.prototype = {
debug: false, debug: false,
TraceKit: tracekit, // alias to TraceKit TraceKit: TraceKit$1, // alias to TraceKit
/* /*
* Configure Raven with a DSN and extra options * Configure Raven with a DSN and extra options
@ -1824,7 +1926,7 @@ Raven$2.prototype = {
} }
globalOptions.instrument = instrument; globalOptions.instrument = instrument;
tracekit.collectWindowErrors = !!globalOptions.collectWindowErrors; TraceKit$1.collectWindowErrors = !!globalOptions.collectWindowErrors;
// return for chaining // return for chaining
return self; return self;
@ -1841,7 +1943,7 @@ Raven$2.prototype = {
install: function() { install: function() {
var self = this; var self = this;
if (self.isSetup() && !self._isRavenInstalled) { if (self.isSetup() && !self._isRavenInstalled) {
tracekit.report.subscribe(function() { TraceKit$1.report.subscribe(function() {
self._handleOnErrorStackInfo.apply(self, arguments); self._handleOnErrorStackInfo.apply(self, arguments);
}); });
@ -2005,7 +2107,7 @@ Raven$2.prototype = {
* @return {Raven} * @return {Raven}
*/ */
uninstall: function() { uninstall: function() {
tracekit.report.uninstall(); TraceKit$1.report.uninstall();
this._detachPromiseRejectionHandler(); this._detachPromiseRejectionHandler();
this._unpatchFunctionToString(); this._unpatchFunctionToString();
@ -2100,7 +2202,7 @@ Raven$2.prototype = {
// raises an exception different from the one we asked to // raises an exception different from the one we asked to
// report on. // report on.
try { try {
var stack = tracekit.computeStackTrace(ex); var stack = TraceKit$1.computeStackTrace(ex);
this._handleStackInfo(stack, options); this._handleStackInfo(stack, options);
} catch (ex1) { } catch (ex1) {
if (ex !== ex1) { if (ex !== ex1) {
@ -2116,7 +2218,7 @@ Raven$2.prototype = {
var options = objectMerge(currentOptions, { var options = objectMerge(currentOptions, {
message: message:
'Non-Error exception captured with keys: ' + serializeKeysForMessage(exKeys), 'Non-Error exception captured with keys: ' + serializeKeysForMessage(exKeys),
fingerprint: [md5_1(exKeys)], fingerprint: [md5$2(exKeys)],
extra: currentOptions.extra || {} extra: currentOptions.extra || {}
}); });
options.extra.__serialized__ = serializeException(ex); options.extra.__serialized__ = serializeException(ex);
@ -2165,7 +2267,7 @@ Raven$2.prototype = {
// null exception name so `Error` isn't prefixed to msg // null exception name so `Error` isn't prefixed to msg
ex.name = null; ex.name = null;
var stack = tracekit.computeStackTrace(ex); var stack = TraceKit$1.computeStackTrace(ex);
// stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1] // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
var initialCall = isArray(stack.stack) && stack.stack[1]; var initialCall = isArray(stack.stack) && stack.stack[1];
@ -2312,7 +2414,7 @@ Raven$2.prototype = {
*/ */
getContext: function() { getContext: function() {
// lol javascript // lol javascript
return JSON.parse(stringify_1(this._globalContext)); return JSON.parse(stringify$1(this._globalContext));
}, },
/* /*
@ -2448,12 +2550,12 @@ Raven$2.prototype = {
var lastEventId = options.eventId || this.lastEventId(); var lastEventId = options.eventId || this.lastEventId();
if (!lastEventId) { if (!lastEventId) {
throw new configError('Missing eventId'); throw new RavenConfigError$1('Missing eventId');
} }
var dsn = options.dsn || this._dsn; var dsn = options.dsn || this._dsn;
if (!dsn) { if (!dsn) {
throw new configError('Missing DSN'); throw new RavenConfigError$1('Missing DSN');
} }
var encode = encodeURIComponent; var encode = encodeURIComponent;
@ -3095,11 +3197,11 @@ Raven$2.prototype = {
try { try {
while (i--) dsn[dsnKeys[i]] = m[i] || ''; while (i--) dsn[dsnKeys[i]] = m[i] || '';
} catch (e) { } catch (e) {
throw new configError('Invalid DSN: ' + str); throw new RavenConfigError$1('Invalid DSN: ' + str);
} }
if (dsn.pass && !this._globalOptions.allowSecretKey) { if (dsn.pass && !this._globalOptions.allowSecretKey) {
throw new configError( throw new RavenConfigError$1(
'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key'
); );
} }
@ -3614,7 +3716,7 @@ Raven$2.prototype = {
} }
if (supportsFetch()) { if (supportsFetch()) {
evaluatedFetchParameters.body = stringify_1(opts.data); evaluatedFetchParameters.body = stringify$1(opts.data);
var defaultFetchOptions = objectMerge({}, this._fetchDefaults); var defaultFetchOptions = objectMerge({}, this._fetchDefaults);
var fetchOptions = objectMerge(defaultFetchOptions, evaluatedFetchParameters); var fetchOptions = objectMerge(defaultFetchOptions, evaluatedFetchParameters);
@ -3689,7 +3791,7 @@ Raven$2.prototype = {
}); });
} }
request.send(stringify_1(opts.data)); request.send(stringify$1(opts.data));
}, },
_evaluateHash: function(hash) { _evaluateHash: function(hash) {
@ -3731,22 +3833,22 @@ Raven$2.prototype.setReleaseContext = Raven$2.prototype.setRelease;
var raven = Raven$2; var raven = Raven$2;
/**
* Enforces a single instance of the Raven client, and the
* main entry point for Raven. If you are a consumer of the
* Raven library, you SHOULD load this file (vs raven.js).
**/
var raven$2 = Object.freeze({
default: raven,
__moduleExports: raven
});
var RavenConstructor = ( raven$2 && raven ) || raven$2;
// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
var _window = var _window =
typeof window !== 'undefined' typeof window !== 'undefined'
? window ? window
: typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {}; : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {};
var _Raven = _window.Raven; var _Raven = _window.Raven;
var Raven = new raven(); var Raven = new RavenConstructor();
/* /*
* Allow multiple versions of Raven to be installed. * Allow multiple versions of Raven to be installed.
@ -3820,6 +3922,22 @@ var singleton = Raven;
tooltips: { tooltips: {
controls: true controls: true
}, },
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
captions: { captions: {
active: true active: true
}, },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -57,6 +57,22 @@ import Raven from 'raven-js';
tooltips: { tooltips: {
controls: true, controls: true,
}, },
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
captions: { captions: {
active: true, active: true,
}, },

372
dist/plyr.js vendored
View File

@ -77,7 +77,7 @@ var defaults = {
// Sprite (for icons) // Sprite (for icons)
loadSprite: true, loadSprite: true,
iconPrefix: 'plyr', iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.0.2/plyr.svg', iconUrl: 'https://cdn.plyr.io/3.0.3/plyr.svg',
// Blank video (used to prevent errors on source change) // Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4', blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@ -133,7 +133,12 @@ var defaults = {
}, },
// Default controls // Default controls
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'], controls: ['play-large',
// 'restart',
// 'rewind',
'play',
// 'fast-forward',
'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'],
settings: ['captions', 'quality', 'speed'], settings: ['captions', 'quality', 'speed'],
// Localisation // Localisation
@ -142,7 +147,7 @@ var defaults = {
rewind: 'Rewind {seektime} secs', rewind: 'Rewind {seektime} secs',
play: 'Play', play: 'Play',
pause: 'Pause', pause: 'Pause',
forward: 'Forward {seektime} secs', fastForward: 'Forward {seektime} secs',
seek: 'Seek', seek: 'Seek',
played: 'Played', played: 'Played',
buffered: 'Buffered', buffered: 'Buffered',
@ -190,7 +195,7 @@ var defaults = {
pause: null, pause: null,
restart: null, restart: null,
rewind: null, rewind: null,
forward: null, fastForward: null,
mute: null, mute: null,
volume: null, volume: null,
captions: null, captions: null,
@ -1426,7 +1431,11 @@ var utils = {
// Toggle event listener // Toggle event listener
toggleListener: function toggleListener(elements, event, callback, toggle, passive, capture) { toggleListener: function toggleListener(elements, event, callback) {
var toggle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var capture = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
// Bail if no elemetns, event, or callback // Bail if no elemetns, event, or callback
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) { if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
return; return;
@ -1448,16 +1457,16 @@ var utils = {
var events = event.split(' '); var events = event.split(' ');
// Build options // Build options
// Default to just capture boolean // Default to just the capture boolean for browsers with no passive listener support
var options = utils.is.boolean(capture) ? capture : false; var options = capture;
// If passive events listeners are supported // If passive events listeners are supported
if (support.passiveListeners) { if (support.passiveListeners) {
options = { options = {
// Whether the listener can be passive (i.e. default never prevented) // Whether the listener can be passive (i.e. default never prevented)
passive: utils.is.boolean(passive) ? passive : true, passive: passive,
// Whether the listener is a capturing listener or not // Whether the listener is a capturing listener or not
capture: utils.is.boolean(capture) ? capture : false capture: capture
}; };
} }
@ -1469,13 +1478,23 @@ var utils = {
// Bind event handler // Bind event handler
on: function on(element, events, callback, passive, capture) { on: function on(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments[2];
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
utils.toggleListener(element, events, callback, true, passive, capture); utils.toggleListener(element, events, callback, true, passive, capture);
}, },
// Unbind event handler // Unbind event handler
off: function off(element, events, callback, passive, capture) { off: function off(element) {
var events = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var callback = arguments[2];
var passive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var capture = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
utils.toggleListener(element, events, callback, false, passive, capture); utils.toggleListener(element, events, callback, false, passive, capture);
}, },
@ -1580,6 +1599,60 @@ var utils = {
}, },
// Replace all occurances of a string in a string
replaceAll: function replaceAll() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var find = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
},
// Convert to title case
toTitleCase: function toTitleCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return input.toString().replace(/\w\S*/g, function (text) {
return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();
});
},
// Convert string to pascalCase
toPascalCase: function toPascalCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString();
// Convert kebab case
string = utils.replaceAll(string, '-', ' ');
// Convert snake case
string = utils.replaceAll(string, '_', ' ');
// Convert to title case
string = utils.toTitleCase(string);
// Convert to pascal case
return utils.replaceAll(string, ' ', '');
},
// Convert string to pascalCase
toCamelCase: function toCamelCase() {
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var string = input.toString();
// Convert to pascal case
string = utils.toPascalCase(string);
// Convert first character to lowercase
return string.charAt(0).toLowerCase() + string.slice(1);
},
// Deep extend destination object with N more objects // Deep extend destination object with N more objects
extend: function extend() { extend: function extend() {
var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@ -1756,7 +1829,6 @@ var utils = {
// Plyr support checks // Plyr support checks
// ========================================================================== // ==========================================================================
// Check for feature support
var support = { var support = {
// Basic support // Basic support
audio: 'canPlayType' in document.createElement('audio'), audio: 'canPlayType' in document.createElement('audio'),
@ -2064,6 +2136,8 @@ var Fullscreen = function () {
return; return;
} }
console.warn(this.prefix);
// iOS native fullscreen doesn't need the request step // iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) { if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) { if (this.player.playing) {
@ -2072,7 +2146,7 @@ var Fullscreen = function () {
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullScreen(); this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) { } else if (!utils.is.empty(this.prefix)) {
this.target[this.prefix + 'Request' + this.name](); this.target[this.prefix + 'Request' + this.name]();
} }
@ -2106,6 +2180,8 @@ var Fullscreen = function () {
}, { }, {
key: 'toggle', key: 'toggle',
value: function toggle() { value: function toggle() {
console.warn('TOGGLE');
if (!this.active) { if (!this.active) {
this.enter(); this.enter();
} else { } else {
@ -2423,6 +2499,38 @@ var captions = {
} }
}; };
// ==========================================================================
// Plyr internationalization
// ==========================================================================
var i18n = {
get: function get$$1() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ========================================================================== // ==========================================================================
// Plyr UI // Plyr UI
// ========================================================================== // ==========================================================================
@ -2522,7 +2630,7 @@ var ui = {
// Setup aria attribute for play and iframe title // Setup aria attribute for play and iframe title
setTitle: function setTitle() { setTitle: function setTitle() {
// Find the current text // Find the current text
var label = this.config.i18n.play; var label = i18n.get('play', this.config);
// If there's a media title set, use that for the label // If there's a media title set, use that for the label
if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
@ -2551,7 +2659,7 @@ var ui = {
// Default to media type // Default to media type
var title = !utils.is.empty(this.config.title) ? this.config.title : 'video'; var title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title)); iframe.setAttribute('title', i18n.get('frameTitle', this.config));
} }
}, },
@ -2769,7 +2877,6 @@ var ui = {
// Plyr controls // Plyr controls
// ========================================================================== // ==========================================================================
// Sniff out the browser
var browser$2 = utils.getBrowser(); var browser$2 = utils.getBrowser();
var controls = { var controls = {
@ -2836,7 +2943,7 @@ var controls = {
// Create hidden text label // Create hidden text label
createLabel: function createLabel(type, attr) { createLabel: function createLabel(type, attr) {
var text = this.config.i18n[type]; var text = i18n.get(type, this.config);
var attributes = Object.assign({}, attr); var attributes = Object.assign({}, attr);
switch (type) { switch (type) {
@ -2884,7 +2991,7 @@ var controls = {
createButton: function createButton(buttonType, attr) { createButton: function createButton(buttonType, attr) {
var button = utils.createElement('button'); var button = utils.createElement('button');
var attributes = Object.assign({}, attr); var attributes = Object.assign({}, attr);
var type = buttonType; var type = utils.toCamelCase(buttonType);
var toggle = false; var toggle = false;
var label = void 0; var label = void 0;
@ -2905,7 +3012,7 @@ var controls = {
} }
// Large play button // Large play button
switch (type) { switch (buttonType) {
case 'play': case 'play':
toggle = true; toggle = true;
label = 'play'; label = 'play';
@ -2947,7 +3054,7 @@ var controls = {
default: default:
label = type; label = type;
icon = type; icon = buttonType;
} }
// Setup toggle icon and labels // Setup toggle icon and labels
@ -2962,7 +3069,7 @@ var controls = {
// Add aria attributes // Add aria attributes
attributes['aria-pressed'] = false; attributes['aria-pressed'] = false;
attributes['aria-label'] = this.config.i18n[label]; attributes['aria-label'] = i18n.get(label, this.config);
} else { } else {
button.appendChild(controls.createIcon.call(this, icon)); button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label)); button.appendChild(controls.createLabel.call(this, label));
@ -2994,7 +3101,7 @@ var controls = {
var label = utils.createElement('label', { var label = utils.createElement('label', {
for: attributes.id, for: attributes.id,
class: this.config.classNames.hidden class: this.config.classNames.hidden
}, this.config.i18n[type]); }, i18n.get(type, this.config));
// Seek input // Seek input
var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(this.config.selectors.inputs[type]), { var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(this.config.selectors.inputs[type]), {
@ -3033,11 +3140,11 @@ var controls = {
var suffix = ''; var suffix = '';
switch (type) { switch (type) {
case 'played': case 'played':
suffix = this.config.i18n.played; suffix = i18n.get('played', this.config);
break; break;
case 'buffer': case 'buffer':
suffix = this.config.i18n.buffered; suffix = i18n.get('buffered', this.config);
break; break;
default: default:
@ -3061,7 +3168,7 @@ var controls = {
container.appendChild(utils.createElement('span', { container.appendChild(utils.createElement('span', {
class: this.config.classNames.hidden class: this.config.classNames.hidden
}, this.config.i18n[type])); }, i18n.get(type, this.config)));
container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00')); container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));
@ -3351,7 +3458,7 @@ var controls = {
class: this.config.classNames.control, class: this.config.classNames.control,
'data-plyr-loop-action': option, 'data-plyr-loop-action': option,
}), }),
this.config.i18n[option] i18n.get(option, this.config)
); );
if (['start', 'end'].includes(option)) { if (['start', 'end'].includes(option)) {
const badge = controls.createBadge.call(this, '00:00'); const badge = controls.createBadge.call(this, '00:00');
@ -3370,7 +3477,7 @@ var controls = {
} }
if (!support.textTracks || !captions.getTracks.call(this).length) { if (!support.textTracks || !captions.getTracks.call(this).length) {
return this.config.i18n.none; return i18n.get('none', this.config);
} }
if (this.captions.active) { if (this.captions.active) {
@ -3381,7 +3488,7 @@ var controls = {
} }
} }
return this.config.i18n.disabled; return i18n.get('disabled', this.config);
}, },
@ -3416,7 +3523,7 @@ var controls = {
// Add the "None" option to turn off captions // Add the "None" option to turn off captions
tracks.unshift({ tracks.unshift({
language: '', language: '',
label: this.config.i18n.none label: i18n.get('none', this.config)
}); });
// Generate options // Generate options
@ -3660,7 +3767,7 @@ var controls = {
// Fast forward button // Fast forward button
if (this.config.controls.includes('fast-forward')) { if (this.config.controls.includes('fast-forward')) {
container.appendChild(controls.createButton.call(this, 'fastForward')); container.appendChild(controls.createButton.call(this, 'fast-forward'));
} }
// Progress // Progress
@ -3789,7 +3896,7 @@ var controls = {
'aria-haspopup': true, 'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-' + type, 'aria-controls': 'plyr-settings-' + data.id + '-' + type,
'aria-expanded': false 'aria-expanded': false
}), _this4.config.i18n[type]); }), i18n.get(type, _this4.config));
var value = utils.createElement('span', { var value = utils.createElement('span', {
class: _this4.config.classNames.menu.value class: _this4.config.classNames.menu.value
@ -3825,7 +3932,7 @@ var controls = {
'aria-haspopup': true, 'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-home', 'aria-controls': 'plyr-settings-' + data.id + '-home',
'aria-expanded': false 'aria-expanded': false
}, _this4.config.i18n[type]); }, i18n.get(type, _this4.config));
pane.appendChild(back); pane.appendChild(back);
@ -3967,7 +4074,6 @@ var controls = {
// Plyr Event Listeners // Plyr Event Listeners
// ========================================================================== // ==========================================================================
// Sniff out the browser
var browser$1 = utils.getBrowser(); var browser$1 = utils.getBrowser();
var Listeners = function () { var Listeners = function () {
@ -4074,7 +4180,7 @@ var Listeners = function () {
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); this.player.fastForward();
break; break;
case 37: case 37:
@ -4350,12 +4456,13 @@ var Listeners = function () {
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
var inputEvent = browser$1.isIE ? 'change' : 'input'; var inputEvent = browser$1.isIE ? 'change' : 'input';
// Trigger custom and default handlers // Run default and custom handlers
var proxy = function proxy(event, handlerKey, defaultHandler) { var proxy = function proxy(event, defaultHandler, customHandlerKey) {
var customHandler = _this4.player.config.listeners[handlerKey]; var customHandler = _this4.player.config.listeners[customHandlerKey];
var hasCustomHandler = utils.is.function(customHandler);
// Execute custom handler // Execute custom handler
if (utils.is.function(customHandler)) { if (hasCustomHandler) {
customHandler.call(_this4.player, event); customHandler.call(_this4.player, event);
} }
@ -4365,107 +4472,87 @@ var Listeners = function () {
} }
}; };
// Trigger custom and default handlers
var on = function on(element, type, defaultHandler, customHandlerKey) {
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var customHandler = _this4.player.config.listeners[customHandlerKey];
var hasCustomHandler = utils.is.function(customHandler);
utils.on(element, type, function (event) {
return proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler);
};
// Play/pause toggle // Play/pause toggle
utils.on(this.player.elements.buttons.play, 'click', function (event) { on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play');
return proxy(event, 'play', function () {
_this4.player.togglePlay();
});
});
// Pause // Pause
utils.on(this.player.elements.buttons.restart, 'click', function (event) { on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
return proxy(event, 'restart', function () {
_this4.player.restart();
});
});
// Rewind // Rewind
utils.on(this.player.elements.buttons.rewind, 'click', function (event) { on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
return proxy(event, 'rewind', function () {
_this4.player.rewind();
});
});
// Rewind // Rewind
utils.on(this.player.elements.buttons.forward, 'click', function (event) { on(this.player.elements.buttons.fastForward, 'click', this.player.fastForward, 'fastForward');
return proxy(event, 'forward', function () {
_this4.player.forward();
});
});
// Mute toggle // Mute toggle
utils.on(this.player.elements.buttons.mute, 'click', function (event) { on(this.player.elements.buttons.mute, 'click', function () {
return proxy(event, 'mute', function () { _this4.player.muted = !_this4.player.muted;
_this4.player.muted = !_this4.player.muted; }, 'mute');
});
});
// Captions toggle // Captions toggle
utils.on(this.player.elements.buttons.captions, 'click', function (event) { on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions);
return proxy(event, 'captions', function () {
_this4.player.toggleCaptions();
});
});
// Fullscreen toggle // Fullscreen toggle
utils.on(this.player.elements.buttons.fullscreen, 'click', function (event) { on(this.player.elements.buttons.fullscreen, 'click', function () {
return proxy(event, 'fullscreen', function () { _this4.player.fullscreen.toggle();
_this4.player.fullscreen.toggle(); }, 'fullscreen');
});
});
// Picture-in-Picture // Picture-in-Picture
utils.on(this.player.elements.buttons.pip, 'click', function (event) { on(this.player.elements.buttons.pip, 'click', function () {
return proxy(event, 'pip', function () { _this4.player.pip = 'toggle';
_this4.player.pip = 'toggle'; }, 'pip');
});
});
// Airplay // Airplay
utils.on(this.player.elements.buttons.airplay, 'click', function (event) { on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
return proxy(event, 'airplay', function () {
_this4.player.airplay();
});
});
// Settings menu // Settings menu
utils.on(this.player.elements.buttons.settings, 'click', function (event) { on(this.player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this4.player, event); controls.toggleMenu.call(_this4.player, event);
}); });
// Settings menu // Settings menu
utils.on(this.player.elements.settings.form, 'click', function (event) { on(this.player.elements.settings.form, 'click', function (event) {
event.stopPropagation(); event.stopPropagation();
// Settings menu items - use event delegation as items are added/removed // Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, _this4.player.config.selectors.inputs.language)) { if (utils.matches(event.target, _this4.player.config.selectors.inputs.language)) {
proxy(event, 'language', function () { proxy(event, function () {
_this4.player.language = event.target.value; _this4.player.language = event.target.value;
}); }, 'language');
} else if (utils.matches(event.target, _this4.player.config.selectors.inputs.quality)) { } else if (utils.matches(event.target, _this4.player.config.selectors.inputs.quality)) {
proxy(event, 'quality', function () { proxy(event, function () {
_this4.player.quality = event.target.value; _this4.player.quality = event.target.value;
}); }, 'quality');
} else if (utils.matches(event.target, _this4.player.config.selectors.inputs.speed)) { } else if (utils.matches(event.target, _this4.player.config.selectors.inputs.speed)) {
proxy(event, 'speed', function () { proxy(event, function () {
_this4.player.speed = parseFloat(event.target.value); _this4.player.speed = parseFloat(event.target.value);
}); }, 'speed');
} else { } else {
controls.showTab.call(_this4.player, event); controls.showTab.call(_this4.player, event);
} }
}); });
// Seek // Seek
utils.on(this.player.elements.inputs.seek, inputEvent, function (event) { on(this.player.elements.inputs.seek, inputEvent, 'seek', function (event) {
return proxy(event, 'seek', function () { _this4.player.currentTime = event.target.value / event.target.max * _this4.player.duration;
_this4.player.currentTime = event.target.value / event.target.max * _this4.player.duration;
});
}); });
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) { if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) {
utils.on(this.player.elements.display.currentTime, 'click', function () { on(this.player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start // Do nothing if we're at the start
if (_this4.player.currentTime === 0) { if (_this4.player.currentTime === 0) {
return; return;
@ -4477,79 +4564,75 @@ var Listeners = function () {
} }
// Volume // Volume
utils.on(this.player.elements.inputs.volume, inputEvent, function (event) { on(this.player.elements.inputs.volume, inputEvent, function (event) {
return proxy(event, 'volume', function () { _this4.player.volume = event.target.value;
_this4.player.volume = event.target.value; }, 'volume');
});
});
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser$1.isWebkit) { if (browser$1.isWebkit) {
utils.on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', function (event) { on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', function (event) {
controls.updateRangeFill.call(_this4.player, event.target); controls.updateRangeFill.call(_this4.player, event.target);
}); });
} }
// Seek tooltip // Seek tooltip
utils.on(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) { on(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this4.player, event); return controls.updateSeekTooltip.call(_this4.player, event);
}); });
// Toggle controls visibility based on mouse movement // Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) { if (this.player.config.hideControls) {
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mouseenter mouseleave', function (event) { on(this.player.elements.controls, 'mouseenter mouseleave', function (event) {
_this4.player.elements.controls.hover = event.type === 'mouseenter'; _this4.player.elements.controls.hover = event.type === 'mouseenter';
}); });
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) { on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
_this4.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type); _this4.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
}); });
// Focus in/out on controls // Focus in/out on controls
utils.on(this.player.elements.controls, 'focusin focusout', function (event) { on(this.player.elements.controls, 'focusin focusout', function (event) {
_this4.player.toggleControls(event); _this4.player.toggleControls(event);
}); });
} }
// Mouse wheel for volume // Mouse wheel for volume
utils.on(this.player.elements.inputs.volume, 'wheel', function (event) { on(this.player.elements.inputs.volume, 'wheel', function (event) {
return proxy(event, 'volume', function () { // Detect "natural" scroll - suppored on OS X Safari only
// Detect "natural" scroll - suppored on OS X Safari only // Other browsers on OS X will be inverted until support improves
// Other browsers on OS X will be inverted until support improves var inverted = event.webkitDirectionInvertedFromDevice;
var inverted = event.webkitDirectionInvertedFromDevice; var step = 1 / 50;
var step = 1 / 50; var direction = 0;
var direction = 0;
// Scroll down (or up on natural) to decrease // Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) { if (event.deltaY < 0 || event.deltaX > 0) {
if (inverted) { if (inverted) {
_this4.player.decreaseVolume(step); _this4.player.decreaseVolume(step);
direction = -1; direction = -1;
} else { } else {
_this4.player.increaseVolume(step); _this4.player.increaseVolume(step);
direction = 1; direction = 1;
}
} }
}
// Scroll up (or down on natural) to increase // Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) { if (event.deltaY > 0 || event.deltaX < 0) {
if (inverted) { if (inverted) {
_this4.player.increaseVolume(step); _this4.player.increaseVolume(step);
direction = 1; direction = 1;
} else { } else {
_this4.player.decreaseVolume(step); _this4.player.decreaseVolume(step);
direction = -1; direction = -1;
}
} }
}
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
if (direction === 1 && _this4.player.media.volume < 1 || direction === -1 && _this4.player.media.volume > 0) { if (direction === 1 && _this4.player.media.volume < 1 || direction === -1 && _this4.player.media.volume > 0) {
event.preventDefault(); event.preventDefault();
} }
}); }, 'volume', false);
}, false);
} }
// Reset on destroy // Reset on destroy
@ -4842,7 +4925,7 @@ var Ads = function () {
var update = function update() { var update = function update() {
var time = utils.formatTime(Math.max(_this5.manager.getRemainingTime(), 0)); var time = utils.formatTime(Math.max(_this5.manager.getRemainingTime(), 0));
var label = _this5.player.config.i18n.advertisement + ' - ' + time; var label = i18n.get('advertisement', _this5.player.config) + ' - ' + time;
_this5.elements.container.setAttribute('data-badge-text', label); _this5.elements.container.setAttribute('data-badge-text', label);
}; };
@ -6071,7 +6154,6 @@ var vimeo = {
// Plyr Media // Plyr Media
// ========================================================================== // ==========================================================================
// Sniff out the browser
var browser$3 = utils.getBrowser(); var browser$3 = utils.getBrowser();
var media = { var media = {
@ -6311,17 +6393,11 @@ var source = {
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.0.2 // plyr.js v3.0.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
// Private properties
// TODO: Use a WeakMap for private globals
// const globals = new WeakMap();
// Plyr instance
var Plyr = function () { var Plyr = function () {
function Plyr(target, options) { function Plyr(target, options) {
var _this = this; var _this = this;
@ -6690,8 +6766,8 @@ var Plyr = function () {
*/ */
}, { }, {
key: 'forward', key: 'fastForward',
value: function forward(seekTime) { value: function fastForward(seekTime) {
this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
} }

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

3023
dist/plyr.polyfilled.js vendored

File diff suppressed because it is too large Load Diff

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

33
src/js/controls.js vendored
View File

@ -5,6 +5,7 @@
import support from './support'; import support from './support';
import utils from './utils'; import utils from './utils';
import ui from './ui'; import ui from './ui';
import i18n from './i18n';
import captions from './captions'; import captions from './captions';
// Sniff out the browser // Sniff out the browser
@ -74,7 +75,7 @@ const controls = {
// Create hidden text label // Create hidden text label
createLabel(type, attr) { createLabel(type, attr) {
let text = this.config.i18n[type]; let text = i18n.get(type, this.config);
const attributes = Object.assign({}, attr); const attributes = Object.assign({}, attr);
switch (type) { switch (type) {
@ -126,7 +127,7 @@ const controls = {
createButton(buttonType, attr) { createButton(buttonType, attr) {
const button = utils.createElement('button'); const button = utils.createElement('button');
const attributes = Object.assign({}, attr); const attributes = Object.assign({}, attr);
let type = buttonType; let type = utils.toCamelCase(buttonType);
let toggle = false; let toggle = false;
let label; let label;
@ -147,7 +148,7 @@ const controls = {
} }
// Large play button // Large play button
switch (type) { switch (buttonType) {
case 'play': case 'play':
toggle = true; toggle = true;
label = 'play'; label = 'play';
@ -189,7 +190,7 @@ const controls = {
default: default:
label = type; label = type;
icon = type; icon = buttonType;
} }
// Setup toggle icon and labels // Setup toggle icon and labels
@ -204,7 +205,7 @@ const controls = {
// Add aria attributes // Add aria attributes
attributes['aria-pressed'] = false; attributes['aria-pressed'] = false;
attributes['aria-label'] = this.config.i18n[label]; attributes['aria-label'] = i18n.get(label, this.config);
} else { } else {
button.appendChild(controls.createIcon.call(this, icon)); button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label)); button.appendChild(controls.createLabel.call(this, label));
@ -238,7 +239,7 @@ const controls = {
for: attributes.id, for: attributes.id,
class: this.config.classNames.hidden, class: this.config.classNames.hidden,
}, },
this.config.i18n[type], i18n.get(type, this.config),
); );
// Seek input // Seek input
@ -291,11 +292,11 @@ const controls = {
let suffix = ''; let suffix = '';
switch (type) { switch (type) {
case 'played': case 'played':
suffix = this.config.i18n.played; suffix = i18n.get('played', this.config);
break; break;
case 'buffer': case 'buffer':
suffix = this.config.i18n.buffered; suffix = i18n.get('buffered', this.config);
break; break;
default: default:
@ -322,7 +323,7 @@ const controls = {
{ {
class: this.config.classNames.hidden, class: this.config.classNames.hidden,
}, },
this.config.i18n[type], i18n.get(type, this.config),
), ),
); );
@ -617,7 +618,7 @@ const controls = {
class: this.config.classNames.control, class: this.config.classNames.control,
'data-plyr-loop-action': option, 'data-plyr-loop-action': option,
}), }),
this.config.i18n[option] i18n.get(option, this.config)
); );
if (['start', 'end'].includes(option)) { if (['start', 'end'].includes(option)) {
@ -638,7 +639,7 @@ const controls = {
} }
if (!support.textTracks || !captions.getTracks.call(this).length) { if (!support.textTracks || !captions.getTracks.call(this).length) {
return this.config.i18n.none; return i18n.get('none', this.config);
} }
if (this.captions.active) { if (this.captions.active) {
@ -649,7 +650,7 @@ const controls = {
} }
} }
return this.config.i18n.disabled; return i18n.get('disabled', this.config);
}, },
// Set a list of available captions languages // Set a list of available captions languages
@ -679,7 +680,7 @@ const controls = {
// Add the "None" option to turn off captions // Add the "None" option to turn off captions
tracks.unshift({ tracks.unshift({
language: '', language: '',
label: this.config.i18n.none, label: i18n.get('none', this.config),
}); });
// Generate options // Generate options
@ -927,7 +928,7 @@ const controls = {
// Fast forward button // Fast forward button
if (this.config.controls.includes('fast-forward')) { if (this.config.controls.includes('fast-forward')) {
container.appendChild(controls.createButton.call(this, 'fastForward')); container.appendChild(controls.createButton.call(this, 'fast-forward'));
} }
// Progress // Progress
@ -1069,7 +1070,7 @@ const controls = {
'aria-controls': `plyr-settings-${data.id}-${type}`, 'aria-controls': `plyr-settings-${data.id}-${type}`,
'aria-expanded': false, 'aria-expanded': false,
}), }),
this.config.i18n[type], i18n.get(type, this.config),
); );
const value = utils.createElement('span', { const value = utils.createElement('span', {
@ -1109,7 +1110,7 @@ const controls = {
'aria-controls': `plyr-settings-${data.id}-home`, 'aria-controls': `plyr-settings-${data.id}-home`,
'aria-expanded': false, 'aria-expanded': false,
}, },
this.config.i18n[type], i18n.get(type, this.config),
); );
pane.appendChild(back); pane.appendChild(back);

View File

@ -132,7 +132,10 @@ const defaults = {
// Default controls // Default controls
controls: [ controls: [
'play-large', 'play-large',
// 'restart',
// 'rewind',
'play', 'play',
// 'fast-forward',
'progress', 'progress',
'current-time', 'current-time',
'mute', 'mute',
@ -155,7 +158,7 @@ const defaults = {
rewind: 'Rewind {seektime} secs', rewind: 'Rewind {seektime} secs',
play: 'Play', play: 'Play',
pause: 'Pause', pause: 'Pause',
forward: 'Forward {seektime} secs', fastForward: 'Forward {seektime} secs',
seek: 'Seek', seek: 'Seek',
played: 'Played', played: 'Played',
buffered: 'Buffered', buffered: 'Buffered',
@ -203,7 +206,7 @@ const defaults = {
pause: null, pause: null,
restart: null, restart: null,
rewind: null, rewind: null,
forward: null, fastForward: null,
mute: null, mute: null,
volume: null, volume: null,
captions: null, captions: null,

View File

@ -161,6 +161,8 @@ class Fullscreen {
return; return;
} }
console.warn(this.prefix);
// iOS native fullscreen doesn't need the request step // iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) { if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) { if (this.player.playing) {
@ -169,7 +171,7 @@ class Fullscreen {
} else if (!Fullscreen.native) { } else if (!Fullscreen.native) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullScreen(); this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) { } else if (!utils.is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.name}`](); this.target[`${this.prefix}Request${this.name}`]();
} }
@ -197,6 +199,8 @@ class Fullscreen {
// Toggle state // Toggle state
toggle() { toggle() {
console.warn('TOGGLE');
if (!this.active) { if (!this.active) {
this.enter(); this.enter();
} else { } else {

31
src/js/i18n.js Normal file
View File

@ -0,0 +1,31 @@
// ==========================================================================
// Plyr internationalization
// ==========================================================================
import utils from './utils';
const i18n = {
get(key = '', config = {}) {
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
let string = config.i18n[key];
const replace = {
'{seektime}': config.seekTime,
'{title}': config.title,
};
Object.entries(replace).forEach(([
key,
value,
]) => {
string = utils.replaceAll(string, key, value);
});
return string;
},
};
export default i18n;

View File

@ -128,7 +128,7 @@ class Listeners {
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); this.player.fastForward();
break; break;
case 37: case 37:
@ -379,12 +379,13 @@ class Listeners {
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
const inputEvent = browser.isIE ? 'change' : 'input'; const inputEvent = browser.isIE ? 'change' : 'input';
// Trigger custom and default handlers // Run default and custom handlers
const proxy = (event, handlerKey, defaultHandler) => { const proxy = (event, defaultHandler, customHandlerKey) => {
const customHandler = this.player.config.listeners[handlerKey]; const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = utils.is.function(customHandler);
// Execute custom handler // Execute custom handler
if (utils.is.function(customHandler)) { if (hasCustomHandler) {
customHandler.call(this.player, event); customHandler.call(this.player, event);
} }
@ -394,107 +395,110 @@ class Listeners {
} }
}; };
// Trigger custom and default handlers
const on = (element, type, defaultHandler, customHandlerKey, passive = true) => {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = utils.is.function(customHandler);
utils.on(element, type, event => proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler);
};
// Play/pause toggle // Play/pause toggle
utils.on(this.player.elements.buttons.play, 'click', event => on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play');
proxy(event, 'play', () => {
this.player.togglePlay();
}),
);
// Pause // Pause
utils.on(this.player.elements.buttons.restart, 'click', event => on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
proxy(event, 'restart', () => {
this.player.restart();
}),
);
// Rewind // Rewind
utils.on(this.player.elements.buttons.rewind, 'click', event => on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
proxy(event, 'rewind', () => {
this.player.rewind();
}),
);
// Rewind // Rewind
utils.on(this.player.elements.buttons.forward, 'click', event => on(this.player.elements.buttons.fastForward, 'click', this.player.fastForward, 'fastForward');
proxy(event, 'forward', () => {
this.player.forward();
}),
);
// Mute toggle // Mute toggle
utils.on(this.player.elements.buttons.mute, 'click', event => on(
proxy(event, 'mute', () => { this.player.elements.buttons.mute,
'click',
() => {
this.player.muted = !this.player.muted; this.player.muted = !this.player.muted;
}), },
'mute',
); );
// Captions toggle // Captions toggle
utils.on(this.player.elements.buttons.captions, 'click', event => on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions);
proxy(event, 'captions', () => {
this.player.toggleCaptions();
}),
);
// Fullscreen toggle // Fullscreen toggle
utils.on(this.player.elements.buttons.fullscreen, 'click', event => on(
proxy(event, 'fullscreen', () => { this.player.elements.buttons.fullscreen,
'click',
() => {
this.player.fullscreen.toggle(); this.player.fullscreen.toggle();
}), },
'fullscreen',
); );
// Picture-in-Picture // Picture-in-Picture
utils.on(this.player.elements.buttons.pip, 'click', event => on(
proxy(event, 'pip', () => { this.player.elements.buttons.pip,
'click',
() => {
this.player.pip = 'toggle'; this.player.pip = 'toggle';
}), },
'pip',
); );
// Airplay // Airplay
utils.on(this.player.elements.buttons.airplay, 'click', event => on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
proxy(event, 'airplay', () => {
this.player.airplay();
}),
);
// Settings menu // Settings menu
utils.on(this.player.elements.buttons.settings, 'click', event => { on(this.player.elements.buttons.settings, 'click', event => {
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(this.player, event);
}); });
// Settings menu // Settings menu
utils.on(this.player.elements.settings.form, 'click', event => { on(this.player.elements.settings.form, 'click', event => {
event.stopPropagation(); event.stopPropagation();
// Settings menu items - use event delegation as items are added/removed // Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, this.player.config.selectors.inputs.language)) { if (utils.matches(event.target, this.player.config.selectors.inputs.language)) {
proxy(event, 'language', () => { proxy(
this.player.language = event.target.value; event,
}); () => {
this.player.language = event.target.value;
},
'language',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) { } else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) {
proxy(event, 'quality', () => { proxy(
this.player.quality = event.target.value; event,
}); () => {
this.player.quality = event.target.value;
},
'quality',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) { } else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) {
proxy(event, 'speed', () => { proxy(
this.player.speed = parseFloat(event.target.value); event,
}); () => {
this.player.speed = parseFloat(event.target.value);
},
'speed',
);
} else { } else {
controls.showTab.call(this.player, event); controls.showTab.call(this.player, event);
} }
}); });
// Seek // Seek
utils.on(this.player.elements.inputs.seek, inputEvent, event => on(this.player.elements.inputs.seek, inputEvent, 'seek', event => {
proxy(event, 'seek', () => { this.player.currentTime = event.target.value / event.target.max * this.player.duration;
this.player.currentTime = event.target.value / event.target.max * this.player.duration; });
}),
);
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) { if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) {
utils.on(this.player.elements.display.currentTime, 'click', () => { on(this.player.elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start // Do nothing if we're at the start
if (this.player.currentTime === 0) { if (this.player.currentTime === 0) {
return; return;
@ -506,31 +510,34 @@ class Listeners {
} }
// Volume // Volume
utils.on(this.player.elements.inputs.volume, inputEvent, event => on(
proxy(event, 'volume', () => { this.player.elements.inputs.volume,
inputEvent,
event => {
this.player.volume = event.target.value; this.player.volume = event.target.value;
}), },
'volume',
); );
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) { if (browser.isWebkit) {
utils.on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => { on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => {
controls.updateRangeFill.call(this.player, event.target); controls.updateRangeFill.call(this.player, event.target);
}); });
} }
// Seek tooltip // Seek tooltip
utils.on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event)); on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event));
// Toggle controls visibility based on mouse movement // Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) { if (this.player.config.hideControls) {
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mouseenter mouseleave', event => { on(this.player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls.hover = event.type === 'mouseenter'; this.player.elements.controls.hover = event.type === 'mouseenter';
}); });
// Watch for cursor over controls so they don't hide when trying to interact // Watch for cursor over controls so they don't hide when trying to interact
utils.on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => { on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
this.player.elements.controls.pressed = [ this.player.elements.controls.pressed = [
'mousedown', 'mousedown',
'touchstart', 'touchstart',
@ -538,50 +545,50 @@ class Listeners {
}); });
// Focus in/out on controls // Focus in/out on controls
utils.on(this.player.elements.controls, 'focusin focusout', event => { on(this.player.elements.controls, 'focusin focusout', event => {
this.player.toggleControls(event); this.player.toggleControls(event);
}); });
} }
// Mouse wheel for volume // Mouse wheel for volume
utils.on( on(
this.player.elements.inputs.volume, this.player.elements.inputs.volume,
'wheel', 'wheel',
event => event => {
proxy(event, 'volume', () => { // Detect "natural" scroll - suppored on OS X Safari only
// Detect "natural" scroll - suppored on OS X Safari only // Other browsers on OS X will be inverted until support improves
// Other browsers on OS X will be inverted until support improves const inverted = event.webkitDirectionInvertedFromDevice;
const inverted = event.webkitDirectionInvertedFromDevice; const step = 1 / 50;
const step = 1 / 50; let direction = 0;
let direction = 0;
// Scroll down (or up on natural) to decrease // Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) { if (event.deltaY < 0 || event.deltaX > 0) {
if (inverted) { if (inverted) {
this.player.decreaseVolume(step); this.player.decreaseVolume(step);
direction = -1; direction = -1;
} else { } else {
this.player.increaseVolume(step); this.player.increaseVolume(step);
direction = 1; direction = 1;
}
} }
}
// Scroll up (or down on natural) to increase // Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) { if (event.deltaY > 0 || event.deltaX < 0) {
if (inverted) { if (inverted) {
this.player.increaseVolume(step); this.player.increaseVolume(step);
direction = 1; direction = 1;
} else { } else {
this.player.decreaseVolume(step); this.player.decreaseVolume(step);
direction = -1; direction = -1;
}
} }
}
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) { if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) {
event.preventDefault(); event.preventDefault();
} }
}), },
'volume',
false, false,
); );
} }

View File

@ -7,6 +7,7 @@
/* global google */ /* global google */
import utils from '../utils'; import utils from '../utils';
import i18n from '../i18n';
class Ads { class Ads {
/** /**
@ -178,7 +179,7 @@ class Ads {
const update = () => { const update = () => {
const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0)); const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0));
const label = `${this.player.config.i18n.advertisement} - ${time}`; const label = `${i18n.get('advertisement', this.player.config)} - ${time}`;
this.elements.container.setAttribute('data-badge-text', label); this.elements.container.setAttribute('data-badge-text', label);
}; };

View File

@ -405,7 +405,7 @@ class Plyr {
* Fast forward * Fast forward
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime * @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
*/ */
forward(seekTime) { fastForward(seekTime) {
this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
} }

View File

@ -5,6 +5,7 @@
import utils from './utils'; import utils from './utils';
import captions from './captions'; import captions from './captions';
import controls from './controls'; import controls from './controls';
import i18n from './i18n';
const ui = { const ui = {
addStyleHook() { addStyleHook() {
@ -94,7 +95,7 @@ const ui = {
// Setup aria attribute for play and iframe title // Setup aria attribute for play and iframe title
setTitle() { setTitle() {
// Find the current text // Find the current text
let label = this.config.i18n.play; let label = i18n.get('play', this.config);
// If there's a media title set, use that for the label // If there's a media title set, use that for the label
if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
@ -123,7 +124,7 @@ const ui = {
// Default to media type // Default to media type
const title = !utils.is.empty(this.config.title) ? this.config.title : 'video'; const title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title)); iframe.setAttribute('title', i18n.get('frameTitle', this.config));
} }
}, },

View File

@ -540,7 +540,7 @@ const utils = {
}, },
// Toggle event listener // Toggle event listener
toggleListener(elements, event, callback, toggle, passive, capture) { toggleListener(elements, event, callback, toggle = false, passive = true, capture = false) {
// Bail if no elemetns, event, or callback // Bail if no elemetns, event, or callback
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) { if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
return; return;
@ -562,16 +562,16 @@ const utils = {
const events = event.split(' '); const events = event.split(' ');
// Build options // Build options
// Default to just capture boolean // Default to just the capture boolean for browsers with no passive listener support
let options = utils.is.boolean(capture) ? capture : false; let options = capture;
// If passive events listeners are supported // If passive events listeners are supported
if (support.passiveListeners) { if (support.passiveListeners) {
options = { options = {
// Whether the listener can be passive (i.e. default never prevented) // Whether the listener can be passive (i.e. default never prevented)
passive: utils.is.boolean(passive) ? passive : true, passive,
// Whether the listener is a capturing listener or not // Whether the listener is a capturing listener or not
capture: utils.is.boolean(capture) ? capture : false, capture,
}; };
} }
@ -582,12 +582,12 @@ const utils = {
}, },
// Bind event handler // Bind event handler
on(element, events, callback, passive, capture) { on(element, events = '', callback, passive = true, capture = false) {
utils.toggleListener(element, events, callback, true, passive, capture); utils.toggleListener(element, events, callback, true, passive, capture);
}, },
// Unbind event handler // Unbind event handler
off(element, events, callback, passive, capture) { off(element, events = '', callback, passive = true, capture = false) {
utils.toggleListener(element, events, callback, false, passive, capture); utils.toggleListener(element, events, callback, false, passive, capture);
}, },
@ -678,6 +678,44 @@ const utils = {
return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`; return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`;
}, },
// Replace all occurances of a string in a string
replaceAll(input = '', find = '', replace = '') {
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
},
// Convert to title case
toTitleCase(input = '') {
return input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase());
},
// Convert string to pascalCase
toPascalCase(input = '') {
let string = input.toString();
// Convert kebab case
string = utils.replaceAll(string, '-', ' ');
// Convert snake case
string = utils.replaceAll(string, '_', ' ');
// Convert to title case
string = utils.toTitleCase(string);
// Convert to pascal case
return utils.replaceAll(string, ' ', '');
},
// Convert string to pascalCase
toCamelCase(input = '') {
let string = input.toString();
// Convert to pascal case
string = utils.toPascalCase(string);
// Convert first character to lowercase
return string.charAt(0).toLowerCase() + string.slice(1);
},
// Deep extend destination object with N more objects // Deep extend destination object with N more objects
extend(target = {}, ...sources) { extend(target = {}, ...sources) {
if (!sources.length) { if (!sources.length) {