Compare commits

...

99 Commits

Author SHA1 Message Date
Sam Potts de47071256 v3.4.4
-   Fixed issue with double binding for `click` and `touchstart` for `clickToPlay` option
-   Improved "faux" fullscreen on iPhone X/XS phones with notch
-   Babel 7 upgrade (which reduced the polyfilled build by ~10kb!)
2018-09-25 23:36:50 +10:00
Sam Potts 87072cb690 Clean up 2018-09-25 23:29:43 +10:00
Sam Potts d9565e9250 Improved fullscreen on iPhone X etc 2018-09-25 23:29:32 +10:00
Sam Potts f80b568e67 Reverted large pause button 2018-09-25 23:07:48 +10:00
Sam Potts 7fed689f9a Yarn lock file 2018-09-25 22:25:46 +10:00
Sam Potts 3f48df8f10 Remove babel-polyfill in favour of core-js 2018-09-25 22:25:35 +10:00
Sam Potts cc55092ca6 Babel upgrades 2018-09-25 22:25:04 +10:00
Sam Potts 3331d9d01d Package upgrades 2018-09-25 20:46:58 +10:00
Sam Potts 62d80e6b76 Fix touch vs click issue 2018-09-25 20:43:09 +10:00
Sam Potts 7dc4d9cd22 v3.4.3 2018-08-14 12:16:34 +10:00
Sam Potts 8fb8ae1260 Merge pull request #1163 from sampotts/develop
Fix bug with nodeList for play buttons
2018-08-14 12:14:58 +10:00
Sam Potts 90304369f4 Fix watch 2018-08-14 12:13:16 +10:00
Sam Potts 922456c46c Fix for nodeList as buttons 2018-08-14 12:13:00 +10:00
Sam Potts eaeccd66ae v3.4.2 2018-08-14 11:17:33 +10:00
Sam Potts 7a43649c13 Fix play/pause button state 2018-08-14 11:17:27 +10:00
Sam Potts 525bbf313e v3.4.1 2018-08-14 09:18:09 +10:00
Sam Potts cfaebe9bf2 Fix for controls missing (fixes #1161) 2018-08-14 09:17:58 +10:00
Sam Potts b57b7b2153 v3.4.0
-   Accessibility improvements (see #905)
-   Improvements to the way the controls work on iOS
-   Demo code clean up
-   YouTube quality selection removed due to their poor support for it. As a result, the `qualityrequested` event has been removed
-   Controls spacing improvements
-   Fix for pressed property missing with custom controls (Fixes #1062)
-   Fix #1153: Captions language fallback (thanks @friday)
-   Fix for setting pressed property of undefined (Fixes #1102)
2018-08-14 00:02:01 +10:00
Sam Potts 48bf368316 Merge pull request #1160 from sampotts/develop
v3.4.0
2018-08-14 00:00:24 +10:00
Sam Potts 8f94ce86a0 Merge branch 'master' into develop
# Conflicts:
#	readme.md
2018-08-13 23:59:19 +10:00
Sam Potts 10a9cf08f1 Changelog 2018-08-13 23:57:46 +10:00
Sam Potts 286d0d1794 Fix for pressed property missing with custom controls (Fixes #1062) 2018-08-13 23:52:10 +10:00
Sam Potts 95f6fa2731 Fix for setting pressed property of undefined (Fixes #1102) 2018-08-13 23:46:58 +10:00
Sam Potts 1aeef81288 Controls spacing improvements 2018-08-13 23:43:22 +10:00
Sam Potts 211ad6c8f5 Removed YouTube quality controls 2018-08-13 23:43:08 +10:00
Sam Potts 468b20d227 Moved mute button inside plyr__volume 2018-08-13 23:42:12 +10:00
Sam Potts f6bc42c2bc Fix IE11 issue in demo 2018-08-13 23:03:08 +10:00
Sam Potts 2c01b8ba76 Yarn lock file 2018-08-13 23:02:31 +10:00
Sam Potts 4e1df8677f Fix tooltip alignment 2018-08-13 23:02:14 +10:00
Sam Potts 6953a12e2a Set background color for video 2018-08-13 23:01:56 +10:00
Sam Potts 1d0db89194 Update wrong reference in docs 2018-08-13 23:01:38 +10:00
Sam Potts 297f297d18 Moved i18n to utils 2018-08-13 21:39:16 +10:00
Sam Potts 059205c378 Package updates 2018-08-13 21:39:02 +10:00
Sam Potts f94e53ffb1 Merge pull request #1158 from friday/1153
Fix #1153: Captions language fallback
2018-08-13 09:24:25 +10:00
Albin Larsson a4f1fdec5d Fix #1153: Captions language fallback 2018-08-12 20:12:22 +02:00
Sam Potts 75374eb154 Merge pull request #1147 from jamesoflol/fix-ios-fullscreen-while-stopped
Remove 'video is playing' requirement for iosNative fullscreen
2018-08-05 22:46:58 +10:00
Sam Potts 3ad118c026 3.4.0-beta.2 2018-08-05 22:43:35 +10:00
Sam Potts 0bc6b1f1b3 Fix issue where enter key wasn’t setting focus correctly 2018-08-05 22:41:21 +10:00
Sam Potts 4ea458e1a3 Rounded aria-valuetext to 1 decimal place 2018-08-05 21:48:42 +10:00
Sam Potts aacb172017 Removed aria-labelled-by 2018-08-05 21:48:21 +10:00
James dbf768b1bd Remove 'video is playing' requirement for iosNative fullscreen 2018-08-03 09:58:51 +10:00
Sam Potts b96fcfc8ac v3.4.0-beta.1 2018-08-02 00:55:48 +10:00
Sam Potts 18b4d26bee Merge pull request #1142 from sampotts/a11y-improvements
A11y improvements
2018-08-02 00:47:57 +10:00
Sam Potts 7f4b74e2d4 Fix for hover over iframed players not showing controls 2018-08-02 00:47:03 +10:00
Sam Potts a8f8486cf4 Merge pull request #1143 from mhluska/patch-1
Fix Readme typo (Patron -> Patreon)
2018-08-01 15:09:00 +10:00
Maros Hluska a343e58e53 Fix Readme typo (Patron -> Patreon) 2018-08-01 12:08:02 +07:00
Sam Potts 0892d69ba2 Handle race condition for ads lib loading after source change 2018-08-01 13:56:49 +10:00
Sam Potts ba511b51c7 Box shadow fix for range track 2018-08-01 13:00:51 +10:00
Sam Potts e090581913 Ads on dev or prod only 2018-08-01 11:49:42 +10:00
Sam Potts aaa56caa9c Only focus button if menu wasn’t hidden already 2018-08-01 01:38:57 +10:00
Sam Potts c8db1e55dd Escape closes menu 2018-08-01 01:26:15 +10:00
Sam Potts 58079393e6 Build 2018-08-01 00:58:27 +10:00
Sam Potts 0b44f2d897 Demo config 2018-08-01 00:57:45 +10:00
Sam Potts 2371619486 Linting 2018-08-01 00:56:44 +10:00
Sam Potts 13a54b5dbe Merge branch 'develop' into a11y-improvements
# Conflicts:
#	src/js/controls.js
2018-08-01 00:46:26 +10:00
Sam Potts fa0861ff2e Merge pull request #1141 from friday/1137
Improve captions positioning consistency
2018-08-01 00:41:48 +10:00
Sam Potts 748aa5179f Comments about keydown vs keyup for Firefox 2018-08-01 00:38:19 +10:00
Sam Potts 56a485bac6 Fix Firefox spacebar issue 2018-08-01 00:37:55 +10:00
Albin Larsson 9488de30e5 Fix #1137: Improve captions positioning consistency 2018-07-31 16:26:34 +02:00
Sam Potts e3dfd16096 Merge pull request #1139 from friday/controls-input
Controls input fixes
2018-07-31 09:08:08 +10:00
Albin Larsson c230ccce86 Update controls.md docs 2018-07-31 00:44:07 +02:00
Albin Larsson db22a8e9c4 Improve handling of the 'controls' argument 2018-07-31 00:43:56 +02:00
Sam Potts 3a3358e2b4 Make iOS range fix more universal 2018-07-30 23:29:14 +10:00
Sam Potts 248005e8e0 Fix merge 2018-07-30 23:29:02 +10:00
Sam Potts dae272ef66 Merge branch 'develop' into a11y-improvements
# Conflicts:
#	demo/dist/demo.css
#	dist/plyr.css
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
#	package.json
#	src/js/plyr.js
2018-07-30 23:09:12 +10:00
Sam Potts 2679c5898e v3.2.23 2018-07-30 22:55:34 +10:00
Sam Potts efb7401e6d Merge pull request #1136 from sampotts/develop
v3.3.23
2018-07-30 22:54:03 +10:00
Sam Potts 60a0f0c979 Missed reference 2018-07-30 22:53:42 +10:00
Sam Potts 5d168d0e14 v3.3.23 2018-07-30 22:53:09 +10:00
Sam Potts f964e34d8c Merge pull request #1134 from mjfwebb/hide-empty-controls
Hide empty controls
2018-07-30 22:47:10 +10:00
Sam Potts 021ba0b8e9 Update controls.scss 2018-07-30 22:46:48 +10:00
Sam Potts 96d371546c Merge pull request #1135 from sampotts/master
Merge back
2018-07-30 22:45:59 +10:00
Sam Potts e1780a4df0 Fix for redraw issue 2018-07-30 22:44:19 +10:00
Albin Larsson e5e169a1e2 Don't move caption up when "showing" the lower controls when the controls are empty 2018-07-30 01:02:13 +02:00
mjfwebb 5eda498516 If the plyr__controls is empty it is still showing the transition causing captions to be pushed up when hovering over where the controls would be. This change hides the plyr__controls div when it is empty. 2018-07-29 22:02:16 +02:00
Sam Potts 599b33e55f Click to play fix, poster fix, iOS controls fixes 2018-07-30 01:13:12 +10:00
Sam Potts 3a8332bdb3 Fix for webkit redrawing issue 2018-07-29 12:32:26 +10:00
Sam Potts 44b5d9f6b9 Merge pull request #1131 from friday/1108
Make sure youtube.onReady doesn't run twice
2018-07-29 00:19:37 +10:00
Albin Larsson 24deff0e2d Fix #1108: Make sure youtube.onReady doesn't run twice 2018-07-28 04:04:57 +02:00
Sam Potts 0933b48c2a Merge pull request #1113 from friday/issue-templates
Use GitHub's new issue template prompt
2018-07-27 13:12:49 +10:00
Sam Potts 71578e07ec Merge pull request #1120 from didacte/add-missing-youtube-hl-param
Add support for YouTube's hl param
2018-07-26 00:03:10 +10:00
Léo Renaud-Allaire 671325dd17 Add support for YouTube's hl param 2018-07-24 16:36:25 -04:00
Sam Potts 53a3d06103 Merge branch 'develop' into a11y-improvements
# Conflicts:
#	demo/dist/demo.css
#	demo/dist/demo.js.map
#	demo/dist/demo.min.js
#	demo/dist/demo.min.js.map
#	dist/plyr.css
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
#	package.json
#	yarn.lock
2018-07-24 09:38:26 +10:00
Albin Larsson ad1989e45e New issue templates (with prompt) 2018-07-23 06:21:46 +02:00
Albin Larsson f9ac98bc6d Lowercase contributing.md (like the other docs) 2018-07-23 06:21:25 +02:00
Sam Potts 544ab0086b Logic fix 2018-07-19 10:02:41 +10:00
Sam Potts 13bf80d372 Deployment improvements (auto purge cache etc) 2018-07-19 09:47:11 +10:00
Sam Potts e63ad7c74b Keyboard and focus improvements 2018-07-15 19:23:28 +10:00
Sam Potts ead6601394 Merge 2018-07-02 23:11:59 +10:00
Sam Potts e61ebd8d05 Merge branch 'develop' into a11y-improvements 2018-07-02 23:11:50 +10:00
Sam Potts 3bf1c59bd6 Work on key bindings for menu 2018-06-28 23:44:07 +10:00
Sam Potts e59fe1aacf Merge branch 'develop' into a11y-improvements
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
#	src/js/listeners.js
2018-06-25 23:09:13 +10:00
Sam Potts 1f1d74ba50 Work on menus 2018-06-21 09:01:16 +10:00
Sam Potts bb546fe43f Merge branch 'develop' into a11y-improvements
# Conflicts:
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
2018-06-19 19:24:47 +10:00
Sam Potts 9e1218547b WIP 2018-06-19 09:11:35 +10:00
Sam Potts 715b88c09b Merge branch 'develop' into a11y-improvements
# Conflicts:
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
#	dist/plyr.polyfilled.js.map
#	dist/plyr.polyfilled.min.js
#	dist/plyr.polyfilled.min.js.map
2018-06-18 23:29:25 +10:00
Sam Potts 7b9ef7d757 More work on menus 2018-06-18 23:13:40 +10:00
Sam Potts d64ed4ba5a Merge branch 'develop' into a11y-improvements
# Conflicts:
#	dist/plyr.js.map
#	dist/plyr.min.js
#	dist/plyr.min.js.map
2018-06-18 22:17:34 +10:00
Sam Potts ffd864ed39 Work on controls 2018-06-18 21:39:47 +10:00
63 changed files with 20912 additions and 24628 deletions
+55
View File
@@ -0,0 +1,55 @@
---
name: Bug report
about: Report an issue or unexpected behaviour with Plyr
---
<!--
Before creating the issue, please make sure that...
* You aren't getting any errors in your own code, causing the problem.
* You are using the latest version of Plyr.
* There isn't already an open issue for your problem.
* You are following the documentation correctly (https://github.com/sampotts/plyr/)
* Your problem doesn't happen if you remove Plyr and use native HTML5 media (when applicable).
For problems with autoplay, see our FAQ (https://github.com/sampotts/plyr/wiki/FAQ)
If you have multiple unrelated problems, create separate issues rather than combining them into one.
Note that leaving sections blank or being vague will make it difficult for us to troubleshoot and we may close the issue.
-->
### Expected behaviour
### Actual behaviour
### Steps to reproduce
### Environment
- Browser:
- Version:
- Operating System:
- Version:
### Console errors (if any)
### Link to where the bug is happening
<!--
This link can be either to our demo at https://plyr.io/ if the problem can be observed there, or to a code playground with a **minimal** test case that demonstrates the problem.
You can use one of our prepared templates to get started creating the test case:
* HTML5 video: https://codepen.io/pen?template=bKeqpr
* HTML5 audio: https://codepen.io/pen?template=rKLywR
* YouTube: https://codepen.io/pen?template=GGqbbJ
* Vimeo: https://codepen.io/pen?template=bKeXNq
* Dash.js integration: https://codepen.io/pen?template=zaBgBy
* Hls.js integration: https://codepen.io/pen?template=oyLKQb
* Shaka Player integration: https://codepen.io/pen?template=ZRpzZO
It's important that you keep the issue description and replication demo **minimal**. If your replication includes frameworks, libraries or customizations, this makes it much harder to understand the problem and find the bug. For more help on how to create the demo, see https://github.com/sampotts/plyr/wiki/Writing-helpful-issue-descriptions
-->
+10
View File
@@ -0,0 +1,10 @@
---
name: New feature
about: Request new functionality
---
<!--
Please describe the behaviour that you want to add, and why. Be as clear as possible to avoid confusion.
If you want to request multiple features that aren't directly related, then create one issue per feature.
-->
+10
View File
@@ -0,0 +1,10 @@
---
name: Improvement
about: Request a change that isn't a bug or new feature
---
<!--
Please describe the behaviour that you want to change, and why. Be as clear as possible to avoid confusion.
If you want to request multiple changes that aren't directly related, then create one issue per change.
-->
+2 -14
View File
@@ -1,15 +1,3 @@
*Please _use this issue template_ as it makes replicating and fixing the issue easier for us. If you decide not to use it or you are vague your issue may be close instantly.*
PLEASE USE OUR SPECIFIC ISSUE TEMPLATES for bug reports, features and improvement suggestions.
### Expected behaviour
### Actual behaviour
### Environment
- Browser:
- Version:
- Operating System:
- Version:
### Steps to reproduce
-
Our issue tracker is not for support questions. If you need help, follow our support instructions: https://github.com/sampotts/plyr/blob/master/contributing.md#support
+2 -2
View File
@@ -1,6 +1,6 @@
node_modules
.DS_Store
aws.json
credentials.json
*.mp4
!dist/blank.mp4
index-*.html
@@ -8,4 +8,4 @@ npm-debug.log
yarn-error.log
package-lock.json
*.webm
.idea/
.idea/
+1 -1
View File
@@ -2,7 +2,7 @@ demo
.github
.vscode
*.code-workspace
aws.json
credentials.json
bundles.json
yarn.lock
package-lock.json
+2 -1
View File
@@ -2,5 +2,6 @@
"useTabs": false,
"tabWidth": 4,
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "all",
"printWidth": 120
}
-61
View File
@@ -1,61 +0,0 @@
# Contributing
We welcome bug reports, feature requests and pull requests. If you want to help us out, please follow these guidelines, in order to avoid redundant work.
## Commenting
When commenting, keep a civil tone and stay on topic. Don't ask for support (use [Stack Overflow](https://stackoverflow.com/) or [our Slack](https://bit.ly/plyr-chat) for that), or post "+1" or "I agree" type of comments. Use the emojis instead.
Asking for the status on issues is discouraged. Unless someone has explicitly said in an issue that it's work in progress, most likely that means no one is working on it. We have a lot to do, and it may not be a top priority for us.
We *may* moderate discussions. We do this to avoid threads being "hijacked", to avoid confusion in case the content is misleading or outdated, and to avoid bothering people with github notifications.
## Reporting issues
Our GitHub issue tracker is for bug reports and feature requests. Don't ask for support here. Use [Stack Overflow](https://stackoverflow.com/) or [our Slack](https://bit.ly/plyr-chat) for that.
Please verify that your issue hasn't already been answered by our [FAQ](https://github.com/sampotts/plyr/wiki/FAQ), and that there isn't already an open issue for it.
When applicable, check that your problem doesn't happen without Plyr (see [FAQ#1](https://github.com/sampotts/plyr/wiki/FAQ#1-does-plyr-work-with--)).
Verify that you are following the documentation, are using the latest version of Plyr, and aren't getting any errors in your own code, causing the issues.
Create one issue per problem or request (i.e. don't combine multiple problems to one git issue). Describe the issue as detailed as possible (see [Replication](#replication))
## Replication
In order to solve a problem, we first need to understand it. Please answer these questions when reporting issues or asking for help in [our Slack](https://bit.ly/plyr-chat).
* Does it happen only with specific options and/or specific browsers?
* Does is happen only with HTML5 video, audio, YouTube, Vimeo or a specific library?
* Does the issue happen on [our demo](https://plyr.io/)? If not, please recreate it with a **minimal** example online. You can use our Codepen templates to get started:
* [HTML5 video](https://codepen.io/pen?template=bKeqpr)
* [HTML5 audio](https://codepen.io/pen?template=rKLywR)
* [YouTube](https://codepen.io/pen?template=GGqbbJ)
* [Vimeo](https://codepen.io/pen?template=bKeXNq)
* [Dash.js integration](https://codepen.io/pen?template=zaBgBy)
* [Hls.js integration](https://codepen.io/pen?template=oyLKQb)
* [Shaka Player integration](https://codepen.io/pen?template=ZRpzZO)
It's important that you keep the issue description and replication demo **minimal**. If your replication includes frameworks, libraries or customizations, this makes it harder to debug and understand the issue. While it may be relevant to bring this up (ex: "I need Plyr to trigger the event sooner or it breaks Framework X"), please keep these out of your replication demo if they aren't strictly needed to reproduce the issue. If the issue is caused by something a library does that Plyr doesn't handle, it's more helpful for us if you find out what it is, and replicate the same problem without the library. Otherwise any developer who is willing to help out with the issue has to understand the frameworks, libraries and customizations of *your* choice, or no one will try to fix your issue because it's too much work.
## Requesting features and improvements
If you are missing something in Plyr, you can create a GitHub issue for this as well. Since we prioritize fixing bugs first, and may have a lot of other suggestions and architectural changes to work on as well, these may not be at the top of our list.
If your suggestion is important or urgent to you, you may want to first ensure it's something we want to have in Plyr, and then contribute it as a pull request. [Our Slack](https://bit.ly/plyr-chat) is the best place for questions like this.
## Contributing features and documentation
* Fork Plyr, and create a new branch in your fork, based on the **develop** branch
* To test locally, you can use the demo. First make sure you have installed the dependencies with `npm install` or `yarn`. Run `gulp` to build while you are working, and run a local server from the repository root directory. If you have Python installed, this command should work: `python -m SimpleHTTPServer 8080`. Then go to `http://localhost:8080/demo/`
* Develop and test your modifications.
* Preferably commit your changes as independent logical chunks, with meaningful messages. Make sure you do not commit unnecessary files or changes, such as the build output, or logging and breakpoints you added for testing.
* If your modifications changes the documented behavior or add new features, document these changes in readme.md.
* When finished, push the changes to your GitHub repository and send a pull request to **develop**. Describe what your PR does.
* If the Travis build fails, or if you get a code review with change requests, you can fix these by pushing new or rebased commits to the branch.
+36
View File
@@ -1,3 +1,39 @@
# v3.4.4
- Fixed issue with double binding for `click` and `touchstart` for `clickToPlay` option
- Improved "faux" fullscreen on iPhone X/XS phones with notch
- Babel 7 upgrade (which reduced the polyfilled build by ~10kb!)
# v3.4.3
- Fixed issue with nodeList for custom playback controls
# v3.4.2
- Fix play/pause button state
# v3.4.1
- Bug fix for custom controls (fixes #1161)
# v3.4.0
- Accessibility improvements (see #905)
- Improvements to the way the controls work on iOS
- Demo code clean up
- YouTube quality selection removed due to their poor support for it. As a result, the `qualityrequested` event has been removed
- Controls spacing improvements
- Fix for pressed property missing with custom controls (Fixes #1062)
- Fix #1153: Captions language fallback (thanks @friday)
- Fix for setting pressed property of undefined (Fixes #1102)
# v3.3.23
- Add support for YouTube's hl param (thanks @renaudleo)
- Fix for captions positioning when no controls (thanks @friday and @mjfwebb)
- Fix #1108: Make sure youtube.onReady doesn't run twice (thanks @friday)
- Fix for WebKit redraw loop on the `<input type="range">` elements
# v3.3.22
- Travis & CI improvements (thanks @friday)
+40
View File
@@ -0,0 +1,40 @@
# Contributing
We welcome bug reports, feature requests and pull requests. If you want to help us out, please follow these guidelines, in order to avoid redundant work.
## Support
Before asking questions, read our [documentation](https://github.com/sampotts/plyr) and [FAQ](https://github.com/sampotts/plyr/wiki/FAQ).
If these doesn't answer your question
* Use [Stack Overflow](https://stackoverflow.com/) for questions that doesn't directly involve Plyr. This includes for example how to use Javascript, CSS or HTML5 media in general, and how to use other frameworks, libraries and technology.
* Use [our Slack](https://bit.ly/plyr-chat) if you need help using Plyr or have questions about Plyr.
## Commenting
When commenting, keep a civil tone and stay on topic. Don't ask for [support](#support), or post "+1" or "I agree" type of comments. Use the emojis instead.
Asking for the status on issues is discouraged. Unless someone has explicitly said in an issue that it's work in progress, most likely that means no one is working on it. We have a lot to do, and it may not be a top priority for us.
We *may* moderate discussions. We do this to avoid threads being "hijacked", to avoid confusion in case the content is misleading or outdated, and to avoid bothering people with github notifications.
## Creating issues
Please follow the instructions in our issue templates. Don't use github issues to ask for [support](#support).
## Contributing features and documentation
* If you want to add a feature or make critical changes, you may want to ensure that this is something we also want (so you don't waste your time). Ask us about this in the corresponding issue if there is one, or on [our Slack](https://bit.ly/plyr-chat) otherwise.
* Fork Plyr, and create a new branch in your fork, based on the **develop** branch
* To test locally, you can use the demo. First make sure you have installed the dependencies with `npm install` or `yarn`. Run `gulp` to build while you are working, and run a local server from the repository root directory. If you have Python installed, this command should work: `python -m SimpleHTTPServer 8080`. Then go to `http://localhost:8080/demo/`
* Develop and test your modifications.
* Preferably commit your changes as independent logical chunks, with meaningful messages. Make sure you do not commit unnecessary files or changes, such as the build output, or logging and breakpoints you added for testing.
* If your modifications changes the documented behavior or add new features, document these changes in readme.md.
* When finished, push the changes to your GitHub repository and send a pull request to **develop**. Describe what your PR does.
* If the Travis build fails, or if you get a code review with change requests, you can fix these by pushing new or rebased commits to the branch.
+2
View File
@@ -3,7 +3,9 @@
This is the markup that is rendered for the Plyr controls. You can use the default controls or provide a customized version of markup based on your needs. You can pass the following to the `controls` option:
- `Array` of options (this builds the default controls based on your choices)
- `Element` with the controls
- `String` containing the desired HTML
- `false` (or empty string or array) to disable all controls
- `Function` that will be executed and should return one of the above
## Using default controls
+1 -1
View File
File diff suppressed because one or more lines are too long
+275 -316
View File
@@ -1833,7 +1833,6 @@ typeof navigator === "object" && (function () {
};
this._fetchDefaults = {
method: 'POST',
keepalive: true,
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
// https://caniuse.com/#feat=referrer-policy
// It doesn't. And it throw exception instead of ignoring this parameter...
@@ -1874,7 +1873,7 @@ typeof navigator === "object" && (function () {
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.26.3',
VERSION: '3.27.0',
debug: false,
@@ -2612,34 +2611,40 @@ typeof navigator === "object" && (function () {
)
return;
options = options || {};
options = objectMerge$1(
{
eventId: this.lastEventId(),
dsn: this._dsn,
user: this._globalContext.user || {}
},
options
);
var lastEventId = options.eventId || this.lastEventId();
if (!lastEventId) {
if (!options.eventId) {
throw new configError('Missing eventId');
}
var dsn = options.dsn || this._dsn;
if (!dsn) {
if (!options.dsn) {
throw new configError('Missing DSN');
}
var encode = encodeURIComponent;
var qs = '';
qs += '?eventId=' + encode(lastEventId);
qs += '&dsn=' + encode(dsn);
var encodedOptions = [];
var user = options.user || this._globalContext.user;
if (user) {
if (user.name) qs += '&name=' + encode(user.name);
if (user.email) qs += '&email=' + encode(user.email);
for (var key in options) {
if (key === 'user') {
var user = options.user;
if (user.name) encodedOptions.push('name=' + encode(user.name));
if (user.email) encodedOptions.push('email=' + encode(user.email));
} else {
encodedOptions.push(encode(key) + '=' + encode(options[key]));
}
}
var globalServer = this._getGlobalServer(this._parseDSN(dsn));
var globalServer = this._getGlobalServer(this._parseDSN(options.dsn));
var script = _document.createElement('script');
script.async = true;
script.src = globalServer + '/api/embed/error-page/' + qs;
script.src = globalServer + '/api/embed/error-page/?' + encodedOptions.join('&');
(_document.head || _document.body).appendChild(script);
},
@@ -4087,309 +4092,263 @@ typeof navigator === "object" && (function () {
// ==========================================================================
(function () {
var isLive = window.location.host === 'plyr.io';
var host = window.location.host;
var env = {
prod: host === 'plyr.io',
dev: host === 'dev.plyr.io'
};
document.addEventListener('DOMContentLoaded', function () {
singleton.context(function () {
var selector = '#player';
var container = document.getElementById('container');
// Raven / Sentry
// For demo site (https://plyr.io) only
if (isLive) {
singleton.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
}
document.addEventListener('DOMContentLoaded', function () {
singleton.context(function () {
if (window.shr) {
window.shr.setup({
count: {
classname: 'button__count'
}
});
}
// Setup tab focus
var tabClassName = 'tab-focus';
// Remove class on blur
document.addEventListener('focusout', function (event) {
event.target.classList.remove(tabClassName);
});
// Add classname to tabbed elements
document.addEventListener('keydown', function (event) {
if (event.keyCode !== 9) {
return;
}
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(function () {
document.activeElement.classList.add(tabClassName);
}, 0);
});
// Setup the player
var player = new Plyr('#player', {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',
keyboard: {
global: true
},
tooltips: {
controls: true
},
clickToPlay: false,
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'duration',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
/* i18n: {
restart: '重新開始',
rewind: '快退{seektime}秒',
play: '播放',
pause: '暫停',
fastForward: '快進{seektime}秒',
seek: '尋求',
played: '發揮',
buffered: '緩衝的',
currentTime: '當前時間戳',
duration: '長短',
volume: '音量',
mute: '靜音',
unmute: '取消靜音',
enableCaptions: '開啟字幕',
disableCaptions: '關閉字幕',
enterFullscreen: '進入全螢幕',
exitFullscreen: '退出全螢幕',
frameTitle: '球員為{title}',
captions: '字幕',
settings: '設定',
speed: '速度',
normal: '正常',
quality: '質量',
loop: '循環',
start: 'Start',
end: 'End',
all: 'All',
reset: '重啟',
disabled: '殘',
enabled: '啟用',
advertisement: '廣告',
}, */
captions: {
active: true
},
keys: {
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
},
ads: {
enabled: true,
publisherId: '918848828995742'
}
});
// Expose for tinkering in the console
window.player = player;
// Setup type toggle
var buttons = document.querySelectorAll('[data-source]');
var types = {
video: 'video',
audio: 'audio',
youtube: 'youtube',
vimeo: 'vimeo'
};
var currentType = window.location.hash.replace('#', '');
var historySupport = window.history && window.history.pushState;
// Toggle class on an element
function toggleClass(element, className, state) {
if (element) {
element.classList[state ? 'add' : 'remove'](className);
}
}
// Set a new source
function newSource(type, init) {
// Bail if new type isn't known, it's the current type, or current type is empty (video is default) and new type is video
if (!(type in types) || !init && type === currentType || !currentType.length && type === types.video) {
return;
}
switch (type) {
case types.video:
player.source = {
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4',
type: 'video/mp4',
size: 576
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4',
type: 'video/mp4',
size: 720
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4',
type: 'video/mp4',
size: 1080
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4',
type: 'video/mp4',
size: 1440
}],
poster: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg',
tracks: [{
kind: 'captions',
label: 'English',
srclang: 'en',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
default: true
}, {
kind: 'captions',
label: 'French',
srclang: 'fr',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt'
}]
};
break;
case types.audio:
player.source = {
type: 'audio',
title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
sources: [{
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
type: 'audio/mp3'
}, {
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
type: 'audio/ogg'
}]
};
break;
case types.youtube:
player.source = {
type: 'video',
sources: [{
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
provider: 'youtube'
}]
};
break;
case types.vimeo:
player.source = {
type: 'video',
sources: [{
src: 'https://vimeo.com/76979871',
provider: 'vimeo'
}]
};
break;
default:
break;
}
// Set the current type for next time
currentType = type;
// Remove active classes
Array.from(buttons).forEach(function (button) {
return toggleClass(button.parentElement, 'active', false);
});
// Set active on parent
toggleClass(document.querySelector('[data-source="' + type + '"]'), 'active', true);
// Show cite
Array.from(document.querySelectorAll('.plyr__cite')).forEach(function (cite) {
cite.setAttribute('hidden', '');
});
document.querySelector('.plyr__cite--' + type).removeAttribute('hidden');
}
// Bind to each button
Array.from(buttons).forEach(function (button) {
button.addEventListener('click', function () {
var type = button.getAttribute('data-source');
newSource(type);
if (historySupport) {
window.history.pushState({ type: type }, '', '#' + type);
}
});
});
// List for backwards/forwards
window.addEventListener('popstate', function (event) {
if (event.state && 'type' in event.state) {
newSource(event.state.type);
}
});
// On load
if (historySupport) {
var video = !currentType.length;
// If there's no current type set, assume video
if (video) {
currentType = types.video;
}
// Replace current history state
if (currentType in types) {
window.history.replaceState({
type: currentType
}, '', video ? '' : '#' + currentType);
}
// If it's not video, load the source
if (currentType !== types.video) {
newSource(currentType, true);
}
}
if (window.shr) {
window.shr.setup({
count: {
classname: 'button__count'
}
});
});
} // Setup tab focus
// Google analytics
// For demo site (https://plyr.io) only
/* eslint-disable */
if (isLive) {
(function (i, s, o, g, r, a, m) {
i.GoogleAnalyticsObject = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments);
var tabClassName = 'tab-focus'; // Remove class on blur
document.addEventListener('focusout', function (event) {
if (!event.target.classList || container.contains(event.target)) {
return;
}
event.target.classList.remove(tabClassName);
}); // Add classname to tabbed elements
document.addEventListener('keydown', function (event) {
if (event.keyCode !== 9) {
return;
} // Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(function () {
var focused = document.activeElement;
if (!focused || !focused.classList || container.contains(focused)) {
return;
}
focused.classList.add(tabClassName);
}, 10);
}); // Setup the player
var player = new Plyr(selector, {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',
keyboard: {
global: true
},
tooltips: {
controls: true
},
captions: {
active: true
},
keys: {
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
},
ads: {
enabled: env.prod || env.dev,
publisherId: '918848828995742'
}
}); // Expose for tinkering in the console
window.player = player; // Setup type toggle
var buttons = document.querySelectorAll('[data-source]');
var types = {
video: 'video',
audio: 'audio',
youtube: 'youtube',
vimeo: 'vimeo'
};
var currentType = window.location.hash.replace('#', '');
var historySupport = window.history && window.history.pushState; // Toggle class on an element
function toggleClass(element, className, state) {
if (element) {
element.classList[state ? 'add' : 'remove'](className);
}
} // Set a new source
function newSource(type, init) {
// Bail if new type isn't known, it's the current type, or current type is empty (video is default) and new type is video
if (!(type in types) || !init && type === currentType || !currentType.length && type === types.video) {
return;
}
switch (type) {
case types.video:
player.source = {
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4',
type: 'video/mp4',
size: 576
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4',
type: 'video/mp4',
size: 720
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4',
type: 'video/mp4',
size: 1080
}, {
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4',
type: 'video/mp4',
size: 1440
}],
poster: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg',
tracks: [{
kind: 'captions',
label: 'English',
srclang: 'en',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
default: true
}, {
kind: 'captions',
label: 'French',
srclang: 'fr',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt'
}]
};
i[r].l = 1 * new Date();
a = s.createElement(o);
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
window.ga('create', 'UA-40881672-11', 'auto');
window.ga('send', 'pageview');
}
/* eslint-enable */
break;
case types.audio:
player.source = {
type: 'audio',
title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
sources: [{
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
type: 'audio/mp3'
}, {
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
type: 'audio/ogg'
}]
};
break;
case types.youtube:
player.source = {
type: 'video',
sources: [{
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
provider: 'youtube'
}]
};
break;
case types.vimeo:
player.source = {
type: 'video',
sources: [{
src: 'https://vimeo.com/76979871',
provider: 'vimeo'
}]
};
break;
default:
break;
} // Set the current type for next time
currentType = type; // Remove active classes
Array.from(buttons).forEach(function (button) {
return toggleClass(button.parentElement, 'active', false);
}); // Set active on parent
toggleClass(document.querySelector("[data-source=\"".concat(type, "\"]")), 'active', true); // Show cite
Array.from(document.querySelectorAll('.plyr__cite')).forEach(function (cite) {
cite.setAttribute('hidden', '');
});
document.querySelector(".plyr__cite--".concat(type)).removeAttribute('hidden');
} // Bind to each button
Array.from(buttons).forEach(function (button) {
button.addEventListener('click', function () {
var type = button.getAttribute('data-source');
newSource(type);
if (historySupport) {
window.history.pushState({
type: type
}, '', "#".concat(type));
}
});
}); // List for backwards/forwards
window.addEventListener('popstate', function (event) {
if (event.state && 'type' in event.state) {
newSource(event.state.type);
}
}); // On load
if (historySupport) {
var video = !currentType.length; // If there's no current type set, assume video
if (video) {
currentType = types.video;
} // Replace current history state
if (currentType in types) {
window.history.replaceState({
type: currentType
}, '', video ? '' : "#".concat(currentType));
} // If it's not video, load the source
if (currentType !== types.video) {
newSource(currentType, true);
}
}
});
}); // Raven / Sentry
// For demo site (https://plyr.io) only
if (env.prod) {
singleton.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
} // Google analytics
// For demo site (https://plyr.io) only
/* eslint-disable */
if (env.prod) {
(function (i, s, o, g, r, a, m) {
i.GoogleAnalyticsObject = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments);
};
i[r].l = 1 * new Date();
a = s.createElement(o);
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
window.ga('create', 'UA-40881672-11', 'auto');
window.ga('send', 'pageview');
}
/* eslint-enable */
})();
}());
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+15 -14
View File
@@ -91,21 +91,22 @@
</header>
<main>
<video controls crossorigin playsinline poster="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg" id="player">
<!-- Video files -->
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" type="video/mp4" size="576">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4" type="video/mp4" size="720">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4" type="video/mp4" size="1080">
<!-- <source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4" type="video/mp4" size="1440"> -->
<div id="container">
<video controls crossorigin playsinline poster="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg" id="player">
<!-- Video files -->
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" type="video/mp4" size="576">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4" type="video/mp4" size="720">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4" type="video/mp4" size="1080">
<!-- Caption files -->
<track kind="captions" label="English" srclang="en" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt"
default>
<track kind="captions" label="Français" srclang="fr" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt">
<!-- Caption files -->
<track kind="captions" label="English" srclang="en" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt"
default>
<track kind="captions" label="Français" srclang="fr" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt">
<!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" download>Download</a>
</video>
<!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" download>Download</a>
</video>
</div>
<ul>
<li class="plyr__cite plyr__cite--video" hidden>
@@ -166,7 +167,7 @@
</svg>
<p>If you think Plyr's good,
<a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&amp;url=http%3A%2F%2Fplyr.io&amp;via=Sam_Potts"
target="_blank" data-shr-network="twitter">tweet it</a>
target="_blank" data-shr-network="twitter">tweet it</a> 👍
</p>
</aside>
+30 -64
View File
@@ -7,16 +7,17 @@
import Raven from 'raven-js';
(() => {
const isLive = window.location.host === 'plyr.io';
// Raven / Sentry
// For demo site (https://plyr.io) only
if (isLive) {
Raven.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
}
const { host } = window.location;
const env = {
prod: host === 'plyr.io',
dev: host === 'dev.plyr.io',
};
document.addEventListener('DOMContentLoaded', () => {
Raven.context(() => {
const selector = '#player';
const container = document.getElementById('container');
if (window.shr) {
window.shr.setup({
count: {
@@ -30,6 +31,10 @@ import Raven from 'raven-js';
// Remove class on blur
document.addEventListener('focusout', event => {
if (!event.target.classList || container.contains(event.target)) {
return;
}
event.target.classList.remove(tabClassName);
});
@@ -42,12 +47,18 @@ import Raven from 'raven-js';
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(() => {
document.activeElement.classList.add(tabClassName);
}, 0);
const focused = document.activeElement;
if (!focused || !focused.classList || container.contains(focused)) {
return;
}
focused.classList.add(tabClassName);
}, 10);
});
// Setup the player
const player = new Plyr('#player', {
const player = new Plyr(selector, {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',
@@ -57,57 +68,6 @@ import Raven from 'raven-js';
tooltips: {
controls: true,
},
clickToPlay: false,
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'duration',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
/* i18n: {
restart: '重新開始',
rewind: '快退{seektime}秒',
play: '播放',
pause: '暫停',
fastForward: '快進{seektime}秒',
seek: '尋求',
played: '發揮',
buffered: '緩衝的',
currentTime: '當前時間戳',
duration: '長短',
volume: '音量',
mute: '靜音',
unmute: '取消靜音',
enableCaptions: '開啟字幕',
disableCaptions: '關閉字幕',
enterFullscreen: '進入全螢幕',
exitFullscreen: '退出全螢幕',
frameTitle: '球員為{title}',
captions: '字幕',
settings: '設定',
speed: '速度',
normal: '正常',
quality: '質量',
loop: '循環',
start: 'Start',
end: 'End',
all: 'All',
reset: '重啟',
disabled: '殘',
enabled: '啟用',
advertisement: '廣告',
}, */
captions: {
active: true,
},
@@ -115,7 +75,7 @@ import Raven from 'raven-js';
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
},
ads: {
enabled: true,
enabled: env.prod || env.dev,
publisherId: '918848828995742',
},
});
@@ -311,11 +271,17 @@ import Raven from 'raven-js';
});
});
// Raven / Sentry
// For demo site (https://plyr.io) only
if (env.prod) {
Raven.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
}
// Google analytics
// For demo site (https://plyr.io) only
/* eslint-disable */
if (isLive) {
(function(i, s, o, g, r, a, m) {
if (env.prod) {
((i, s, o, g, r, a, m) => {
i.GoogleAnalyticsObject = r;
i[r] =
i[r] ||
+2 -1
View File
@@ -2,7 +2,8 @@
// Typography
// ==========================================================================
$font-sans-serif: 'Gordita', 'Avenir', 'Helvetica Neue', sans-serif;
$font-sans-serif: 'Gordita', 'Avenir', 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
$font-size-base: 15;
$font-size-small: 13;
+1 -1
View File
File diff suppressed because one or more lines are too long
+7429 -8000
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+10201 -13462
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="plyr-airplay" viewBox="0 0 18 18"><path d="M16 1H2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2H3V3h12v8h-2v2h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/><path d="M4 17h10l-5-6z"/></symbol><symbol id="plyr-captions-off" viewBox="0 0 18 18"><path d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3.3 0 .5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1H1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69zm7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69z" fill-rule="evenodd" fill-opacity=".5"/></symbol><symbol id="plyr-captions-on" viewBox="0 0 18 18"><path d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3.3 0 .5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1H1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69zm7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69z" fill-rule="evenodd"/></symbol><symbol id="plyr-enter-fullscreen" viewBox="0 0 18 18"><path d="M10 3h3.6l-4 4L11 8.4l4-4V8h2V1h-7zM7 9.6l-4 4V10H1v7h7v-2H4.4l4-4z"/></symbol><symbol id="plyr-exit-fullscreen" viewBox="0 0 18 18"><path d="M1 12h3.6l-4 4L2 17.4l4-4V17h2v-7H1zM16 .6l-4 4V1h-2v7h7V6h-3.6l4-4z"/></symbol><symbol id="plyr-fast-forward" viewBox="0 0 18 18"><path d="M7.875 7.171L0 1v16l7.875-6.171V17L18 9 7.875 1z"/></symbol><symbol id="plyr-muted" viewBox="0 0 18 18"><path d="M12.4 12.5l2.1-2.1 2.1 2.1 1.4-1.4L15.9 9 18 6.9l-1.4-1.4-2.1 2.1-2.1-2.1L11 6.9 13.1 9 11 11.1zM3.786 6.008H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601L3.786 6.008z"/></symbol><symbol id="plyr-pause" viewBox="0 0 18 18"><path d="M6 1H3c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1zM12 1c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1h-3z"/></symbol><symbol id="plyr-pip" viewBox="0 0 18 18"><path d="M13.293 3.293L7.022 9.564l1.414 1.414 6.271-6.271L17 7V1h-6z"/><path d="M13 15H3V5h5V3H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-6h-2v5z"/></symbol><symbol id="plyr-play" viewBox="0 0 18 18"><path d="M15.562 8.1L3.87.225C3.052-.337 2 .225 2 1.125v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"/></symbol><symbol id="plyr-restart" viewBox="0 0 18 18"><path d="M9.7 1.2l.7 6.4 2.1-2.1c1.9 1.9 1.9 5.1 0 7-.9 1-2.2 1.5-3.5 1.5-1.3 0-2.6-.5-3.5-1.5-1.9-1.9-1.9-5.1 0-7 .6-.6 1.4-1.1 2.3-1.3l-.6-1.9C6 2.6 4.9 3.2 4 4.1 1.3 6.8 1.3 11.2 4 14c1.3 1.3 3.1 2 4.9 2 1.9 0 3.6-.7 4.9-2 2.7-2.7 2.7-7.1 0-9.9L16 1.9l-6.3-.7z"/></symbol><symbol id="plyr-rewind" viewBox="0 0 18 18"><path d="M10.125 1L0 9l10.125 8v-6.171L18 17V1l-7.875 6.171z"/></symbol><symbol id="plyr-settings" viewBox="0 0 18 18"><path d="M16.135 7.784a2 2 0 0 1-1.23-2.969c.322-.536.225-.998-.094-1.316l-.31-.31c-.318-.318-.78-.415-1.316-.094a2 2 0 0 1-2.969-1.23C10.065 1.258 9.669 1 9.219 1h-.438c-.45 0-.845.258-.997.865a2 2 0 0 1-2.969 1.23c-.536-.322-.999-.225-1.317.093l-.31.31c-.318.318-.415.781-.093 1.317a2 2 0 0 1-1.23 2.969C1.26 7.935 1 8.33 1 8.781v.438c0 .45.258.845.865.997a2 2 0 0 1 1.23 2.969c-.322.536-.225.998.094 1.316l.31.31c.319.319.782.415 1.316.094a2 2 0 0 1 2.969 1.23c.151.607.547.865.997.865h.438c.45 0 .845-.258.997-.865a2 2 0 0 1 2.969-1.23c.535.321.997.225 1.316-.094l.31-.31c.318-.318.415-.781.094-1.316a2 2 0 0 1 1.23-2.969c.607-.151.865-.547.865-.997v-.438c0-.451-.26-.846-.865-.997zM9 12a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol id="plyr-volume" viewBox="0 0 18 18"><path d="M15.6 3.3c-.4-.4-1-.4-1.4 0-.4.4-.4 1 0 1.4C15.4 5.9 16 7.4 16 9c0 1.6-.6 3.1-1.8 4.3-.4.4-.4 1 0 1.4.2.2.5.3.7.3.3 0 .5-.1.7-.3C17.1 13.2 18 11.2 18 9s-.9-4.2-2.4-5.7z"/><path d="M11.282 5.282a.909.909 0 0 0 0 1.316c.735.735.995 1.458.995 2.402 0 .936-.425 1.917-.995 2.487a.909.909 0 0 0 0 1.316c.145.145.636.262 1.018.156a.725.725 0 0 0 .298-.156C13.773 11.733 14.13 10.16 14.13 9c0-.17-.002-.34-.011-.51-.053-.992-.319-2.005-1.522-3.208a.909.909 0 0 0-1.316 0zM3.786 6.008H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601L3.786 6.008z"/></symbol></svg>
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="plyr-airplay"><path d="M16 1H2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2H3V3h12v8h-2v2h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/><path d="M4 17h10l-5-6z"/></symbol><symbol id="plyr-captions-off"><path d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3.3 0 .5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1H1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69zm7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69z" fill-rule="evenodd" fill-opacity=".5"/></symbol><symbol id="plyr-captions-on"><path d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3.3 0 .5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1H1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69zm7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7 0 2.26 1.65 3.69 3.63 3.69z" fill-rule="evenodd"/></symbol><symbol id="plyr-enter-fullscreen"><path d="M10 3h3.6l-4 4L11 8.4l4-4V8h2V1h-7zM7 9.6l-4 4V10H1v7h7v-2H4.4l4-4z"/></symbol><symbol id="plyr-exit-fullscreen"><path d="M1 12h3.6l-4 4L2 17.4l4-4V17h2v-7H1zM16 .6l-4 4V1h-2v7h7V6h-3.6l4-4z"/></symbol><symbol id="plyr-fast-forward"><path d="M7.875 7.171L0 1v16l7.875-6.171V17L18 9 7.875 1z"/></symbol><symbol id="plyr-muted"><path d="M12.4 12.5l2.1-2.1 2.1 2.1 1.4-1.4L15.9 9 18 6.9l-1.4-1.4-2.1 2.1-2.1-2.1L11 6.9 13.1 9 11 11.1zM3.786 6.008H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601L3.786 6.008z"/></symbol><symbol id="plyr-pause"><path d="M6 1H3c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1zM12 1c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1h-3z"/></symbol><symbol id="plyr-pip"><path d="M13.293 3.293L7.022 9.564l1.414 1.414 6.271-6.271L17 7V1h-6z"/><path d="M13 15H3V5h5V3H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-6h-2v5z"/></symbol><symbol id="plyr-play"><path d="M15.562 8.1L3.87.225C3.052-.337 2 .225 2 1.125v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"/></symbol><symbol id="plyr-restart"><path d="M9.7 1.2l.7 6.4 2.1-2.1c1.9 1.9 1.9 5.1 0 7-.9 1-2.2 1.5-3.5 1.5-1.3 0-2.6-.5-3.5-1.5-1.9-1.9-1.9-5.1 0-7 .6-.6 1.4-1.1 2.3-1.3l-.6-1.9C6 2.6 4.9 3.2 4 4.1 1.3 6.8 1.3 11.2 4 14c1.3 1.3 3.1 2 4.9 2 1.9 0 3.6-.7 4.9-2 2.7-2.7 2.7-7.1 0-9.9L16 1.9l-6.3-.7z"/></symbol><symbol id="plyr-rewind"><path d="M10.125 1L0 9l10.125 8v-6.171L18 17V1l-7.875 6.171z"/></symbol><symbol id="plyr-settings"><path d="M16.135 7.784a2 2 0 0 1-1.23-2.969c.322-.536.225-.998-.094-1.316l-.31-.31c-.318-.318-.78-.415-1.316-.094a2 2 0 0 1-2.969-1.23C10.065 1.258 9.669 1 9.219 1h-.438c-.45 0-.845.258-.997.865a2 2 0 0 1-2.969 1.23c-.536-.322-.999-.225-1.317.093l-.31.31c-.318.318-.415.781-.093 1.317a2 2 0 0 1-1.23 2.969C1.26 7.935 1 8.33 1 8.781v.438c0 .45.258.845.865.997a2 2 0 0 1 1.23 2.969c-.322.536-.225.998.094 1.316l.31.31c.319.319.782.415 1.316.094a2 2 0 0 1 2.969 1.23c.151.607.547.865.997.865h.438c.45 0 .845-.258.997-.865a2 2 0 0 1 2.969-1.23c.535.321.997.225 1.316-.094l.31-.31c.318-.318.415-.781.094-1.316a2 2 0 0 1 1.23-2.969c.607-.151.865-.547.865-.997v-.438c0-.451-.26-.846-.865-.997zM9 12a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol id="plyr-volume"><path d="M15.6 3.3c-.4-.4-1-.4-1.4 0-.4.4-.4 1 0 1.4C15.4 5.9 16 7.4 16 9c0 1.6-.6 3.1-1.8 4.3-.4.4-.4 1 0 1.4.2.2.5.3.7.3.3 0 .5-.1.7-.3C17.1 13.2 18 11.2 18 9s-.9-4.2-2.4-5.7z"/><path d="M11.282 5.282a.909.909 0 0 0 0 1.316c.735.735.995 1.458.995 2.402 0 .936-.425 1.917-.995 2.487a.909.909 0 0 0 0 1.316c.145.145.636.262 1.018.156a.725.725 0 0 0 .298-.156C13.773 11.733 14.13 10.16 14.13 9c0-.17-.002-.34-.011-.51-.053-.992-.319-2.005-1.522-3.208a.909.909 0 0 0-1.316 0zM3.786 6.008H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601L3.786 6.008z"/></symbol></svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

+112 -74
View File
@@ -29,18 +29,12 @@ const sourcemaps = require('gulp-sourcemaps');
const uglify = require('gulp-uglify-es').default;
const commonjs = require('rollup-plugin-commonjs');
const resolve = require('rollup-plugin-node-resolve');
const FastlyPurge = require('fastly-purge');
const through = require('through2');
const bundles = require('./bundles.json');
const pkg = require('./package.json');
// Get AWS config
let aws = {};
try {
aws = require('./aws.json'); //eslint-disable-line
} catch (e) {
// Do nothing
}
const minSuffix = '.min';
// Paths
@@ -50,7 +44,7 @@ const paths = {
// Source paths
src: {
sass: path.join(root, 'src/sass/**/*.scss'),
js: path.join(root, 'src/js/**/*'),
js: path.join(root, 'src/js/**/*.js'),
sprite: path.join(root, 'src/sprite/*.svg'),
},
@@ -61,7 +55,7 @@ const paths = {
// Source paths
src: {
sass: path.join(root, 'demo/src/sass/**/*.scss'),
js: path.join(root, 'demo/src/js/**/*'),
js: path.join(root, 'demo/src/js/**/*.js'),
},
// Output paths
@@ -94,28 +88,26 @@ const sizeOptions = { showFiles: true, gzip: true };
const browsers = ['> 1%'];
// Babel config
const babelrc = {
presets: [[
'env',
{
targets: {
browsers,
const babelrc = (polyfill = false) => ({
presets: [
[
'@babel/preset-env',
{
targets: {
browsers,
},
useBuiltIns: polyfill ? 'usage' : false,
modules: false,
},
useBuiltIns: true,
modules: false,
},
]],
plugins: ['external-helpers'],
],
],
babelrc: false,
exclude: 'node_modules/**',
};
});
// Clean out /dist
gulp.task('clean', () => {
const dirs = [
paths.plyr.output,
paths.demo.output,
].map(dir => path.join(dir, '**/*'));
const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*'));
// Don't delete the mp4
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
@@ -129,6 +121,7 @@ const build = {
const name = `js:${key}`;
tasks.js.push(name);
const { output } = paths[bundle];
const polyfill = name.includes('polyfilled');
return gulp.task(name, () =>
gulp
@@ -138,11 +131,7 @@ const build = {
.pipe(
rollup(
{
plugins: [
resolve(),
commonjs(),
babel(babelrc),
],
plugins: [resolve(), commonjs(), babel(babelrc(polyfill))],
},
options,
),
@@ -187,9 +176,11 @@ const build = {
.src(paths[bundle].src.sprite)
.pipe(
svgmin({
plugins: [{
removeDesc: true,
}],
plugins: [
{
removeDesc: true,
},
],
}),
)
.pipe(svgstore())
@@ -238,9 +229,18 @@ gulp.task('default', () => {
// Publish a version to CDN and demo
// --------------------------------------------
// If aws is setup
if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
// Get deployment config
let credentials = {};
try {
credentials = require('./credentials.json'); //eslint-disable-line
} catch (e) {
// Do nothing
}
// If deployment is setup
if (Object.keys(credentials).includes('aws') && Object.keys(credentials).includes('fastly')) {
const { version } = pkg;
const { aws, fastly } = credentials;
// Get branch info
const branch = {
@@ -248,10 +248,6 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
master: 'master',
develop: 'develop',
};
const allowed = [
branch.master,
branch.develop,
];
const maxAge = 31536000; // 1 year
const options = {
@@ -279,21 +275,40 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
},
};
const regex = '(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?';
const regex =
'(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?';
const semver = new RegExp(`v${regex}`, 'gi');
const localPath = new RegExp('(../)?dist', 'gi');
const versionPath = `https://${aws.cdn.domain}/${version}`;
const cdnpath = new RegExp(`${aws.cdn.domain}/${regex}/`, 'gi');
const renameFile = rename(p => {
p.basename = p.basename.replace(minSuffix, ''); // eslint-disable-line
p.dirname = p.dirname.replace('.', version); // eslint-disable-line
});
// Check we're on the correct branch to deploy
const canDeploy = () => {
const allowed = [branch.master, branch.develop];
if (!allowed.includes(branch.current)) {
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
return false;
}
return true;
};
gulp.task('version', () => {
if (!canDeploy()) {
return null;
}
console.log(`Updating versions to '${version}'...`);
// Replace versioned URLs in source
const files = [
'plyr.js',
'plyr.polyfilled.js',
'defaults.js',
];
const files = ['plyr.js', 'plyr.polyfilled.js', 'defaults.js'];
return gulp
.src(files.map(file => path.join(root, `src/js/${file}`)))
@@ -304,8 +319,7 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
// Publish version to CDN bucket
gulp.task('cdn', () => {
if (!allowed.includes(branch.current)) {
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
if (!canDeploy()) {
return null;
}
@@ -315,14 +329,14 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
return (
gulp
.src(paths.upload)
.pipe(
rename(p => {
p.basename = p.basename.replace(minSuffix, ''); // eslint-disable-line
p.dirname = p.dirname.replace('.', version); // eslint-disable-line
}),
)
.pipe(renameFile)
// Remove min suffix from source map URL
.pipe(replace(/sourceMappingURL=([\w-?.]+)/, (match, p1) => `sourceMappingURL=${p1.replace(minSuffix, '')}`))
.pipe(
replace(
/sourceMappingURL=([\w-?.]+)/,
(match, p1) => `sourceMappingURL=${p1.replace(minSuffix, '')}`,
),
)
.pipe(
size({
showFiles: true,
@@ -334,18 +348,46 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
);
});
// Purge the fastly cache incase any 403/404 are cached
gulp.task('purge', () => {
const list = [];
return gulp
.src(paths.upload)
.pipe(
through.obj((file, enc, cb) => {
const filename = file.path.split('/').pop();
list.push(`${versionPath}/${filename}`);
cb(null);
}),
)
.on('end', () => {
const purge = new FastlyPurge(fastly.token);
list.forEach(url => {
console.log(`Purging ${url}...`);
purge.url(url, (error, result) => {
if (error) {
console.log(error);
} else if (result) {
console.log(result);
}
});
});
});
});
// Publish to demo bucket
gulp.task('demo', () => {
if (!allowed.includes(branch.current)) {
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
if (!canDeploy()) {
return null;
}
console.log(`Uploading '${version}' demo to ${aws.demo.domain}...`);
// Replace versioned files in readme.md
gulp
.src([`${root}/readme.md`])
gulp.src([`${root}/readme.md`])
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
.pipe(gulp.dest(root));
@@ -359,8 +401,7 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
pages.push(error);
}
gulp
.src(pages)
gulp.src(pages)
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.demo, options.demo));
@@ -399,22 +440,19 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
}));
}); */
// Open the demo site to check it's sweet
gulp.task('open', () => {
console.log(`Opening ${aws.demo.domain}...`);
// A file must be specified or gulp will skip the task
// Doesn't matter which file since we set the URL above
// Weird, I know...
return gulp.src([`${paths.demo.root}index.html`]).pipe(
open('', {
url: `http://${aws.demo.domain}`,
// Open the demo site to check it's ok
gulp.task('open', callback => {
gulp.src(__filename).pipe(
open({
uri: `https://${aws.demo.domain}`,
}),
);
callback();
});
// Do everything
gulp.task('publish', callback => {
run('version', tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'cdn', 'demo', callback);
});
gulp.task('deploy', () =>
run('version', tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'cdn', 'purge', 'demo', 'open'),
);
}
+31 -31
View File
@@ -1,8 +1,7 @@
{
"name": "plyr",
"version": "3.3.22",
"description":
"A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"version": "3.4.4",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>",
"keywords": [
@@ -37,56 +36,57 @@
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.7.0",
"babel-eslint": "^9.0.0",
"@babel/preset-env": "^7.1.0",
"del": "^3.0.0",
"eslint": "^5.1.0",
"eslint-config-airbnb-base": "^13.0.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.13.0",
"eslint": "^5.6.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^3.1.0",
"eslint-plugin-import": "^2.14.0",
"fastly-purge": "^1.0.1",
"git-branch": "^2.0.1",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^5.0.0",
"gulp-autoprefixer": "^6.0.0",
"gulp-better-rollup": "^3.3.0",
"gulp-clean-css": "^3.9.4",
"gulp-clean-css": "^3.10.0",
"gulp-concat": "^2.6.1",
"gulp-filter": "^5.1.0",
"gulp-header": "^2.0.5",
"gulp-open": "^3.0.1",
"gulp-postcss": "^7.0.1",
"gulp-rename": "^1.3.0",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"gulp-replace": "^1.0.0",
"gulp-s3": "^0.11.0",
"gulp-sass": "^4.0.1",
"gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-svgmin": "^1.2.4",
"gulp-svgstore": "^6.1.1",
"gulp-svgmin": "^2.1.0",
"gulp-svgstore": "^7.0.0",
"gulp-uglify-es": "^1.0.4",
"gulp-util": "^3.0.8",
"postcss-custom-properties": "^7.0.0",
"postcss-custom-properties": "^8.0.5",
"prettier-eslint": "^8.8.2",
"prettier-stylelint": "^0.4.2",
"remark-cli": "^5.0.0",
"remark-validate-links": "^7.0.0",
"rollup-plugin-babel": "^3.0.7",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.3.0",
"remark-validate-links": "^7.1.0",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-node-resolve": "^3.4.0",
"run-sequence": "^2.2.1",
"stylelint": "^9.3.0",
"stylelint-config-prettier": "^3.3.0",
"stylelint": "^9.5.0",
"stylelint-config-prettier": "^4.0.0",
"stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.0.0",
"stylelint-order": "^0.8.1",
"stylelint-scss": "^3.1.3",
"stylelint-selector-bem-pattern": "^2.0.0"
"stylelint-config-sass-guidelines": "^5.2.0",
"stylelint-order": "^1.0.0",
"stylelint-scss": "^3.3.1",
"stylelint-selector-bem-pattern": "^2.0.0",
"through2": "^2.0.3"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0",
"core-js": "^2.5.7",
"custom-event-polyfill": "^1.0.6",
"loadjs": "^3.5.4",
"raven-js": "^3.26.3",
"url-polyfill": "^1.0.13"
"raven-js": "^3.27.0",
"url-polyfill": "^1.1.0"
}
}
+94 -95
View File
@@ -8,26 +8,26 @@ A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo medi
## Features
* **Accessible** - full support for VTT captions and screen readers
* **[Customisable](#html)** - make the player look how you want with the markup you want
* **Good HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
- **Accessible** - full support for VTT captions and screen readers
- **[Customisable](#html)** - make the player look how you want with the markup you want
- **Good HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
`<span>` or `<a href="#">` button hacks
* **Responsive** - works with any screen size
* **HTML Video & Audio** - support for both formats
* **[Embedded Video](#embeds)** - support for YouTube and Vimeo video playback
* **[Monetization](#ads)** - make money from your videos
* **[Streaming](#try-plyr-online)** - support for hls.js, Shaka and dash.js streaming playback
* **[API](#api)** - toggle playback, volume, seeking, and more through a standardized API
* **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
* **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
* **[Shortcuts](#shortcuts)** - supports keyboard shortcuts
* **Picture-in-Picture** - supports Safari's picture-in-picture mode
* **Playsinline** - supports the `playsinline` attribute
* **Speed controls** - adjust speed on the fly
* **Multiple captions** - support for multiple caption tracks
* **i18n support** - support for internationalization of controls
* **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required
* **SASS** - to include in your build processes
- **Responsive** - works with any screen size
- **HTML Video & Audio** - support for both formats
- **[Embedded Video](#embeds)** - support for YouTube and Vimeo video playback
- **[Monetization](#ads)** - make money from your videos
- **[Streaming](#try-plyr-online)** - support for hls.js, Shaka and dash.js streaming playback
- **[API](#api)** - toggle playback, volume, seeking, and more through a standardized API
- **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
- **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
- **[Shortcuts](#shortcuts)** - supports keyboard shortcuts
- **Picture-in-Picture** - supports Safari's picture-in-picture mode
- **Playsinline** - supports the `playsinline` attribute
- **Speed controls** - adjust speed on the fly
- **Multiple captions** - support for multiple caption tracks
- **i18n support** - support for internationalization of controls
- **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required
- **SASS** - to include in your build processes
Oh and yes, it works with Bootstrap.
@@ -80,7 +80,7 @@ Plyr extends upon the standard [HTML5 media element](https://developer.mozilla.o
</audio>
```
For YouTube and Vimeo players, Plyr uses progressive enhancement to enhance the default `<iframe>` embeds. Below are some examples. The `plyr__video-embed` classname will make the embed responsive. You can add the `autoplay`, `loop` and `playsinline` (YouTube only) query parameters to the URL and they will be set as config options automatically. For YouTube, the `origin` should be updated to reflect the domain you're hosting the embed on, or you can opt to omit it.
For YouTube and Vimeo players, Plyr uses progressive enhancement to enhance the default `<iframe>` embeds. Below are some examples. The `plyr__video-embed` classname will make the embed responsive. You can add the `autoplay`, `loop`, `hl` (YouTube only) and `playsinline` (YouTube only) query parameters to the URL and they will be set as config options automatically. For YouTube, the `origin` should be updated to reflect the domain you're hosting the embed on, or you can opt to omit it.
#### YouTube embed
@@ -132,13 +132,13 @@ See [initialising](#initialising) for more information on advanced setups.
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
```html
<script src="https://cdn.plyr.io/3.3.22/plyr.js"></script>
<script src="https://cdn.plyr.io/3.4.4/plyr.js"></script>
```
...or...
```html
<script src="https://cdn.plyr.io/3.3.22/plyr.polyfilled.js"></script>
<script src="https://cdn.plyr.io/3.4.4/plyr.polyfilled.js"></script>
```
### CSS
@@ -152,21 +152,21 @@ Include the `plyr.css` stylsheet into your `<head>`
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.3.22/plyr.css">
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.4/plyr.css">
```
### SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.3.22/plyr.svg`.
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.4/plyr.svg`.
## Ads
Plyr has partnered up with [vi.ai](http://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
Plyr has partnered up with [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
* [Sign up for a vi.ai account](http://vi.ai/publisher-video-monetization/?aid=plyrio)
* Grab your publisher ID from the code snippet
* Enable ads in the [config options](#options) and enter your publisher ID
- [Sign up for a vi.ai account](https://vi.ai/publisher-video-monetization/?aid=plyrio)
- Grab your publisher ID from the code snippet
- Enable ads in the [config options](#options) and enter your publisher ID
Any questions regarding the ads can be sent straight to vi.ai and any issues with rendering raised through GitHub issues.
@@ -213,10 +213,10 @@ WebVTT captions are supported. To add a caption track, check the HTML example ab
You can specify a range of arguments for the constructor to use:
* A CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
* A [`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
* A [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)
* A [jQuery](https://jquery.com) object
- A CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
- A [`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
- A [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)
- A [jQuery](https://jquery.com) object
_Note_: If a `NodeList`, `Array`, or jQuery object are passed, the first element will be used for setup. To setup multiple players, see [setting up multiple players](#setting-up-multiple-players) below.
@@ -286,11 +286,11 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
| `debug` | Boolean | `false` | Display debugging information in the console |
| `controls` | Array, Function or Element | `['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']` | 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](controls.md) for more info on how the html needs to be structured. |
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If you're using the default controls are used then you can specify which settings to show in the menu |
| `i18n` | Object | See [defaults.js](/src/js/config/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
| `i18n` | Object | See [defaults.js](/src/js/config/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
| `loadSprite` | Boolean | `true` | Load the SVG sprite specified as the `iconUrl` option (if a URL). If `false`, it is assumed you are handling sprite loading yourself. |
| `iconUrl` | String | `null` | Specify a URL or path to the SVG sprite. See the [SVG section](#svg) for more info. |
| `iconPrefix` | String | `plyr` | 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. |
| `blankUrl` | String | `https://cdn.plyr.io/static/blank.mp4` | Specify a URL or path to a blank video file used to properly cancel network requests. |
| `blankVideo` | String | `https://cdn.plyr.io/static/blank.mp4` | Specify a URL or path to a blank video file used to properly cancel network requests. |
| `autoplay` | Boolean | `false` | 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. |
| `autopause`&sup1; | Boolean | `true` | Only allow one player playing at once. |
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
@@ -307,7 +307,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
| `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. |
| `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. |
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. |
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
@@ -365,9 +365,9 @@ player.fullscreen.enter(); // Enter fullscreen
| `fullscreen.exit()` | - | Exit fullscreen. |
| `fullscreen.toggle()` | - | Toggle fullscreen. |
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
| `toggleControls(toggle)` | Boolean | Toggle the controls (video only). Takes optional truthy value to force it on/off. |
| `toggleControls(toggle)` | Boolean | Toggle the controls (video only). Takes optional truthy value to force it on/off. |
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
| `once(event, function)` | String, Function | Add an event listener for the specified event once. |
| `once(event, function)` | String, Function | Add an event listener for the specified event once. |
| `off(event, function)` | String, Function | Remove an event listener for the specified event. |
| `supports(type)` | String | Check support for a mime type. |
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
@@ -391,32 +391,32 @@ player.currentTime; // 10
player.fullscreen.active; // false;
```
| Property | Getter | Setter | Description |
| -------------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
| `stopped` | ✓ | - | Returns a boolean indicating if the current player is stopped. |
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
| `buffered` | ✓ | - | Returns a float between 0 and 1 indicating how much of the media is buffered |
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
| `duration` | ✓ | - | Returns the duration for the current media. |
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
| `speed` | ✓ | ✓ | 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. |
| `quality`&sup1; | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#the-source-setter) below for examples. |
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
| Property | Getter | Setter | Description |
| -------------------- | ------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
| `stopped` | ✓ | - | Returns a boolean indicating if the current player is stopped. |
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
| `buffered` | ✓ | - | Returns a float between 0 and 1 indicating how much of the media is buffered |
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
| `duration` | ✓ | - | Returns the duration for the current media. |
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
| `speed` | ✓ | ✓ | 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. |
| `quality`&sup1; | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#the-source-setter) below for examples. |
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter 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. |
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
| `pip` | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+. |
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
| `pip` | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+. |
1. YouTube only. HTML5 will follow.
2. HTML5 only
@@ -565,6 +565,7 @@ player.on('ready', event => {
| `loadstart` | Sent when loading of the media begins. |
| `loadeddata` | The first frame of the media has finished loading. |
| `loadedmetadata` | The media's metadata has finished loading; all attributes now contain as much useful information as they're going to. |
| `qualitychange` | The quality of playback has changed. |
| `canplay` | Sent when enough data is available that the media can be played, at least for a couple of frames. This corresponds to the `HAVE_ENOUGH_DATA` `readyState`. |
| `canplaythrough` | Sent when the ready state changes to `CAN_PLAY_THROUGH`, indicating that the entire media can be played without interruption, assuming the download rate remains at least at the current level. _Note:_ Manually setting the `currentTime` will eventually fire a `canplaythrough` event in firefox. Other browsers might not fire this event. |
| `stalled` | Sent when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming. |
@@ -575,11 +576,9 @@ player.on('ready', event => {
#### YouTube only
| Event Type | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information. |
| `qualitychange` | The quality of playback has changed. |
| `qualityrequested` | A change to playback quality has been requested. _Note:_ A change to quality can only be _requested_ via the API. There is no guarantee the quality will change to the level requested. You should listen to the `qualitychange` event for true changes. |
| Event Type | Description |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information. |
_Note:_ These events also bubble up the DOM. The event target will be the container element.
@@ -591,8 +590,8 @@ YouTube and Vimeo are currently supported and function much like a HTML5 video.
to access the API's directly. You can do so via the `embed` property of your player object - e.g. `player.embed`. You can then use the relevant methods from the
third party APIs. More info on the respective API's here:
* [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
* [Vimeo player.js Reference](https://github.com/vimeo/player.js)
- [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
- [Vimeo player.js Reference](https://github.com/vimeo/player.js)
_Note_: Not all API methods may work 100%. Your mileage may vary. It's better to use the Plyr API where possible.
@@ -652,9 +651,9 @@ const supported = Plyr.supported('video', 'html5', true);
The arguments are:
* Media type (`audio` or `video`)
* Provider (`html5`, `youtube` or `vimeo`)
* Whether the player has the `playsinline` attribute (only applicable to iOS 10+)
- Media type (`audio` or `video`)
- Provider (`html5`, `youtube` or `vimeo`)
- Whether the player has the `playsinline` attribute (only applicable to iOS 10+)
### Disable support programatically
@@ -687,33 +686,33 @@ Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me]
Plyr costs money to run, not only my time. I donate my time for free as I enjoy building Plyr but unfortunately have to pay for domains, hosting, and more. Any help with costs is appreciated...
* [Donate via Patron](https://www.patreon.com/plyr)
* [Donate via PayPal](https://www.paypal.me/pottsy/20usd)
- [Donate via Patreon](https://www.patreon.com/plyr)
- [Donate via PayPal](https://www.paypal.me/pottsy/20usd)
## Mentions
* [ProductHunt](https://www.producthunt.com/tech/plyr)
* [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
* [HTML5 Weekly #177](http://html5weekly.com/issues/177)
* [Responsive Design #149](http://us4.campaign-archive2.com/?u=559bc631fe5294fc66f5f7f89&id=451a61490f)
* [Web Design Weekly #174](https://web-design-weekly.com/2015/02/24/web-design-weekly-174/)
* [Hacker News](https://news.ycombinator.com/item?id=9136774)
* [Web Platform Daily](http://webplatformdaily.org/releases/2015-03-04)
* [LayerVault Designer News](https://news.layervault.com/stories/45394-plyr--a-simple-html5-media-player)
* [The Treehouse Show #131](https://teamtreehouse.com/library/episode-131-origami-react-responsive-hero-images)
* [noupe.com](http://www.noupe.com/design/html5-plyr-is-a-responsive-and-accessible-video-player-94389.html)
- [ProductHunt](https://www.producthunt.com/tech/plyr)
- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
- [HTML5 Weekly #177](http://html5weekly.com/issues/177)
- [Responsive Design #149](http://us4.campaign-archive2.com/?u=559bc631fe5294fc66f5f7f89&id=451a61490f)
- [Web Design Weekly #174](https://web-design-weekly.com/2015/02/24/web-design-weekly-174/)
- [Hacker News](https://news.ycombinator.com/item?id=9136774)
- [Web Platform Daily](http://webplatformdaily.org/releases/2015-03-04)
- [LayerVault Designer News](https://news.layervault.com/stories/45394-plyr--a-simple-html5-media-player)
- [The Treehouse Show #131](https://teamtreehouse.com/library/episode-131-origami-react-responsive-hero-images)
- [noupe.com](http://www.noupe.com/design/html5-plyr-is-a-responsive-and-accessible-video-player-94389.html)
## Used by
* [Selz.com](https://selz.com)
* [Peugeot.fr](http://www.peugeot.fr/marque-et-technologie/technologies/peugeot-i-cockpit.html)
* [Peugeot.de](http://www.peugeot.de/modelle/modellberater/208-3-turer/fotos-videos.html)
* [TomTom.com](http://prioritydriving.tomtom.com/)
* [DIGBMX](http://digbmx.com/)
* [Grime Archive](https://grimearchive.com/)
* [koel - A personal music streaming server that works.](http://koel.phanan.net/)
* [Oscar Radio](http://oscar-radio.xyz/)
* [Sparkk TV](https://www.sparkktv.com/)
- [Selz.com](https://selz.com)
- [Peugeot.fr](http://www.peugeot.fr/marque-et-technologie/technologies/peugeot-i-cockpit.html)
- [Peugeot.de](http://www.peugeot.de/modelle/modellberater/208-3-turer/fotos-videos.html)
- [TomTom.com](http://prioritydriving.tomtom.com/)
- [DIGBMX](http://digbmx.com/)
- [Grime Archive](https://grimearchive.com/)
- [koel - A personal music streaming server that works.](http://koel.phanan.net/)
- [Oscar Radio](http://oscar-radio.xyz/)
- [Sparkk TV](https://www.sparkktv.com/)
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
@@ -721,8 +720,8 @@ Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the abo
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)
* [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
- [PayPal's Accessible HTML5 Video Player](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
+3 -4
View File
@@ -4,7 +4,6 @@
// ==========================================================================
import controls from './controls';
import i18n from './i18n';
import support from './support';
import { dedupe } from './utils/arrays';
import browser from './utils/browser';
@@ -18,6 +17,7 @@ import {
} from './utils/elements';
import { on, triggerEvent } from './utils/events';
import fetch from './utils/fetch';
import i18n from './utils/i18n';
import is from './utils/is';
import { getHTML } from './utils/strings';
import { parseUrl } from './utils/urls';
@@ -83,9 +83,8 @@ const captions = {
// * active: The state preferred by user settings or config
// * toggled: The real captions state
const languages = dedupe(
Array.from(navigator.languages || navigator.language || navigator.userLanguage).map(language => language.split('-')[0]),
);
const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
const languages = dedupe(browserLanguages.map(language => language.split('-')[0]));
let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
+6 -14
View File
@@ -68,19 +68,7 @@ const defaults = {
// Quality default
quality: {
default: 576,
options: [
4320,
2880,
2160,
1440,
1080,
720,
576,
480,
360,
240,
'default', // YouTube's "auto"
],
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
},
// Set loops
@@ -268,8 +256,9 @@ const defaults = {
// YouTube
'statechange',
// Quality
'qualitychange',
'qualityrequested',
// Ads
'adsloaded',
@@ -354,6 +343,9 @@ const defaults = {
isTouch: 'plyr--is-touch',
uiSupported: 'plyr--full-ui',
noTransition: 'plyr--no-transition',
display: {
time: 'plyr__time',
},
menu: {
value: 'plyr__menu__value',
badge: 'plyr__badge',
+496 -328
View File
File diff suppressed because it is too large Load Diff
+34 -3
View File
@@ -1,8 +1,10 @@
// ==========================================================================
// Fullscreen wrapper
// https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API#prefixing
// https://webkit.org/blog/7929/designing-websites-for-iphone-x/
// ==========================================================================
import { repaint } from './utils/animation';
import browser from './utils/browser';
import { hasClass, toggleClass, trapFocus } from './utils/elements';
import { on, triggerEvent } from './utils/events';
@@ -45,6 +47,37 @@ function toggleFallback(toggle = false) {
// Toggle class hook
toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
// Force full viewport on iPhone X+
if (browser.isIos) {
let viewport = document.head.querySelector('meta[name="viewport"]');
const property = 'viewport-fit=cover';
// Inject the viewport meta if required
if (!viewport) {
viewport = document.createElement('meta');
viewport.setAttribute('name', 'viewport');
}
// Check if the property already exists
const hasProperty = is.string(viewport.content) && viewport.content.includes(property);
if (toggle) {
this.cleanupViewport = !hasProperty;
if (!hasProperty) {
viewport.content += `,${property}`;
}
} else if (this.cleanupViewport) {
viewport.content = viewport.content
.split(',')
.filter(part => part.trim() !== property)
.join(',');
}
// Force a repaint as sometimes Safari doesn't want to fill the screen
setTimeout(() => repaint(this.target), 100);
}
// Toggle button and fire events
onChange.call(this);
}
@@ -177,9 +210,7 @@ class Fullscreen {
// iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) {
this.target.webkitEnterFullscreen();
}
this.target.webkitEnterFullscreen();
} else if (!Fullscreen.native) {
toggleFallback.call(this, true);
} else if (!this.prefix) {
+3
View File
@@ -82,6 +82,9 @@ const html5 = {
triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: input,
});
// Save to storage
player.storage.set({ quality: input });
},
});
},
+331 -264
View File
@@ -4,8 +4,9 @@
import controls from './controls';
import ui from './ui';
import { repaint } from './utils/animation';
import browser from './utils/browser';
import { getElement, getElements, getFocusElement, matches, toggleClass, toggleHidden } from './utils/elements';
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements';
import { on, once, toggleListener, triggerEvent } from './utils/events';
import is from './utils/is';
@@ -13,14 +14,19 @@ class Listeners {
constructor(player) {
this.player = player;
this.lastKey = null;
this.focusTimer = null;
this.lastKeyDown = null;
this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
this.setTabFocus = this.setTabFocus.bind(this);
this.firstTouch = this.firstTouch.bind(this);
}
// Handle key presses
handleKey(event) {
const { player } = this;
const { elements } = player;
const code = event.keyCode ? event.keyCode : event.which;
const pressed = event.type === 'keydown';
const repeat = pressed && code === this.lastKey;
@@ -39,27 +45,32 @@ class Listeners {
// Seek by the number keys
const seekByKey = () => {
// Divide the max duration into 10th's and times by the number value
this.player.currentTime = this.player.duration / 10 * (code - 48);
player.currentTime = (player.duration / 10) * (code - 48);
};
// Handle the key on keydown
// Reset on keyup
if (pressed) {
// Which keycodes 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];
// Check focused element
// and if the focused element is not editable (e.g. text input)
// and any that accept key input http://webaim.org/techniques/keyboard/
const focused = getFocusElement();
if (
is.element(focused) &&
(focused !== this.player.elements.inputs.seek &&
matches(focused, this.player.config.selectors.editable))
) {
return;
const focused = document.activeElement;
if (is.element(focused)) {
const { editable } = player.config.selectors;
const { seek } = elements.inputs;
if (focused !== seek && matches(focused, editable)) {
return;
}
if (event.which === 32 && matches(focused, 'button, [role^="menuitem"]')) {
return;
}
}
// Which keycodes 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];
// If the code is found prevent default (e.g. prevent scrolling for arrows)
if (preventDefault.includes(code)) {
event.preventDefault();
@@ -87,55 +98,55 @@ class Listeners {
case 75:
// Space and K key
if (!repeat) {
this.player.togglePlay();
player.togglePlay();
}
break;
case 38:
// Arrow up
this.player.increaseVolume(0.1);
player.increaseVolume(0.1);
break;
case 40:
// Arrow down
this.player.decreaseVolume(0.1);
player.decreaseVolume(0.1);
break;
case 77:
// M key
if (!repeat) {
this.player.muted = !this.player.muted;
player.muted = !player.muted;
}
break;
case 39:
// Arrow forward
this.player.forward();
player.forward();
break;
case 37:
// Arrow back
this.player.rewind();
player.rewind();
break;
case 70:
// F key
this.player.fullscreen.toggle();
player.fullscreen.toggle();
break;
case 67:
// C key
if (!repeat) {
this.player.toggleCaptions();
player.toggleCaptions();
}
break;
case 76:
// L key
this.player.loop = !this.player.loop;
player.loop = !player.loop;
break;
/* case 73:
/* case 73:
this.setLoop('start');
break;
@@ -153,8 +164,8 @@ class Listeners {
// Escape is handle natively when in full screen
// So we only need to worry about non native
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle();
if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
player.fullscreen.toggle();
}
// Store last code for next cycle
@@ -171,61 +182,102 @@ class Listeners {
// Device is touch enabled
firstTouch() {
this.player.touch = true;
const { player } = this;
const { elements } = player;
player.touch = true;
// Add touch class
toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
toggleClass(elements.container, player.config.classNames.isTouch, true);
}
setTabFocus(event) {
const { player } = this;
const { elements } = player;
clearTimeout(this.focusTimer);
// Ignore any key other than tab
if (event.type === 'keydown' && event.which !== 9) {
return;
}
// Store reference to event timeStamp
if (event.type === 'keydown') {
this.lastKeyDown = event.timeStamp;
}
// Remove current classes
const removeCurrent = () => {
const className = player.config.classNames.tabFocus;
const current = getElements.call(player, `.${className}`);
toggleClass(current, className, false);
};
// Determine if a key was pressed to trigger this event
const wasKeyDown = event.timeStamp - this.lastKeyDown <= 20;
// Ignore focus events if a key was pressed prior
if (event.type === 'focus' && !wasKeyDown) {
return;
}
// Remove all current
removeCurrent();
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(() => {
const focused = document.activeElement;
// Ignore if current focus element isn't inside the player
if (!elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
// Global window & document listeners
global(toggle = true) {
const { player } = this;
// Keyboard shortcuts
if (this.player.config.keyboard.global) {
toggleListener.call(this.player, window, 'keydown keyup', this.handleKey, toggle, false);
if (player.config.keyboard.global) {
toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false);
}
// Click anywhere closes menu
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle);
toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch);
once.call(player, document.body, 'touchstart', this.firstTouch);
// Tab focus detection
toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
}
// Container listeners
container() {
const { player } = this;
const { elements } = player;
// Keyboard shortcuts
if (!this.player.config.keyboard.global && this.player.config.keyboard.focused) {
on.call(this.player, this.player.elements.container, 'keydown keyup', this.handleKey, false);
if (!player.config.keyboard.global && player.config.keyboard.focused) {
on.call(player, elements.container, 'keydown keyup', this.handleKey, false);
}
// Detect tab focus
// Remove class on blur/focusout
on.call(this.player, this.player.elements.container, 'focusout', event => {
toggleClass(event.target, this.player.config.classNames.tabFocus, false);
});
// Add classname to tabbed elements
on.call(this.player, this.player.elements.container, 'keydown', event => {
if (event.keyCode !== 9) {
return;
}
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(() => {
toggleClass(getFocusElement(), this.player.config.classNames.tabFocus, true);
}, 0);
});
// Toggle controls on mouse events and entering fullscreen
on.call(
this.player,
this.player.elements.container,
player,
elements.container,
'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen',
event => {
const { controls } = this.player.elements;
const { controls } = elements;
// Remove button states for fullscreen
if (event.type === 'enterfullscreen') {
if (controls && event.type === 'enterfullscreen') {
controls.pressed = false;
controls.hover = false;
}
@@ -236,85 +288,83 @@ class Listeners {
let delay = 0;
if (show) {
ui.toggleControls.call(this.player, true);
ui.toggleControls.call(player, true);
// Use longer timeout for touch devices
delay = this.player.touch ? 3000 : 2000;
delay = player.touch ? 3000 : 2000;
}
// Clear timer
clearTimeout(this.player.timers.controls);
// Timer to prevent flicker when seeking
this.player.timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking
player.timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
},
);
}
// Listen for media events
media() {
const { player } = this;
const { elements } = player;
// Time change on media
on.call(this.player, this.player.media, 'timeupdate seeking seeked', event =>
controls.timeUpdate.call(this.player, event),
);
on.call(player, player.media, 'timeupdate seeking seeked', event => controls.timeUpdate.call(player, event));
// Display duration
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', event =>
controls.durationUpdate.call(this.player, event),
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', event =>
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(this.player, this.player.media, 'canplay', () => {
toggleHidden(this.player.elements.volume, !this.player.hasAudio);
toggleHidden(this.player.elements.buttons.mute, !this.player.hasAudio);
on.call(player, player.media, 'canplay', () => {
toggleHidden(elements.volume, !player.hasAudio);
toggleHidden(elements.buttons.mute, !player.hasAudio);
});
// Handle the media finishing
on.call(this.player, this.player.media, 'ended', () => {
on.call(player, player.media, 'ended', () => {
// Show poster on end
if (this.player.isHTML5 && this.player.isVideo && this.player.config.resetOnEnd) {
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart
this.player.restart();
player.restart();
}
});
// Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', event =>
controls.updateProgress.call(this.player, event),
on.call(player, player.media, 'progress playing seeking seeked', event =>
controls.updateProgress.call(player, event),
);
// Handle volume changes
on.call(this.player, this.player.media, 'volumechange', event =>
controls.updateVolume.call(this.player, event),
);
on.call(player, player.media, 'volumechange', event => controls.updateVolume.call(player, event));
// Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', event =>
ui.checkPlaying.call(this.player, event),
on.call(player, player.media, 'playing play pause ended emptied timeupdate', event =>
ui.checkPlaying.call(player, event),
);
// Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', event =>
ui.checkLoading.call(this.player, event),
);
on.call(player, player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(player, event));
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
on.call(this.player, this.player.media, 'playing', () => {
if (!this.player.ads) {
on.call(player, player.media, 'playing', () => {
if (!player.ads) {
return;
}
// If ads are enabled, wait for them first
if (this.player.ads.enabled && !this.player.ads.initialized) {
if (player.ads.enabled && !player.ads.initialized) {
// Wait for manager response
this.player.ads.managerPromise.then(() => this.player.ads.play()).catch(() => this.player.play());
player.ads.managerPromise.then(() => player.ads.play()).catch(() => player.play());
}
});
// Click video
if (this.player.supported.ui && this.player.config.clickToPlay && !this.player.isAudio) {
if (player.supported.ui && player.config.clickToPlay && !player.isAudio) {
// Re-fetch the wrapper
const wrapper = getElement.call(this.player, `.${this.player.config.classNames.video}`);
const wrapper = getElement.call(player, `.${player.config.classNames.video}`);
// Bail if there's no wrapper (this should never happen)
if (!is.element(wrapper)) {
@@ -322,28 +372,33 @@ class Listeners {
}
// On click play, pause ore restart
on.call(this.player, wrapper, 'click', () => {
// Touch devices will just show controls (if we're hiding controls)
if (this.player.config.hideControls && this.player.touch && !this.player.paused) {
on.call(player, elements.container, 'click', event => {
const targets = [elements.container, wrapper];
// Ignore if click if not container or in video wrapper
if (!targets.includes(event.target) && !wrapper.contains(event.target)) {
return;
}
if (this.player.paused) {
this.player.play();
} else if (this.player.ended) {
this.player.restart();
this.player.play();
// Touch devices will just show controls (if hidden)
if (player.touch && player.config.hideControls) {
return;
}
if (player.ended) {
player.restart();
player.play();
} else {
this.player.pause();
player.togglePlay();
}
});
}
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
if (player.supported.ui && player.config.disableContextMenu) {
on.call(
this.player,
this.player.elements.wrapper,
player,
elements.wrapper,
'contextmenu',
event => {
event.preventDefault();
@@ -353,220 +408,230 @@ class Listeners {
}
// Volume change
on.call(this.player, this.player.media, 'volumechange', () => {
on.call(player, player.media, 'volumechange', () => {
// Save to storage
this.player.storage.set({ volume: this.player.volume, muted: this.player.muted });
player.storage.set({
volume: player.volume,
muted: player.muted,
});
});
// Speed change
on.call(this.player, this.player.media, 'ratechange', () => {
on.call(player, player.media, 'ratechange', () => {
// Update UI
controls.updateSetting.call(this.player, 'speed');
controls.updateSetting.call(player, 'speed');
// Save to storage
this.player.storage.set({ speed: this.player.speed });
});
// Quality request
on.call(this.player, this.player.media, 'qualityrequested', event => {
// Save to storage
this.player.storage.set({ quality: event.detail.quality });
player.storage.set({ speed: player.speed });
});
// Quality change
on.call(this.player, this.player.media, 'qualitychange', event => {
on.call(player, player.media, 'qualitychange', event => {
// Update UI
controls.updateSetting.call(this.player, 'quality', null, event.detail.quality);
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
});
// Proxy events to container
// Bubble up key events for Edge
const proxyEvents = this.player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(this.player, this.player.media, proxyEvents, event => {
const proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(player, player.media, proxyEvents, event => {
let { detail = {} } = event;
// Get error details from media
if (event.type === 'error') {
detail = this.player.media.error;
detail = player.media.error;
}
triggerEvent.call(this.player, this.player.elements.container, event.type, true, detail);
triggerEvent.call(player, elements.container, event.type, true, detail);
});
}
// Run default and custom handlers
proxy(event, defaultHandler, customHandlerKey) {
const { player } = this;
const customHandler = player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler);
let returned = true;
// Execute custom handler
if (hasCustomHandler) {
returned = customHandler.call(player, event);
}
// Only call default handler if not prevented in custom handler
if (returned && is.function(defaultHandler)) {
defaultHandler.call(player, event);
}
}
// Trigger custom and default handlers
bind(element, type, defaultHandler, customHandlerKey, passive = true) {
const { player } = this;
const customHandler = player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler);
on.call(
player,
element,
type,
event => this.proxy(event, defaultHandler, customHandlerKey),
passive && !hasCustomHandler,
);
}
// Listen for control events
controls() {
const { player } = this;
const { elements } = player;
// IE doesn't support input event, so we fallback to change
const inputEvent = browser.isIE ? 'change' : 'input';
// Run default and custom handlers
const proxy = (event, defaultHandler, customHandlerKey) => {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler);
let returned = true;
// Execute custom handler
if (hasCustomHandler) {
returned = customHandler.call(this.player, event);
}
// Only call default handler if not prevented in custom handler
if (returned && is.function(defaultHandler)) {
defaultHandler.call(this.player, event);
}
};
// Trigger custom and default handlers
const bind = (element, type, defaultHandler, customHandlerKey, passive = true) => {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler);
on.call(
this.player,
element,
type,
event => proxy(event, defaultHandler, customHandlerKey),
passive && !hasCustomHandler,
);
};
// Play/pause toggle
if (this.player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(button => {
bind(button, 'click', this.player.togglePlay, 'play');
if (elements.buttons.play) {
Array.from(elements.buttons.play).forEach(button => {
this.bind(button, 'click', player.togglePlay, 'play');
});
}
// Pause
bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
this.bind(elements.buttons.restart, 'click', player.restart, 'restart');
// Rewind
bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
this.bind(elements.buttons.rewind, 'click', player.rewind, 'rewind');
// Rewind
bind(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward');
this.bind(elements.buttons.fastForward, 'click', player.forward, 'fastForward');
// Mute toggle
bind(
this.player.elements.buttons.mute,
this.bind(
elements.buttons.mute,
'click',
() => {
this.player.muted = !this.player.muted;
player.muted = !player.muted;
},
'mute',
);
// Captions toggle
bind(this.player.elements.buttons.captions, 'click', () => this.player.toggleCaptions());
this.bind(elements.buttons.captions, 'click', () => player.toggleCaptions());
// Fullscreen toggle
bind(
this.player.elements.buttons.fullscreen,
this.bind(
elements.buttons.fullscreen,
'click',
() => {
this.player.fullscreen.toggle();
player.fullscreen.toggle();
},
'fullscreen',
);
// Picture-in-Picture
bind(
this.player.elements.buttons.pip,
this.bind(
elements.buttons.pip,
'click',
() => {
this.player.pip = 'toggle';
player.pip = 'toggle';
},
'pip',
);
// Airplay
bind(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
this.bind(elements.buttons.airplay, 'click', player.airplay, 'airplay');
// Settings menu
bind(this.player.elements.buttons.settings, 'click', event => {
controls.toggleMenu.call(this.player, event);
});
// Settings menu
bind(this.player.elements.settings.form, 'click', event => {
// Settings menu - click toggle
this.bind(elements.buttons.settings, 'click', event => {
// Prevent the document click listener closing the menu
event.stopPropagation();
// Go back to home tab on click
const showHomeTab = () => {
const id = `plyr-settings-${this.player.id}-home`;
controls.showTab.call(this.player, id);
};
controls.toggleMenu.call(player, event);
});
// Settings menu items - use event delegation as items are added/removed
if (matches(event.target, this.player.config.selectors.inputs.language)) {
proxy(
event,
() => {
this.player.currentTrack = Number(event.target.value);
showHomeTab();
},
'language',
);
} else if (matches(event.target, this.player.config.selectors.inputs.quality)) {
proxy(
event,
() => {
this.player.quality = event.target.value;
showHomeTab();
},
'quality',
);
} else if (matches(event.target, this.player.config.selectors.inputs.speed)) {
proxy(
event,
() => {
this.player.speed = parseFloat(event.target.value);
showHomeTab();
},
'speed',
);
} else {
const tab = event.target;
controls.showTab.call(this.player, tab.getAttribute('aria-controls'));
// Settings menu - keyboard toggle
// We have to bind to keyup otherwise Firefox triggers a click when a keydown event handler shifts focus
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220143
this.bind(
elements.buttons.settings,
'keyup',
event => {
const code = event.which;
// We only care about space and return
if (![13, 32].includes(code)) {
return;
}
// Because return triggers a click anyway, all we need to do is set focus
if (code === 13) {
controls.focusFirstMenuItem.call(player, null, true);
return;
}
// Prevent scroll
event.preventDefault();
// Prevent playing video (Firefox)
event.stopPropagation();
// Toggle menu
controls.toggleMenu.call(player, event);
},
null,
false, // Can't be passive as we're preventing default
);
// Escape closes menu
this.bind(elements.settings.menu, 'keydown', event => {
if (event.which === 27) {
controls.toggleMenu.call(player, event);
}
});
// Set range input alternative "value", which matches the tooltip time (#954)
bind(this.player.elements.inputs.seek, 'mousedown mousemove', event => {
const clientRect = this.player.elements.progress.getBoundingClientRect();
const percent = 100 / clientRect.width * (event.pageX - clientRect.left);
this.bind(elements.inputs.seek, 'mousedown mousemove', event => {
const rect = elements.progress.getBoundingClientRect();
const percent = (100 / rect.width) * (event.pageX - rect.left);
event.currentTarget.setAttribute('seek-value', percent);
});
// Pause while seeking
bind(this.player.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 code = event.keyCode ? event.keyCode : event.which;
const eventType = event.type;
const attribute = 'play-on-seeked';
if ((eventType === 'keydown' || eventType === 'keyup') && (code !== 39 && code !== 37)) {
if (is.keyboardEvent(event) && (code !== 39 && code !== 37)) {
return;
}
// Was playing before?
const play = seek.hasAttribute('play-on-seeked');
const play = seek.hasAttribute(attribute);
// Done seeking
const done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute('play-on-seeked');
this.player.play();
} else if (!done && this.player.playing) {
seek.setAttribute('play-on-seeked', '');
this.player.pause();
seek.removeAttribute(attribute);
player.play();
} else if (!done && player.playing) {
seek.setAttribute(attribute, '');
player.pause();
}
});
// Fix range inputs on iOS
// Super weird iOS bug where after you interact with an <input type="range">,
// it takes over further interactions on the page. This is a hack
if (browser.isIos) {
const inputs = getElements.call(player, 'input[type="range"]');
Array.from(inputs).forEach(input => this.bind(input, inputEvent, event => repaint(event.target)));
}
// Seek
bind(
this.player.elements.inputs.seek,
this.bind(
elements.inputs.seek,
inputEvent,
event => {
const seek = event.currentTarget;
@@ -580,70 +645,71 @@ class Listeners {
seek.removeAttribute('seek-value');
this.player.currentTime = seekTo / seek.max * this.player.duration;
player.currentTime = (seekTo / seek.max) * player.duration;
},
'seek',
);
// Current time invert
// Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !is.element(this.player.elements.display.duration)) {
bind(this.player.elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start
if (this.player.currentTime === 0) {
return;
}
this.player.config.invertTime = !this.player.config.invertTime;
controls.timeUpdate.call(this.player);
});
}
// Volume
bind(
this.player.elements.inputs.volume,
inputEvent,
event => {
this.player.volume = event.target.value;
},
'volume',
// Seek tooltip
this.bind(elements.progress, 'mouseenter mouseleave mousemove', event =>
controls.updateSeekTooltip.call(player, event),
);
// Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(element => {
bind(element, 'input', event => controls.updateRangeFill.call(this.player, event.target));
Array.from(getElements.call(player, 'input[type="range"]')).forEach(element => {
this.bind(element, 'input', event => controls.updateRangeFill.call(player, event.target));
});
}
// Seek tooltip
bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', event =>
controls.updateSeekTooltip.call(this.player, event),
// Current time invert
// Only if one time element is used for both currentTime and duration
if (player.config.toggleInvert && !is.element(elements.display.duration)) {
this.bind(elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start
if (player.currentTime === 0) {
return;
}
player.config.invertTime = !player.config.invertTime;
controls.timeUpdate.call(player);
});
}
// Volume
this.bind(
elements.inputs.volume,
inputEvent,
event => {
player.volume = event.target.value;
},
'volume',
);
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
bind(this.player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
this.bind(elements.controls, 'mouseenter mouseleave', event => {
elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
// Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
bind(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
this.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
this.bind(elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
});
// Focus in/out on controls
bind(this.player.elements.controls, 'focusin focusout', event => {
const { config, elements, timers } = this.player;
this.bind(elements.controls, 'focusin focusout', event => {
const { config, elements, timers } = player;
const isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
// Toggle
ui.toggleControls.call(this.player, event.type === 'focusin');
ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay
if (event.type === 'focusin') {
if (isFocusIn) {
// Restore transition
setTimeout(() => {
toggleClass(elements.controls, config.classNames.noTransition, false);
@@ -654,14 +720,15 @@ class Listeners {
// Clear timer
clearTimeout(timers.controls);
// Hide
timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
}
});
// Mouse wheel for volume
bind(
this.player.elements.inputs.volume,
this.bind(
elements.inputs.volume,
'wheel',
event => {
// Detect "natural" scroll - suppored on OS X Safari only
@@ -675,10 +742,10 @@ class Listeners {
const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
// Change the volume by 2%
this.player.increaseVolume(direction / 50);
player.increaseVolume(direction / 50);
// Don't break page scrolling at max and min
const { volume } = this.player.media;
const { volume } = player.media;
if ((direction === 1 && volume < 1) || (direction === -1 && volume > 0)) {
event.preventDefault();
}
+6 -5
View File
@@ -6,9 +6,9 @@
/* global google */
import i18n from '../i18n';
import { createElement } from '../utils/elements';
import { triggerEvent } from '../utils/events';
import i18n from '../utils/i18n';
import is from '../utils/is';
import loadScript from '../utils/loadScript';
import { formatTime } from '../utils/time';
@@ -207,6 +207,11 @@ class Ads {
* @param {Event} adsManagerLoadedEvent
*/
onAdsManagerLoaded(event) {
// Load could occur after a source change (race condition)
if (!this.enabled) {
return;
}
// Get the ads manager
const settings = new google.ima.AdsRenderingSettings();
@@ -240,10 +245,6 @@ class Ads {
});
}
// Get skippable state
// TODO: Skip button
// this.player.debug.warn(this.manager.getAdSkippableState());
// Set volume to match player
this.manager.setVolume(this.player.volume);
+5 -54
View File
@@ -2,9 +2,7 @@
// YouTube plugin
// ==========================================================================
import controls from '../controls';
import ui from '../ui';
import { dedupe } from '../utils/arrays';
import { createElement, replaceElement, toggleClass } from '../utils/elements';
import { triggerEvent } from '../utils/events';
import fetch from '../utils/fetch';
@@ -23,37 +21,6 @@ function parseId(url) {
return url.match(regex) ? RegExp.$2 : url;
}
// Standardise YouTube quality unit
function mapQualityUnit(input) {
const qualities = {
hd2160: 2160,
hd1440: 1440,
hd1080: 1080,
hd720: 720,
large: 480,
medium: 360,
small: 240,
tiny: 144,
};
const entry = Object.entries(qualities).find(entry => entry.includes(input));
if (entry) {
// Get the match corresponding to the input
return entry.find(value => value !== input);
}
return 'default';
}
function mapQualityUnits(levels) {
if (is.empty(levels)) {
return levels;
}
return dedupe(levels.map(level => mapQualityUnit(level)));
}
// Set playback state and trigger change (only on actual change)
function assurePlaybackState(play) {
if (play && !this.embed.hasPlayed) {
@@ -188,6 +155,7 @@ const youtube = {
videoId,
playerVars: {
autoplay: player.config.autoplay ? 1 : 0, // Autoplay
hl: player.config.hl, // iframe interface language
controls: player.supported.ui ? 0 : 1, // Only show controls if not fully supported
rel: 0, // No related vids
showinfo: 0, // Hide info
@@ -224,11 +192,6 @@ const youtube = {
triggerEvent.call(player, player.media, 'error');
}
},
onPlaybackQualityChange() {
triggerEvent.call(player, player.media, 'qualitychange', false, {
quality: player.media.quality,
});
},
onPlaybackRateChange(event) {
// Get the instance
const instance = event.target;
@@ -239,6 +202,10 @@ const youtube = {
triggerEvent.call(player, player.media, 'ratechange');
},
onReady(event) {
// Bail if onReady has already been called. See issue #1108
if (is.function(player.media.play)) {
return;
}
// Get the instance
const instance = event.target;
@@ -294,16 +261,6 @@ const youtube = {
},
});
// Quality
Object.defineProperty(player.media, 'quality', {
get() {
return mapQualityUnit(instance.getPlaybackQuality());
},
set(input) {
instance.setPlaybackQuality(mapQualityUnit(input));
},
});
// Volume
let { volume } = player.config;
Object.defineProperty(player.media, 'volume', {
@@ -452,12 +409,6 @@ const youtube = {
player.media.duration = instance.getDuration();
triggerEvent.call(player, player.media, 'durationchange');
}
// Get quality
controls.setQualityMenu.call(
player,
mapQualityUnits(instance.getAvailableQualityLevels()),
);
}
break;
+14 -10
View File
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
// plyr.js v3.3.22
// plyr.js v3.4.4
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
@@ -75,16 +75,17 @@ class Plyr {
// Elements cache
this.elements = {
container: null,
captions: null,
buttons: {},
display: {},
progress: {},
inputs: {},
settings: {
popup: null,
menu: null,
panes: {},
tabs: {},
panels: {},
buttons: {},
},
captions: null,
};
// Captions
@@ -171,7 +172,7 @@ class Plyr {
this.elements.container.className = '';
// Get attributes from URL and set config
if (url.searchParams.length) {
if (url.search.length) {
const truthy = ['1', 'true'];
if (truthy.includes(url.searchParams.get('autoplay'))) {
@@ -185,6 +186,7 @@ class Plyr {
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(url.searchParams.get('playsinline'));
this.config.hl = url.searchParams.get('hl'); // TODO: Should this be setting language?
} else {
this.config.playsinline = true;
}
@@ -220,7 +222,7 @@ class Plyr {
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
@@ -292,7 +294,9 @@ class Plyr {
this.fullscreen = new Fullscreen(this);
// Setup ads if provided
this.ads = new Ads(this);
if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required
if (this.config.autoplay) {
@@ -694,9 +698,6 @@ class Plyr {
quality = value;
}
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality });
// Update config
config.selected = quality;
@@ -932,13 +933,16 @@ class Plyr {
if (hiding && this.config.controls.includes('settings') && !is.empty(this.config.settings)) {
controls.toggleMenu.call(this, false);
}
// Trigger event on change
if (hiding !== isHidden) {
const eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName);
}
return !hiding;
}
return false;
}
+1 -2
View File
@@ -1,11 +1,10 @@
// ==========================================================================
// Plyr Polyfilled Build
// plyr.js v3.3.22
// plyr.js v3.4.4
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
import 'babel-polyfill';
import 'custom-event-polyfill';
import 'url-polyfill';
import Plyr from './plyr';
+1 -1
View File
@@ -4,11 +4,11 @@
import captions from './captions';
import controls from './controls';
import i18n from './i18n';
import support from './support';
import browser from './utils/browser';
import { getElement, toggleClass } from './utils/elements';
import { ready, triggerEvent } from './utils/events';
import i18n from './utils/i18n';
import is from './utils/is';
import loadImage from './utils/loadImage';
+7 -3
View File
@@ -23,8 +23,12 @@ export const transitionEndEvent = (() => {
// Force repaint of element
export function repaint(element) {
setTimeout(() => {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
try {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0);
}
+34 -17
View File
@@ -70,12 +70,19 @@ export function createElement(type, attributes, text) {
// Inaert an element after another
export function insertAfter(element, target) {
if (!is.element(element) || !is.element(target)) {
return;
}
target.parentNode.insertBefore(element, target.nextSibling);
}
// Insert a DocumentFragment
export function insertElement(type, parent, attributes, text) {
// Inject the new <element>
if (!is.element(parent)) {
return;
}
parent.appendChild(createElement(type, attributes, text));
}
@@ -95,6 +102,10 @@ export function removeElement(element) {
// Remove all child elements
export function emptyElement(element) {
if (!is.element(element)) {
return;
}
let { length } = element.childNodes;
while (length > 0) {
@@ -180,7 +191,7 @@ export function toggleHidden(element, hidden) {
let hide = hidden;
if (!is.boolean(hide)) {
hide = !element.hasAttribute('hidden');
hide = !element.hidden;
}
if (hide) {
@@ -192,6 +203,10 @@ export function toggleHidden(element, hidden) {
// Mirror Element.classList.toggle, with IE compatibility for "force" argument
export function toggleClass(element, className, force) {
if (is.nodeList(element)) {
return Array.from(element).map(e => toggleClass(e, className, force));
}
if (is.element(element)) {
let method = 'toggle';
if (typeof force !== 'undefined') {
@@ -202,7 +217,7 @@ export function toggleClass(element, className, force) {
return element.classList.contains(className);
}
return null;
return false;
}
// Has class name
@@ -238,19 +253,6 @@ export function getElement(selector) {
return this.elements.container.querySelector(selector);
}
// Get the focused element
export function getFocusElement() {
let focused = document.activeElement;
if (!focused || focused === document.body) {
focused = null;
} else {
focused = document.querySelector(':focus');
}
return focused;
}
// Trap focus inside container
export function trapFocus(element = null, toggle = false) {
if (!is.element(element)) {
@@ -268,7 +270,7 @@ export function trapFocus(element = null, toggle = false) {
}
// Get the current focused element
const focused = getFocusElement();
const focused = document.activeElement;
if (focused === last && !event.shiftKey) {
// Move focus to first element that can be tabbed if Shift isn't used
@@ -283,3 +285,18 @@ export function trapFocus(element = null, toggle = false) {
toggleListener.call(this, this.elements.container, 'keydown', trap, toggle, false);
}
// Set focus and tab focus class
export function setFocus(element = null, tabFocus = false) {
if (!is.element(element)) {
return;
}
// Set regular focus
element.focus();
// If we want to mimic keyboard focus via tab
if (tabFocus) {
toggleClass(element, this.config.classNames.tabFocus);
}
}
+3 -3
View File
@@ -2,9 +2,9 @@
// Plyr internationalization
// ==========================================================================
import is from './utils/is';
import { getDeep } from './utils/objects';
import { replaceAll } from './utils/strings';
import is from './is';
import { getDeep } from './objects';
import { replaceAll } from './strings';
const i18n = {
get(key = '', config = {}) {
+2
View File
@@ -16,6 +16,7 @@ const isNodeList = input => instanceOf(input, NodeList);
const isElement = input => instanceOf(input, Element);
const isTextNode = input => getConstructor(input) === Text;
const isEvent = input => instanceOf(input, Event);
const isKeyboardEvent = input => instanceOf(input, KeyboardEvent);
const isCue = input => instanceOf(input, window.TextTrackCue) || instanceOf(input, window.VTTCue);
const isTrack = input => instanceOf(input, TextTrack) || (!isNullOrUndefined(input) && isString(input.kind));
@@ -56,6 +57,7 @@ export default {
element: isElement,
textNode: isTextNode,
event: isEvent,
keyboardEvent: isKeyboardEvent,
cue: isCue,
track: isTrack,
url: isUrl,
+1 -1
View File
@@ -24,7 +24,7 @@ export function getPercentage(current, max) {
return 0;
}
return (current / max * 100).toFixed(2);
return ((current / max) * 100).toFixed(2);
}
// Replace all occurances of a string in a string
+4 -3
View File
@@ -17,7 +17,6 @@
padding: $plyr-control-spacing;
position: absolute;
text-align: center;
transform: translateY(-($plyr-control-spacing * 4));
transition: transform 0.4s ease-in-out;
width: 100%;
@@ -53,6 +52,8 @@
display: block;
}
.plyr--hide-controls .plyr__captions {
transform: translateY(-($plyr-control-spacing * 1.5));
// If the lower controls are shown and not empty
.plyr:not(.plyr--hide-controls) .plyr__controls:not(:empty) ~ .plyr__captions {
transform: translateY(-($plyr-control-spacing * 4));
}
+18 -4
View File
@@ -41,7 +41,7 @@
display: none;
}
// Audio styles
// Audio control
.plyr--audio .plyr__control {
&.plyr__tab-focus,
&:hover,
@@ -51,6 +51,21 @@
}
}
// Video control
.plyr--video .plyr__control {
svg {
filter: drop-shadow(0 1px 1px rgba(#000, 0.15));
}
// Hover and tab focus
&.plyr__tab-focus,
&:hover,
&[aria-expanded='true'] {
background: $plyr-video-control-bg-hover;
color: $plyr-video-control-color-hover;
}
}
// Large play button (video only)
.plyr__control--overlaid {
background: rgba($plyr-video-control-bg-hover, 0.8);
@@ -66,11 +81,10 @@
transform: translate(-50%, -50%);
z-index: 2;
// Offset icon to make the play button look right
svg {
height: $plyr-control-icon-size-large;
left: 2px; // Offset to make the play button look right
left: 2px;
position: relative;
width: $plyr-control-icon-size-large;
}
&:hover,
+34 -41
View File
@@ -18,36 +18,48 @@
> .plyr__control,
.plyr__progress,
.plyr__time,
.plyr__menu {
margin-left: ($plyr-control-spacing / 2);
&:first-child,
&:first-child + [data-plyr='pause'] {
margin-left: 0;
margin-right: auto;
}
}
.plyr__menu,
.plyr__volume {
margin-left: ($plyr-control-spacing / 2);
}
.plyr__menu + .plyr__control,
> .plyr__control + .plyr__menu,
> .plyr__control + .plyr__control,
.plyr__progress + .plyr__control {
margin-left: floor($plyr-control-spacing / 4);
}
> .plyr__control:first-child,
> .plyr__control:first-child + [data-plyr='pause'] {
margin-left: 0;
margin-right: auto;
}
// Hide empty controls
&:empty {
display: none;
}
@media (min-width: $plyr-bp-sm) {
> .plyr__control,
.plyr__menu,
.plyr__progress,
.plyr__time,
.plyr__menu {
.plyr__volume {
margin-left: $plyr-control-spacing;
}
> .plyr__control + .plyr__control,
.plyr__menu + .plyr__control,
> .plyr__control + .plyr__menu {
margin-left: ($plyr-control-spacing / 2);
}
}
}
// Audio controls
.plyr--audio .plyr__controls {
background: $plyr-audio-controls-bg;
border-radius: inherit;
color: $plyr-audio-control-color;
padding: $plyr-control-spacing;
}
// Video controls
.plyr--video .plyr__controls {
background: linear-gradient(
@@ -59,37 +71,18 @@
bottom: 0;
color: $plyr-video-control-color;
left: 0;
padding: ($plyr-control-spacing * 3.5) $plyr-control-spacing
$plyr-control-spacing;
padding: ($plyr-control-spacing * 2) ($plyr-control-spacing / 2) ($plyr-control-spacing / 2);
position: absolute;
right: 0;
transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
z-index: 2;
z-index: 3;
.plyr__control {
svg {
filter: drop-shadow(0 1px 1px rgba(#000, 0.15));
}
// Hover and tab focus
&.plyr__tab-focus,
&:hover,
&[aria-expanded='true'] {
background: $plyr-video-control-bg-hover;
color: $plyr-video-control-color-hover;
}
@media (min-width: $plyr-bp-sm) {
padding: ($plyr-control-spacing * 3.5) $plyr-control-spacing $plyr-control-spacing;
}
}
// Audio controls
.plyr--audio .plyr__controls {
background: $plyr-audio-controls-bg;
border-radius: inherit;
color: $plyr-audio-control-color;
padding: $plyr-control-spacing;
}
// Hide controls
// Hide video controls
.plyr--video.plyr--hide-controls .plyr__controls {
opacity: 0;
pointer-events: none;
+44 -39
View File
@@ -39,7 +39,8 @@
> div {
overflow: hidden;
transition: height 0.35s cubic-bezier(0.4, 0, 0.2, 1), width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
transition: height 0.35s cubic-bezier(0.4, 0, 0.2, 1),
width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
// Arrow
@@ -54,18 +55,16 @@
width: 0;
}
ul {
list-style: none;
margin: 0;
overflow: hidden;
[role='menu'] {
padding: $plyr-control-padding;
}
li {
margin-top: 2px;
[role='menuitem'],
[role='menuitemradio'] {
margin-top: 2px;
&:first-child {
margin-top: 0;
}
&:first-child {
margin-top: 0;
}
}
@@ -75,10 +74,17 @@
color: $plyr-menu-color;
display: flex;
font-size: $plyr-font-size-menu;
padding: ceil($plyr-control-padding / 2) ($plyr-control-padding * 2);
padding: ceil($plyr-control-padding / 2)
ceil($plyr-control-padding * 1.5);
user-select: none;
width: 100%;
> span {
align-items: inherit;
display: flex;
width: 100%;
}
&::after {
border: 4px solid transparent;
content: '';
@@ -135,50 +141,49 @@
}
}
label.plyr__control {
.plyr__control[role='menuitemradio'] {
padding-left: $plyr-control-padding;
input[type='radio'] + span {
background: rgba(#000, 0.1);
&::before,
&::after {
border-radius: 100%;
}
&::before {
background: rgba(#000, 0.1);
content: '';
display: block;
flex-shrink: 0;
height: 16px;
margin-right: $plyr-control-spacing;
position: relative;
transition: all 0.3s ease;
width: 16px;
&::after {
background: #fff;
border-radius: 100%;
content: '';
height: 6px;
left: 5px;
opacity: 0;
position: absolute;
top: 5px;
transform: scale(0);
transition: transform 0.3s ease, opacity 0.3s ease;
width: 6px;
}
}
input[type='radio']:checked + span {
background: $plyr-color-main;
&::after {
background: #fff;
border: 0;
height: 6px;
left: 12px;
opacity: 0;
top: 50%;
transform: translateY(-50%) scale(0);
transition: transform 0.3s ease, opacity 0.3s ease;
width: 6px;
}
&[aria-checked='true'] {
&::before {
background: $plyr-color-main;
}
&::after {
opacity: 1;
transform: scale(1);
transform: translateY(-50%) scale(1);
}
}
input[type='radio']:focus + span {
@include plyr-tab-focus();
}
&.plyr__tab-focus input[type='radio'] + span,
&:hover input[type='radio'] + span {
&.plyr__tab-focus::before,
&:hover::before {
background: rgba(#000, 0.1);
}
}
@@ -188,7 +193,7 @@
align-items: center;
display: flex;
margin-left: auto;
margin-right: -$plyr-control-padding;
margin-right: -($plyr-control-padding - 2);
overflow: hidden;
padding-left: ceil($plyr-control-padding * 3.5);
pointer-events: none;
+1 -2
View File
@@ -12,12 +12,11 @@
opacity: 0;
position: absolute;
top: 0;
transition: opacity 0.3s ease;
transition: opacity 0.2s ease;
width: 100%;
z-index: 1;
}
.plyr--stopped.plyr__poster-enabled .plyr__poster {
opacity: 1;
pointer-events: none;
}
-1
View File
@@ -3,7 +3,6 @@
// --------------------------------------------------------------
.plyr__progress {
display: flex;
flex: 1;
left: $plyr-range-thumb-height / 2;
margin-right: $plyr-range-thumb-height;
+14 -4
View File
@@ -19,7 +19,11 @@
&::-webkit-slider-runnable-track {
@include plyr-range-track();
background-image: linear-gradient(to right, currentColor var(--value, 0%), transparent var(--value, 0%));
background-image: linear-gradient(
to right,
currentColor var(--value, 0%),
transparent var(--value, 0%)
);
}
&::-webkit-slider-thumb {
@@ -140,15 +144,21 @@
// Pressed styles
&:active {
&::-webkit-slider-thumb {
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
@include plyr-range-thumb-active(
$plyr-audio-range-thumb-shadow-color
);
}
&::-moz-range-thumb {
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
@include plyr-range-thumb-active(
$plyr-audio-range-thumb-shadow-color
);
}
&::-ms-thumb {
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
@include plyr-range-thumb-active(
$plyr-audio-range-thumb-shadow-color
);
}
}
}
+2
View File
@@ -10,6 +10,7 @@
color: $plyr-tooltip-color;
font-size: $plyr-font-size-small;
font-weight: $plyr-font-weight-regular;
left: 50%;
line-height: 1.3;
margin-bottom: ($plyr-tooltip-padding * 2);
opacity: 0;
@@ -64,6 +65,7 @@
// Last tooltip
.plyr__controls > .plyr__control:last-child .plyr__tooltip {
left: auto;
right: 0;
transform: translate(0, 10px) scale(0.8);
transform-origin: 100% 100%;
+1
View File
@@ -3,6 +3,7 @@
// --------------------------------------------------------------
.plyr--video {
background: #000;
overflow: hidden;
// Menu open
+5 -2
View File
@@ -3,20 +3,23 @@
// --------------------------------------------------------------
.plyr__volume {
align-items: center;
display: flex;
flex: 1;
position: relative;
input[type='range'] {
margin-left: ($plyr-control-spacing / 2);
position: relative;
z-index: 2;
}
@media (min-width: $plyr-bp-sm) {
max-width: 50px;
max-width: 90px;
}
@media (min-width: $plyr-bp-md) {
max-width: 80px;
max-width: 110px;
}
}
+2 -3
View File
@@ -5,7 +5,7 @@
// Nicer focus styles
// ---------------------------------------
@mixin plyr-tab-focus($color: $plyr-tab-focus-default-color) {
box-shadow: 0 0 0 3px rgba($color, 0.35);
box-shadow: 0 0 0 5px rgba($color, 0.5);
outline: 0;
}
@@ -28,7 +28,7 @@
border: 0;
border-radius: ($plyr-range-track-height / 2);
height: $plyr-range-track-height;
transition: all 0.3s ease;
transition: box-shadow 0.3s ease;
user-select: none;
}
@@ -37,7 +37,6 @@
border: 0;
border-radius: 100%;
box-shadow: $plyr-range-thumb-shadow;
box-sizing: border-box;
height: $plyr-range-thumb-height;
position: relative;
transition: all 0.2s ease;
-1
View File
@@ -3,7 +3,6 @@
// ==========================================================================
$plyr-control-icon-size: 18px !default;
$plyr-control-icon-size-large: 20px !default;
$plyr-control-spacing: 10px !default;
$plyr-control-padding: ($plyr-control-spacing * 0.7) !default;
$plyr-control-radius: 3px !default;
+4
View File
@@ -22,3 +22,7 @@
width: 1px;
}
}
.plyr [hidden] {
display: none !important;
}
+1474 -1670
View File
File diff suppressed because it is too large Load Diff