Compare commits

...

41 Commits

Author SHA1 Message Date
Sam Potts efe70ab48e v3.3.11 2018-06-11 16:39:35 +10:00
Sam Potts 1e1874d86b Merge pull request #1009 from friday/contributing
Contributing document and codepen demo updates
2018-06-08 10:52:37 +10:00
Albin Larsson 16624b90d3 Clarifications due to recent non-constructive comments in #1001 2018-06-07 14:30:05 +02:00
Albin Larsson ed14b656a8 Add contributing document 2018-06-07 11:47:51 +02:00
Albin Larsson 1d0cf16254 Readme: Replace streaming section with codepen templates for all supported formats and libraries (and updated code) 2018-06-07 11:47:34 +02:00
Sam Potts 05b85da3f4 Update controls.md (fixes #996) 2018-06-02 19:48:48 +10:00
Sam Potts a4caba120c Merge branch 'master' of github.com:sampotts/plyr
# Conflicts:
#	demo/dist/demo.css
#	dist/plyr.css
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
2018-05-31 23:43:40 +10:00
Sam Potts 969a877a34 v3.3.10 2018-05-31 23:41:48 +10:00
Sam Potts fb22a90d33 Merge pull request #993 from sampotts/develop
v3.3.10
2018-05-31 23:39:51 +10:00
Sam Potts 108bd3dfa0 Fixed incorrect BEM formatting, fixed buffer alignment 2018-05-31 23:33:59 +10:00
Sam Potts 5a445ae647 Merge pull request #988 from kim-company/translate-qualities
Translate quality badges and quality names
2018-05-31 22:43:31 +10:00
Philip Giuliani 56668f58b6 Rename qualityName to label 2018-05-31 14:40:56 +02:00
Sam Potts eec96e5879 Merge pull request #990 from friday/travis
Travis integration
2018-05-31 18:33:00 +10:00
Sam Potts c6c9d877e4 Merge pull request #992 from philipgiuliani/quality-resume-time
Wait for the metadata to be loaded before setting the currentTime
2018-05-31 18:32:10 +10:00
Philip Giuliani 61f4b998e1 Wait for the metadata to be loaded before setting the currentTime 2018-05-31 10:17:41 +02:00
Sam Potts 25a319d884 Merge pull request #989 from friday/pr-template
Proposed changes to PR template
2018-05-31 08:34:02 +10:00
Albin Larsson 4bf678fe6c Proposed changes to PR template (develop as base branch, exclude build and typo fix) 2018-05-30 19:52:15 +02:00
Albin Larsson 359acd6bb9 Lint and build in travis 2018-05-30 19:40:17 +02:00
Albin Larsson 2fce385691 Add npm scripts for linting and building 2018-05-30 19:40:17 +02:00
Albin Larsson a82c61c539 Gulp: Add option to build only 2018-05-30 19:39:57 +02:00
Philip Giuliani a8fa125a96 Refactor getDeep method 2018-05-30 17:30:10 +02:00
Philip Giuliani 6435ced707 Return undefined when the key is not present. 2018-05-30 17:10:38 +02:00
Philip Giuliani 94dc0d176c Rebuild 2018-05-30 17:03:41 +02:00
Philip Giuliani 23c21252e8 Rebuild 2018-05-30 17:02:07 +02:00
Philip Giuliani 41c7dff0e8 Add getDeep method to utils 2018-05-30 17:02:07 +02:00
Philip Giuliani e3bae562fc Rebuild 2018-05-30 17:02:07 +02:00
Philip Giuliani 1c1668bfc3 Implement translation support for qualityName and qualityBadge 2018-05-30 17:02:07 +02:00
Philip Giuliani e0c09c51f2 Allow nested translations 2018-05-30 17:02:07 +02:00
Philip Giuliani 8de06fb862 Accept quality 0 2018-05-30 17:02:07 +02:00
Sam Potts 64412868d8 ESLint tweak 2018-05-30 17:02:07 +02:00
Sam Potts 450958c290 Merge pull request #981 from friday/hls-captions
Improve captions handling for streaming
2018-05-30 21:44:42 +10:00
Sam Potts 963fe11ad6 Update readme.md 2018-05-30 00:22:01 +10:00
Sam Potts ce199e4b6b Merge pull request #986 from friday/701
Call duration update method manually if user config has duration
2018-05-30 00:20:15 +10:00
Albin Larsson 9d798893b5 Call duration update method manually if user config has duration 2018-05-29 16:06:07 +02:00
Albin Larsson 64399e0717 Defer initial captions update to next tick, to avoid event being triggered to early 2018-05-28 17:54:25 +02:00
Albin Larsson f58e23b325 Change to using addtrack and removetrack listeners since 'change' didn't trigger in firefox for embedded captions (may also be a hls.js issue) 2018-05-28 16:19:44 +02:00
Albin Larsson 812e07b734 Replace browser language detection in defaults.js with explicit 'auto' option 2018-05-28 07:43:37 +02:00
Albin Larsson c9298fde76 Fix typo 2018-05-28 07:08:38 +02:00
Albin Larsson 0109454a34 Ensure language is set in case the track is added after initialization, and trigger languagechange event when language is initially set 2018-05-28 06:08:03 +02:00
Albin Larsson 813f703211 Add option to watch caption track changes and update language options 2018-05-28 06:08:03 +02:00
Albin Larsson 7aad747c25 Optimize captions code reused and ensure captionsenabled/captionsdisabled
will be sent on initial setup
2018-05-28 05:44:54 +02:00
34 changed files with 1201 additions and 2372 deletions
+5 -5
View File
@@ -1,8 +1,8 @@
### Link to related issue (if applicable)
### Sumary of proposed changes
### Summary of proposed changes
### Task list
- [ ] Tested on [supported browsers](https://github.com/sampotts/plyr#browser-support)
- [ ] Gulp build completed
### Checklist
- [ ] Use `develop` as the base branch
- [ ] Exclude the gulp build from the PR
- [ ] Test on [supported browsers](https://github.com/sampotts/plyr#browser-support)
+7
View File
@@ -0,0 +1,7 @@
language: node_js
node_js:
- 'lts/*'
script:
- npm run lint
- npm run build
+52
View File
@@ -0,0 +1,52 @@
# Contributing
We welcome bug reports, feature requests and pull requests. If you want to help us out, please follow these guidelines, in order to avoid redundant work.
## Reporting issues
Our GitHub issue tracker is for bug reports and feature requests. Don't create support issues here. Use [Stack Overflow](https://stackoverflow.com/) or [our Slack](https://bit.ly/plyr-chat) for that.
Please verify that your issue hasn't already been answered by our FAQ (https://github.com/sampotts/plyr/wiki/FAQ), or that there isn't already an open issue for it.
When applicable, check that your problem doesn't happen without Plyr (see [FAQ#1](https://github.com/sampotts/plyr/wiki/FAQ#1-does-plyr-work-with--)).
Verify that you are following the documentation, are using the latest version of Plyr, and aren't getting any errors in your own code, causing the issues.
Describe the issue as detailed as possible, answering these questions:
* Does it happen only with specific options and/or specific browsers?
* Does is happen only with HTML5 video, audio, youtube, vimeo or a specific library?
* Does the issue happen on [our demo](https://plyr.io/)? If not, please recreate it with a **minimal** example online. You can use our Codepen templates to get started:
* [HTML5 video](https://codepen.io/pen?template=bKeqpr)
* [HTML5 audio](https://codepen.io/pen?template=rKLywR)
* [YouTube](https://codepen.io/pen?template=GGqbbJ)
* [Vimeo](https://codepen.io/pen?template=bKeXNq)
* [Dash.js integration](https://codepen.io/pen?template=zaBgBy)
* [Hls.js integration](https://codepen.io/pen?template=oyLKQb)
* [Shaka Player integration](https://codepen.io/pen?template=ZRpzZO)
It's important that you keep the issue description and replication demo **minimal**. If your implementation is using a framework, library or custom methods, which aren't needed to reproduce the issue, this makes it harder to debug and understand the issue. While it may be relevant to bring this up (ex: "I need Plyr to trigger the event sooner or it breaks Framework X") it also means that the person who is trying to fix the issue either has to know or learn your frameworks, libraries and custom methods, or that no one will try to fix your issue because it's too much work.
In order to keep things on topic and to avoid bothering people with github notifications, please don't combine multiple problems or bugs into one issue, don't comment on issues unless your comment is related to that issue, and don't post "+1" or "I agree" type of comments. Use the emojis instead.
Last but not least: Keep a civil tone in issues and comments. Non-constructive comments may be removed.
## Requesting features and improvements
If you are missing something in Plyr, you can create a GitHub issue for this as well. Since we prioritize fixing bugs first, and may have a lot of other suggestions and architectural changes to work on as well, these may not be at the top of our list. If it's important or urgent to you, you may want to first ensure it's something we want to have in Plyr, and then contribute it as a pull request.
## Contributing features and documentation
* Fork Plyr, and create a new branch in your fork, based on the **develop** branch
* To test locally, you can use the demo. First make sure you have installed the dependencies with `npm install` or `yarn`. Run `gulp` to build while you are working, and run a local server from the repository root directory. If you have Python installed, this command should work: `python -m SimpleHTTPServer 8080`. Then go to `http://localhost:8080/demo/`
* Develop and test your modifications.
* Preferably commit your changes as independent logical chunks, with meaningful messages. Make sure you do not commit unnecessary files or changes, such as logging or breakpoints you added for testing, and the build output.
* If your modifications changes the documented behavior or add new features, document these changes in readme.md.
* When finished, push the changes to your GitHub repository and send a pull request to **develop**. Describe what your PR does.
* If the Travis build fails, or if you get a code review with change requests, you can fix these by pushing new or rebased commits to the branch.
+416 -397
View File
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -116,9 +116,8 @@ const controls = `
<span class="plyr__tooltip" role="tooltip">Forward {seektime} secs</span>
</button>
<div class="plyr__progress">
<label for="plyr-seek-{id}" class="plyr__sr-only">Seek</label>
<input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" id="plyr-seek-{id}">
<progress class="plyr__progress--buffer" min="0" max="100" value="0">% buffered</progress>
<input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
<progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
<span role="tooltip" class="plyr__tooltip">00:00</span>
</div>
<div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
@@ -130,8 +129,7 @@ const controls = `
<span class="label--not-pressed plyr__tooltip" role="tooltip">Mute</span>
</button>
<div class="plyr__volume">
<label for="plyr-volume-{id}" class="plyr__sr-only">Volume</label>
<input data-plyr="volume" type="range" min="0" max="1" step="0.05" value="1" autocomplete="off" id="plyr-volume-{id}">
<input data-plyr="volume" type="range" min="0" max="1" step="0.05" value="1" autocomplete="off" aria-label="Volume">
</div>
<button type="button" class="plyr__control" aria-pressed="true" aria-label="Enable captions" data-plyr="captions">
<svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-captions-on"></use></svg>
+1 -1
View File
File diff suppressed because one or more lines are too long
+95 -15
View File
@@ -767,11 +767,12 @@ function getLocationOrigin() {
// Oh dear IE10...
if (!document.location.origin) {
document.location.origin =
return (
document.location.protocol +
'//' +
document.location.hostname +
(document.location.port ? ':' + document.location.port : '');
(document.location.port ? ':' + document.location.port : '')
);
}
return document.location.origin;
@@ -1866,7 +1867,7 @@ Raven.prototype = {
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.25.2',
VERSION: '3.26.1',
debug: false,
@@ -2032,7 +2033,7 @@ Raven.prototype = {
if (isFunction$1(options)) {
args = func || [];
func = options;
options = undefined;
options = {};
}
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 {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
*/
wrap: function(options, func, _before) {
@@ -2156,8 +2157,9 @@ Raven.prototype = {
_promiseRejectionHandler: function(event) {
this._logDebug('debug', 'Raven caught unhandled promise rejection:', event);
this.captureException(event.reason, {
extra: {
unhandledPromiseRejection: true
mechanism: {
type: 'onunhandledrejection',
handled: false
}
});
},
@@ -2834,7 +2836,15 @@ Raven.prototype = {
}
var originalCallback = args[0];
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
@@ -2861,7 +2871,15 @@ Raven.prototype = {
// preserve arity
try {
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) {
// can sometimes get 'Permission denied to access property "handle Event'
@@ -2901,7 +2919,20 @@ Raven.prototype = {
return orig.call(
this,
evtName,
self.wrap(fn, undefined, before),
self.wrap(
{
mechanism: {
type: 'instrument',
data: {
target: global,
function: 'addEventListener',
handler: fn.name
}
}
},
fn,
before
),
capture,
secure
);
@@ -2935,7 +2966,17 @@ Raven.prototype = {
'requestAnimationFrame',
function(orig) {
return function(cb) {
return orig(self.wrap(cb));
return orig(
self.wrap(
{
mechanism: {
type: 'instrument',
data: {function: 'requestAnimationFrame', handler: orig.name}
}
},
cb
)
);
};
},
wrappedBuiltIns
@@ -2998,7 +3039,15 @@ Raven.prototype = {
function wrapProp(prop, xhr) {
if (prop in xhr && isFunction$1(xhr[prop])) {
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
}
}
@@ -3063,7 +3112,19 @@ Raven.prototype = {
xhr,
'onreadystatechange',
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 */
);
} else {
@@ -3287,10 +3348,16 @@ Raven.prototype = {
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 (!this._ignoreOnError) {
this._handleStackInfo.apply(this, arguments);
this._handleStackInfo(stackInfo, options);
}
},
@@ -3427,6 +3494,19 @@ Raven.prototype = {
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!
this._send(data);
},
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+109 -114
View File
@@ -1300,6 +1300,14 @@ var utils = {
},
// Get a nested value in an object
getDeep: function getDeep(object, path) {
return path.split('.').reduce(function (obj, key) {
return obj && obj[key];
}, object);
},
// Get the closest value in an array
closest: function closest(array, value) {
if (!utils.is.array(array) || !array.length) {
@@ -1719,6 +1727,13 @@ var html5 = {
player.media.src = supported[0].getAttribute('src');
// Restore time
var onLoadedMetaData = function onLoadedMetaData() {
player.currentTime = currentTime;
player.off('loadedmetadata', onLoadedMetaData);
};
player.on('loadedmetadata', onLoadedMetaData);
// Load new source
player.media.load();
@@ -1727,9 +1742,6 @@ var html5 = {
player.play();
}
// Restore time
player.currentTime = currentTime;
// Trigger change event
utils.dispatchEvent.call(player, player.media, 'qualitychange', false, {
quality: input
@@ -1771,11 +1783,15 @@ var i18n = {
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)) {
if (utils.is.empty(key) || utils.is.empty(config)) {
return '';
}
var string = config.i18n[key];
var string = utils.getDeep(config.i18n, key);
if (utils.is.empty(string)) {
return '';
}
var replace = {
'{seektime}': config.seekTime,
@@ -2449,27 +2465,7 @@ var controls = {
// Get the badge HTML for HD, 4K etc
var getBadge = function getBadge(quality) {
var label = '';
switch (quality) {
case 2160:
label = '4K';
break;
case 1440:
case 1080:
case 720:
label = 'HD';
break;
case 576:
case 480:
label = 'SD';
break;
default:
break;
}
var label = i18n.get('qualityBadge.' + quality, _this3.config);
if (!label.length) {
return null;
@@ -2492,7 +2488,6 @@ var controls = {
// Translate a value into a nice label
// TODO: Localisation
getLabel: function getLabel(setting, value) {
switch (setting) {
case 'speed':
@@ -2500,7 +2495,13 @@ var controls = {
case 'quality':
if (utils.is.number(value)) {
return value + 'p';
var label = i18n.get('qualityLabel.' + value, this.config);
if (!label.length) {
return value + 'p';
}
return label;
}
return utils.toTitleCase(value);
@@ -2660,12 +2661,7 @@ var controls = {
// Generate options
tracks.forEach(function (track) {
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.captions.language.toLowerCase());
});
// Store reference
this.options.captions = tracks.map(function (track) {
return track.language;
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);
@@ -3268,28 +3264,6 @@ var captions = {
return;
}
// Set default language if not set
var stored = this.storage.get('language');
if (!utils.is.empty(stored)) {
this.captions.language = stored;
}
if (utils.is.empty(this.captions.language)) {
this.captions.language = this.config.captions.language.toLowerCase();
}
// Set captions enabled state if not set
if (!utils.is.boolean(this.captions.active)) {
var active = this.storage.get('captions');
if (utils.is.boolean(active)) {
this.captions.active = active;
} else {
this.captions.active = this.config.captions.active;
}
}
// Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || this.isHTML5 && !support.textTracks) {
// Clear menu and hide
@@ -3307,17 +3281,6 @@ var captions = {
utils.insertAfter(this.elements.captions, this.elements.wrapper);
}
// Set the class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Get tracks
var tracks = captions.getTracks.call(this);
// If no caption file exists, hide container for caption text
if (utils.is.empty(tracks)) {
return;
}
// Get browser info
var browser = utils.getBrowser();
@@ -3340,14 +3303,52 @@ var captions = {
});
}
// Set language
captions.setLanguage.call(this);
// Try to load the value from storage
var active = this.storage.get('captions');
// Enable UI
captions.show.call(this);
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
active = this.config.captions.active;
}
// Set available languages in list
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
// Set toggled state
this.toggleCaptions(active);
// Watch changes to textTracks and update captions menu
if (this.config.captions.update) {
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
}
// Update available languages in list next tick (the event must not be triggered before the listeners)
setTimeout(captions.update.bind(this), 0);
},
update: function update() {
// Update tracks
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
if (!this.language) {
var language = this.config.captions.language;
if (language === 'auto') {
var _split = (navigator.language || navigator.userLanguage).split('-');
var _split2 = slicedToArray(_split, 1);
language = _split2[0];
}
this.language = this.storage.get('language') || (language || '').toLowerCase();
}
// Toggle the class hooks
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Update available languages in list
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
@@ -3508,25 +3509,6 @@ var captions = {
} else {
this.debug.warn('No captions element to render to');
}
},
// Display captions container and button (for initialization)
show: function show() {
// Try to load the value from storage
var active = this.storage.get('captions');
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
active = this.config.captions.active;
} else {
this.captions.active = active;
}
if (active) {
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);
utils.toggleState(this.elements.buttons.captions, true);
}
}
};
@@ -3628,7 +3610,7 @@ var defaults$1 = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.3.9/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.3.11/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -3667,7 +3649,10 @@ var defaults$1 = {
// Captions settings
captions: {
active: false,
language: (navigator.language || navigator.userLanguage).split('-')[0]
language: 'auto',
// Listen to new tracks added after Plyr is initialized.
// This is needed for streaming captions, but may result in unselectable options
update: false
},
// Fullscreen settings
@@ -3724,7 +3709,15 @@ var defaults$1 = {
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad'
advertisement: 'Ad',
qualityBadge: {
2160: '4K',
1440: 'HD',
1080: 'HD',
720: 'HD',
576: 'SD',
480: 'SD'
}
},
// URLs
@@ -3812,9 +3805,8 @@ var defaults$1 = {
display: {
currentTime: '.plyr__time--current',
duration: '.plyr__time--duration',
buffer: '.plyr__progress--buffer',
played: '.plyr__progress--played',
loop: '.plyr__progress--loop',
buffer: '.plyr__progress__buffer',
loop: '.plyr__progress__loop', // Used later
volume: '.plyr__volume--display'
},
progress: '.plyr__progress',
@@ -4183,8 +4175,10 @@ var ui = {
// Remove native controls
ui.toggleNativeControls.call(this);
// Captions
captions.setup.call(this);
// Setup captions for HTML5
if (this.isHTML5) {
captions.setup.call(this);
}
// Reset volume
this.volume = null;
@@ -4237,6 +4231,12 @@ var ui = {
if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) {
ui.setPoster.call(this, this.poster);
}
// Manually set the duration if user has overridden it.
// The event listeners for it doesn't get called if preload is disabled (#701)
if (this.config.duration) {
controls.durationUpdate.call(this);
}
},
@@ -7294,24 +7294,19 @@ var Plyr = function () {
}
// If the method is called without parameter, toggle based on current value
var show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
return;
}
// Set global
this.captions.active = show;
var active = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Toggle state
utils.toggleState(this.elements.buttons.captions, this.captions.active);
utils.toggleState(this.elements.buttons.captions, active);
// Add class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, active);
// Trigger an event
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
// Update state and trigger event
if (active !== this.captions.active) {
this.captions.active = active;
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
}
}
/**
@@ -7843,7 +7838,7 @@ var Plyr = function () {
quality = Number(input);
}
if (!utils.is.number(quality) || quality === 0) {
if (!utils.is.number(quality)) {
quality = this.storage.get('quality');
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+127 -155
View File
@@ -20,7 +20,7 @@ if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef
});
var _core = createCommonjsModule(function (module) {
var core = module.exports = { version: '2.5.6' };
var core = module.exports = { version: '2.5.3' };
if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
});
var _core_1 = _core.version;
@@ -333,18 +333,11 @@ var _arrayIncludes = function (IS_INCLUDES) {
};
};
var _shared = createCommonjsModule(function (module) {
var SHARED = '__core-js_shared__';
var store = _global[SHARED] || (_global[SHARED] = {});
(module.exports = function (key, value) {
return store[key] || (store[key] = value !== undefined ? value : {});
})('versions', []).push({
version: _core.version,
mode: _library ? 'pure' : 'global',
copyright: '© 2018 Denis Pushkarev (zloirock.ru)'
});
});
var _shared = function (key) {
return store[key] || (store[key] = {});
};
var shared = _shared('keys');
@@ -998,7 +991,7 @@ var _iterDefine = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORC
var VALUES_BUG = false;
var proto = Base.prototype;
var $native = proto[ITERATOR$2] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
var $default = $native || getMethod(DEFAULT);
var $default = (!BUGGY && $native) || getMethod(DEFAULT);
var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
var methods, key, IteratorPrototype;
@@ -1009,7 +1002,7 @@ var _iterDefine = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORC
// Set @@toStringTag to native iterators
_setToStringTag(IteratorPrototype, TAG, true);
// fix for some old engines
if (!_library && typeof IteratorPrototype[ITERATOR$2] != 'function') _hide(IteratorPrototype, ITERATOR$2, returnThis);
if (!_library && !_has(IteratorPrototype, ITERATOR$2)) _hide(IteratorPrototype, ITERATOR$2, returnThis);
}
}
// fix Array#{values, @@iterator}.name in V8 / FF
@@ -2503,11 +2496,9 @@ function set(target, propertyKey, V /* , receiver */) {
}
if (_has(ownDesc, 'value')) {
if (ownDesc.writable === false || !_isObject(receiver)) return false;
if (existingDescriptor = _objectGopd.f(receiver, propertyKey)) {
if (existingDescriptor.get || existingDescriptor.set || existingDescriptor.writable === false) return false;
existingDescriptor.value = V;
_objectDp.f(receiver, propertyKey, existingDescriptor);
} else _objectDp.f(receiver, propertyKey, _propertyDesc(0, V));
existingDescriptor = _objectGopd.f(receiver, propertyKey) || _propertyDesc(0);
existingDescriptor.value = V;
_objectDp.f(receiver, propertyKey, existingDescriptor);
return true;
}
return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true);
@@ -2652,8 +2643,7 @@ var _microtask = function () {
};
// environments with maybe non-completely correct, but existent Promise
} else if (Promise$1 && Promise$1.resolve) {
// Promise.resolve without an argument throws an error in LG WebOS 2
var promise = Promise$1.resolve(undefined);
var promise = Promise$1.resolve();
notify = function () {
promise.then(flush);
};
@@ -2710,10 +2700,6 @@ var _perform = function (exec) {
}
};
var navigator$1 = _global.navigator;
var _userAgent = navigator$1 && navigator$1.userAgent || '';
var _promiseResolve = function (C, x) {
_anObject(C);
if (_isObject(x) && x.constructor === C) return x;
@@ -2728,12 +2714,9 @@ var microtask = _microtask();
var PROMISE = 'Promise';
var TypeError$1 = _global.TypeError;
var process$2 = _global.process;
var versions = process$2 && process$2.versions;
var v8 = versions && versions.v8 || '';
var $Promise = _global[PROMISE];
var isNode$1 = _classof(process$2) == 'process';
var empty = function () { /* empty */ };
@@ -2748,13 +2731,7 @@ var USE_NATIVE = !!function () {
exec(empty, empty);
};
// unhandled rejections tracking support, NodeJS Promise without it fails @@species test
return (isNode$1 || typeof PromiseRejectionEvent == 'function')
&& promise.then(empty) instanceof FakePromise
// v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
// https://bugs.chromium.org/p/chromium/issues/detail?id=830565
// we can't detect it synchronously, so just check versions
&& v8.indexOf('6.6') !== 0
&& _userAgent.indexOf('Chrome/66') === -1;
return (isNode$1 || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise;
} catch (e) { /* empty */ }
}();
@@ -2776,7 +2753,7 @@ var notify = function (promise, isReject) {
var resolve = reaction.resolve;
var reject = reaction.reject;
var domain = reaction.domain;
var result, then, exited;
var result, then;
try {
if (handler) {
if (!ok) {
@@ -2786,11 +2763,8 @@ var notify = function (promise, isReject) {
if (handler === true) result = value;
else {
if (domain) domain.enter();
result = handler(value); // may throw
if (domain) {
domain.exit();
exited = true;
}
result = handler(value);
if (domain) domain.exit();
}
if (result === reaction.promise) {
reject(TypeError$1('Promise-chain cycle'));
@@ -2799,7 +2773,6 @@ var notify = function (promise, isReject) {
} else resolve(result);
} else reject(value);
} catch (e) {
if (domain && !exited) domain.exit();
reject(e);
}
};
@@ -4182,6 +4155,10 @@ var _stringPad = function (that, maxLength, fillString, left) {
return left ? stringFiller + S : S + stringFiller;
};
var navigator$1 = _global.navigator;
var _userAgent = navigator$1 && navigator$1.userAgent || '';
// https://github.com/tc39/proposal-string-pad-start-end
@@ -6706,6 +6683,14 @@ var utils = {
},
// Get a nested value in an object
getDeep: function getDeep(object, path) {
return path.split('.').reduce(function (obj, key) {
return obj && obj[key];
}, object);
},
// Get the closest value in an array
closest: function closest(array, value) {
if (!utils.is.array(array) || !array.length) {
@@ -7125,6 +7110,13 @@ var html5 = {
player.media.src = supported[0].getAttribute('src');
// Restore time
var onLoadedMetaData = function onLoadedMetaData() {
player.currentTime = currentTime;
player.off('loadedmetadata', onLoadedMetaData);
};
player.on('loadedmetadata', onLoadedMetaData);
// Load new source
player.media.load();
@@ -7133,9 +7125,6 @@ var html5 = {
player.play();
}
// Restore time
player.currentTime = currentTime;
// Trigger change event
utils.dispatchEvent.call(player, player.media, 'qualitychange', false, {
quality: input
@@ -7177,11 +7166,15 @@ var i18n = {
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)) {
if (utils.is.empty(key) || utils.is.empty(config)) {
return '';
}
var string = config.i18n[key];
var string = utils.getDeep(config.i18n, key);
if (utils.is.empty(string)) {
return '';
}
var replace = {
'{seektime}': config.seekTime,
@@ -7855,27 +7848,7 @@ var controls = {
// Get the badge HTML for HD, 4K etc
var getBadge = function getBadge(quality) {
var label = '';
switch (quality) {
case 2160:
label = '4K';
break;
case 1440:
case 1080:
case 720:
label = 'HD';
break;
case 576:
case 480:
label = 'SD';
break;
default:
break;
}
var label = i18n.get('qualityBadge.' + quality, _this3.config);
if (!label.length) {
return null;
@@ -7898,7 +7871,6 @@ var controls = {
// Translate a value into a nice label
// TODO: Localisation
getLabel: function getLabel(setting, value) {
switch (setting) {
case 'speed':
@@ -7906,7 +7878,13 @@ var controls = {
case 'quality':
if (utils.is.number(value)) {
return value + 'p';
var label = i18n.get('qualityLabel.' + value, this.config);
if (!label.length) {
return value + 'p';
}
return label;
}
return utils.toTitleCase(value);
@@ -8066,12 +8044,7 @@ var controls = {
// Generate options
tracks.forEach(function (track) {
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.captions.language.toLowerCase());
});
// Store reference
this.options.captions = tracks.map(function (track) {
return track.language;
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);
@@ -8674,28 +8647,6 @@ var captions = {
return;
}
// Set default language if not set
var stored = this.storage.get('language');
if (!utils.is.empty(stored)) {
this.captions.language = stored;
}
if (utils.is.empty(this.captions.language)) {
this.captions.language = this.config.captions.language.toLowerCase();
}
// Set captions enabled state if not set
if (!utils.is.boolean(this.captions.active)) {
var active = this.storage.get('captions');
if (utils.is.boolean(active)) {
this.captions.active = active;
} else {
this.captions.active = this.config.captions.active;
}
}
// Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || this.isHTML5 && !support.textTracks) {
// Clear menu and hide
@@ -8713,17 +8664,6 @@ var captions = {
utils.insertAfter(this.elements.captions, this.elements.wrapper);
}
// Set the class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Get tracks
var tracks = captions.getTracks.call(this);
// If no caption file exists, hide container for caption text
if (utils.is.empty(tracks)) {
return;
}
// Get browser info
var browser = utils.getBrowser();
@@ -8746,14 +8686,52 @@ var captions = {
});
}
// Set language
captions.setLanguage.call(this);
// Try to load the value from storage
var active = this.storage.get('captions');
// Enable UI
captions.show.call(this);
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
active = this.config.captions.active;
}
// Set available languages in list
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
// Set toggled state
this.toggleCaptions(active);
// Watch changes to textTracks and update captions menu
if (this.config.captions.update) {
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
}
// Update available languages in list next tick (the event must not be triggered before the listeners)
setTimeout(captions.update.bind(this), 0);
},
update: function update() {
// Update tracks
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
if (!this.language) {
var language = this.config.captions.language;
if (language === 'auto') {
var _split = (navigator.language || navigator.userLanguage).split('-');
var _split2 = slicedToArray(_split, 1);
language = _split2[0];
}
this.language = this.storage.get('language') || (language || '').toLowerCase();
}
// Toggle the class hooks
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Update available languages in list
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
@@ -8914,25 +8892,6 @@ var captions = {
} else {
this.debug.warn('No captions element to render to');
}
},
// Display captions container and button (for initialization)
show: function show() {
// Try to load the value from storage
var active = this.storage.get('captions');
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
active = this.config.captions.active;
} else {
this.captions.active = active;
}
if (active) {
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);
utils.toggleState(this.elements.buttons.captions, true);
}
}
};
@@ -9034,7 +8993,7 @@ var defaults$1 = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.3.9/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.3.11/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -9073,7 +9032,10 @@ var defaults$1 = {
// Captions settings
captions: {
active: false,
language: (navigator.language || navigator.userLanguage).split('-')[0]
language: 'auto',
// Listen to new tracks added after Plyr is initialized.
// This is needed for streaming captions, but may result in unselectable options
update: false
},
// Fullscreen settings
@@ -9130,7 +9092,15 @@ var defaults$1 = {
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad'
advertisement: 'Ad',
qualityBadge: {
2160: '4K',
1440: 'HD',
1080: 'HD',
720: 'HD',
576: 'SD',
480: 'SD'
}
},
// URLs
@@ -9218,9 +9188,8 @@ var defaults$1 = {
display: {
currentTime: '.plyr__time--current',
duration: '.plyr__time--duration',
buffer: '.plyr__progress--buffer',
played: '.plyr__progress--played',
loop: '.plyr__progress--loop',
buffer: '.plyr__progress__buffer',
loop: '.plyr__progress__loop', // Used later
volume: '.plyr__volume--display'
},
progress: '.plyr__progress',
@@ -9589,8 +9558,10 @@ var ui = {
// Remove native controls
ui.toggleNativeControls.call(this);
// Captions
captions.setup.call(this);
// Setup captions for HTML5
if (this.isHTML5) {
captions.setup.call(this);
}
// Reset volume
this.volume = null;
@@ -9643,6 +9614,12 @@ var ui = {
if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) {
ui.setPoster.call(this, this.poster);
}
// Manually set the duration if user has overridden it.
// The event listeners for it doesn't get called if preload is disabled (#701)
if (this.config.duration) {
controls.durationUpdate.call(this);
}
},
@@ -12700,24 +12677,19 @@ var Plyr = function () {
}
// If the method is called without parameter, toggle based on current value
var show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
return;
}
// Set global
this.captions.active = show;
var active = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Toggle state
utils.toggleState(this.elements.buttons.captions, this.captions.active);
utils.toggleState(this.elements.buttons.captions, active);
// Add class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, active);
// Trigger an event
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
// Update state and trigger event
if (active !== this.captions.active) {
this.captions.active = active;
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
}
}
/**
@@ -13249,7 +13221,7 @@ var Plyr = function () {
quality = Number(input);
}
if (!utils.is.number(quality) || quality === 0) {
if (!utils.is.number(quality)) {
quality = this.storage.get('quality');
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+6 -1
View File
@@ -226,9 +226,14 @@ gulp.task('watch', () => {
gulp.watch(paths.demo.src.sass, tasks.sass);
});
// Build distribution
gulp.task('build', () => {
run(tasks.clean, tasks.js, tasks.sass, tasks.sprite);
});
// Default gulp task
gulp.task('default', () => {
run(tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'watch');
run('build', 'watch');
});
// Publish a version to CDN and demo
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "plyr",
"version": "3.3.9",
"version": "3.3.11",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"main": "./dist/plyr.js",
@@ -65,6 +65,8 @@
"doc": "readme.md"
},
"scripts": {
"build": "gulp build",
"lint": "eslint src/js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Sam Potts <sam@potts.es>",
@@ -72,7 +74,7 @@
"babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0",
"loadjs": "^3.5.4",
"raven-js": "^3.25.2",
"raven-js": "^3.26.1",
"url-polyfill": "^1.0.13"
}
}
+9 -16
View File
@@ -51,6 +51,10 @@ Some awesome folks have made plugins for CMSs and Components for JavaScript fram
Here's a quick run through on getting up and running. There's also a [demo on Codepen](http://codepen.io/sampotts/pen/jARJYp). You can grab all of the source with [NPM](https://www.npmjs.com/package/plyr) using `npm install plyr`.
### Try Plyr online
You can try Plyr in Codepen using our minimal templates: [HTML5 video](https://codepen.io/pen?template=bKeqpr), [HTML5 audio](https://codepen.io/pen?template=rKLywR), [YouTube](https://codepen.io/pen?template=GGqbbJ), [Vimeo](https://codepen.io/pen?template=bKeXNq). For Streaming we also have example integrations with: [Dash.js](https://codepen.io/pen?template=zaBgBy), [Hls.js](https://codepen.io/pen?template=oyLKQb) and [Shaka Player](https://codepen.io/pen?template=ZRpzZO)
### HTML
Plyr extends upon the standard [HTML5 media element](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) markup so that's all you need for those types.
@@ -128,13 +132,13 @@ See [initialising](#initialising) for more information on advanced setups.
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
```html
<script src="https://cdn.plyr.io/3.3.9/plyr.js"></script>
<script src="https://cdn.plyr.io/3.3.11/plyr.js"></script>
```
...or...
```html
<script src="https://cdn.plyr.io/3.3.9/plyr.polyfilled.js"></script>
<script src="https://cdn.plyr.io/3.3.11/plyr.polyfilled.js"></script>
```
### CSS
@@ -148,13 +152,13 @@ Include the `plyr.css` stylsheet into your `<head>`
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.3.9/plyr.css">
<link rel="stylesheet" href="https://cdn.plyr.io/3.3.11/plyr.css">
```
### SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.3.9/plyr.svg`.
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.3.11/plyr.svg`.
## Ads
@@ -303,7 +307,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
| `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. |
| `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. |
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
| `captions` | Object | `{ active: false, language: window.navigator.language.split('-')[0] }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). |
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. |
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
@@ -607,17 +611,6 @@ document then the shortcuts will work when any element has focus, apart from an
| `C` | Toggle captions |
| `L` | Toggle loop |
## Streaming
Because Plyr is an extension of the standard HTML5 video and audio elements, third party streaming plugins can be used with Plyr. Massive thanks to Matias
Russitto ([@russitto](https://github.com/russitto)) for working on this. Here's a few examples:
* Using [hls.js](https://github.com/dailymotion/hls.js) - [Demo](http://codepen.io/sampotts/pen/JKEMqB)
* Using [Shaka](https://github.com/google/shaka-player) - [Demo](http://codepen.io/sampotts/pen/zBNpVR)
* Using [dash.js](https://github.com/Dash-Industry-Forum/dash.js) - [Demo](http://codepen.io/sampotts/pen/BzpJXN)
_Note_: These need updating to use the new v3 syntax but would still work.
## Fullscreen
Fullscreen in Plyr is supported by all browsers that [currently support it](http://caniuse.com/#feat=fullscreen).
+37 -57
View File
@@ -16,28 +16,6 @@ const captions = {
return;
}
// Set default language if not set
const stored = this.storage.get('language');
if (!utils.is.empty(stored)) {
this.captions.language = stored;
}
if (utils.is.empty(this.captions.language)) {
this.captions.language = this.config.captions.language.toLowerCase();
}
// Set captions enabled state if not set
if (!utils.is.boolean(this.captions.active)) {
const active = this.storage.get('captions');
if (utils.is.boolean(active)) {
this.captions.active = active;
} else {
this.captions.active = this.config.captions.active;
}
}
// Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
// Clear menu and hide
@@ -55,17 +33,6 @@ const captions = {
utils.insertAfter(this.elements.captions, this.elements.wrapper);
}
// Set the class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Get tracks
const tracks = captions.getTracks.call(this);
// If no caption file exists, hide container for caption text
if (utils.is.empty(tracks)) {
return;
}
// Get browser info
const browser = utils.getBrowser();
@@ -94,14 +61,45 @@ const captions = {
});
}
// Set language
captions.setLanguage.call(this);
// Try to load the value from storage
let active = this.storage.get('captions');
// Enable UI
captions.show.call(this);
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
({ active } = this.config.captions);
}
// Set available languages in list
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
// Set toggled state
this.toggleCaptions(active);
// Watch changes to textTracks and update captions menu
if (this.config.captions.update) {
utils.on(this.media.textTracks, 'addtrack removetrack', captions.update.bind(this));
}
// Update available languages in list next tick (the event must not be triggered before the listeners)
setTimeout(captions.update.bind(this), 0);
},
update() {
// Update tracks
const tracks = captions.getTracks.call(this);
this.options.captions = tracks.map(({language}) => language);
// Set language if it hasn't been set already
if (!this.language) {
let { language } = this.config.captions;
if (language === 'auto') {
[ language ] = (navigator.language || navigator.userLanguage).split('-');
}
this.language = this.storage.get('language') || (language || '').toLowerCase();
}
// Toggle the class hooks
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Update available languages in list
if ((this.config.controls || []).includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
@@ -247,24 +245,6 @@ const captions = {
this.debug.warn('No captions element to render to');
}
},
// Display captions container and button (for initialization)
show() {
// Try to load the value from storage
let active = this.storage.get('captions');
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
({ active } = this.config.captions);
} else {
this.captions.active = active;
}
if (active) {
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);
utils.toggleState(this.elements.buttons.captions, true);
}
},
};
export default captions;
+9 -27
View File
@@ -664,27 +664,7 @@ const controls = {
// Get the badge HTML for HD, 4K etc
const getBadge = quality => {
let label = '';
switch (quality) {
case 2160:
label = '4K';
break;
case 1440:
case 1080:
case 720:
label = 'HD';
break;
case 576:
case 480:
label = 'SD';
break;
default:
break;
}
const label = i18n.get(`qualityBadge.${quality}`, this.config);
if (!label.length) {
return null;
@@ -708,7 +688,6 @@ const controls = {
},
// Translate a value into a nice label
// TODO: Localisation
getLabel(setting, value) {
switch (setting) {
case 'speed':
@@ -716,7 +695,13 @@ const controls = {
case 'quality':
if (utils.is.number(value)) {
return `${value}p`;
const label = i18n.get(`qualityLabel.${value}`, this.config);
if (!label.length) {
return `${value}p`;
}
return label;
}
return utils.toTitleCase(value);
@@ -883,13 +868,10 @@ const controls = {
'language',
track.label,
track.language !== 'enabled' ? controls.createBadge.call(this, track.language.toUpperCase()) : null,
track.language.toLowerCase() === this.captions.language.toLowerCase(),
track.language.toLowerCase() === this.language,
);
});
// Store reference
this.options.captions = tracks.map(track => track.language);
controls.updateSetting.call(this, type, list);
},
+15 -5
View File
@@ -56,7 +56,7 @@ const defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.3.9/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.3.11/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -115,7 +115,10 @@ const defaults = {
// Captions settings
captions: {
active: false,
language: (navigator.language || navigator.userLanguage).split('-')[0],
language: 'auto',
// Listen to new tracks added after Plyr is initialized.
// This is needed for streaming captions, but may result in unselectable options
update: false,
},
// Fullscreen settings
@@ -187,6 +190,14 @@ const defaults = {
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad',
qualityBadge: {
2160: '4K',
1440: 'HD',
1080: 'HD',
720: 'HD',
576: 'SD',
480: 'SD',
},
},
// URLs
@@ -311,9 +322,8 @@ const defaults = {
display: {
currentTime: '.plyr__time--current',
duration: '.plyr__time--duration',
buffer: '.plyr__progress--buffer',
played: '.plyr__progress--played',
loop: '.plyr__progress--loop',
buffer: '.plyr__progress__buffer',
loop: '.plyr__progress__loop', // Used later
volume: '.plyr__volume--display',
},
progress: '.plyr__progress',
+7 -3
View File
@@ -99,6 +99,13 @@ const html5 = {
// Set new source
player.media.src = supported[0].getAttribute('src');
// Restore time
const onLoadedMetaData = () => {
player.currentTime = currentTime;
player.off('loadedmetadata', onLoadedMetaData);
};
player.on('loadedmetadata', onLoadedMetaData);
// Load new source
player.media.load();
@@ -107,9 +114,6 @@ const html5 = {
player.play();
}
// Restore time
player.currentTime = currentTime;
// Trigger change event
utils.dispatchEvent.call(player, player.media, 'qualitychange', false, {
quality: input,
+6 -2
View File
@@ -6,11 +6,15 @@ import utils from './utils';
const i18n = {
get(key = '', config = {}) {
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
if (utils.is.empty(key) || utils.is.empty(config)) {
return '';
}
let string = config.i18n[key];
let string = utils.getDeep(config.i18n, key);
if (utils.is.empty(string)) {
return '';
}
const replace = {
'{seektime}': config.seekTime,
+10 -15
View File
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
// plyr.js v3.3.9
// plyr.js v3.3.11
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
@@ -675,7 +675,7 @@ class Plyr {
quality = Number(input);
}
if (!utils.is.number(quality) || quality === 0) {
if (!utils.is.number(quality)) {
quality = this.storage.get('quality');
}
@@ -838,24 +838,19 @@ class Plyr {
}
// If the method is called without parameter, toggle based on current value
const show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
return;
}
// Set global
this.captions.active = show;
const active = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Toggle state
utils.toggleState(this.elements.buttons.captions, this.captions.active);
utils.toggleState(this.elements.buttons.captions, active);
// Add class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, active);
// Trigger an event
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
// Update state and trigger event
if (active !== this.captions.active) {
this.captions.active = active;
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
}
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr Polyfilled Build
// plyr.js v3.3.9
// plyr.js v3.3.11
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
+10 -2
View File
@@ -55,8 +55,10 @@ const ui = {
// Remove native controls
ui.toggleNativeControls.call(this);
// Captions
captions.setup.call(this);
// Setup captions for HTML5
if (this.isHTML5) {
captions.setup.call(this);
}
// Reset volume
this.volume = null;
@@ -109,6 +111,12 @@ const ui = {
if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) {
ui.setPoster.call(this, this.poster);
}
// Manually set the duration if user has overridden it.
// The event listeners for it doesn't get called if preload is disabled (#701)
if (this.config.duration) {
controls.durationUpdate.call(this);
}
},
// Setup aria attribute for play and iframe title
+5
View File
@@ -728,6 +728,11 @@ const utils = {
return JSON.parse(JSON.stringify(object));
},
// Get a nested value in an object
getDeep(object, path) {
return path.split('.').reduce((obj, key) => obj && obj[key], object);
},
// Get the closest value in an array
closest(array, value) {
if (!utils.is.array(array) || !array.length) {
+17 -13
View File
@@ -5,16 +5,21 @@
.plyr__progress {
display: flex;
flex: 1;
position: relative;
margin-right: $plyr-range-thumb-height;
left: $plyr-range-thumb-height / 2;
margin-right: $plyr-range-thumb-height;
position: relative;
input[type='range'],
&__buffer {
margin-left: -($plyr-range-thumb-height / 2);
margin-right: -($plyr-range-thumb-height / 2);
// Offset the range thumb in order to be able to calculate the relative progress (#954)
width: calc(100% + #{$plyr-range-thumb-height});
}
input[type='range'] {
position: relative;
z-index: 2;
// Offset the range thumb in order to be able to calculate the relative progress (#954)
width: calc(100% + #{$plyr-range-thumb-height}) !important;
margin: 0 -#{$plyr-range-thumb-height / 2} !important;
}
// Seek tooltip to show time
@@ -24,18 +29,17 @@
}
}
.plyr__progress--buffer {
.plyr__progress__buffer {
-webkit-appearance: none; /* stylelint-disable-line */
background: transparent;
border: 0;
border-radius: 100px;
height: $plyr-range-track-height;
left: 0;
margin: -($plyr-range-track-height / 2) 0 0;
margin-top: -($plyr-range-track-height / 2);
padding: 0;
position: absolute;
top: 50%;
width: 100%;
&::-webkit-progress-bar {
background: transparent;
@@ -63,17 +67,17 @@
}
}
.plyr--video .plyr__progress--buffer {
.plyr--video .plyr__progress__buffer {
box-shadow: 0 1px 1px rgba(#000, 0.15);
color: $plyr-video-progress-buffered-bg;
}
.plyr--audio .plyr__progress--buffer {
.plyr--audio .plyr__progress__buffer {
color: $plyr-audio-progress-buffered-bg;
}
// Loading state
.plyr--loading .plyr__progress--buffer {
.plyr--loading .plyr__progress__buffer {
animation: plyr-progress 1s linear infinite;
background-image: linear-gradient(
-45deg,
@@ -90,10 +94,10 @@
color: transparent;
}
.plyr--video.plyr--loading .plyr__progress--buffer {
.plyr--video.plyr--loading .plyr__progress__buffer {
background-color: $plyr-video-progress-buffered-bg;
}
.plyr--audio.plyr--loading .plyr__progress--buffer {
.plyr--audio.plyr--loading .plyr__progress__buffer {
background-color: $plyr-audio-progress-buffered-bg;
}
+2 -2
View File
@@ -31,12 +31,12 @@
@import 'components/controls';
@import 'components/embed';
@import 'components/menus';
@import 'components/progress';
@import 'components/poster';
@import 'components/sliders';
@import 'components/poster';
@import 'components/times';
@import 'components/tooltips';
@import 'components/video';
@import 'components/progress';
@import 'components/volume';
@import 'states/fullscreen';
+238 -1524
View File
File diff suppressed because it is too large Load Diff