commit
58f5380694
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@ -2,11 +2,11 @@
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
"dbaeumer.vscode-eslint",
|
||||
"wix.vscode-import-cost",
|
||||
"esbenp.prettier-vscode",
|
||||
"shinnn.stylelint",
|
||||
"wayou.vscode-todo-highlight"
|
||||
"wayou.vscode-todo-highlight",
|
||||
"wix.vscode-import-cost",
|
||||
"stylelint.vscode-stylelint",
|
||||
"pflannery.vscode-versionlens"
|
||||
]
|
||||
}
|
||||
|
32
changelog.md
32
changelog.md
@ -1,15 +1,35 @@
|
||||
## v3.5.6
|
||||
### v3.5.7
|
||||
|
||||
- Typescript typings (thanks @ondratra)
|
||||
- `togglePlay` now also returns a `Promise` (thanks @azizhk)
|
||||
- Documentation improvements (thanks @MaxGiting, @0xflotus and @thatrobotdev)
|
||||
- Accessibility tweak for the play button (thanks @lunika)
|
||||
- Fix for ads configuration (thanks @SoftCreatR)
|
||||
- Added localisation key for PIP (picture-in-picture) (thanks @lmislm)
|
||||
- Preserve viewBox attribute in SVG sprite symbols (thanks @bseib)
|
||||
- Fix being unable to unmute autoplayed video on iOS (thanks @sumanbh)
|
||||
- Fixed Plyr container not resizing responsively (thanks @shravan2x)
|
||||
- Change vimeo demo video (thanks @thatrobotdev)
|
||||
- Fix for `Uncaught RangeError: Maximum call stack size exceeded` (thanks @laukstein)
|
||||
- Improve fullscreen experience on some devices (thanks @savroff)
|
||||
- Improvements to buffering state for embedded players (thanks @doostinharrell)
|
||||
- Prevents IE11 with resetOnEnd option set to true to play video again (thanks @Felipe K. De Boni)
|
||||
- Fix for multiple poster image downloads (use the native poster only for HTML5 videos)
|
||||
- Various presentational fixes
|
||||
- Removed logic to hide/show volume controls based on audio track detection due to it's problematic nature. If you want to hide volume control, use the `controls` option to do so.
|
||||
|
||||
### v3.5.6
|
||||
|
||||
- Another Edge fix (thanks Nick Hawk via Slack)
|
||||
|
||||
## v3.5.5
|
||||
### v3.5.5
|
||||
|
||||
- YouTube fix for when there are other embeds on the page (thanks @aFarkas)
|
||||
- Separated demo dependencies into their own package.json
|
||||
- Fix for Edge controls flexbox issue when resizing the player (thanks Nick Hawk via Slack)
|
||||
- More aspect ratio fixes
|
||||
|
||||
## v3.5.4
|
||||
### v3.5.4
|
||||
|
||||
- Added: Set download URL via new setter
|
||||
- Improvement: The order of the `controls` option now effects the order in the DOM - i.e. you can re-order the controls - Note: this may break any custom CSS you have setup. Please see the changes in the PR to the default SASS
|
||||
@ -22,7 +42,7 @@
|
||||
- Improvement: Automatic aspect ratio for YouTube is now supported, meaning all aspect ratios are set based on media content - Note: we're now using a different API to get YouTube video metadata so you may need to adjust any CSPs you have setup
|
||||
- Fix for menu in the Shadow DOM (thanks @emielbeinema)
|
||||
|
||||
## v3.5.3
|
||||
### v3.5.3
|
||||
|
||||
- Improved the usage of the `ratio` config option; it now works as expected and for all video types. The default has not changed, it is to dynamically, where possible (except YouTube where 16:9 is used) determine the ratio from the media source so this is not a breaking change.
|
||||
- Added new `ratio` getter and setter
|
||||
@ -30,11 +50,11 @@
|
||||
- Fix: Allow absolute paths in preview thumbnails
|
||||
- Improvement: Allow optional hours and ms in VTT parser in preview thumbnails
|
||||
|
||||
## v3.5.2
|
||||
### v3.5.2
|
||||
|
||||
- Fixed issue where the preview thumbnail was present while scrubbing
|
||||
|
||||
## v3.5.1
|
||||
### v3.5.1
|
||||
|
||||
- Fixed build issues with babel and browserslist
|
||||
|
||||
|
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
2956
demo/dist/demo.js
vendored
2956
demo/dist/demo.js
vendored
File diff suppressed because it is too large
Load Diff
4
demo/dist/demo.min.js
vendored
4
demo/dist/demo.min.js
vendored
File diff suppressed because one or more lines are too long
2
demo/dist/demo.min.js.map
vendored
2
demo/dist/demo.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
demo/dist/demo.svg
vendored
2
demo/dist/demo.svg
vendored
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.6 KiB |
2
demo/dist/error.css
vendored
2
demo/dist/error.css
vendored
File diff suppressed because one or more lines are too long
@ -68,7 +68,7 @@ const sources = {
|
||||
type: 'video',
|
||||
sources: [
|
||||
{
|
||||
src: 'https://vimeo.com/76979871',
|
||||
src: 'https://vimeo.com/383514704',
|
||||
provider: 'vimeo',
|
||||
},
|
||||
],
|
||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
204
dist/plyr.js
vendored
204
dist/plyr.js
vendored
@ -2,7 +2,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define('Plyr', factory) :
|
||||
(global = global || self, global.Plyr = factory());
|
||||
}(this, function () { 'use strict';
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
@ -41,6 +41,40 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function ownKeys(object, enumerableOnly) {
|
||||
var keys = Object.keys(object);
|
||||
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var symbols = Object.getOwnPropertySymbols(object);
|
||||
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
||||
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
||||
});
|
||||
keys.push.apply(keys, symbols);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
function _objectSpread2(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i] != null ? arguments[i] : {};
|
||||
|
||||
if (i % 2) {
|
||||
ownKeys(Object(source), true).forEach(function (key) {
|
||||
_defineProperty(target, key, source[key]);
|
||||
});
|
||||
} else if (Object.getOwnPropertyDescriptors) {
|
||||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
||||
} else {
|
||||
ownKeys(Object(source)).forEach(function (key) {
|
||||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function _slicedToArray(arr, i) {
|
||||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
|
||||
}
|
||||
@ -66,6 +100,10 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}
|
||||
|
||||
function _iterableToArrayLimit(arr, i) {
|
||||
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _arr = [];
|
||||
var _n = true;
|
||||
var _d = false;
|
||||
@ -583,7 +621,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform)
|
||||
};
|
||||
|
||||
// ==========================================================================
|
||||
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
|
||||
// https://www.youtube.com/watch?v=NPM6172J22g
|
||||
|
||||
@ -700,7 +737,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles,
|
||||
detail: Object.assign({}, detail, {
|
||||
detail: _objectSpread2({}, detail, {
|
||||
plyr: this
|
||||
})
|
||||
}); // Dispatch the event
|
||||
@ -929,9 +966,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
// Attribute selector
|
||||
attributes[key] = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return extend(existing, attributes);
|
||||
@ -1202,6 +1236,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var wrapper = this.elements.wrapper;
|
||||
var ratio = getAspectRatio.call(this, input);
|
||||
|
||||
var _ref = is$1.array(ratio) ? ratio : [0, 0],
|
||||
@ -1210,14 +1245,14 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
h = _ref2[1];
|
||||
|
||||
var padding = 100 / w * h;
|
||||
this.elements.wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||
wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||
|
||||
if (this.isVimeo && this.supported.ui) {
|
||||
var height = 240;
|
||||
var offset = (height - padding) / (height / 50);
|
||||
this.media.style.transform = "translateY(-".concat(offset, "%)");
|
||||
} else if (this.isHTML5) {
|
||||
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -1249,7 +1284,12 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
},
|
||||
// Get quality levels
|
||||
getQualityOptions: function getQualityOptions() {
|
||||
// Get sizes from <source> elements
|
||||
// Whether we're forcing all options (e.g. for streaming)
|
||||
if (this.config.quality.forced) {
|
||||
return this.config.quality.options;
|
||||
} // Get sizes from <source> elements
|
||||
|
||||
|
||||
return html5.getSources.call(this).map(function (source) {
|
||||
return Number(source.getAttribute('size'));
|
||||
}).filter(Boolean);
|
||||
@ -1277,6 +1317,10 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
return source && Number(source.getAttribute('size'));
|
||||
},
|
||||
set: function set(input) {
|
||||
// If we're using an an external handler...
|
||||
if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) {
|
||||
player.config.quality.onChange(input);
|
||||
} else {
|
||||
// Get sources
|
||||
var sources = html5.getSources.call(player); // Get first match for requested size
|
||||
|
||||
@ -1300,6 +1344,10 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
if (preload !== 'none' || readyState) {
|
||||
// Restore time
|
||||
player.once('loadedmetadata', function () {
|
||||
if (player.currentTime === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.currentTime = currentTime; // Resume playing
|
||||
|
||||
if (!paused) {
|
||||
@ -1308,6 +1356,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}); // Load new source
|
||||
|
||||
player.media.load();
|
||||
}
|
||||
} // Trigger change event
|
||||
|
||||
|
||||
@ -1678,7 +1727,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
// Bail if the value isn't a number
|
||||
if (!is$1.number(time)) {
|
||||
return formatTime(null, displayHours, inverted);
|
||||
return formatTime(undefined, displayHours, inverted);
|
||||
} // Format time component to add leading zero
|
||||
|
||||
|
||||
@ -1787,9 +1836,11 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
createLabel: function createLabel(key) {
|
||||
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
var text = i18n.get(key, this.config);
|
||||
var attributes = Object.assign({}, attr, {
|
||||
|
||||
var attributes = _objectSpread2({}, attr, {
|
||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
||||
});
|
||||
|
||||
return createElement('span', attributes, text);
|
||||
},
|
||||
// Create a badge
|
||||
@ -1999,7 +2050,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
var _this2 = this;
|
||||
|
||||
// Navigate through menus via arrow keys and space
|
||||
on(menuItem, 'keydown keyup', function (event) {
|
||||
on.call(this, menuItem, 'keydown keyup', function (event) {
|
||||
// We only care about space and ⬆️ ⬇️️ ➡️
|
||||
if (![32, 38, 39, 40].includes(event.which)) {
|
||||
return;
|
||||
@ -2041,7 +2092,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}, false); // Enter will fire a `click` event but we still need to manage focus
|
||||
// So we bind to keyup which fires after and set focus here
|
||||
|
||||
on(menuItem, 'keyup', function (event) {
|
||||
on.call(this, menuItem, 'keyup', function (event) {
|
||||
if (event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
@ -2118,9 +2169,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
case 'speed':
|
||||
_this3.speed = parseFloat(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event));
|
||||
@ -2229,9 +2277,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
case 'progress':
|
||||
setProgress(this.elements.display.buffer, this.buffered * 100);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2885,9 +2930,11 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
if (control === 'mute') {
|
||||
volume.appendChild(createButton.call(_this10, 'mute'));
|
||||
} // Volume range control
|
||||
// Ignored on iOS as it's handled globally
|
||||
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
|
||||
|
||||
|
||||
if (control === 'volume') {
|
||||
if (control === 'volume' && !browser.isIos) {
|
||||
// Set the attributes
|
||||
var attributes = {
|
||||
max: 1,
|
||||
@ -2946,7 +2993,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click
|
||||
|
||||
on(menuItem, 'click', function () {
|
||||
on.call(_this10, menuItem, 'click', function () {
|
||||
showMenuPanel.call(_this10, type, false);
|
||||
});
|
||||
var flex = createElement('span', null, i18n.get(type, _this10.config));
|
||||
@ -2977,7 +3024,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
class: _this10.config.classNames.hidden
|
||||
}, i18n.get('menuBack', _this10.config))); // Go back via keyboard
|
||||
|
||||
on(pane, 'keydown', function (event) {
|
||||
on.call(_this10, pane, 'keydown', function (event) {
|
||||
// We only care about <-
|
||||
if (event.which !== 37) {
|
||||
return;
|
||||
@ -2990,7 +3037,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
showMenuPanel.call(_this10, 'home', true);
|
||||
}, false); // Go back via button click
|
||||
|
||||
on(backButton, 'click', function () {
|
||||
on.call(_this10, backButton, 'click', function () {
|
||||
showMenuPanel.call(_this10, 'home', false);
|
||||
}); // Add to pane
|
||||
|
||||
@ -3652,7 +3699,9 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
// Quality default
|
||||
quality: {
|
||||
default: 576,
|
||||
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240]
|
||||
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
|
||||
forced: false,
|
||||
onChange: null
|
||||
},
|
||||
// Set loops
|
||||
loop: {
|
||||
@ -3729,6 +3778,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
frameTitle: 'Player for {title}',
|
||||
captions: 'Captions',
|
||||
settings: 'Settings',
|
||||
pip: 'PIP',
|
||||
menuBack: 'Go back to previous menu',
|
||||
speed: 'Speed',
|
||||
normal: 'Normal',
|
||||
@ -4168,7 +4218,9 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
} else if (!Fullscreen.native || this.forceFallback) {
|
||||
toggleFallback.call(this, true);
|
||||
} else if (!this.prefix) {
|
||||
this.target.requestFullscreen();
|
||||
this.target.requestFullscreen({
|
||||
navigationUI: "hide"
|
||||
});
|
||||
} else if (!is$1.empty(this.prefix)) {
|
||||
this.target["".concat(this.prefix, "Request").concat(this.property)]();
|
||||
}
|
||||
@ -4475,6 +4527,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
Object.assign(target, {
|
||||
pressed: _this3.playing
|
||||
});
|
||||
target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config));
|
||||
}); // Only update controls on non timeupdate events
|
||||
|
||||
if (is$1.event(event) && event.type === 'timeupdate') {
|
||||
@ -4655,19 +4708,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
// L key
|
||||
player.loop = !player.loop;
|
||||
break;
|
||||
|
||||
/* case 73:
|
||||
this.setLoop('start');
|
||||
break;
|
||||
case 76:
|
||||
this.setLoop();
|
||||
break;
|
||||
case 79:
|
||||
this.setLoop('end');
|
||||
break; */
|
||||
|
||||
default:
|
||||
break;
|
||||
} // Escape is handle natively when in full screen
|
||||
// So we only need to worry about non native
|
||||
|
||||
@ -4884,19 +4924,15 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
|
||||
return controls.durationUpdate.call(player, event);
|
||||
}); // Check for audio tracks on load
|
||||
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
|
||||
|
||||
on.call(player, player.media, 'canplay loadeddata', function () {
|
||||
toggleHidden(elements.volume, !player.hasAudio);
|
||||
toggleHidden(elements.buttons.mute, !player.hasAudio);
|
||||
}); // Handle the media finishing
|
||||
|
||||
on.call(player, player.media, 'ended', function () {
|
||||
// Show poster on end
|
||||
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
|
||||
// Restart
|
||||
player.restart();
|
||||
player.restart(); // Call pause otherwise IE11 will start playing the video again
|
||||
|
||||
player.pause();
|
||||
}
|
||||
}); // Check for buffer progress
|
||||
|
||||
@ -5009,7 +5045,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
} // Only call default handler if not prevented in custom handler
|
||||
|
||||
|
||||
if (returned && is$1.function(defaultHandler)) {
|
||||
if (returned !== false && is$1.function(defaultHandler)) {
|
||||
defaultHandler.call(player, event);
|
||||
}
|
||||
} // Trigger custom and default handlers
|
||||
@ -5397,12 +5433,13 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
async = args.async,
|
||||
maxTries = (args.numRetries || 0) + 1,
|
||||
beforeCallbackFn = args.before || devnull,
|
||||
pathname = path.replace(/[\?|#].*$/, ''),
|
||||
pathStripped = path.replace(/^(css|img)!/, ''),
|
||||
isLegacyIECss,
|
||||
e;
|
||||
numTries = numTries || 0;
|
||||
|
||||
if (/(^css!|\.css$)/.test(path)) {
|
||||
if (/(^css!|\.css$)/.test(pathname)) {
|
||||
// css
|
||||
e = doc.createElement('link');
|
||||
e.rel = 'stylesheet';
|
||||
@ -5415,7 +5452,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
e.rel = 'preload';
|
||||
e.as = 'style';
|
||||
}
|
||||
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) {
|
||||
} else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) {
|
||||
// image
|
||||
e = doc.createElement('img');
|
||||
e.src = pathStripped;
|
||||
@ -5888,6 +5925,12 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
frame.setAttribute('tabindex', -1);
|
||||
}
|
||||
});
|
||||
player.embed.on('bufferstart', function () {
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
});
|
||||
player.embed.on('bufferend', function () {
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
});
|
||||
player.embed.on('play', function () {
|
||||
assurePlaybackState.call(player, true);
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
@ -6321,7 +6364,9 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
assurePlaybackState$1.call(player, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 3:
|
||||
// Trigger waiting event to add loading classes to container as the video buffers.
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
break;
|
||||
}
|
||||
|
||||
@ -6363,11 +6408,13 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
wrap(this.media, this.elements.wrapper); // Faux poster container
|
||||
|
||||
if (this.isEmbed) {
|
||||
this.elements.poster = createElement('div', {
|
||||
class: this.config.classNames.poster
|
||||
});
|
||||
this.elements.wrapper.appendChild(this.elements.poster);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isHTML5) {
|
||||
html5.extend.call(this);
|
||||
@ -6736,9 +6783,6 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -7010,7 +7054,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
cb: Date.now(),
|
||||
AV_WIDTH: 640,
|
||||
AV_HEIGHT: 480,
|
||||
AV_CDIM2: this.publisherId
|
||||
AV_CDIM2: config.publisherId
|
||||
};
|
||||
var base = 'https://go.aniview.com/api/adserver6/vast/';
|
||||
return "".concat(base, "?").concat(buildUrlParams(params));
|
||||
@ -7075,6 +7119,21 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
*/
|
||||
|
||||
|
||||
var fitRatio = function fitRatio(ratio, outer) {
|
||||
var targetRatio = outer.width / outer.height;
|
||||
var result = {};
|
||||
|
||||
if (ratio > targetRatio) {
|
||||
result.width = outer.width;
|
||||
result.height = 1 / ratio * outer.width;
|
||||
} else {
|
||||
result.height = outer.height;
|
||||
result.width = ratio * outer.height;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var PreviewThumbnails =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
@ -7619,9 +7678,15 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}, {
|
||||
key: "setScrubbingContainerSize",
|
||||
value: function setScrubbingContainerSize() {
|
||||
this.elements.scrubbing.container.style.width = "".concat(this.player.media.clientWidth, "px"); // Can't use media.clientHeight - html5 video goes big and does black bars above and below
|
||||
var _fitRatio = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight
|
||||
}),
|
||||
width = _fitRatio.width,
|
||||
height = _fitRatio.height;
|
||||
|
||||
this.elements.scrubbing.container.style.height = "".concat(this.player.media.clientWidth / this.thumbAspectRatio, "px");
|
||||
this.elements.scrubbing.container.style.width = "".concat(width, "px");
|
||||
this.elements.scrubbing.container.style.height = "".concat(height, "px");
|
||||
} // Sprites need to be offset to the correct location
|
||||
|
||||
}, {
|
||||
@ -7634,9 +7699,9 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px"); // eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.height = "".concat(previewImage.naturalHeight * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px"); // eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.width = "".concat(previewImage.naturalWidth * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
@ -7674,8 +7739,13 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
key: "thumbContainerHeight",
|
||||
get: function get() {
|
||||
if (this.mouseDown) {
|
||||
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio);
|
||||
var _fitRatio2 = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight
|
||||
}),
|
||||
height = _fitRatio2.height;
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
|
||||
@ -8159,10 +8229,10 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
key: "pause",
|
||||
value: function pause() {
|
||||
if (!this.playing || !is$1.function(this.media.pause)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.media.pause();
|
||||
return this.media.pause();
|
||||
}
|
||||
/**
|
||||
* Get playing state
|
||||
@ -8180,10 +8250,10 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
var toggle = is$1.boolean(input) ? input : !this.playing;
|
||||
|
||||
if (toggle) {
|
||||
this.play();
|
||||
} else {
|
||||
this.pause();
|
||||
return this.play();
|
||||
}
|
||||
|
||||
return this.pause();
|
||||
}
|
||||
/**
|
||||
* Stop playback
|
||||
@ -8216,7 +8286,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}, {
|
||||
key: "rewind",
|
||||
value: function rewind(seekTime) {
|
||||
this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime -= is$1.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
/**
|
||||
* Fast forward
|
||||
@ -8226,7 +8296,7 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
}, {
|
||||
key: "forward",
|
||||
value: function forward(seekTime) {
|
||||
this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime += is$1.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
/**
|
||||
* Seek to a time
|
||||
@ -9128,4 +9198,4 @@ typeof navigator === "object" && (function (global, factory) {
|
||||
|
||||
return Plyr;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
4
dist/plyr.min.js
vendored
4
dist/plyr.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.min.js.map
vendored
2
dist/plyr.min.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/plyr.min.mjs
vendored
4
dist/plyr.min.mjs
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.min.mjs.map
vendored
2
dist/plyr.min.mjs.map
vendored
File diff suppressed because one or more lines are too long
200
dist/plyr.mjs
vendored
200
dist/plyr.mjs
vendored
@ -35,6 +35,40 @@ function _defineProperty(obj, key, value) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function ownKeys(object, enumerableOnly) {
|
||||
var keys = Object.keys(object);
|
||||
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var symbols = Object.getOwnPropertySymbols(object);
|
||||
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
||||
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
||||
});
|
||||
keys.push.apply(keys, symbols);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
function _objectSpread2(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i] != null ? arguments[i] : {};
|
||||
|
||||
if (i % 2) {
|
||||
ownKeys(Object(source), true).forEach(function (key) {
|
||||
_defineProperty(target, key, source[key]);
|
||||
});
|
||||
} else if (Object.getOwnPropertyDescriptors) {
|
||||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
||||
} else {
|
||||
ownKeys(Object(source)).forEach(function (key) {
|
||||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function _slicedToArray(arr, i) {
|
||||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
|
||||
}
|
||||
@ -60,6 +94,10 @@ function _iterableToArray(iter) {
|
||||
}
|
||||
|
||||
function _iterableToArrayLimit(arr, i) {
|
||||
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _arr = [];
|
||||
var _n = true;
|
||||
var _d = false;
|
||||
@ -577,7 +615,6 @@ var browser = {
|
||||
isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform)
|
||||
};
|
||||
|
||||
// ==========================================================================
|
||||
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
|
||||
// https://www.youtube.com/watch?v=NPM6172J22g
|
||||
|
||||
@ -694,7 +731,7 @@ function triggerEvent(element) {
|
||||
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles,
|
||||
detail: Object.assign({}, detail, {
|
||||
detail: _objectSpread2({}, detail, {
|
||||
plyr: this
|
||||
})
|
||||
}); // Dispatch the event
|
||||
@ -923,9 +960,6 @@ function getAttributesFromSelector(sel, existingAttributes) {
|
||||
// Attribute selector
|
||||
attributes[key] = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return extend(existing, attributes);
|
||||
@ -1196,6 +1230,7 @@ function setAspectRatio(input) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var wrapper = this.elements.wrapper;
|
||||
var ratio = getAspectRatio.call(this, input);
|
||||
|
||||
var _ref = is$1.array(ratio) ? ratio : [0, 0],
|
||||
@ -1204,14 +1239,14 @@ function setAspectRatio(input) {
|
||||
h = _ref2[1];
|
||||
|
||||
var padding = 100 / w * h;
|
||||
this.elements.wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||
wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||
|
||||
if (this.isVimeo && this.supported.ui) {
|
||||
var height = 240;
|
||||
var offset = (height - padding) / (height / 50);
|
||||
this.media.style.transform = "translateY(-".concat(offset, "%)");
|
||||
} else if (this.isHTML5) {
|
||||
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -1243,7 +1278,12 @@ var html5 = {
|
||||
},
|
||||
// Get quality levels
|
||||
getQualityOptions: function getQualityOptions() {
|
||||
// Get sizes from <source> elements
|
||||
// Whether we're forcing all options (e.g. for streaming)
|
||||
if (this.config.quality.forced) {
|
||||
return this.config.quality.options;
|
||||
} // Get sizes from <source> elements
|
||||
|
||||
|
||||
return html5.getSources.call(this).map(function (source) {
|
||||
return Number(source.getAttribute('size'));
|
||||
}).filter(Boolean);
|
||||
@ -1271,6 +1311,10 @@ var html5 = {
|
||||
return source && Number(source.getAttribute('size'));
|
||||
},
|
||||
set: function set(input) {
|
||||
// If we're using an an external handler...
|
||||
if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) {
|
||||
player.config.quality.onChange(input);
|
||||
} else {
|
||||
// Get sources
|
||||
var sources = html5.getSources.call(player); // Get first match for requested size
|
||||
|
||||
@ -1294,6 +1338,10 @@ var html5 = {
|
||||
if (preload !== 'none' || readyState) {
|
||||
// Restore time
|
||||
player.once('loadedmetadata', function () {
|
||||
if (player.currentTime === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.currentTime = currentTime; // Resume playing
|
||||
|
||||
if (!paused) {
|
||||
@ -1302,6 +1350,7 @@ var html5 = {
|
||||
}); // Load new source
|
||||
|
||||
player.media.load();
|
||||
}
|
||||
} // Trigger change event
|
||||
|
||||
|
||||
@ -1672,7 +1721,7 @@ function formatTime() {
|
||||
|
||||
// Bail if the value isn't a number
|
||||
if (!is$1.number(time)) {
|
||||
return formatTime(null, displayHours, inverted);
|
||||
return formatTime(undefined, displayHours, inverted);
|
||||
} // Format time component to add leading zero
|
||||
|
||||
|
||||
@ -1781,9 +1830,11 @@ var controls = {
|
||||
createLabel: function createLabel(key) {
|
||||
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
var text = i18n.get(key, this.config);
|
||||
var attributes = Object.assign({}, attr, {
|
||||
|
||||
var attributes = _objectSpread2({}, attr, {
|
||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
|
||||
});
|
||||
|
||||
return createElement('span', attributes, text);
|
||||
},
|
||||
// Create a badge
|
||||
@ -1993,7 +2044,7 @@ var controls = {
|
||||
var _this2 = this;
|
||||
|
||||
// Navigate through menus via arrow keys and space
|
||||
on(menuItem, 'keydown keyup', function (event) {
|
||||
on.call(this, menuItem, 'keydown keyup', function (event) {
|
||||
// We only care about space and ⬆️ ⬇️️ ➡️
|
||||
if (![32, 38, 39, 40].includes(event.which)) {
|
||||
return;
|
||||
@ -2035,7 +2086,7 @@ var controls = {
|
||||
}, false); // Enter will fire a `click` event but we still need to manage focus
|
||||
// So we bind to keyup which fires after and set focus here
|
||||
|
||||
on(menuItem, 'keyup', function (event) {
|
||||
on.call(this, menuItem, 'keyup', function (event) {
|
||||
if (event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
@ -2112,9 +2163,6 @@ var controls = {
|
||||
case 'speed':
|
||||
_this3.speed = parseFloat(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event));
|
||||
@ -2223,9 +2271,6 @@ var controls = {
|
||||
case 'progress':
|
||||
setProgress(this.elements.display.buffer, this.buffered * 100);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2879,9 +2924,11 @@ var controls = {
|
||||
if (control === 'mute') {
|
||||
volume.appendChild(createButton.call(_this10, 'mute'));
|
||||
} // Volume range control
|
||||
// Ignored on iOS as it's handled globally
|
||||
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
|
||||
|
||||
|
||||
if (control === 'volume') {
|
||||
if (control === 'volume' && !browser.isIos) {
|
||||
// Set the attributes
|
||||
var attributes = {
|
||||
max: 1,
|
||||
@ -2940,7 +2987,7 @@ var controls = {
|
||||
|
||||
bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click
|
||||
|
||||
on(menuItem, 'click', function () {
|
||||
on.call(_this10, menuItem, 'click', function () {
|
||||
showMenuPanel.call(_this10, type, false);
|
||||
});
|
||||
var flex = createElement('span', null, i18n.get(type, _this10.config));
|
||||
@ -2971,7 +3018,7 @@ var controls = {
|
||||
class: _this10.config.classNames.hidden
|
||||
}, i18n.get('menuBack', _this10.config))); // Go back via keyboard
|
||||
|
||||
on(pane, 'keydown', function (event) {
|
||||
on.call(_this10, pane, 'keydown', function (event) {
|
||||
// We only care about <-
|
||||
if (event.which !== 37) {
|
||||
return;
|
||||
@ -2984,7 +3031,7 @@ var controls = {
|
||||
showMenuPanel.call(_this10, 'home', true);
|
||||
}, false); // Go back via button click
|
||||
|
||||
on(backButton, 'click', function () {
|
||||
on.call(_this10, backButton, 'click', function () {
|
||||
showMenuPanel.call(_this10, 'home', false);
|
||||
}); // Add to pane
|
||||
|
||||
@ -3646,7 +3693,9 @@ var defaults$1 = {
|
||||
// Quality default
|
||||
quality: {
|
||||
default: 576,
|
||||
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240]
|
||||
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
|
||||
forced: false,
|
||||
onChange: null
|
||||
},
|
||||
// Set loops
|
||||
loop: {
|
||||
@ -3723,6 +3772,7 @@ var defaults$1 = {
|
||||
frameTitle: 'Player for {title}',
|
||||
captions: 'Captions',
|
||||
settings: 'Settings',
|
||||
pip: 'PIP',
|
||||
menuBack: 'Go back to previous menu',
|
||||
speed: 'Speed',
|
||||
normal: 'Normal',
|
||||
@ -4162,7 +4212,9 @@ function () {
|
||||
} else if (!Fullscreen.native || this.forceFallback) {
|
||||
toggleFallback.call(this, true);
|
||||
} else if (!this.prefix) {
|
||||
this.target.requestFullscreen();
|
||||
this.target.requestFullscreen({
|
||||
navigationUI: "hide"
|
||||
});
|
||||
} else if (!is$1.empty(this.prefix)) {
|
||||
this.target["".concat(this.prefix, "Request").concat(this.property)]();
|
||||
}
|
||||
@ -4469,6 +4521,7 @@ var ui = {
|
||||
Object.assign(target, {
|
||||
pressed: _this3.playing
|
||||
});
|
||||
target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config));
|
||||
}); // Only update controls on non timeupdate events
|
||||
|
||||
if (is$1.event(event) && event.type === 'timeupdate') {
|
||||
@ -4649,19 +4702,6 @@ function () {
|
||||
// L key
|
||||
player.loop = !player.loop;
|
||||
break;
|
||||
|
||||
/* case 73:
|
||||
this.setLoop('start');
|
||||
break;
|
||||
case 76:
|
||||
this.setLoop();
|
||||
break;
|
||||
case 79:
|
||||
this.setLoop('end');
|
||||
break; */
|
||||
|
||||
default:
|
||||
break;
|
||||
} // Escape is handle natively when in full screen
|
||||
// So we only need to worry about non native
|
||||
|
||||
@ -4878,19 +4918,15 @@ function () {
|
||||
|
||||
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
|
||||
return controls.durationUpdate.call(player, event);
|
||||
}); // Check for audio tracks on load
|
||||
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
|
||||
|
||||
on.call(player, player.media, 'canplay loadeddata', function () {
|
||||
toggleHidden(elements.volume, !player.hasAudio);
|
||||
toggleHidden(elements.buttons.mute, !player.hasAudio);
|
||||
}); // Handle the media finishing
|
||||
|
||||
on.call(player, player.media, 'ended', function () {
|
||||
// Show poster on end
|
||||
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
|
||||
// Restart
|
||||
player.restart();
|
||||
player.restart(); // Call pause otherwise IE11 will start playing the video again
|
||||
|
||||
player.pause();
|
||||
}
|
||||
}); // Check for buffer progress
|
||||
|
||||
@ -5003,7 +5039,7 @@ function () {
|
||||
} // Only call default handler if not prevented in custom handler
|
||||
|
||||
|
||||
if (returned && is$1.function(defaultHandler)) {
|
||||
if (returned !== false && is$1.function(defaultHandler)) {
|
||||
defaultHandler.call(player, event);
|
||||
}
|
||||
} // Trigger custom and default handlers
|
||||
@ -5391,12 +5427,13 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
|
||||
async = args.async,
|
||||
maxTries = (args.numRetries || 0) + 1,
|
||||
beforeCallbackFn = args.before || devnull,
|
||||
pathname = path.replace(/[\?|#].*$/, ''),
|
||||
pathStripped = path.replace(/^(css|img)!/, ''),
|
||||
isLegacyIECss,
|
||||
e;
|
||||
numTries = numTries || 0;
|
||||
|
||||
if (/(^css!|\.css$)/.test(path)) {
|
||||
if (/(^css!|\.css$)/.test(pathname)) {
|
||||
// css
|
||||
e = doc.createElement('link');
|
||||
e.rel = 'stylesheet';
|
||||
@ -5409,7 +5446,7 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
|
||||
e.rel = 'preload';
|
||||
e.as = 'style';
|
||||
}
|
||||
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) {
|
||||
} else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) {
|
||||
// image
|
||||
e = doc.createElement('img');
|
||||
e.src = pathStripped;
|
||||
@ -5882,6 +5919,12 @@ var vimeo = {
|
||||
frame.setAttribute('tabindex', -1);
|
||||
}
|
||||
});
|
||||
player.embed.on('bufferstart', function () {
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
});
|
||||
player.embed.on('bufferend', function () {
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
});
|
||||
player.embed.on('play', function () {
|
||||
assurePlaybackState.call(player, true);
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
@ -6315,7 +6358,9 @@ var youtube = {
|
||||
assurePlaybackState$1.call(player, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 3:
|
||||
// Trigger waiting event to add loading classes to container as the video buffers.
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
break;
|
||||
}
|
||||
|
||||
@ -6357,11 +6402,13 @@ var media = {
|
||||
|
||||
wrap(this.media, this.elements.wrapper); // Faux poster container
|
||||
|
||||
if (this.isEmbed) {
|
||||
this.elements.poster = createElement('div', {
|
||||
class: this.config.classNames.poster
|
||||
});
|
||||
this.elements.wrapper.appendChild(this.elements.poster);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isHTML5) {
|
||||
html5.extend.call(this);
|
||||
@ -6730,9 +6777,6 @@ function () {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -7004,7 +7048,7 @@ function () {
|
||||
cb: Date.now(),
|
||||
AV_WIDTH: 640,
|
||||
AV_HEIGHT: 480,
|
||||
AV_CDIM2: this.publisherId
|
||||
AV_CDIM2: config.publisherId
|
||||
};
|
||||
var base = 'https://go.aniview.com/api/adserver6/vast/';
|
||||
return "".concat(base, "?").concat(buildUrlParams(params));
|
||||
@ -7069,6 +7113,21 @@ var parseVtt = function parseVtt(vttDataString) {
|
||||
*/
|
||||
|
||||
|
||||
var fitRatio = function fitRatio(ratio, outer) {
|
||||
var targetRatio = outer.width / outer.height;
|
||||
var result = {};
|
||||
|
||||
if (ratio > targetRatio) {
|
||||
result.width = outer.width;
|
||||
result.height = 1 / ratio * outer.width;
|
||||
} else {
|
||||
result.height = outer.height;
|
||||
result.width = ratio * outer.height;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var PreviewThumbnails =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
@ -7613,9 +7672,15 @@ function () {
|
||||
}, {
|
||||
key: "setScrubbingContainerSize",
|
||||
value: function setScrubbingContainerSize() {
|
||||
this.elements.scrubbing.container.style.width = "".concat(this.player.media.clientWidth, "px"); // Can't use media.clientHeight - html5 video goes big and does black bars above and below
|
||||
var _fitRatio = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight
|
||||
}),
|
||||
width = _fitRatio.width,
|
||||
height = _fitRatio.height;
|
||||
|
||||
this.elements.scrubbing.container.style.height = "".concat(this.player.media.clientWidth / this.thumbAspectRatio, "px");
|
||||
this.elements.scrubbing.container.style.width = "".concat(width, "px");
|
||||
this.elements.scrubbing.container.style.height = "".concat(height, "px");
|
||||
} // Sprites need to be offset to the correct location
|
||||
|
||||
}, {
|
||||
@ -7628,9 +7693,9 @@ function () {
|
||||
|
||||
var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px"); // eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.height = "".concat(previewImage.naturalHeight * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px"); // eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.width = "".concat(previewImage.naturalWidth * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign
|
||||
|
||||
@ -7668,8 +7733,13 @@ function () {
|
||||
key: "thumbContainerHeight",
|
||||
get: function get() {
|
||||
if (this.mouseDown) {
|
||||
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio);
|
||||
var _fitRatio2 = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight
|
||||
}),
|
||||
height = _fitRatio2.height;
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
|
||||
@ -8153,10 +8223,10 @@ function () {
|
||||
key: "pause",
|
||||
value: function pause() {
|
||||
if (!this.playing || !is$1.function(this.media.pause)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.media.pause();
|
||||
return this.media.pause();
|
||||
}
|
||||
/**
|
||||
* Get playing state
|
||||
@ -8174,10 +8244,10 @@ function () {
|
||||
var toggle = is$1.boolean(input) ? input : !this.playing;
|
||||
|
||||
if (toggle) {
|
||||
this.play();
|
||||
} else {
|
||||
this.pause();
|
||||
return this.play();
|
||||
}
|
||||
|
||||
return this.pause();
|
||||
}
|
||||
/**
|
||||
* Stop playback
|
||||
@ -8210,7 +8280,7 @@ function () {
|
||||
}, {
|
||||
key: "rewind",
|
||||
value: function rewind(seekTime) {
|
||||
this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime -= is$1.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
/**
|
||||
* Fast forward
|
||||
@ -8220,7 +8290,7 @@ function () {
|
||||
}, {
|
||||
key: "forward",
|
||||
value: function forward(seekTime) {
|
||||
this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime += is$1.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
/**
|
||||
* Seek to a time
|
||||
|
1290
dist/plyr.polyfilled.js
vendored
1290
dist/plyr.polyfilled.js
vendored
File diff suppressed because it is too large
Load Diff
4
dist/plyr.polyfilled.min.js
vendored
4
dist/plyr.polyfilled.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.polyfilled.min.js.map
vendored
2
dist/plyr.polyfilled.min.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/plyr.polyfilled.min.mjs
vendored
4
dist/plyr.polyfilled.min.mjs
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.polyfilled.min.mjs.map
vendored
2
dist/plyr.polyfilled.min.mjs.map
vendored
File diff suppressed because one or more lines are too long
1288
dist/plyr.polyfilled.mjs
vendored
1288
dist/plyr.polyfilled.mjs
vendored
File diff suppressed because it is too large
Load Diff
2
dist/plyr.svg
vendored
2
dist/plyr.svg
vendored
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.6 KiB |
29
gulpfile.js
29
gulpfile.js
@ -1,7 +1,6 @@
|
||||
// ==========================================================================
|
||||
// Gulp build script
|
||||
// ==========================================================================
|
||||
/* global require, __dirname */
|
||||
/* eslint no-console: "off" */
|
||||
|
||||
const path = require('path');
|
||||
@ -41,6 +40,7 @@ const plumber = require('gulp-plumber');
|
||||
const size = require('gulp-size');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const through = require('through2');
|
||||
const browserSync = require('browser-sync').create();
|
||||
// ------------------------------------
|
||||
// Deployment
|
||||
// ------------------------------------
|
||||
@ -222,7 +222,13 @@ Object.entries(build.sprite).forEach(([filename, entry]) => {
|
||||
gulp
|
||||
.src(src)
|
||||
.pipe(plumber())
|
||||
.pipe(imagemin())
|
||||
.pipe(
|
||||
imagemin([
|
||||
imagemin.svgo({
|
||||
plugins: [{ removeViewBox: false }],
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.pipe(svgstore())
|
||||
.pipe(rename({ basename: path.parse(filename).name }))
|
||||
.pipe(size(sizeOptions))
|
||||
@ -245,11 +251,23 @@ gulp.task('watch', () => {
|
||||
gulp.watch(paths.demo.src.sass, gulp.parallel(...tasks.css));
|
||||
});
|
||||
|
||||
// Serve via browser sync
|
||||
gulp.task('serve', () =>
|
||||
browserSync.init({
|
||||
server: {
|
||||
baseDir: paths.demo.root,
|
||||
},
|
||||
notify: false,
|
||||
watch: true,
|
||||
ghostMode: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// Build distribution
|
||||
gulp.task('build', gulp.series(tasks.clean, gulp.parallel(...tasks.js, ...tasks.css, ...tasks.sprite)));
|
||||
|
||||
// Default gulp task
|
||||
gulp.task('default', gulp.series('build', 'watch'));
|
||||
gulp.task('default', gulp.series('build', gulp.parallel('serve', 'watch')));
|
||||
|
||||
// Publish a version to CDN and demo
|
||||
// --------------------------------------------
|
||||
@ -331,7 +349,10 @@ gulp.task('version', done => {
|
||||
const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js'];
|
||||
|
||||
return gulp
|
||||
.src(files.map(file => path.join(__dirname, `src/js/${file}`)), { base: '.' })
|
||||
.src(
|
||||
files.map(file => path.join(__dirname, `src/js/${file}`)),
|
||||
{ base: '.' },
|
||||
)
|
||||
.pipe(replace(semver, `v${version}`))
|
||||
.pipe(replace(cdnpath, `${domain}/${version}/`))
|
||||
.pipe(gulp.dest('./'));
|
||||
|
68
package.json
68
package.json
@ -5,6 +5,7 @@
|
||||
"homepage": "https://plyr.io",
|
||||
"author": "Sam Potts <sam@potts.es>",
|
||||
"main": "dist/plyr.js",
|
||||
"types": "src/js/plyr.d.ts",
|
||||
"module": "dist/plyr.min.mjs",
|
||||
"jsnext:main": "dist/plyr.min.mjs",
|
||||
"browser": "dist/plyr.min.js",
|
||||
@ -36,61 +37,62 @@
|
||||
"deploy": "yarn lint && gulp deploy"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ansi-colors": "^4.0.1",
|
||||
"aws-sdk": "^2.478.0",
|
||||
"@babel/core": "^7.4.5",
|
||||
"@babel/preset-env": "^7.4.5",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"del": "^4.1.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
"eslint-config-prettier": "^5.0.0",
|
||||
"eslint-plugin-import": "^2.17.3",
|
||||
"eslint-plugin-simple-import-sort": "^4.0.0",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"aws-sdk": "^2.610.0",
|
||||
"@babel/core": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"browser-sync": "^2.26.7",
|
||||
"del": "^5.1.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^6.9.0",
|
||||
"eslint-plugin-import": "^2.20.0",
|
||||
"eslint-plugin-simple-import-sort": "^5.0.1",
|
||||
"fancy-log": "^1.3.3",
|
||||
"fastly-purge": "^1.0.1",
|
||||
"git-branch": "^2.0.1",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^6.1.0",
|
||||
"gulp-awspublish": "^4.0.0",
|
||||
"gulp-autoprefixer": "^7.0.1",
|
||||
"gulp-awspublish": "^4.1.1",
|
||||
"gulp-better-rollup": "^4.0.1",
|
||||
"gulp-clean-css": "^4.2.0",
|
||||
"gulp-filter": "^6.0.0",
|
||||
"gulp-header": "^2.0.7",
|
||||
"gulp-imagemin": "^6.0.0",
|
||||
"gulp-header": "^2.0.9",
|
||||
"gulp-imagemin": "^7.1.0",
|
||||
"gulp-open": "^3.0.1",
|
||||
"gulp-plumber": "^1.2.1",
|
||||
"gulp-postcss": "^8.0.0",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-replace": "^1.0.0",
|
||||
"gulp-sass": "^4.0.2",
|
||||
"gulp-size": "^3.0.0",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"gulp-svgstore": "^7.0.1",
|
||||
"gulp-terser": "^1.2.0",
|
||||
"postcss-custom-properties": "^9.0.1",
|
||||
"prettier-eslint": "^9.0.0",
|
||||
"postcss-custom-properties": "^9.0.2",
|
||||
"prettier-eslint": "^9.0.1",
|
||||
"prettier-stylelint": "^0.4.2",
|
||||
"remark-cli": "^6.0.1",
|
||||
"remark-validate-links": "^8.0.3",
|
||||
"rollup": "^1.15.6",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.0.3",
|
||||
"stylelint": "^10.1.0",
|
||||
"stylelint-config-prettier": "^5.2.0",
|
||||
"stylelint-config-recommended": "^2.2.0",
|
||||
"stylelint-config-sass-guidelines": "^6.0.0",
|
||||
"stylelint-order": "^3.0.0",
|
||||
"stylelint-scss": "^3.8.0",
|
||||
"remark-cli": "^7.0.1",
|
||||
"remark-validate-links": "^9.2.0",
|
||||
"rollup": "^1.29.1",
|
||||
"rollup-plugin-babel": "^4.3.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"stylelint": "^13.0.0",
|
||||
"stylelint-config-prettier": "^8.0.1",
|
||||
"stylelint-config-recommended": "^3.0.0",
|
||||
"stylelint-config-sass-guidelines": "^6.2.0",
|
||||
"stylelint-order": "^4.0.0",
|
||||
"stylelint-scss": "^3.13.0",
|
||||
"stylelint-selector-bem-pattern": "^2.1.0",
|
||||
"through2": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.1.4",
|
||||
"core-js": "^3.6.4",
|
||||
"custom-event-polyfill": "^1.0.7",
|
||||
"loadjs": "^3.6.1",
|
||||
"loadjs": "^4.2.0",
|
||||
"rangetouch": "^2.0.0",
|
||||
"url-polyfill": "^1.1.5"
|
||||
"url-polyfill": "^1.1.7"
|
||||
}
|
||||
}
|
||||
|
@ -9,22 +9,29 @@
|
||||
"**/node_modules": true,
|
||||
"**/dist": true
|
||||
},
|
||||
|
||||
// Linting
|
||||
"stylelint.enable": true,
|
||||
"css.validate": false,
|
||||
"scss.validate": false,
|
||||
"javascript.validate.enable": false,
|
||||
|
||||
// Prettier
|
||||
"prettier.eslintIntegration": true,
|
||||
"prettier.stylelintIntegration": true,
|
||||
|
||||
// Formatting
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
// Trim on save
|
||||
"files.trimTrailingWhitespace": true
|
||||
"files.trimTrailingWhitespace": true,
|
||||
|
||||
// Special file associations
|
||||
"files.associations": {
|
||||
".eslintrc": "jsonc"
|
||||
},
|
||||
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
readme.md
12
readme.md
@ -116,7 +116,7 @@ import Plyr from 'plyr';
|
||||
const player = new Plyr('#player');
|
||||
```
|
||||
|
||||
Alertnatively you can include the `plyr.js` script before the closing `</body>` tag and then in your JS create a new instance of Plyr as below.
|
||||
Alternatively you can include the `plyr.js` script before the closing `</body>` tag and then in your JS create a new instance of Plyr as below.
|
||||
|
||||
```html
|
||||
<script src="path/to/plyr.js"></script>
|
||||
@ -630,7 +630,7 @@ Fullscreen in Plyr is supported by all browsers that [currently support it](http
|
||||
Plyr supports the last 2 versions of most _modern_ browsers.
|
||||
|
||||
| Browser | Supported |
|
||||
| ------------- | ------------- |
|
||||
| ------------- | --------------- |
|
||||
| Safari | ✓ |
|
||||
| Mobile Safari | ✓¹ |
|
||||
| Firefox | ✓ |
|
||||
@ -638,7 +638,7 @@ Plyr supports the last 2 versions of most _modern_ browsers.
|
||||
| Opera | ✓ |
|
||||
| Edge | ✓ |
|
||||
| IE11 | ✓³ |
|
||||
| IE10 | ✓²³ |
|
||||
| IE10 | ✓<sup>2,3</sup> |
|
||||
|
||||
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled as they are handled device wide.
|
||||
2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported. No native fullscreen support, fallback can be used (see [options](#options)).
|
||||
@ -682,7 +682,7 @@ Some awesome folks have made plugins for CMSs and Components for JavaScript fram
|
||||
| --------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| WordPress | Brandon Lavigne ([@drrobotnik](https://github.com/drrobotnik)) | [https://wordpress.org/plugins/plyr/](https://wordpress.org/plugins/plyr/) |
|
||||
| Angular | Simon Bobrov ([@smnbbrv](https://github.com/smnbbrv)) | [https://github.com/smnbbrv/ngx-plyr](https://github.com/smnbbrv/ngx-plyr) |
|
||||
| React | Jose Miguel Bejarano ([@xDae](https://github.com/xDae)) | [https://github.com/xDae/react-plyr](https://github.com/xDae/react-plyr) |
|
||||
| React | Chintan Prajapati ([@chintan9](https://github.com/chintan9)) | [https://github.com/chintan9/plyr-react](https://github.com/chintan9/plyr-react) |
|
||||
| Vue | Gabe Dunn ([@redxtech](https://github.com/redxtech)) | [https://github.com/redxtech/vue-plyr](https://github.com/redxtech/vue-plyr) |
|
||||
| Neos | Jon Uhlmann ([@jonnitto](https://github.com/jonnitto)) | [https://packagist.org/packages/jonnitto/plyr](https://packagist.org/packages/jonnitto/plyr) |
|
||||
| Kirby | Dominik Pschenitschni ([@dpschen](https://github.com/dpschen)) | [https://github.com/dpschen/kirby-plyrtag](https://github.com/dpschen/kirby-plyrtag) |
|
||||
@ -735,9 +735,7 @@ If you want to be added to the list, open a pull request. It'd be awesome to see
|
||||
|
||||
# Useful links and credits
|
||||
|
||||
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality was originally ported from:
|
||||
|
||||
- [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player)
|
||||
- [PayPal's Accessible HTML5 Video Player (which Plyr was originally ported from)](https://github.com/paypal/accessible-html5-video-player)
|
||||
- [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
|
||||
|
||||
# Thanks
|
||||
|
@ -70,6 +70,8 @@ const defaults = {
|
||||
quality: {
|
||||
default: 576,
|
||||
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
|
||||
forced: false,
|
||||
onChange: null,
|
||||
},
|
||||
|
||||
// Set loops
|
||||
@ -164,6 +166,7 @@ const defaults = {
|
||||
frameTitle: 'Player for {title}',
|
||||
captions: 'Captions',
|
||||
settings: 'Settings',
|
||||
pip: 'PIP',
|
||||
menuBack: 'Go back to previous menu',
|
||||
speed: 'Speed',
|
||||
normal: 'Normal',
|
||||
|
21
src/js/controls.js
vendored
21
src/js/controls.js
vendored
@ -139,10 +139,7 @@ const controls = {
|
||||
// Create hidden text label
|
||||
createLabel(key, attr = {}) {
|
||||
const text = i18n.get(key, this.config);
|
||||
|
||||
const attributes = Object.assign({}, attr, {
|
||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' '),
|
||||
});
|
||||
const attributes = { ...attr, class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') };
|
||||
|
||||
return createElement('span', attributes, text);
|
||||
},
|
||||
@ -402,7 +399,8 @@ const controls = {
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220143
|
||||
bindMenuItemShortcuts(menuItem, type) {
|
||||
// Navigate through menus via arrow keys and space
|
||||
on(
|
||||
on.call(
|
||||
this,
|
||||
menuItem,
|
||||
'keydown keyup',
|
||||
event => {
|
||||
@ -452,7 +450,7 @@ const controls = {
|
||||
|
||||
// Enter will fire a `click` event but we still need to manage focus
|
||||
// So we bind to keyup which fires after and set focus here
|
||||
on(menuItem, 'keyup', event => {
|
||||
on.call(this, menuItem, 'keyup', event => {
|
||||
if (event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
@ -1380,7 +1378,9 @@ const controls = {
|
||||
}
|
||||
|
||||
// Volume range control
|
||||
if (control === 'volume') {
|
||||
// Ignored on iOS as it's handled globally
|
||||
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
|
||||
if (control === 'volume' && !browser.isIos) {
|
||||
// Set the attributes
|
||||
const attributes = {
|
||||
max: 1,
|
||||
@ -1463,7 +1463,7 @@ const controls = {
|
||||
bindMenuItemShortcuts.call(this, menuItem, type);
|
||||
|
||||
// Show menu on click
|
||||
on(menuItem, 'click', () => {
|
||||
on.call(this, menuItem, 'click', () => {
|
||||
showMenuPanel.call(this, type, false);
|
||||
});
|
||||
|
||||
@ -1515,7 +1515,8 @@ const controls = {
|
||||
);
|
||||
|
||||
// Go back via keyboard
|
||||
on(
|
||||
on.call(
|
||||
this,
|
||||
pane,
|
||||
'keydown',
|
||||
event => {
|
||||
@ -1535,7 +1536,7 @@ const controls = {
|
||||
);
|
||||
|
||||
// Go back via button click
|
||||
on(backButton, 'click', () => {
|
||||
on.call(this, backButton, 'click', () => {
|
||||
showMenuPanel.call(this, 'home', false);
|
||||
});
|
||||
|
||||
|
@ -228,7 +228,7 @@ class Fullscreen {
|
||||
} else if (!Fullscreen.native || this.forceFallback) {
|
||||
toggleFallback.call(this, true);
|
||||
} else if (!this.prefix) {
|
||||
this.target.requestFullscreen();
|
||||
this.target.requestFullscreen({ navigationUI: "hide" });
|
||||
} else if (!is.empty(this.prefix)) {
|
||||
this.target[`${this.prefix}Request${this.property}`]();
|
||||
}
|
||||
|
@ -30,6 +30,11 @@ const html5 = {
|
||||
|
||||
// Get quality levels
|
||||
getQualityOptions() {
|
||||
// Whether we're forcing all options (e.g. for streaming)
|
||||
if (this.config.quality.forced) {
|
||||
return this.config.quality.options;
|
||||
}
|
||||
|
||||
// Get sizes from <source> elements
|
||||
return html5.getSources
|
||||
.call(this)
|
||||
@ -60,6 +65,10 @@ const html5 = {
|
||||
return source && Number(source.getAttribute('size'));
|
||||
},
|
||||
set(input) {
|
||||
// If we're using an an external handler...
|
||||
if (player.config.quality.forced && is.function(player.config.quality.onChange)) {
|
||||
player.config.quality.onChange(input);
|
||||
} else {
|
||||
// Get sources
|
||||
const sources = html5.getSources.call(player);
|
||||
// Get first match for requested size
|
||||
@ -80,6 +89,10 @@ const html5 = {
|
||||
if (preload !== 'none' || readyState) {
|
||||
// Restore time
|
||||
player.once('loadedmetadata', () => {
|
||||
if (player.currentTime === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.currentTime = currentTime;
|
||||
|
||||
// Resume playing
|
||||
@ -91,6 +104,7 @@ const html5 = {
|
||||
// Load new source
|
||||
player.media.load();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger change event
|
||||
triggerEvent.call(player, player.media, 'qualitychange', false, {
|
||||
|
@ -6,7 +6,7 @@ import controls from './controls';
|
||||
import ui from './ui';
|
||||
import { repaint } from './utils/animation';
|
||||
import browser from './utils/browser';
|
||||
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements';
|
||||
import { getElement, getElements, matches, toggleClass } from './utils/elements';
|
||||
import { off, on, once, toggleListener, triggerEvent } from './utils/events';
|
||||
import is from './utils/is';
|
||||
import { getAspectRatio, setAspectRatio } from './utils/style';
|
||||
@ -377,19 +377,15 @@ class Listeners {
|
||||
controls.durationUpdate.call(player, event),
|
||||
);
|
||||
|
||||
// Check for audio tracks on load
|
||||
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
|
||||
on.call(player, player.media, 'canplay loadeddata', () => {
|
||||
toggleHidden(elements.volume, !player.hasAudio);
|
||||
toggleHidden(elements.buttons.mute, !player.hasAudio);
|
||||
});
|
||||
|
||||
// Handle the media finishing
|
||||
on.call(player, player.media, 'ended', () => {
|
||||
// Show poster on end
|
||||
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
|
||||
// Restart
|
||||
player.restart();
|
||||
|
||||
// Call pause otherwise IE11 will start playing the video again
|
||||
player.pause();
|
||||
}
|
||||
});
|
||||
|
||||
@ -663,7 +659,7 @@ class Listeners {
|
||||
const code = event.keyCode ? event.keyCode : event.which;
|
||||
const attribute = 'play-on-seeked';
|
||||
|
||||
if (is.keyboardEvent(event) && (code !== 39 && code !== 37)) {
|
||||
if (is.keyboardEvent(event) && code !== 39 && code !== 37) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -39,12 +39,14 @@ const media = {
|
||||
wrap(this.media, this.elements.wrapper);
|
||||
|
||||
// Faux poster container
|
||||
if (this.isEmbed) {
|
||||
this.elements.poster = createElement('div', {
|
||||
class: this.config.classNames.poster,
|
||||
});
|
||||
|
||||
this.elements.wrapper.appendChild(this.elements.poster);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isHTML5) {
|
||||
html5.extend.call(this);
|
||||
|
@ -136,7 +136,7 @@ class Ads {
|
||||
cb: Date.now(),
|
||||
AV_WIDTH: 640,
|
||||
AV_HEIGHT: 480,
|
||||
AV_CDIM2: this.publisherId,
|
||||
AV_CDIM2: config.publisherId,
|
||||
};
|
||||
|
||||
const base = 'https://go.aniview.com/api/adserver6/vast/';
|
||||
|
@ -63,6 +63,20 @@ const parseVtt = vttDataString => {
|
||||
* - This implementation uses multiple separate img elements. Other implementations use background-image on one element. This would be nice and simple, but Firefox and Safari have flickering issues with replacing backgrounds of larger images. It seems that YouTube perhaps only avoids this because they don't have the option for high-res previews (even the fullscreen ones, when mousedown/seeking). Images appear over the top of each other, and previous ones are discarded once the new ones have been rendered
|
||||
*/
|
||||
|
||||
const fitRatio = (ratio, outer) => {
|
||||
const targetRatio = outer.width / outer.height;
|
||||
const result = {};
|
||||
if (ratio > targetRatio) {
|
||||
result.width = outer.width;
|
||||
result.height = (1 / ratio) * outer.width;
|
||||
} else {
|
||||
result.height = outer.height;
|
||||
result.width = ratio * outer.height;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
class PreviewThumbnails {
|
||||
/**
|
||||
* PreviewThumbnails constructor.
|
||||
@ -540,8 +554,11 @@ class PreviewThumbnails {
|
||||
|
||||
get thumbContainerHeight() {
|
||||
if (this.mouseDown) {
|
||||
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio);
|
||||
const { height } = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight,
|
||||
});
|
||||
return height;
|
||||
}
|
||||
|
||||
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
|
||||
@ -624,9 +641,12 @@ class PreviewThumbnails {
|
||||
|
||||
// Can't use 100% width, in case the video is a different aspect ratio to the video container
|
||||
setScrubbingContainerSize() {
|
||||
this.elements.scrubbing.container.style.width = `${this.player.media.clientWidth}px`;
|
||||
// Can't use media.clientHeight - html5 video goes big and does black bars above and below
|
||||
this.elements.scrubbing.container.style.height = `${this.player.media.clientWidth / this.thumbAspectRatio}px`;
|
||||
const { width, height } = fitRatio(this.thumbAspectRatio, {
|
||||
width: this.player.media.clientWidth,
|
||||
height: this.player.media.clientHeight,
|
||||
});
|
||||
this.elements.scrubbing.container.style.width = `${width}px`;
|
||||
this.elements.scrubbing.container.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
// Sprites need to be offset to the correct location
|
||||
@ -639,9 +659,9 @@ class PreviewThumbnails {
|
||||
const multiplier = this.thumbContainerHeight / frame.h;
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.height = `${Math.floor(previewImage.naturalHeight * multiplier)}px`;
|
||||
previewImage.style.height = `${previewImage.naturalHeight * multiplier}px`;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.width = `${Math.floor(previewImage.naturalWidth * multiplier)}px`;
|
||||
previewImage.style.width = `${previewImage.naturalWidth * multiplier}px`;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
previewImage.style.left = `-${frame.x * multiplier}px`;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
@ -335,6 +335,14 @@ const vimeo = {
|
||||
}
|
||||
});
|
||||
|
||||
player.embed.on('bufferstart', () => {
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
});
|
||||
|
||||
player.embed.on('bufferend', () => {
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
});
|
||||
|
||||
player.embed.on('play', () => {
|
||||
assurePlaybackState.call(player, true);
|
||||
triggerEvent.call(player, player.media, 'playing');
|
||||
|
@ -416,6 +416,12 @@ const youtube = {
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Trigger waiting event to add loading classes to container as the video buffers.
|
||||
triggerEvent.call(player, player.media, 'waiting');
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
560
src/js/plyr.d.ts
vendored
Normal file
560
src/js/plyr.d.ts
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
// Type definitions for plyr 3.5
|
||||
// Project: https://plyr.io
|
||||
// Definitions by: ondratra <https://github.com/ondratra>
|
||||
// TypeScript Version: 3.0
|
||||
|
||||
export = Plyr;
|
||||
export as namespace Plyr;
|
||||
|
||||
|
||||
declare class Plyr {
|
||||
/**
|
||||
* Setup a new instance
|
||||
*/
|
||||
static setup(targets: NodeList | HTMLElement | HTMLElement[] | string, options?: Plyr.Options): Plyr[];
|
||||
|
||||
/**
|
||||
* Check for support
|
||||
* @param mediaType
|
||||
* @param provider
|
||||
* @param playsInline Whether the player has the playsinline attribute (only applicable to iOS 10+)
|
||||
*/
|
||||
static supported(mediaType?: Plyr.MediaType, provider?: Plyr.Provider, playsInline?: boolean): Plyr.Support;
|
||||
|
||||
constructor(targets: NodeList | HTMLElement | HTMLElement[] | string, options?: Plyr.Options);
|
||||
|
||||
/**
|
||||
* Indicates if the current player is HTML5.
|
||||
*/
|
||||
readonly isHTML5: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player is an embedded player.
|
||||
*/
|
||||
readonly isEmbed: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player is playing.
|
||||
*/
|
||||
readonly playing: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player is paused.
|
||||
*/
|
||||
readonly paused: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player is stopped.
|
||||
*/
|
||||
readonly stopped: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player has finished playback.
|
||||
*/
|
||||
readonly ended: boolean;
|
||||
|
||||
/**
|
||||
* Returns a float between 0 and 1 indicating how much of the media is buffered
|
||||
*/
|
||||
readonly buffered: number;
|
||||
|
||||
/**
|
||||
* Gets or sets the currentTime for the player. The setter accepts a float in seconds.
|
||||
*/
|
||||
currentTime: number;
|
||||
|
||||
/**
|
||||
* Indicates if the current player is seeking.
|
||||
*/
|
||||
readonly seeking: boolean;
|
||||
|
||||
/**
|
||||
* Returns the duration for the current media.
|
||||
*/
|
||||
readonly duration: number;
|
||||
|
||||
/**
|
||||
* Gets or sets the volume for the player. The setter accepts a float between 0 and 1.
|
||||
*/
|
||||
volume: number;
|
||||
|
||||
/**
|
||||
* Gets or sets the muted state of the player. The setter accepts a boolean.
|
||||
*/
|
||||
muted: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current media has an audio track.
|
||||
*/
|
||||
readonly hasAudio: boolean;
|
||||
|
||||
/**
|
||||
* Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5.
|
||||
*/
|
||||
speed: number;
|
||||
|
||||
/**
|
||||
* Gets or sets the quality for the player. The setter accepts a value from the options specified in your config.
|
||||
* Remarks: YouTube only. HTML5 will follow.
|
||||
*/
|
||||
quality: string;
|
||||
|
||||
/**
|
||||
* Gets or sets the current loop state of the player.
|
||||
*/
|
||||
loop: boolean;
|
||||
|
||||
/**
|
||||
* Gets or sets the current source for the player.
|
||||
*/
|
||||
source: Plyr.SourceInfo;
|
||||
|
||||
/**
|
||||
* Gets or sets the current poster image URL for the player.
|
||||
*/
|
||||
poster: string;
|
||||
|
||||
/**
|
||||
* Gets or sets the autoplay state of the player.
|
||||
*/
|
||||
autoplay: boolean;
|
||||
|
||||
/**
|
||||
* Gets or sets the caption track by index. 1 means the track is missing or captions is not active
|
||||
*/
|
||||
currentTrack: number;
|
||||
|
||||
/**
|
||||
* Gets or sets the preferred captions language for the player. The setter accepts an ISO twoletter language code. Support for the languages is dependent on the captions you include.
|
||||
* If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use currentTrack instead.
|
||||
*/
|
||||
language: string;
|
||||
|
||||
/**
|
||||
* Gets or sets the picture-in-picture state of the player. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+.
|
||||
*/
|
||||
pip: boolean;
|
||||
|
||||
readonly fullscreen: Plyr.FullscreenControl;
|
||||
|
||||
/**
|
||||
* Start playback.
|
||||
* For HTML5 players, play() will return a Promise in some browsers - WebKit and Mozilla according to MDN at time of writing.
|
||||
*/
|
||||
play(): Promise<void> | void;
|
||||
|
||||
/**
|
||||
* Pause playback.
|
||||
*/
|
||||
pause(): void;
|
||||
|
||||
/**
|
||||
* Toggle playback, if no parameters are passed, it will toggle based on current status.
|
||||
*/
|
||||
togglePlay(toggle?: boolean): boolean;
|
||||
|
||||
/**
|
||||
* Stop playback and reset to start.
|
||||
*/
|
||||
stop(): void;
|
||||
|
||||
/**
|
||||
* Restart playback.
|
||||
*/
|
||||
restart(): void;
|
||||
|
||||
/**
|
||||
* Rewind playback by the specified seek time. If no parameter is passed, the default seek time will be used.
|
||||
*/
|
||||
rewind(seekTime?: number): void;
|
||||
|
||||
/**
|
||||
* Fast forward by the specified seek time. If no parameter is passed, the default seek time will be used.
|
||||
*/
|
||||
forward(seekTime?: number): void;
|
||||
|
||||
/**
|
||||
* Increase volume by the specified step. If no parameter is passed, the default step will be used.
|
||||
*/
|
||||
increaseVolume(step?: number): void;
|
||||
|
||||
/**
|
||||
* Increase volume by the specified step. If no parameter is passed, the default step will be used.
|
||||
*/
|
||||
decreaseVolume(step?: number): void;
|
||||
|
||||
/**
|
||||
* Toggle captions display. If no parameter is passed, it will toggle based on current status.
|
||||
*/
|
||||
toggleCaptions(toggle?: boolean): void;
|
||||
|
||||
/**
|
||||
* Trigger the airplay dialog on supported devices.
|
||||
*/
|
||||
airplay(): void;
|
||||
|
||||
/**
|
||||
* Toggle the controls (video only). Takes optional truthy value to force it on/off.
|
||||
*/
|
||||
toggleControls(toggle: boolean): void;
|
||||
|
||||
/**
|
||||
* Add an event listener for the specified event.
|
||||
*/
|
||||
on(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
|
||||
|
||||
/**
|
||||
* Add an event listener for the specified event once.
|
||||
*/
|
||||
once(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
|
||||
|
||||
/**
|
||||
* Remove an event listener for the specified event.
|
||||
*/
|
||||
off(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
|
||||
|
||||
/**
|
||||
* Check support for a mime type.
|
||||
*/
|
||||
supports(type: string): boolean;
|
||||
|
||||
/**
|
||||
* Destroy lib instance
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
declare namespace Plyr {
|
||||
type MediaType = "audio" | "video";
|
||||
type Provider = "html5" | "youtube" | "vimeo";
|
||||
type StandardEvent = "progress" | "playing" | "play" | "pause" | "timeupdate" | "volumechange" | "seeking" | "seeked" | "ratechange" | "ended" | "enterfullscreen" | "exitfullscreen"
|
||||
| "captionsenabled" | "captionsdisabled" | "languagechange" | "controlshidden" | "controlsshown" | "ready";
|
||||
type Html5Event = "loadstart" | "loadeddata" | "loadedmetadata" | "canplay" | "canplaythrough" | "stalled" | "waiting" | "emptied" | "cuechange" | "error";
|
||||
type YoutubeEvent = "statechange" | "qualitychange" | "qualityrequested";
|
||||
|
||||
interface FullscreenControl {
|
||||
/**
|
||||
* Indicates if the current player is in fullscreen mode.
|
||||
*/
|
||||
readonly active: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if the current player has fullscreen enabled.
|
||||
*/
|
||||
readonly enabled: boolean;
|
||||
|
||||
/**
|
||||
* Enter fullscreen. If fullscreen is not supported, a fallback ""full window/viewport"" is used instead.
|
||||
*/
|
||||
enter(): void;
|
||||
|
||||
/**
|
||||
* Exit fullscreen.
|
||||
*/
|
||||
exit(): void;
|
||||
|
||||
/**
|
||||
* Toggle fullscreen.
|
||||
*/
|
||||
toggle(): void;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
/**
|
||||
* Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Display debugging information in the console
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function;
|
||||
* id (the unique id for the player), seektime (the seektime step in seconds), and title (the media title). See controls.md for more info on how the html needs to be structured.
|
||||
* Defaults to ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']
|
||||
*/
|
||||
controls?: string[] | ((id: string, seektime: number, title: string) => unknown) | Element;
|
||||
|
||||
/**
|
||||
* If you're using the default controls are used then you can specify which settings to show in the menu
|
||||
* Defaults to ['captions', 'quality', 'speed', 'loop']
|
||||
*/
|
||||
settings?: string[];
|
||||
|
||||
/**
|
||||
* Used for internationalization (i18n) of the text within the UI.
|
||||
*/
|
||||
i18n?: any;
|
||||
|
||||
/**
|
||||
* Load the SVG sprite specified as the iconUrl option (if a URL). If false, it is assumed you are handling sprite loading yourself.
|
||||
*/
|
||||
loadSprite?: boolean;
|
||||
|
||||
/**
|
||||
* Specify a URL or path to the SVG sprite. See the SVG section for more info.
|
||||
*/
|
||||
iconUrl?: string;
|
||||
|
||||
/**
|
||||
* Specify the id prefix for the icons used in the default controls (e.g. plyr-play would be plyr).
|
||||
* This is to prevent clashes if you're using your own SVG sprite but with the default controls.
|
||||
* Most people can ignore this option.
|
||||
*/
|
||||
iconPrefix?: string;
|
||||
|
||||
/**
|
||||
* Specify a URL or path to a blank video file used to properly cancel network requests.
|
||||
*/
|
||||
blankUrl?: string;
|
||||
|
||||
/**
|
||||
* Autoplay the media on load. This is generally advised against on UX grounds. It is also disabled by default in some browsers.
|
||||
* If the autoplay attribute is present on a <video> or <audio> element, this will be automatically set to true.
|
||||
*/
|
||||
autoplay?: boolean;
|
||||
|
||||
/**
|
||||
* Only allow one player playing at once.
|
||||
*/
|
||||
autopause?: boolean;
|
||||
|
||||
/**
|
||||
* The time, in seconds, to seek when a user hits fast forward or rewind.
|
||||
*/
|
||||
seekTime?: number;
|
||||
|
||||
/**
|
||||
* A number, between 0 and 1, representing the initial volume of the player.
|
||||
*/
|
||||
volume?: number;
|
||||
|
||||
/**
|
||||
* Whether to start playback muted. If the muted attribute is present on a <video> or <audio> element, this will be automatically set to true.
|
||||
*/
|
||||
muted?: boolean;
|
||||
|
||||
/**
|
||||
* Click (or tap) of the video container will toggle play/pause.
|
||||
*/
|
||||
clickToPlay?: boolean;
|
||||
|
||||
/**
|
||||
* Disable right click menu on video to help as very primitive obfuscation to prevent downloads of content.
|
||||
*/
|
||||
disableContextMenu?: boolean;
|
||||
|
||||
/**
|
||||
* Hide video controls automatically after 2s of no mouse or focus movement, on control element blur (tab out), on playback start or entering fullscreen.
|
||||
* As soon as the mouse is moved, a control element is focused or playback is paused, the controls reappear instantly.
|
||||
*/
|
||||
hideControls?: boolean;
|
||||
|
||||
/**
|
||||
* Reset the playback to the start once playback is complete.
|
||||
*/
|
||||
resetOnEnd?: boolean;
|
||||
|
||||
/**
|
||||
* Enable keyboard shortcuts for focused players only or globally
|
||||
*/
|
||||
keyboard?: KeyboardOptions;
|
||||
|
||||
/**
|
||||
* controls: Display control labels as tooltips on :hover & :focus (by default, the labels are screen reader only).
|
||||
* seek: Display a seek tooltip to indicate on click where the media would seek to.
|
||||
*/
|
||||
tooltips?: TooltipOptions;
|
||||
|
||||
/**
|
||||
* Specify a custom duration for media.
|
||||
*/
|
||||
duration?: number;
|
||||
|
||||
/**
|
||||
* Displays the duration of the media on the metadataloaded event (on startup) in the current time display.
|
||||
* This will only work if the preload attribute is not set to none (or is not set at all) and you choose not to display the duration (see controls option).
|
||||
*/
|
||||
displayDuration?: boolean;
|
||||
|
||||
/**
|
||||
* Display the current time as a countdown rather than an incremental counter.
|
||||
*/
|
||||
invertTime?: boolean;
|
||||
|
||||
/**
|
||||
* Allow users to click to toggle the above.
|
||||
*/
|
||||
toggleInvert?: boolean;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
listeners?: {[key: string]: (error: PlyrEvent) => void};
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
captions?: CaptionOptions;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
fullscreen?: FullScreenOptions;
|
||||
|
||||
/**
|
||||
* The aspect ratio you want to use for embedded players.
|
||||
*/
|
||||
ratio?: string;
|
||||
|
||||
/**
|
||||
* enabled: Allow use of local storage to store user settings. key: The key name to use.
|
||||
*/
|
||||
storage?: StorageOptions;
|
||||
|
||||
/**
|
||||
* selected: The default speed for playback. options: Options to display in the menu. Most browsers will refuse to play slower than 0.5.
|
||||
*/
|
||||
speed?: SpeedOptions;
|
||||
|
||||
/**
|
||||
* Currently only supported by YouTube. default is the default quality level, determined by YouTube. options are the options to display.
|
||||
*/
|
||||
quality?: QualityOptions;
|
||||
|
||||
/**
|
||||
* active: Whether to loop the current video. If the loop attribute is present on a <video> or <audio> element,
|
||||
* this will be automatically set to true This is an object to support future functionality.
|
||||
*/
|
||||
loop?: LoopOptions;
|
||||
|
||||
/**
|
||||
* enabled: Whether to enable vi.ai ads. publisherId: Your unique vi.ai publisher ID.
|
||||
*/
|
||||
ads?: AdOptions;
|
||||
}
|
||||
|
||||
interface QualityOptions {
|
||||
default: string;
|
||||
options: string[];
|
||||
}
|
||||
|
||||
interface LoopOptions {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
interface AdOptions {
|
||||
enabled: boolean;
|
||||
publisherId: string;
|
||||
}
|
||||
|
||||
interface SpeedOptions {
|
||||
selected: number;
|
||||
options: number[];
|
||||
}
|
||||
|
||||
interface KeyboardOptions {
|
||||
focused?: boolean;
|
||||
global?: boolean;
|
||||
}
|
||||
|
||||
interface TooltipOptions {
|
||||
controls?: boolean;
|
||||
seek?: boolean;
|
||||
}
|
||||
|
||||
interface FullScreenOptions {
|
||||
enabled?: boolean;
|
||||
fallback?: boolean;
|
||||
allowAudio?: boolean;
|
||||
}
|
||||
|
||||
interface CaptionOptions {
|
||||
active?: boolean;
|
||||
language?: string;
|
||||
update?: boolean;
|
||||
}
|
||||
|
||||
interface StorageOptions {
|
||||
enabled?: boolean;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
interface SourceInfo {
|
||||
/**
|
||||
* Note: YouTube and Vimeo are currently not supported as audio sources.
|
||||
*/
|
||||
type: MediaType;
|
||||
|
||||
/**
|
||||
* Title of the new media. Used for the aria-label attribute on the play button, and outer container. YouTube and Vimeo are populated automatically.
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* This is an array of sources. For HTML5 media, the properties of this object are mapped directly to HTML attributes so more can be added to the object if required.
|
||||
*/
|
||||
sources: Source[];
|
||||
|
||||
/**
|
||||
* The URL for the poster image (HTML5 video only).
|
||||
*/
|
||||
poster?: string;
|
||||
|
||||
/**
|
||||
* An array of track objects. Each element in the array is mapped directly to a track element and any keys mapped directly to HTML attributes so as in the example above,
|
||||
* it will render as <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default> and similar for the French version.
|
||||
* Booleans are converted to HTML5 value-less attributes.
|
||||
*/
|
||||
tracks?: Track[];
|
||||
}
|
||||
|
||||
interface Source {
|
||||
/**
|
||||
* The URL of the media file (or YouTube/Vimeo URL).
|
||||
*/
|
||||
src: string;
|
||||
/**
|
||||
* The MIME type of the media file (if HTML5).
|
||||
*/
|
||||
type?: string;
|
||||
provider?: Provider;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
type TrackKind = "subtitles" | "captions" | "descriptions" | "chapters" | "metadata";
|
||||
interface Track {
|
||||
/**
|
||||
* Indicates how the text track is meant to be used
|
||||
*/
|
||||
kind: TrackKind;
|
||||
/**
|
||||
* Indicates a user-readable title for the track
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The language of the track text data. It must be a valid BCP 47 language tag. If the kind attribute is set to subtitles, then srclang must be defined.
|
||||
*/
|
||||
srcLang?: string;
|
||||
/**
|
||||
* The URL of the track (.vtt file).
|
||||
*/
|
||||
src: string;
|
||||
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
interface PlyrEvent extends CustomEvent {
|
||||
readonly detail: { readonly plyr: Plyr; };
|
||||
}
|
||||
|
||||
interface Support {
|
||||
api: boolean;
|
||||
ui: boolean;
|
||||
}
|
||||
}
|
@ -368,10 +368,10 @@ class Plyr {
|
||||
*/
|
||||
pause() {
|
||||
if (!this.playing || !is.function(this.media.pause)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.media.pause();
|
||||
return this.media.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -411,10 +411,10 @@ class Plyr {
|
||||
const toggle = is.boolean(input) ? input : !this.playing;
|
||||
|
||||
if (toggle) {
|
||||
this.play();
|
||||
} else {
|
||||
this.pause();
|
||||
return this.play();
|
||||
}
|
||||
|
||||
return this.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -441,7 +441,7 @@ class Plyr {
|
||||
* @param {Number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
|
||||
*/
|
||||
rewind(seekTime) {
|
||||
this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime -= is.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -449,7 +449,7 @@ class Plyr {
|
||||
* @param {Number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
|
||||
*/
|
||||
forward(seekTime) {
|
||||
this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
this.currentTime += is.number(seekTime) ? seekTime : this.config.seekTime;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -198,7 +198,9 @@ const ui = {
|
||||
// Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
|
||||
backgroundSize: '',
|
||||
});
|
||||
|
||||
ui.togglePoster.call(this, true);
|
||||
|
||||
return poster;
|
||||
})
|
||||
);
|
||||
@ -214,6 +216,7 @@ const ui = {
|
||||
// Set state
|
||||
Array.from(this.elements.buttons.play || []).forEach(target => {
|
||||
Object.assign(target, { pressed: this.playing });
|
||||
target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config));
|
||||
});
|
||||
|
||||
// Only update controls on non timeupdate events
|
||||
|
@ -90,9 +90,7 @@ export function triggerEvent(element, type = '', bubbles = false, detail = {}) {
|
||||
// Create and dispatch the event
|
||||
const event = new CustomEvent(type, {
|
||||
bubbles,
|
||||
detail: Object.assign({}, detail, {
|
||||
plyr: this,
|
||||
}),
|
||||
detail: { ...detail, plyr: this,},
|
||||
});
|
||||
|
||||
// Dispatch the event
|
||||
|
@ -56,11 +56,12 @@ export function setAspectRatio(input) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { wrapper } = this.elements;
|
||||
const ratio = getAspectRatio.call(this, input);
|
||||
const [w, h] = is.array(ratio) ? ratio : [0, 0];
|
||||
const padding = (100 / w) * h;
|
||||
|
||||
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||
wrapper.style.paddingBottom = `${padding}%`;
|
||||
|
||||
// For Vimeo we have an extra <div> to hide the standard controls and UI
|
||||
if (this.isVimeo && this.supported.ui) {
|
||||
@ -68,7 +69,7 @@ export function setAspectRatio(input) {
|
||||
const offset = (height - padding) / (height / 50);
|
||||
this.media.style.transform = `translateY(-${offset}%)`;
|
||||
} else if (this.isHTML5) {
|
||||
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
}
|
||||
|
||||
return { padding, ratio };
|
||||
|
@ -13,7 +13,7 @@ export const getSeconds = value => Math.trunc(value % 60, 10);
|
||||
export function formatTime(time = 0, displayHours = false, inverted = false) {
|
||||
// Bail if the value isn't a number
|
||||
if (!is.number(time)) {
|
||||
return formatTime(null, displayHours, inverted);
|
||||
return formatTime(undefined, displayHours, inverted);
|
||||
}
|
||||
|
||||
// Format time component to add leading zero
|
||||
|
@ -5,24 +5,27 @@
|
||||
// Base
|
||||
.plyr {
|
||||
@include plyr-font-smoothing($plyr-font-smoothing);
|
||||
|
||||
align-items: center;
|
||||
direction: ltr;
|
||||
display: flex;
|
||||
font-family: $plyr-font-family;
|
||||
font-variant-numeric: tabular-nums; // Force monosace-esque number widths
|
||||
font-weight: $plyr-font-weight-regular;
|
||||
height: 100%;
|
||||
line-height: $plyr-line-height;
|
||||
max-width: 100%;
|
||||
min-width: 200px;
|
||||
position: relative;
|
||||
text-shadow: none;
|
||||
transition: box-shadow 0.3s ease;
|
||||
z-index: 0; // Force any border radius
|
||||
|
||||
// Media elements
|
||||
video,
|
||||
audio {
|
||||
border-radius: inherit;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
audio,
|
||||
iframe {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
7
src/sass/components/audio.scss
Normal file
7
src/sass/components/audio.scss
Normal file
@ -0,0 +1,7 @@
|
||||
// --------------------------------------------------------------
|
||||
// Audio styles
|
||||
// --------------------------------------------------------------
|
||||
|
||||
.plyr--audio {
|
||||
display: block;
|
||||
}
|
@ -41,14 +41,6 @@
|
||||
&.plyr__time + .plyr__time {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&.plyr__volume {
|
||||
padding-right: ($plyr-control-spacing / 2);
|
||||
}
|
||||
|
||||
&.plyr__volume:first-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide empty controls
|
||||
|
@ -14,11 +14,10 @@
|
||||
|
||||
.plyr__video-wrapper {
|
||||
background: #000;
|
||||
border-radius: inherit;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
// Require z-index to force border-radius
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||
@ -33,12 +32,9 @@ $embed-padding: ((100 / 16) * 9);
|
||||
.plyr__video-embed iframe,
|
||||
.plyr__video-wrapper--fixed-ratio video {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// If the full custom UI is supported
|
||||
|
@ -5,11 +5,11 @@
|
||||
.plyr__volume {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
input[type='range'] {
|
||||
margin-left: ($plyr-control-spacing / 2);
|
||||
margin-right: ($plyr-control-spacing / 2);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
@ -22,16 +22,3 @@
|
||||
max-width: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide sound controls on iOS
|
||||
// It's not supported to change volume using JavaScript:
|
||||
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
|
||||
.plyr--is-ios .plyr__volume {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Vimeo has no toggle mute method so hide mute button
|
||||
// https://github.com/vimeo/player.js/issues/236#issuecomment-384663183
|
||||
.plyr--is-ios.plyr--vimeo [data-plyr='mute'] {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
@import 'base';
|
||||
|
||||
@import 'components/audio';
|
||||
@import 'components/badges';
|
||||
@import 'components/captions';
|
||||
@import 'components/control';
|
||||
|
@ -24,8 +24,8 @@
|
||||
// Fallback for unsupported browsers
|
||||
.plyr--fullscreen-fallback {
|
||||
@include plyr-fullscreen-active();
|
||||
|
||||
bottom: 0;
|
||||
display: block;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user