Compare commits

..

38 Commits

Author SHA1 Message Date
20bf5a8833 chore(release): 3.7.3 2022-11-17 17:36:00 +11:00
de0402bebf fix: simplify logic for isFunction assertion method 2022-11-17 12:23:41 +11:00
db1b89b1c1 chore: update types to include string for controls 2022-11-17 12:23:41 +11:00
ea3675fcdc fix: revert pip change for iphone and add comment 2022-11-17 12:23:41 +11:00
e8beabd6a8 chore: upgrade packages 2022-11-17 12:23:41 +11:00
433acd6f41 chore: use .node-version instead of .nvmrc 2022-11-17 12:23:41 +11:00
69ced1313a fix: force nowrap in progress tooltips 2022-11-17 12:23:41 +11:00
511a7db7c2 chore(deps): bump minimatch from 3.0.4 to 3.1.2 (#2573)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-17 12:22:30 +11:00
6a62e3d085 i18n: Make captions autodetect text direction (#2540)
This uses browser mechanism for detecting appropiate direction for text, dir=auto.
2022-11-17 12:22:10 +11:00
aba00d0ab6 plyr.io used on Thousands of Index Websites (#2516) 2022-11-17 12:21:27 +11:00
c115dfc6a6 fixed menu border radius bug (#2548) 2022-11-17 12:13:40 +11:00
6030b300f6 fix iPhone no PIP bug (#2533) 2022-11-17 11:57:04 +11:00
fff26351c9 chore(deps): bump socket.io-parser from 4.0.4 to 4.0.5 (#2564)
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.0.4...4.0.5)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-17 11:52:32 +11:00
e0fb524382 navigator.platform is deprecated (#2530)
I'm submitting a little update for browser detection:

1. `navigator.platform` is deprecated - it's prefered to use `navigator.userAgent` https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
2. No need for the brackets in the RegEx - you can test this on https://regex101.com

And that's about it. Maybe I missed the point, do let me know! ❤️
2022-09-15 15:08:03 +10:00
ebda039395 Added configurable property to elements for re-use (#2489) 2022-09-09 22:52:01 +10:00
ed456197f5 chore(deps): bump terser from 5.10.0 to 5.14.2 (#2506)
Bumps [terser](https://github.com/terser/terser) from 5.10.0 to 5.14.2.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-09 22:49:05 +10:00
66187c348a docs: replace example video ID with one that still works (#2518) 2022-09-09 22:47:42 +10:00
cacaef7def Improve accessibility on control buttons with aria-pressed (#2523) 2022-09-09 22:46:24 +10:00
42b8f7bdab Fix for calc() in newer Dart Sass versions (#2519) 2022-09-09 22:45:16 +10:00
c90f044dac v3.7.2
- Fix: Add `@babel/plugin-proposal-optional-chaining` to transform optional chaining in build output
2022-04-20 20:15:42 +10:00
5f2cb90bc6 fix: add babel plugin to transform optional chaining 2022-04-20 20:12:08 +10:00
537ad2fe7d v3.7.1
- Feat: Minor styling improvements to the preview thumbnails (🚨 Requires a SCSS/CSS update 🚨)
- Fix: fix invalid CSS @charset rule in Sass files (thanks @Hashen110!)
- Chore: Replace deprecated KeyboardEvent `keyCode` references to use `key` instead (thanks @Hashen110!)
- Various other code clean up and typo fixes (thanks @Hashen110!)
2022-04-20 15:02:17 +10:00
e7621bec0f chore: syntax tweak 2022-04-20 14:52:39 +10:00
e3acba5859 chore: add words to custom cspell dictionary 2022-04-20 14:50:59 +10:00
d7086074e8 chore: disable ads in demo site 2022-04-20 14:50:29 +10:00
04e6c43da7 chore: fix typo in comment 2022-04-20 14:50:14 +10:00
73bfb5211b feat: minor tweaks to the preview thumbs UI 2022-04-20 14:31:42 +10:00
4a78b656da fix JSDoc comments (#2468) 2022-04-19 22:00:48 +10:00
776fd099f3 fix invalid CSS @charset rule (#2466) 2022-04-19 22:00:20 +10:00
bdcd98f4a7 remove redundant local variable (#2467) 2022-04-19 21:59:29 +10:00
01417f958a Add website to showcase in README.md (#2339)
* Add website to showcase in README.md

* Move showcase entry to bottom of list
2022-04-19 00:59:27 +10:00
412d5df06e fix: use consistent border-radius variable 2022-04-18 22:34:03 +10:00
3e35461e04 chore: use key property of KeyboardEvent 2022-04-18 22:34:03 +10:00
1edec28d86 remove deprecated keyCode (#2463) 2022-04-18 22:23:22 +10:00
99a7e9e1d7 fix typo (#2464) 2022-04-18 22:16:06 +10:00
24eef153cb remove deprecated keyCode (#2461) 2022-04-18 21:47:48 +10:00
3f09cf566b fix typo in a comment (#2462)
* fix typo in a comment

* fix typo in a comment
2022-04-18 21:42:52 +10:00
29318591c0 add markers types (#2460) 2022-04-18 21:24:35 +10:00
33 changed files with 1902 additions and 1598 deletions

1
.node-version Normal file
View File

@ -0,0 +1 @@
16.18.1

1
.nvmrc
View File

@ -1 +0,0 @@
16

View File

@ -1,3 +1,29 @@
### v3.7.3
- Fix: force nowrap in progress tooltips (related: #2549) (thanks @raad-altaie!)
- Feat(i18n): Make captions autodetect text direction (#2540) (thanks @ebraminio!)
- Fix: fixed menu border radius bug (#2548) (thanks @raad-altaie!)
- Chore: navigator.platform is deprecated (#2530) (thanks @stamat!)
- Feat: Added configurable property to elements for re-use (#2489) (thanks @NoirHusky!)
- Docs: Replace example video ID with one that still works (#2518) (thanks @luvejo!)
- Fix: Improve accessibility on control buttons with aria-pressed (#2523) (thanks @emilkarl!)
- Fix: Fix for calc() in newer Dart Sass versions (#2519) (thanks @ckhicks!)
- Fix: simplify logic for isFunction assertion method
- Chore: update types to include string for controls
- Chore: upgrade packages
- Chore: use `.node-version` instead of `.nvmrc`
### v3.7.2
- Fix: Add `@babel/plugin-proposal-optional-chaining` to transform optional chaining in build output
### v3.7.1
- Feat: Minor styling improvements to the preview thumbnails (🚨 Requires a SCSS/CSS update 🚨)
- Fix: Fix invalid CSS @charset rule in Sass files (thanks @Hashen110!)
- Chore: Replace deprecated KeyboardEvent `keyCode` references to use `key` instead (thanks @Hashen110!)
- Various other code clean up and typo fixes (thanks @Hashen110!)
## v3.7.0 ## v3.7.0
- Feat: Add markers support (🚨 Requires a SCSS/CSS update 🚨) (thanks @ForeverSc and @fengshuo!) - Feat: Add markers support (🚨 Requires a SCSS/CSS update 🚨) (thanks @ForeverSc and @fengshuo!)

View File

@ -137,13 +137,13 @@ See [initialising](#initialising) for more information on advanced setups.
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills separately as part of your application but to make life easier you can use the polyfilled build. You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills separately as part of your application but to make life easier you can use the polyfilled build.
```html ```html
<script src="https://cdn.plyr.io/3.7.0/plyr.js"></script> <script src="https://cdn.plyr.io/3.7.3/plyr.js"></script>
``` ```
...or... ...or...
```html ```html
<script src="https://cdn.plyr.io/3.7.0/plyr.polyfilled.js"></script> <script src="https://cdn.plyr.io/3.7.3/plyr.polyfilled.js"></script>
``` ```
## CSS ## CSS
@ -157,13 +157,13 @@ Include the `plyr.css` stylesheet into your `<head>`.
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following: If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html ```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.0/plyr.css" /> <link rel="stylesheet" href="https://cdn.plyr.io/3.7.3/plyr.css" />
``` ```
## SVG Sprite ## SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.7.0/plyr.svg`. reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.7.3/plyr.svg`.
# Ads # Ads
@ -613,7 +613,7 @@ player.source = {
type: 'video', type: 'video',
sources: [ sources: [
{ {
src: '143418951', src: '76979871',
provider: 'vimeo', provider: 'vimeo',
}, },
], ],
@ -845,6 +845,8 @@ Plyr costs money to run, not only my time. I donate my time for free as I enjoy
- [pressakey.com | Blog-Magazin für Videospiele](https://pressakey.com) - [pressakey.com | Blog-Magazin für Videospiele](https://pressakey.com)
- [STROLLÿN: Work with a View](https://strollyn.com) - [STROLLÿN: Work with a View](https://strollyn.com)
- [CFDA Runway360](https://runway360.cfda.com/) - [CFDA Runway360](https://runway360.cfda.com/)
- [NKLAV | Filmmaker](https://nklav.com)
- [GDI.JS.ORG - Google Drive Index](https://gitlab.com/GoogleDriveIndex/Google-Drive-Index)
If you want to be added to the list, open a pull request. It'd be awesome to see how you're using Plyr 😎 If you want to be added to the list, open a pull request. It'd be awesome to see how you're using Plyr 😎

View File

@ -1,6 +1,10 @@
{ {
"version": "0.2", "version": "0.2",
"ignorePaths": ["package.json", "dist/*", "demo/node_modules/*"], "ignorePaths": [
"package.json",
"dist/*",
"demo/node_modules/*"
],
"dictionaryDefinitions": [], "dictionaryDefinitions": [],
"dictionaries": [], "dictionaries": [],
"words": [ "words": [
@ -13,9 +17,11 @@
"fastly", "fastly",
"fullscreen", "fullscreen",
"gordita", "gordita",
"loadjs",
"magazin", "magazin",
"menuitemradio", "menuitemradio",
"noupe", "noupe",
"otransitionend",
"playsinline", "playsinline",
"plyr", "plyr",
"rutheneum", "rutheneum",

View File

@ -17,9 +17,10 @@ import toggleClass from './toggle-class';
(() => { (() => {
const production = 'plyr.io'; const production = 'plyr.io';
const isProduction = window.location.host.includes(production);
// Sentry for demo site (https://plyr.io) only // Sentry for demo site (https://plyr.io) only
if (window.location.host === production) { if (isProduction) {
Sentry.init({ Sentry.init({
dsn: 'https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555', dsn: 'https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555',
whitelistUrls: [production].map((d) => new RegExp(`https://(([a-z0-9])+(.))*${d}`)), whitelistUrls: [production].map((d) => new RegExp(`https://(([a-z0-9])+(.))*${d}`)),
@ -53,10 +54,10 @@ import toggleClass from './toggle-class';
captions: { captions: {
active: true, active: true,
}, },
ads: { /* ads: {
enabled: window.location.host.includes(production), enabled: isProduction,
publisherId: '918848828995742', publisherId: '918848828995742',
}, }, */
previewThumbnails: { previewThumbnails: {
enabled: true, enabled: true,
src: ['https://cdn.plyr.io/static/demo/thumbs/100p.vtt', 'https://cdn.plyr.io/static/demo/thumbs/240p.vtt'], src: ['https://cdn.plyr.io/static/demo/thumbs/100p.vtt', 'https://cdn.plyr.io/static/demo/thumbs/240p.vtt'],

View File

@ -13,7 +13,7 @@ document.addEventListener('focusout', (event) => {
// Add classname to tabbed elements // Add classname to tabbed elements
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if (event.keyCode !== 9) { if (event.key !== 'Tab') {
return; return;
} }

View File

@ -1,7 +1,9 @@
@charset "UTF-8";
// ========================================================================== // ==========================================================================
// Plyr.io Demo Page // Plyr.io Demo Page
// ========================================================================== // ==========================================================================
@charset 'UTF-8';
@import '../../../../src/sass/lib/css-vars'; @import '../../../../src/sass/lib/css-vars';
$css-vars-use-native: true; $css-vars-use-native: true;

View File

@ -1,7 +1,8 @@
@charset "UTF-8";
// ========================================================================== // ==========================================================================
// Plyr.io Error Page // Plyr.io Error Page
// ========================================================================== // ==========================================================================
@charset 'UTF-8';
// Settings // Settings
@import '../settings/colors'; @import '../settings/colors';

View File

@ -1,6 +1,6 @@
{ {
"name": "plyr", "name": "plyr",
"version": "3.7.0", "version": "3.7.3",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player", "description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io", "homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>", "author": "Sam Potts <sam@potts.es>",
@ -40,18 +40,19 @@
"start": "gulp" "start": "gulp"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.17.9", "@babel/core": "^7.20.2",
"@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.16.11", "@babel/plugin-proposal-optional-chaining": "^7.18.9",
"@babel/preset-env": "^7.20.2",
"@sampotts/eslint-config": "1.1.7", "@sampotts/eslint-config": "1.1.7",
"autoprefixer": "^10.4.4", "autoprefixer": "^10.4.13",
"aws-sdk": "^2.1116.0", "aws-sdk": "^2.1256.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"browser-sync": "^2.27.9", "browser-sync": "^2.27.10",
"colorette": "2.0.16", "colorette": "2.0.19",
"cspell": "^5.19.7", "cspell": "^6.14.2",
"cssnano": "^5.1.7", "cssnano": "^5.1.14",
"del": "^6.0.0", "del": "^6.1.1",
"eslint": "^7.23.0", "eslint": "^7.23.0",
"fancy-log": "^2.0.0", "fancy-log": "^2.0.0",
"git-branch": "^2.0.1", "git-branch": "^2.0.1",
@ -73,25 +74,25 @@
"gulp-sourcemaps": "^3.0.0", "gulp-sourcemaps": "^3.0.0",
"gulp-svgstore": "^9.0.0", "gulp-svgstore": "^9.0.0",
"gulp-terser": "^2.1.0", "gulp-terser": "^2.1.0",
"postcss": "^8.4.12", "postcss": "^8.4.19",
"postcss-custom-properties": "^12.1.7", "postcss-custom-properties": "^12.1.9",
"postcss-scss": "^4.0.3", "postcss-scss": "^4.0.5",
"prettier-eslint": "^12.0.0", "prettier-eslint": "^12.0.0",
"prettier-stylelint": "^0.4.2", "prettier-stylelint": "^0.4.2",
"remark-cli": "^10.0.1", "remark-cli": "^11.0.0",
"remark-validate-links": "^11.0.2", "remark-validate-links": "^12.1.0",
"rollup": "^2.70.2", "rollup": "^3.3.0",
"rollup-plugin-babel": "^4.4.0", "rollup-plugin-babel": "^4.4.0",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"sass": "^1.50.0", "sass": "^1.56.1",
"stylelint": "^14.7.1", "stylelint": "^14.15.0",
"stylelint-config-prettier": "^9.0.3", "stylelint-config-prettier": "^9.0.4",
"stylelint-config-sass-guidelines": "^9.0.1", "stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-selector-bem-pattern": "^2.1.1" "stylelint-selector-bem-pattern": "^2.1.1"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.22.0", "core-js": "^3.26.1",
"custom-event-polyfill": "^1.0.7", "custom-event-polyfill": "^1.0.7",
"loadjs": "^4.2.0", "loadjs": "^4.2.0",
"rangetouch": "^2.0.1", "rangetouch": "^2.0.1",

View File

@ -47,6 +47,7 @@ const captions = {
// Inject the container // Inject the container
if (!is.element(this.elements.captions)) { if (!is.element(this.elements.captions)) {
this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions)); this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions));
this.elements.captions.setAttribute('dir', 'auto');
insertAfter(this.elements.captions, this.elements.wrapper); insertAfter(this.elements.captions, this.elements.wrapper);
} }

View File

@ -61,7 +61,7 @@ const defaults = {
// Sprite (for icons) // Sprite (for icons)
loadSprite: true, loadSprite: true,
iconPrefix: 'plyr', iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.7.0/plyr.svg', iconUrl: 'https://cdn.plyr.io/3.7.3/plyr.svg',
// Blank video (used to prevent errors on source change) // Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4', blankVideo: 'https://cdn.plyr.io/static/blank.mp4',

25
src/js/controls.js vendored
View File

@ -404,7 +404,7 @@ const controls = {
'keydown keyup', 'keydown keyup',
(event) => { (event) => {
// We only care about space and ⬆️ ⬇️️ ➡️ // We only care about space and ⬆️ ⬇️️ ➡️
if (![32, 38, 39, 40].includes(event.which)) { if (!['Space', 'ArrowUp', 'ArrowDown', 'ArrowRight'].includes(event.key)) {
return; return;
} }
@ -420,13 +420,13 @@ const controls = {
const isRadioButton = matches(menuItem, '[role="menuitemradio"]'); const isRadioButton = matches(menuItem, '[role="menuitemradio"]');
// Show the respective menu // Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) { if (!isRadioButton && ['Space', 'ArrowRight'].includes(event.key)) {
controls.showMenuPanel.call(this, type, true); controls.showMenuPanel.call(this, type, true);
} else { } else {
let target; let target;
if (event.which !== 32) { if (event.key !== 'Space') {
if (event.which === 40 || (isRadioButton && event.which === 39)) { if (event.key === 'ArrowDown' || (isRadioButton && event.key === 'ArrowRight')) {
target = menuItem.nextElementSibling; target = menuItem.nextElementSibling;
if (!is.element(target)) { if (!is.element(target)) {
@ -450,9 +450,7 @@ const controls = {
// Enter will fire a `click` event but we still need to manage focus // 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 // So we bind to keyup which fires after and set focus here
on.call(this, menuItem, 'keyup', (event) => { on.call(this, menuItem, 'keyup', (event) => {
if (event.which !== 13) { if (event.key !== 'Return') return;
return;
}
controls.focusFirstMenuItem.call(this, null, true); controls.focusFirstMenuItem.call(this, null, true);
}); });
@ -506,7 +504,7 @@ const controls = {
menuItem, menuItem,
'click keyup', 'click keyup',
(event) => { (event) => {
if (is.keyboardEvent(event) && event.which !== 32) { if (is.keyboardEvent(event) && event.key !== 'Space') {
return; return;
} }
@ -1139,7 +1137,7 @@ const controls = {
if (is.boolean(input)) { if (is.boolean(input)) {
show = input; show = input;
} else if (is.keyboardEvent(input) && input.which === 27) { } else if (is.keyboardEvent(input) && input.key === 'Escape') {
show = false; show = false;
} else if (is.event(input)) { } else if (is.event(input)) {
// If Plyr is in a shadowDOM, the event target is set to the component, instead of the // If Plyr is in a shadowDOM, the event target is set to the component, instead of the
@ -1527,10 +1525,7 @@ const controls = {
pane, pane,
'keydown', 'keydown',
(event) => { (event) => {
// We only care about <- if (event.key !== 'ArrowLeft') return;
if (event.which !== 37) {
return;
}
// Prevent seek // Prevent seek
event.preventDefault(); event.preventDefault();
@ -1720,13 +1715,17 @@ const controls = {
if (!is.empty(this.elements.buttons)) { if (!is.empty(this.elements.buttons)) {
const addProperty = (button) => { const addProperty = (button) => {
const className = this.config.classNames.controlPressed; const className = this.config.classNames.controlPressed;
button.setAttribute('aria-pressed', 'false');
Object.defineProperty(button, 'pressed', { Object.defineProperty(button, 'pressed', {
configurable: true,
enumerable: true, enumerable: true,
get() { get() {
return hasClass(button, className); return hasClass(button, className);
}, },
set(pressed = false) { set(pressed = false) {
toggleClass(button, className, pressed); toggleClass(button, className, pressed);
button.setAttribute('aria-pressed', pressed ? 'true' : 'false');
}, },
}); });
}; };

View File

@ -207,7 +207,7 @@ class Fullscreen {
// Trap focus inside container // Trap focus inside container
trapFocus = (event) => { trapFocus = (event) => {
// Bail if iOS, not active, not the tab key // Bail if iOS, not active, not the tab key
if (browser.isIos || !this.active || event.key !== 'Tab' || event.keyCode !== 9) { if (browser.isIos || !this.active || event.key !== 'Tab') {
return; return;
} }

View File

@ -29,25 +29,25 @@ class Listeners {
handleKey(event) { handleKey(event) {
const { player } = this; const { player } = this;
const { elements } = player; const { elements } = player;
const code = event.keyCode ? event.keyCode : event.which; const { key, type, altKey, ctrlKey, metaKey, shiftKey } = event;
const pressed = event.type === 'keydown'; const pressed = type === 'keydown';
const repeat = pressed && code === this.lastKey; const repeat = pressed && key === this.lastKey;
// Bail if a modifier key is set // Bail if a modifier key is set
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { if (altKey || ctrlKey || metaKey || shiftKey) {
return; return;
} }
// If the event is bubbled from the media element // If the event is bubbled from the media element
// Firefox doesn't get the keycode for whatever reason // Firefox doesn't get the key for whatever reason
if (!is.number(code)) { if (!key) {
return; return;
} }
// Seek by the number keys // Seek by increment
const seekByKey = () => { const seekByIncrement = (increment) => {
// Divide the max duration into 10th's and times by the number value // Divide the max duration into 10th's and times by the number value
player.currentTime = (player.duration / 10) * (code - 48); player.currentTime = (player.duration / 10) * increment;
}; };
// Handle the key on keydown // Handle the key on keydown
@ -65,113 +65,114 @@ class Listeners {
return; return;
} }
if (event.which === 32 && matches(focused, 'button, [role^="menuitem"]')) { if (event.key === 'Space' && matches(focused, 'button, [role^="menuitem"]')) {
return; return;
} }
} }
// Which keycodes should we prevent default // Which keys should we prevent default
const preventDefault = [32, 37, 38, 39, 40, 48, 49, 50, 51, 52, 53, 54, 56, 57, 67, 70, 73, 75, 76, 77, 79]; const preventDefault = [
'Space',
'ArrowLeft',
'ArrowUp',
'ArrowRight',
'ArrowDown',
// If the code is found prevent default (e.g. prevent scrolling for arrows) '0',
if (preventDefault.includes(code)) { '1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'c',
'f',
'k',
'l',
'm',
];
// If the key is found prevent default (e.g. prevent scrolling for arrows)
if (preventDefault.includes(key)) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }
switch (code) { switch (key) {
case 48: case '0':
case 49: case '1':
case 50: case '2':
case 51: case '3':
case 52: case '4':
case 53: case '5':
case 54: case '6':
case 55: case '7':
case 56: case '8':
case 57: case '9':
// 0-9
if (!repeat) { if (!repeat) {
seekByKey(); seekByIncrement(parseInt(key, 10));
} }
break; break;
case 32: case 'Space':
case 75: case 'k':
// Space and K key
if (!repeat) { if (!repeat) {
silencePromise(player.togglePlay()); silencePromise(player.togglePlay());
} }
break; break;
case 38: case 'ArrowUp':
// Arrow up
player.increaseVolume(0.1); player.increaseVolume(0.1);
break; break;
case 40: case 'ArrowDown':
// Arrow down
player.decreaseVolume(0.1); player.decreaseVolume(0.1);
break; break;
case 77: case 'm':
// M key
if (!repeat) { if (!repeat) {
player.muted = !player.muted; player.muted = !player.muted;
} }
break; break;
case 39: case 'ArrowRight':
// Arrow forward
player.forward(); player.forward();
break; break;
case 37: case 'ArrowLeft':
// Arrow back
player.rewind(); player.rewind();
break; break;
case 70: case 'f':
// F key
player.fullscreen.toggle(); player.fullscreen.toggle();
break; break;
case 67: case 'c':
// C key
if (!repeat) { if (!repeat) {
player.toggleCaptions(); player.toggleCaptions();
} }
break; break;
case 76: case 'l':
// L key
player.loop = !player.loop; player.loop = !player.loop;
break; break;
/* case 73:
this.setLoop('start');
break;
case 76:
this.setLoop();
break;
case 79:
this.setLoop('end');
break; */
default: default:
break; break;
} }
// Escape is handle natively when in full screen // Escape is handle natively when in full screen
// So we only need to worry about non native // So we only need to worry about non native
if (code === 27 && !player.fullscreen.usingNative && player.fullscreen.active) { if (key === 'Escape' && !player.fullscreen.usingNative && player.fullscreen.active) {
player.fullscreen.toggle(); player.fullscreen.toggle();
} }
// Store last code for next cycle // Store last key for next cycle
this.lastKey = code; this.lastKey = key;
} else { } else {
this.lastKey = null; this.lastKey = null;
} }
@ -196,17 +197,18 @@ class Listeners {
setTabFocus = (event) => { setTabFocus = (event) => {
const { player } = this; const { player } = this;
const { elements } = player; const { elements } = player;
const { key, type, timeStamp } = event;
clearTimeout(this.focusTimer); clearTimeout(this.focusTimer);
// Ignore any key other than tab // Ignore any key other than tab
if (event.type === 'keydown' && event.which !== 9) { if (type === 'keydown' && key !== 'Tab') {
return; return;
} }
// Store reference to event timeStamp // Store reference to event timeStamp
if (event.type === 'keydown') { if (type === 'keydown') {
this.lastKeyDown = event.timeStamp; this.lastKeyDown = timeStamp;
} }
// Remove current classes // Remove current classes
@ -217,10 +219,10 @@ class Listeners {
}; };
// Determine if a key was pressed to trigger this event // Determine if a key was pressed to trigger this event
const wasKeyDown = event.timeStamp - this.lastKeyDown <= 20; const wasKeyDown = timeStamp - this.lastKeyDown <= 20;
// Ignore focus events if a key was pressed prior // Ignore focus events if a key was pressed prior
if (event.type === 'focus' && !wasKeyDown) { if (type === 'focus' && !wasKeyDown) {
return; return;
} }
@ -229,7 +231,7 @@ class Listeners {
// Delay the adding of classname until the focus has changed // Delay the adding of classname until the focus has changed
// This event fires before the focusin event // This event fires before the focusin event
if (event.type !== 'focusout') { if (type !== 'focusout') {
this.focusTimer = setTimeout(() => { this.focusTimer = setTimeout(() => {
const focused = document.activeElement; const focused = document.activeElement;
@ -661,15 +663,12 @@ class Listeners {
elements.buttons.settings, elements.buttons.settings,
'keyup', 'keyup',
(event) => { (event) => {
const code = event.which; if (!['Space', 'Enter'].includes(event.key)) {
// We only care about space and return
if (![13, 32].includes(code)) {
return; return;
} }
// Because return triggers a click anyway, all we need to do is set focus // Because return triggers a click anyway, all we need to do is set focus
if (code === 13) { if (event.key === 'Enter') {
controls.focusFirstMenuItem.call(player, null, true); controls.focusFirstMenuItem.call(player, null, true);
return; return;
} }
@ -689,7 +688,7 @@ class Listeners {
// Escape closes menu // Escape closes menu
this.bind(elements.settings.menu, 'keydown', (event) => { this.bind(elements.settings.menu, 'keydown', (event) => {
if (event.which === 27) { if (event.key === 'Escape') {
controls.toggleMenu.call(player, event); controls.toggleMenu.call(player, event);
} }
}); });
@ -704,10 +703,9 @@ class Listeners {
// Pause while seeking // Pause while seeking
this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', (event) => { this.bind(elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', (event) => {
const seek = event.currentTarget; const seek = event.currentTarget;
const code = event.keyCode ? event.keyCode : event.which;
const attribute = 'play-on-seeked'; const attribute = 'play-on-seeked';
if (is.keyboardEvent(event) && code !== 39 && code !== 37) { if (is.keyboardEvent(event) && !['ArrowLeft', 'ArrowRight'].includes(event.key)) {
return; return;
} }
@ -883,7 +881,7 @@ class Listeners {
elements.inputs.volume, elements.inputs.volume,
'wheel', 'wheel',
(event) => { (event) => {
// Detect "natural" scroll - suppored on OS X Safari only // Detect "natural" scroll - supported on OS X Safari only
// Other browsers on OS X will be inverted until support improves // Other browsers on OS X will be inverted until support improves
const inverted = event.webkitDirectionInvertedFromDevice; const inverted = event.webkitDirectionInvertedFromDevice;
// Get delta from event. Invert if `inverted` is true // Get delta from event. Invert if `inverted` is true

View File

@ -240,7 +240,7 @@ class Ads {
/** /**
* This method is called whenever the ads are ready inside the AdDisplayContainer * This method is called whenever the ads are ready inside the AdDisplayContainer
* @param {Event} adsManagerLoadedEvent * @param {Event} event - adsManagerLoadedEvent
*/ */
onAdsManagerLoaded = (event) => { onAdsManagerLoaded = (event) => {
// Load could occur after a source change (race condition) // Load could occur after a source change (race condition)
@ -581,6 +581,7 @@ class Ads {
/** /**
* Handles callbacks after an ad event was invoked * Handles callbacks after an ad event was invoked
* @param {String} event - Event type * @param {String} event - Event type
* @param args
*/ */
trigger = (event, ...args) => { trigger = (event, ...args) => {
const handlers = this.events[event]; const handlers = this.events[event];

View File

@ -2,6 +2,7 @@ import { createElement } from '../utils/elements';
import { once } from '../utils/events'; import { once } from '../utils/events';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import is from '../utils/is'; import is from '../utils/is';
import { clamp } from '../utils/numbers';
import { formatTime } from '../utils/time'; import { formatTime } from '../utils/time';
// Arg: vttDataString example: "WEBVTT\n\n1\n00:00:05.000 --> 00:00:10.000\n1080p-00001.jpg" // Arg: vttDataString example: "WEBVTT\n\n1\n00:00:05.000 --> 00:00:10.000\n1080p-00001.jpg"
@ -109,9 +110,7 @@ class PreviewThumbnails {
this.player.elements.display.seekTooltip.hidden = this.enabled; this.player.elements.display.seekTooltip.hidden = this.enabled;
} }
if (!this.enabled) { if (!this.enabled) return;
return;
}
this.getThumbnails().then(() => { this.getThumbnails().then(() => {
if (!this.enabled) { if (!this.enabled) {
@ -205,18 +204,12 @@ class PreviewThumbnails {
}; };
startMove = (event) => { startMove = (event) => {
if (!this.loaded) { if (!this.loaded) return;
return;
}
if (!is.event(event) || !['touchmove', 'mousemove'].includes(event.type)) { if (!is.event(event) || !['touchmove', 'mousemove'].includes(event.type)) return;
return;
}
// Wait until media has a duration // Wait until media has a duration
if (!this.player.media.duration) { if (!this.player.media.duration) return;
return;
}
if (event.type === 'touchmove') { if (event.type === 'touchmove') {
// Calculate seek hover position as approx video seconds // Calculate seek hover position as approx video seconds
@ -391,7 +384,7 @@ class PreviewThumbnails {
} }
}); });
// Only proceed if either thumbnum or thumbfilename has changed // Only proceed if either thumb num or thumbfilename has changed
if (thumbNum !== this.showingThumb) { if (thumbNum !== this.showingThumb) {
this.showingThumb = thumbNum; this.showingThumb = thumbNum;
this.loadImage(qualityIndex); this.loadImage(qualityIndex);
@ -562,11 +555,7 @@ class PreviewThumbnails {
}; };
get currentImageContainer() { get currentImageContainer() {
if (this.mouseDown) { return this.mouseDown ? this.elements.scrubbing.container : this.elements.thumb.imageContainer;
return this.elements.scrubbing.container;
}
return this.elements.thumb.imageContainer;
} }
get usingSprites() { get usingSprites() {
@ -599,11 +588,7 @@ class PreviewThumbnails {
} }
get currentImageElement() { get currentImageElement() {
if (this.mouseDown) { return this.mouseDown ? this.currentScrubbingImageElement : this.currentThumbnailImageElement;
return this.currentScrubbingImageElement;
}
return this.currentThumbnailImageElement;
} }
set currentImageElement(element) { set currentImageElement(element) {
@ -643,46 +628,39 @@ class PreviewThumbnails {
// Set the size to be about a quarter of the size of video. Unless option dynamicSize === false, in which case it needs to be set in CSS // Set the size to be about a quarter of the size of video. Unless option dynamicSize === false, in which case it needs to be set in CSS
setThumbContainerSizeAndPos = () => { setThumbContainerSizeAndPos = () => {
const { imageContainer } = this.elements.thumb;
if (!this.sizeSpecifiedInCSS) { if (!this.sizeSpecifiedInCSS) {
const thumbWidth = Math.floor(this.thumbContainerHeight * this.thumbAspectRatio); const thumbWidth = Math.floor(this.thumbContainerHeight * this.thumbAspectRatio);
this.elements.thumb.imageContainer.style.height = `${this.thumbContainerHeight}px`; imageContainer.style.height = `${this.thumbContainerHeight}px`;
this.elements.thumb.imageContainer.style.width = `${thumbWidth}px`; imageContainer.style.width = `${thumbWidth}px`;
} else if ( } else if (imageContainer.clientHeight > 20 && imageContainer.clientWidth < 20) {
this.elements.thumb.imageContainer.clientHeight > 20 && const thumbWidth = Math.floor(imageContainer.clientHeight * this.thumbAspectRatio);
this.elements.thumb.imageContainer.clientWidth < 20 imageContainer.style.width = `${thumbWidth}px`;
) { } else if (imageContainer.clientHeight < 20 && imageContainer.clientWidth > 20) {
const thumbWidth = Math.floor(this.elements.thumb.imageContainer.clientHeight * this.thumbAspectRatio); const thumbHeight = Math.floor(imageContainer.clientWidth / this.thumbAspectRatio);
this.elements.thumb.imageContainer.style.width = `${thumbWidth}px`; imageContainer.style.height = `${thumbHeight}px`;
} else if (
this.elements.thumb.imageContainer.clientHeight < 20 &&
this.elements.thumb.imageContainer.clientWidth > 20
) {
const thumbHeight = Math.floor(this.elements.thumb.imageContainer.clientWidth / this.thumbAspectRatio);
this.elements.thumb.imageContainer.style.height = `${thumbHeight}px`;
} }
this.setThumbContainerPos(); this.setThumbContainerPos();
}; };
setThumbContainerPos = () => { setThumbContainerPos = () => {
const seekbarRect = this.player.elements.progress.getBoundingClientRect(); const scrubberRect = this.player.elements.progress.getBoundingClientRect();
const plyrRect = this.player.elements.container.getBoundingClientRect(); const containerRect = this.player.elements.container.getBoundingClientRect();
const { container } = this.elements.thumb; const { container } = this.elements.thumb;
// Find the lowest and highest desired left-position, so we don't slide out the side of the video container // Find the lowest and highest desired left-position, so we don't slide out the side of the video container
const minVal = plyrRect.left - seekbarRect.left + 10; const min = containerRect.left - scrubberRect.left + 10;
const maxVal = plyrRect.right - seekbarRect.left - container.clientWidth - 10; const max = containerRect.right - scrubberRect.left - container.clientWidth - 10;
// Set preview container position to: mousepos, minus seekbar.left, minus half of previewContainer.clientWidth // Set preview container position to: mousepos, minus seekbar.left, minus half of previewContainer.clientWidth
let previewPos = this.mousePosX - seekbarRect.left - container.clientWidth / 2; const position = this.mousePosX - scrubberRect.left - container.clientWidth / 2;
const clamped = clamp(position, min, max);
if (previewPos < minVal) { // Move the popover position
previewPos = minVal; container.style.left = `${clamped}px`;
}
if (previewPos > maxVal) { // The arrow can follow the cursor
previewPos = maxVal; container.style.setProperty('--preview-arrow-offset', `${position - clamped}px`);
}
container.style.left = `${previewPos}px`;
}; };
// Can't use 100% width, in case the video is a different aspect ratio to the video container // Can't use 100% width, in case the video is a different aspect ratio to the video container
@ -697,9 +675,7 @@ class PreviewThumbnails {
// Sprites need to be offset to the correct location // Sprites need to be offset to the correct location
setImageSizeAndOffset = (previewImage, frame) => { setImageSizeAndOffset = (previewImage, frame) => {
if (!this.usingSprites) { if (!this.usingSprites) return;
return;
}
// Find difference between height and preview container height // Find difference between height and preview container height
const multiplier = this.thumbContainerHeight / frame.h; const multiplier = this.thumbContainerHeight / frame.h;

27
src/js/plyr.d.ts vendored
View File

@ -212,7 +212,7 @@ declare class Plyr {
airplay(): void; airplay(): void;
/** /**
* Sets the preview thubmnails for the current source. * Sets the preview thumbnails for the current source.
*/ */
setPreviewThumbnails(source: Plyr.PreviewThumbnailsOptions): void; setPreviewThumbnails(source: Plyr.PreviewThumbnailsOptions): void;
@ -272,8 +272,8 @@ declare namespace Plyr {
controlsshown: PlyrEvent; controlsshown: PlyrEvent;
ready: PlyrEvent; ready: PlyrEvent;
}; };
// For retrocompatibility, we keep StandadEvent // For retrocompatibility, we keep StandardEvent
type StandadEvent = keyof Plyr.StandardEventMap; type StandardEvent = keyof Plyr.StandardEventMap;
type Html5EventMap = { type Html5EventMap = {
loadstart: PlyrEvent; loadstart: PlyrEvent;
loadeddata: PlyrEvent; loadeddata: PlyrEvent;
@ -341,7 +341,7 @@ declare namespace Plyr {
* 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. * 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'] * 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; controls?: string | 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 * If you're using the default controls are used then you can specify which settings to show in the menu
@ -459,7 +459,7 @@ declare namespace Plyr {
* Allows binding of event listeners to the controls before the default handlers. See the defaults.js for available listeners. * 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. * If your handler prevents default on the event (event.preventDefault()), the default handler will not fire.
*/ */
listeners?: {[key: string]: (error: PlyrEvent) => void}; 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. * active: Toggles if captions should be active by default. language: Sets the default language to load (if available). 'auto' uses the browser language.
@ -523,6 +523,11 @@ declare namespace Plyr {
* Media Metadata Options. * Media Metadata Options.
*/ */
mediaMetadata?: MediaMetadataOptions; mediaMetadata?: MediaMetadataOptions;
/**
* Markers Options
*/
markers?: MarkersOptions;
} }
interface QualityOptions { interface QualityOptions {
@ -594,6 +599,16 @@ declare namespace Plyr {
artwork?: MediaMetadataArtwork[]; artwork?: MediaMetadataArtwork[];
} }
interface MarkersPoints {
time: number;
label: string;
}
interface MarkersOptions {
enabled: boolean;
points: MarkersPoints[];
}
export interface Elements { export interface Elements {
buttons: { buttons: {
airplay?: HTMLButtonElement; airplay?: HTMLButtonElement;
@ -685,7 +700,7 @@ declare namespace Plyr {
} }
interface PlyrEvent extends CustomEvent { interface PlyrEvent extends CustomEvent {
readonly detail: {readonly plyr: Plyr}; readonly detail: { readonly plyr: Plyr };
} }
enum YoutubeState { enum YoutubeState {

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.7.0 // plyr.js v3.7.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
@ -649,7 +649,7 @@ class Plyr {
/** /**
* Set playback speed * Set playback speed
* @param {Number} speed - the speed of playback (0.5-2.0) * @param {Number} input - the speed of playback (0.5-2.0)
*/ */
set speed(input) { set speed(input) {
let speed = null; let speed = null;
@ -933,8 +933,7 @@ class Plyr {
* @param {Boolean} input - Whether to autoplay or not * @param {Boolean} input - Whether to autoplay or not
*/ */
set autoplay(input) { set autoplay(input) {
const toggle = is.boolean(input) ? input : this.config.autoplay; this.config.autoplay = is.boolean(input) ? input : this.config.autoplay;
this.config.autoplay = toggle;
} }
/** /**
@ -954,7 +953,7 @@ class Plyr {
/** /**
* Set the caption track by index * Set the caption track by index
* @param {Number} - Caption index * @param {Number} input - Caption index
*/ */
set currentTrack(input) { set currentTrack(input) {
captions.set.call(this, input, false); captions.set.call(this, input, false);
@ -972,7 +971,7 @@ class Plyr {
/** /**
* Set the wanted language for captions * Set the wanted language for captions
* Since tracks can be added later it won't update the actual caption track until there is a matching track * Since tracks can be added later it won't update the actual caption track until there is a matching track
* @param {String} - Two character ISO language code (e.g. EN, FR, PT, etc) * @param {String} input - Two character ISO language code (e.g. EN, FR, PT, etc)
*/ */
set language(input) { set language(input) {
captions.setLanguage.call(this, input, false); captions.setLanguage.call(this, input, false);

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr Polyfilled Build // Plyr Polyfilled Build
// plyr.js v3.7.0 // plyr.js v3.7.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================

View File

@ -38,6 +38,9 @@ const support = {
// Picture-in-picture support // Picture-in-picture support
// Safari & Chrome only currently // Safari & Chrome only currently
pip: (() => { pip: (() => {
// While iPhone's support picture-in-picture for some apps, seemingly Safari isn't one of them
// It will throw the following error when trying to enter picture-in-picture
// `NotSupportedError: The Picture-in-Picture mode is not supported.`
if (browser.isIPhone) { if (browser.isIPhone) {
return false; return false;
} }

View File

@ -5,12 +5,10 @@
const browser = { const browser = {
isIE: Boolean(window.document.documentMode), isIE: Boolean(window.document.documentMode),
isEdge: window.navigator.userAgent.includes('Edge'), isEdge: /Edge/g.test(navigator.userAgent),
isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent), isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/g.test(navigator.userAgent),
isIPhone: /(iPhone|iPod)/gi.test(navigator.platform), isIPhone: /iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1,
isIos: isIos: /iPad|iPhone|iPod/gi.test(navigator.userAgent) && navigator.maxTouchPoints > 1,
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) ||
/(iPad|iPhone|iPod)/gi.test(navigator.platform),
}; };
export default browser; export default browser;

View File

@ -9,7 +9,7 @@ const isObject = (input) => getConstructor(input) === Object;
const isNumber = (input) => getConstructor(input) === Number && !Number.isNaN(input); const isNumber = (input) => getConstructor(input) === Number && !Number.isNaN(input);
const isString = (input) => getConstructor(input) === String; const isString = (input) => getConstructor(input) === String;
const isBoolean = (input) => getConstructor(input) === Boolean; const isBoolean = (input) => getConstructor(input) === Boolean;
const isFunction = (input) => getConstructor(input) === Function; const isFunction = (input) => typeof input === 'function';
const isArray = (input) => Array.isArray(input); const isArray = (input) => Array.isArray(input);
const isWeakMap = (input) => instanceOf(input, WeakMap); const isWeakMap = (input) => instanceOf(input, WeakMap);
const isNodeList = (input) => instanceOf(input, NodeList); const isNodeList = (input) => instanceOf(input, NodeList);

View File

@ -7,7 +7,7 @@
* @param {Number} input * @param {Number} input
* @param {Number} min The lower boundary of the output range * @param {Number} min The lower boundary of the output range
* @param {Number} max The upper boundary of the output range * @param {Number} max The upper boundary of the output range
* @returns A number in the range [min, max] * @returns A number within the bounds of min and max
* @type Number * @type Number
*/ */
export function clamp(input = 0, min = 0, max = 255) { export function clamp(input = 0, min = 0, max = 255) {

View File

@ -11,9 +11,7 @@ export function generateId(prefix) {
// Format string // Format string
export function format(input, ...args) { export function format(input, ...args) {
if (is.empty(input)) { if (is.empty(input)) return input;
return input;
}
return input.toString().replace(/{(\d+)}/g, (match, i) => args[i].toString()); return input.toString().replace(/{(\d+)}/g, (match, i) => args[i].toString());
} }
@ -27,7 +25,7 @@ export function getPercentage(current, max) {
return ((current / max) * 100).toFixed(2); return ((current / max) * 100).toFixed(2);
} }
// Replace all occurances of a string in a string // Replace all occurrences of a string in a string
export const replaceAll = (input = '', find = '', replace = '') => export const replaceAll = (input = '', find = '', replace = '') =>
input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString()); input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());

View File

@ -26,7 +26,7 @@
&__container { &__container {
animation: plyr-popup 0.2s ease; animation: plyr-popup 0.2s ease;
background: $plyr-menu-background; background: $plyr-menu-background;
border-radius: 4px; border-radius: $plyr-menu-radius;
bottom: 100%; bottom: 100%;
box-shadow: $plyr-menu-shadow; box-shadow: $plyr-menu-shadow;
color: $plyr-menu-color; color: $plyr-menu-color;
@ -192,7 +192,7 @@
align-items: center; align-items: center;
display: flex; display: flex;
margin-left: auto; margin-left: auto;
margin-right: calc((#{$plyr-control-padding} - 2) * -1); margin-right: calc((#{$plyr-control-padding} - 2px) * -1);
overflow: hidden; overflow: hidden;
padding-left: calc(#{$plyr-control-padding} * 3.5); padding-left: calc(#{$plyr-control-padding} * 3.5);
pointer-events: none; pointer-events: none;

View File

@ -27,7 +27,6 @@ $plyr-progress-offset: $plyr-range-thumb-height;
left: 0; left: 0;
max-width: 120px; max-width: 120px;
overflow-wrap: break-word; overflow-wrap: break-word;
white-space: normal;
} }
} }

View File

@ -32,7 +32,7 @@
bottom: calc(#{$plyr-preview-arrow-size} * -1); bottom: calc(#{$plyr-preview-arrow-size} * -1);
content: ''; content: '';
height: 0; height: 0;
left: 50%; left: calc(50% + var(--preview-arrow-offset));
position: absolute; position: absolute;
transform: translateX(-50%); transform: translateX(-50%);
width: 0; width: 0;
@ -46,15 +46,27 @@
position: relative; position: relative;
z-index: 0; z-index: 0;
img { img,
height: 100%; // Non sprite images are 100%. Sprites will have their size applied by JavaScript &::after {
height: 100%;
left: 0; left: 0;
max-height: none;
max-width: none;
position: absolute; position: absolute;
top: 0; top: 0;
width: 100%; width: 100%;
} }
&::after {
border-radius: inherit;
box-shadow: inset 0 0 0 1px rgba(#000, 15%);
content: '';
pointer-events: none;
}
img {
// Non sprite images are 100%. Sprites will have their size applied by JavaScript
max-height: none;
max-width: none;
}
} }
// Seek time text // Seek time text

View File

@ -4,7 +4,7 @@
$plyr-preview-padding: $plyr-tooltip-padding !default; $plyr-preview-padding: $plyr-tooltip-padding !default;
$plyr-preview-background: $plyr-tooltip-background !default; $plyr-preview-background: $plyr-tooltip-background !default;
$plyr-preview-radius: 6px !default; $plyr-preview-radius: $plyr-tooltip-radius !default;
$plyr-preview-shadow: $plyr-tooltip-shadow !default; $plyr-preview-shadow: $plyr-tooltip-shadow !default;
$plyr-preview-arrow-size: $plyr-tooltip-arrow-size !default; $plyr-preview-arrow-size: $plyr-tooltip-arrow-size !default;
$plyr-preview-image-background: $plyr-color-gray-200 !default; $plyr-preview-image-background: $plyr-color-gray-200 !default;

View File

@ -1,9 +1,11 @@
@charset "UTF-8";
// ========================================================================== // ==========================================================================
// Plyr styles // Plyr styles
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// TODO: Review use of BEM classnames // TODO: Review use of BEM classnames
// ========================================================================== // ==========================================================================
@charset 'UTF-8';
@import 'lib/css-vars'; @import 'lib/css-vars';
$css-vars-use-native: true; $css-vars-use-native: true;

View File

@ -7,5 +7,5 @@ $plyr-tooltip-color: var(--plyr-tooltip-color, $plyr-color-gray-700) !default;
$plyr-tooltip-padding: calc(#{$plyr-control-spacing} / 2); $plyr-tooltip-padding: calc(#{$plyr-control-spacing} / 2);
$plyr-tooltip-padding: var(--plyr-tooltip-padding, $plyr-tooltip-padding) !default; $plyr-tooltip-padding: var(--plyr-tooltip-padding, $plyr-tooltip-padding) !default;
$plyr-tooltip-arrow-size: var(--plyr-tooltip-arrow-size, 4px) !default; $plyr-tooltip-arrow-size: var(--plyr-tooltip-arrow-size, 4px) !default;
$plyr-tooltip-radius: var(--plyr-tooltip-radius, 3px) !default; $plyr-tooltip-radius: var(--plyr-tooltip-radius, 5px) !default;
$plyr-tooltip-shadow: var(--plyr-tooltip-shadow, 0 1px 2px rgba(0, 0, 0, 0.15)) !default; $plyr-tooltip-shadow: var(--plyr-tooltip-shadow, 0 1px 2px rgba(0, 0, 0, 0.15)) !default;

View File

@ -118,7 +118,7 @@ Object.entries(build.js).forEach(([filename, entry]) => {
}, },
], ],
], ],
plugins: ['@babel/plugin-proposal-class-properties'], plugins: ['@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-optional-chaining'],
babelrc: false, babelrc: false,
exclude: [/\/core-js\//], exclude: [/\/core-js\//],
}), }),

3026
yarn.lock

File diff suppressed because it is too large Load Diff