Compare commits

...

528 Commits

Author SHA1 Message Date
2b7fe9a4f9 v3.0.6 2018-03-28 00:17:15 +11:00
951df64b7f v3.0.5 2018-03-27 23:52:26 +11:00
0976afe282 v3.0.4 2018-03-27 23:47:58 +11:00
7b1e4abda7 Controls fixes 2018-03-27 23:43:38 +11:00
0cf75eed3f Revert API method change 2018-03-27 21:15:11 +11:00
d96957d086 Allow fullscreen in iframe 2018-03-27 21:13:22 +11:00
1a032ea498 Fix for seeking issue 2018-03-27 21:10:06 +11:00
5d079da1b8 Use object.entries 2018-03-27 10:41:06 +11:00
9c1bc6ab08 Fixes for fast forward and issues with event.preventDefault() 2018-03-27 10:36:08 +11:00
3d2ba8c009 Update readme.md 2018-03-22 09:11:42 +11:00
e872ce3f77 Update readme.md 2018-03-22 09:10:50 +11:00
b77756da04 Typo 2018-03-22 01:15:10 +11:00
9b23e13ce8 v3.0.3 2018-03-22 01:13:37 +11:00
5eafe9baff Vimeo offset tweak (fixes #826) 2018-03-22 01:08:08 +11:00
c251c94131 Fix for .stop() method (fixes #819) 2018-03-22 01:02:38 +11:00
17041efc71 Check for array for speed options (fixes #252) 2018-03-22 00:33:14 +11:00
05b8e8a6e0 Restore as float (fixes #828) 2018-03-22 00:28:42 +11:00
f998b996fa Fix for Firefox fullscreen oddness (Fixes #821) 2018-03-22 00:26:01 +11:00
958b47c435 Merge branch 'master' of github.com:sampotts/plyr 2018-03-22 00:06:26 +11:00
a27248d3b6 Merge pull request #820 from saadshahd/patch-1
Fix fast-forward control
2018-03-22 00:05:24 +11:00
1b1f7be7ff Merge branch 'master' of github.com:sampotts/plyr 2018-03-22 00:04:34 +11:00
59d4a27240 Improve Sprite checking (fixes #827) 2018-03-22 00:04:28 +11:00
75e9f3c2e3 Fix fast-forward control
fast-forward control doesn't work.
2018-03-21 12:15:57 +02:00
7132eccf50 Merge pull request #822 from DanielRuf/patch/fix-options-link
fix the options link in the readme
2018-03-21 09:12:06 +11:00
e953c6398c fix the options link in the readme 2018-03-20 15:05:51 +01:00
bb7eea27e5 v3.0.2 2018-03-18 22:46:36 +11:00
595c5e95bc Fix for Safari with adblockers 2018-03-18 22:37:06 +11:00
43e6dcd41d Fix for local storage issue 2018-03-18 01:37:24 +11:00
b06c8ae43f Changelog updated 2018-03-18 01:14:18 +11:00
c7ea13c0c7 Sentry in live only 2018-03-18 01:08:05 +11:00
0f8c6e147b Added Sentry 2018-03-18 00:21:23 +11:00
e566365288 Typo 2018-03-17 23:44:40 +11:00
a06e0f5890 Updated screenshot 2018-03-17 23:40:28 +11:00
3bccc0da01 v3.0.0 2018-03-17 23:33:25 +11:00
a0173d991e Removed beta message 2018-03-17 23:31:34 +11:00
600f0eb8a3 Merge branch 'beta'
# Conflicts:
#	readme.md
2018-03-17 23:30:16 +11:00
5db73b1327 Added buffered getter 2018-03-17 23:27:40 +11:00
5cb1628cd8 Vimeo fix 2018-03-15 10:29:05 +11:00
c74b75e8e1 3.0.0-beta.20 2018-03-13 23:35:17 +11:00
0538476d6f 3.0.0-beta.19 2018-03-13 22:15:28 +11:00
5ebe18d081 Typography fix 2018-03-13 22:00:40 +11:00
207adde36d 3.0.0-beta.18 2018-03-13 21:44:18 +11:00
1b13ddaa54 Update ads 2018-03-13 21:42:01 +11:00
9981c349be Fix for null manager race condition 2018-03-11 18:23:47 +11:00
b3365a7373 Normalised event names and removed unused 2018-03-11 12:54:51 +11:00
9a0c1c830d Merge pull request #804 from friday/ads-trigger-arguments
[v3] Add optional arguments to Ads.trigger
2018-03-11 10:55:29 +11:00
ef27ba16f4 Add optional argument to Ads.trigger (currently only used for adblocker error) 2018-03-10 16:20:33 +01:00
e206edc1f6 Event listener fixes, loadScript promise, ads tweaks 2018-03-11 02:03:35 +11:00
c734bc4957 Merge branch 'beta' of github.com:sampotts/plyr into beta 2018-03-10 23:32:55 +11:00
572b8a7aca Manually merged PRs 2018-03-10 23:32:48 +11:00
eebae4a227 Merge pull request #802 from gehaktmolen/ad-hotfixes
Advertisement couldnt be loaded when creative dimensions do not align after resizing
2018-03-10 23:32:15 +11:00
e0562752ea Merge pull request #795 from frogg/patch-1
Added link that explains Webkit's autoplay blocker
2018-03-10 23:29:22 +11:00
6a2ca534d2 Removed redundant wrappers within the adsmanager promises. 2018-03-09 14:29:37 +01:00
7adc2bc6c8 Unneeded else has been removed within the play() method. 2018-03-09 13:21:19 +01:00
ba8d7831a7 Made sure play() returns a promise. 2018-03-09 12:50:57 +01:00
69ffcbad27 Ad block detection would not work when calling play() right after creating the player instance, so the adsManager now also rejects on such a case. Also made sure that calling play() will wait for the adsManager promise to resolve or otherwise return the media.play() method. 2018-03-09 11:17:24 +01:00
819f7d1080 Resizing the ad container while having it on display none will return offset width and height of 0, which will cause ads not to play when ad sizes are set within the clients DSP. Also making sure that the inner containers of the ad container are full size. The container is now hidden/ displayed using z-index. 2018-03-07 15:43:48 +01:00
409b588458 Made sure that cue points for midrolls are not displayed when the ad rule for a midroll doesn't exceed the total play time of a video. 2018-03-07 15:17:30 +01:00
e90a603d57 Removed a double this.enabled variable and updated a comment in ads.js. Also made sure the adsmanager promise also can fail, so we can use it to wait for getting the advertisement ready when someone clicks the play button. Otherwise there it can look glitchy when the actual video starts playing and the video ad plays a few seconds later because the vast tag was slow to retrieve. Also fixed a typo. 2018-03-06 17:27:59 +01:00
6f061621ad v3.0.0-beta.17 2018-03-03 23:16:15 +11:00
0300610108 Typo 2018-03-03 23:14:57 +11:00
2fba5f152c 3.0.0-beta.16 2018-03-03 23:06:54 +11:00
317b08c703 Ready event fix, YouTube play event fix, docs update 2018-03-03 23:06:12 +11:00
e6db374a72 Added link that explains Webkit's autoplay blocker 2018-02-24 16:19:55 +01:00
bfb550b8d0 Package updates 2018-02-22 23:46:52 +11:00
174234c166 v3.0.0-beta.15 2018-02-19 09:55:16 +11:00
24b4220de5 Fix IE CORS captions 2018-02-19 09:52:46 +11:00
f1895a4cce Pause button fix, polyfilled build, unminified builds 2018-02-17 19:34:15 +11:00
c2a6306d46 Merge pull request #781 from friday/gulp-unminified-js-output
Build both minified and non-minified js-bundles
2018-02-17 09:22:34 +11:00
7ac732f45b Merge branch 'beta' into gulp-unminified-js-output 2018-02-17 09:22:19 +11:00
c90f1bdf08 v3.0.0-beta.13 2018-02-13 00:02:13 +11:00
6a9be8d16b Fix for custom controls 2018-02-13 00:01:19 +11:00
58c2c52c95 Merge branch 'beta' of github.com:sampotts/plyr into beta 2018-02-11 15:09:40 +11:00
73a39769d4 Fullscreen API changes, color settings tweaks 2018-02-11 15:09:34 +11:00
7221e26eca Merge pull request #780 from friday/captions-ie11-indexsizeerror
Fix harmless but annoying IE error 'IndexSizeError'
2018-02-06 12:51:45 +11:00
98adb8e784 Fix harmless but annoying IE error 'IndexSizeError' 2018-02-06 02:39:01 +01:00
a59dcb2f53 Gulp js build: create both minified and non-minified outputs 2018-02-06 01:57:27 +01:00
d21b58e1c9 Copy 2018-02-06 11:26:13 +11:00
d6e84cbabb Nicer checks 2018-02-06 11:12:03 +11:00
fcccf1d479 Copy 2018-02-06 11:08:26 +11:00
211db12a3d Readme merge 2018-02-06 11:06:46 +11:00
ab7f277a1b Merge pull request #769 from redxtech/add-vue-plyr-to-readme
Add vue-plyr to readme
2018-02-06 10:51:42 +11:00
ce1d5a60d6 Remove eslint-rule 'no-shadow' (common variable names should be able to exist in different scopes) 2018-02-05 23:22:20 +01:00
f67315e20c 3.0.0-beta.12 2018-02-06 00:25:50 +11:00
2150c44036 Added backwards compatibility for <div> embeds 2018-02-06 00:24:48 +11:00
70c9fbdde3 Removed fetch dependency 2018-02-05 21:43:32 +11:00
f3ea31c515 Merge branch 'beta' of github.com:sampotts/plyr into beta
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
2018-02-05 21:28:16 +11:00
1ee88cba16 Testing fetch 2018-02-05 21:26:18 +11:00
af3ae75229 Update readme.md 2018-02-03 23:16:52 +11:00
d76ef3ff91 Small UI tweaks and fix for instanceof issue 2018-01-31 19:33:00 +11:00
2691c7c9d6 Version bump + icon fix 2018-01-30 13:01:05 +11:00
26b1d8ce8f Fix UMD stuff 2018-01-30 12:57:19 +11:00
6fae148fc1 Deploy 2018-01-30 09:26:09 +11:00
bb51647fe2 Merge pull request #772 from sampotts/fix/ads-blocked
Fix: ads blocked and media playing before ad plays
2018-01-30 09:22:54 +11:00
71efbe7a92 Merge branch 'beta' into fix/ads-blocked
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
#	src/js/plugins/ads.js
#	src/js/plyr.js
2018-01-30 09:22:14 +11:00
afd695cb39 Fix typo's 2018-01-29 22:45:46 +01:00
c4eb4c97ac fix(ads): Fixes media from playing when ads are blocked 2018-01-29 22:40:08 +01:00
d5a1a7ca1c Add vue-plyr to readme 2018-01-28 23:03:05 -07:00
8f7a8940f3 Version bump 2018-01-25 22:42:52 +11:00
5e68f8c8dd Attempt to fix YouTube message error, added ads references, changes to bool 2018-01-25 22:41:30 +11:00
b4e22e2e7b Restored “Ad” in label 2018-01-24 09:06:20 +11:00
d4234da9aa Docs tweak 2018-01-23 19:13:35 +11:00
1f53b27d4e Version bump 2018-01-23 18:00:08 +11:00
cc128e6088 Renamed property, UI tweak 2018-01-23 10:31:21 +11:00
3c8acb4e9e Fix bug where ad would play on every play action 2018-01-22 21:07:08 +01:00
ebf53d14b1 Small tweaks 2018-01-22 23:39:09 +11:00
b298587c0b Bug fix 2018-01-22 23:20:03 +11:00
26e9aaceb8 Version bump 2018-01-22 23:17:40 +11:00
384f3d6eda Gulp 2018-01-22 23:16:50 +11:00
3aa5747c90 Merge pull request #760 from sampotts/beta-with-ads
Beta with ads
2018-01-22 23:15:52 +11:00
5671235fd9 Formatting, events and ad countdown added 2018-01-22 23:15:10 +11:00
1dd5c9efd9 Converted demo to iife 2018-01-20 15:23:36 +11:00
5fad152cbf Formatting and “ad” badge 2018-01-19 20:24:15 +11:00
f33ca846f2 Build 2018-01-19 14:30:28 +11:00
986c802acd Merge branch 'beta-with-ads' of github.com:sampotts/plyr into beta-with-ads
# Conflicts:
#	src/js/plugins/ads.js
2018-01-19 14:30:11 +11:00
98dd818bf6 Merge branch 'beta' into beta-with-ads
# Conflicts:
#	demo/dist/demo.js
#	demo/dist/demo.js.map
#	dist/plyr.js
#	dist/plyr.js.map
2018-01-19 14:28:39 +11:00
021f6c8460 Comments and formatting 2018-01-19 14:26:25 +11:00
392f837398 Merge pull request #763 from gehaktmolen/beta-with-ads-adjustments
Beta with ads - Reloading ads on new video
2018-01-19 14:26:08 +11:00
6831c30534 Transition event fix 2018-01-19 14:22:26 +11:00
ed6048034b Noticed that Plyr stopped working when ads are blocked. 2018-01-18 14:04:47 +01:00
8af312fe3c Updated pause and resume content methods within Ads class. 2018-01-18 12:31:10 +01:00
d87ada4f58 Reformatted ads codebase and added/ changed comments. Also removed un-used events. 2018-01-18 12:26:53 +01:00
31c8166562 Fixed string literal and position issue of the midroll cue inside the time line. Added a check for the progress element existence. 2018-01-17 15:57:10 +01:00
0cb2f95888 Removed an un-used variable. 2018-01-17 15:43:08 +01:00
896ea7c689 Added cue markings within the time line for when midrolls will be displayed. Removed unusued callback parameter. 2018-01-17 15:38:26 +01:00
1d1eb02bd7 Added the logging of our main promises to their resolving callback. Otherwise they come up as null. 2018-01-17 14:44:44 +01:00
3583165b30 Removed logic related to starting the ad by clicking/ tapping the advertisement container. Ad is started by plyr play method. 2018-01-17 14:09:11 +01:00
d822f0c6bf Adsmanager is now re/pre-loaded with new ads when the video is done or an ad error appears. Will make it possible to request ads when a new video is loaded. Added comments and missing events within the adsmanagerloader method. 2018-01-17 13:58:39 +01:00
9e52296dc6 Moved the ads container to be outside of the video wrapper. This way we can easily move the ad in front or behind the video controls based on content resume or pause IMA events. 2018-01-17 12:19:32 +01:00
12a7a4142c Moved the logic for pausing and playing the video to content pause/ resume IMA events to avoid flickering. Also used events for resolving the adsmanager and adsloader promises. 2018-01-17 11:32:13 +01:00
8348f79742 Fix loading/playing of the ads when there is no valid ads.tagUrl 2018-01-17 08:31:36 +01:00
ec73d34bd3 Some tweaks 2018-01-16 23:06:40 +01:00
1cef48d4f8 fix blocking play() if ads are set 2018-01-15 23:30:54 +01:00
c24c05226d Merge pull request #761 from gehaktmolen/beta-with-ads-promises
Beta with ads - Added promises, missing events, new ad tag and additional logging.
2018-01-15 20:13:51 +01:00
4b0005c28e Added promises, missing events, new ad tag and additional logging. 2018-01-15 14:47:34 +01:00
8064405dbc Tweaks 2018-01-14 23:33:18 +11:00
22e8892993 Code tweaks 2018-01-14 23:15:33 +11:00
cfc86bcb7c Merge branch 'beta' into beta-with-ads
# Conflicts:
#	demo/dist/demo.js
#	demo/dist/demo.js.map
#	dist/plyr.js
#	dist/plyr.js.map
2018-01-14 22:55:44 +11:00
5f96ec6ac2 Minor logic tweak 2018-01-14 22:54:03 +11:00
bbdf225d7b Fix loading google ima sdk 2018-01-14 08:21:35 +01:00
025fc1090b Implementing ads plugin 2018-01-13 23:02:59 +01:00
d9ec1d1b8e Progressively enhance <iframe> embeds 2018-01-12 19:35:46 +11:00
2e5d06ad85 Publish script tweaks 2018-01-09 20:43:58 +11:00
bacd049eb0 Package 2018-01-09 20:26:56 +11:00
6fd7b26bb2 Beta version up on https://plyr.io/beta and CDN 2018-01-09 20:25:46 +11:00
1df79cf7f8 Publishing to /beta 2018-01-09 19:35:17 +11:00
e6badacf0d Recommended extensions for VS Code 2018-01-09 18:52:36 +11:00
a0d9d5eca8 Styling tweaks 2018-01-09 09:14:59 +11:00
8786377a75 Use CSS custom property for webkit range fill 2018-01-08 20:53:18 +11:00
f0322e8d94 Merge branch 'develop' of github.com:sampotts/plyr into develop 2018-01-08 19:42:29 +11:00
3f2ac3fca0 Package tweaks 2018-01-08 19:42:21 +11:00
9872207e87 Merge pull request #752 from friday/gulp-fix
Enable gulp builds when aws.json isn't present.
2018-01-08 12:12:11 +11:00
58d942e737 Fix gulp build for user who doesn't have aws.json 2018-01-08 02:01:39 +01:00
dd190155c4 Empty storage fix 2018-01-07 23:16:55 +11:00
ebb3b83b27 Merge branch 'master' into develop
# Conflicts:
#	src/js/plyr.js
2018-01-07 22:46:28 +11:00
79a43120ae Merge pull request #740 from gurinderhans/master
double click player to toggle fullscreen
2018-01-07 22:40:20 +11:00
2b36ab7ef5 Merge branch 'master' into develop
# Conflicts:
#	.vscode/settings.json
#	demo/dist/demo.css
#	demo/dist/demo.js
#	demo/error.html
#	demo/index.html
#	demo/src/js/main.js
#	demo/src/less/lib/fontface.less
#	dist/plyr.css
#	dist/plyr.js
#	gulpfile.js
#	package-lock.json
#	package.json
#	readme.md
#	src/js/plyr.js
#	src/less/plyr.less
#	src/scss/plyr.scss
2018-01-07 22:39:28 +11:00
eb38e0394d Slider styling 2018-01-07 22:35:52 +11:00
b79ffacd9c Merge pull request #726 from xDae/master
Added react port
2018-01-05 21:23:45 +11:00
e14e2cfaff Merge branch 'develop' of https://github.com/Selz/plyr into develop 2018-01-05 10:37:38 +11:00
04119b27e6 Tweaks 2018-01-05 10:37:34 +11:00
Sam
92cb9e22e2 Started on error handling 2018-01-04 13:43:56 +11:00
888c6773d0 Merge pull request #743 from jwpage/patch-1
Fix `enabled` smartphone example in README
2018-01-03 11:45:33 +11:00
cfd653f02a Fix enabled smartphone example in README 2018-01-03 09:55:14 +11:00
Sam
6b9106ddb1 Fix for promise issue on Chrome, loading fix 2017-12-27 21:40:35 +00:00
838bd49a1d add comment about no fullscreen if player type is audio 2017-12-23 21:47:01 -08:00
800e0dedfb add double click to make full screen support 2017-12-23 21:41:08 -08:00
965fc0b2f5 Docs, restored loadSprite and supported static methods 2017-12-23 18:42:52 +00:00
98ac98b4c2 Vimeo fix 2017-12-21 00:20:56 +00:00
178b0d8020 Badge tweak 2017-12-21 00:06:59 +00:00
3bd1c1ff56 Fix for scrubber 2017-12-20 22:06:26 +00:00
7d4bf91a35 Small fixes 2017-12-20 21:45:06 +00:00
ac3d1d1242 Docs 2017-12-20 21:10:43 +00:00
742018a716 Fix SASS bundle 2017-12-20 21:08:44 +00:00
a2ef691b4b Comment update 2017-12-20 20:49:41 +00:00
9e0c406a4a Remove chaning ability and return promise for play() 2017-12-20 20:47:02 +00:00
f3df7aba15 Split up settings file 2017-12-20 18:00:05 +00:00
6bc3592381 Fix for rounding 2017-12-20 15:32:16 +00:00
6864149989 Converted to SASS/SCSS 2017-12-20 15:14:05 +00:00
aab53fa91f Minor tweaks 2017-12-18 16:57:23 +00:00
423f67b4f4 Copy 2017-12-08 15:55:12 +00:00
82f81f4f73 More Edge fixes and small UI bugs 2017-12-08 15:54:08 +00:00
c8990bd379 IE & Edge fixes, Storage & Console classes 2017-12-08 10:05:38 +00:00
e6cc4ba1f5 Added react port 2017-12-04 17:05:14 +02:00
de54929bb7 Safari CSS fixes and variables setup 2017-11-25 22:54:04 +11:00
fd77831303 Fix display for current language on change 2017-11-25 01:19:16 +11:00
cda574e627 Map 2017-11-24 08:47:17 +11:00
fda1977119 Fix for keyboard increase/decrease volume 2017-11-24 08:47:11 +11:00
f7bf0961cf Fix overflow issue on small players with menu open 2017-11-23 22:32:07 +11:00
61325bbad1 Vimeo captions fix 2017-11-23 20:50:07 +11:00
921cefd212 Moved to provider + type to make it cleaner in future, fix for multiple players 2017-11-23 17:35:35 +11:00
de6f0f1b77 Updated data attributes to data-plyr namespace. Speed menu fixes 2017-11-23 12:57:43 +11:00
7382553a78 Work on touch controls 2017-11-22 00:04:11 +11:00
4b82e89845 Fix for playing getter as currentTime check is flaky 2017-11-21 21:14:44 +11:00
d3b31e595a Handle no audio, more docs in code, fix for playing getter 2017-11-21 20:14:57 +11:00
f33bc5a5c6 Minor tweaks 2017-11-21 14:05:55 +11:00
f518ec108b Vimeo autopause option 2017-11-21 13:33:51 +11:00
edfc6cd475 Play button as toggle button, tooltip changes, docs updated, fullscreen fix 2017-11-21 13:12:36 +11:00
92cd67effb Docs to build custom CSS for plyr 2017-11-20 23:53:54 +11:00
da1e987444 WIP on docs example 2017-11-20 21:23:49 +11:00
2d4a166218 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
2017-11-20 10:48:53 +11:00
feae00224e Added ended and playing getters 2017-11-20 10:48:28 +11:00
000afdd300 Merge branch 'develop' of github.com:Selz/plyr into develop 2017-11-19 23:35:23 +11:00
0dc9681ae8 YouTube title 2017-11-19 23:35:07 +11:00
dc391c98c6 UI tweaks 2017-11-19 21:15:24 +11:00
4b62a5c74d Captions fix 2017-11-19 17:54:38 +11:00
3f744ef63a Formatting 2017-11-18 19:36:36 +11:00
5a244b7fed Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
#	src/js/controls.js
2017-11-18 19:33:01 +11:00
4dca4bf93c Minor fixes 2017-11-18 19:31:45 +11:00
6984d6fb16 Controls cleanup, work on captions bug, click to invert time 2017-11-18 19:30:26 +11:00
d7a1c44281 Using fetch instead of xhr, grabbing title for YouTube 2017-11-16 11:38:06 +01:00
c64b8f6940 Started on error handling, Safari icon fix 2017-11-14 17:27:40 +01:00
9c4b53d761 Fix for YouTube .getVideoData() issue (fixes #709) 2017-11-14 13:28:52 +01:00
022b436c3f YouTube fix 2017-11-14 13:22:23 +01:00
6058bb0245 Docs 2017-11-12 08:16:47 +00:00
3ade86537d Docs, keyboard shortcut fixes 2017-11-12 08:14:14 +00:00
d80be3b903 More docs, event renamed 2017-11-12 07:45:52 +00:00
1a26681d87 Added Sparkk TV to the list of users 2017-11-10 00:19:44 +11:00
9171297764 More docs 2017-11-10 00:06:46 +11:00
ebd1a52e5d Config docs 2017-11-09 21:18:09 +11:00
743c84188e Fix for destroy 2017-11-09 20:54:14 +11:00
4879bea4a0 Moved console methods out of the root of the object 2017-11-09 20:01:13 +11:00
66917fd39b Use callback for loading Vimeo API 2017-11-09 19:44:07 +11:00
fd97607e55 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
2017-11-09 19:41:12 +11:00
f878581c8f UI bugs 2017-11-09 19:40:45 +11:00
f9602acf61 Callback for loadScript 2017-11-09 16:06:49 +11:00
86a5724bdb Missing code highlight 2017-11-09 00:22:40 +11:00
63c963d726 More docs 2017-11-09 00:21:44 +11:00
88bfa35b99 Test 2017-11-09 00:06:34 +11:00
bb66be98da Volume fixes and other tidy up work 2017-11-08 23:46:20 +11:00
c948e95ade Looping, increase/decrease volume fix 2017-11-08 00:37:14 +11:00
1a5f4b1b9e Merge branch 'develop' of github.com:Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	dist/plyr.js.map
#	src/js/defaults.js
2017-11-07 23:23:17 +11:00
3f41a0cf54 Merge branch 'develop' of github.com:Selz/plyr into develop
# Conflicts:
#	readme.md
2017-11-07 23:21:35 +11:00
966ca1acc3 Trap focus fix 2017-11-07 18:01:24 +11:00
e2c7491ccd Using the “href” attribute on SVG if supported, using hasAttribute 2017-11-07 09:58:37 +11:00
0ec0addbda Update readme.md 2017-11-07 09:16:41 +11:00
fae4ca12e0 Started on docs 2017-11-06 23:06:14 +11:00
84505da84b Automatic Vimeo aspect ratio 2017-11-06 22:00:00 +11:00
2497e25b3c Global listeners to prevent repeat binding 2017-11-06 21:19:00 +11:00
9c4b7e7094 Keyboard logic 2017-11-06 20:34:11 +11:00
3a9beaed59 Vimeo captions fix 2017-11-06 20:27:19 +11:00
1578525ee3 Finished aspect ratio setting 2017-11-06 20:24:07 +11:00
62c1b368cf Cleanup 2017-11-06 19:53:27 +11:00
f8ecea8fb7 Added Vimeo playback speed 2017-11-06 19:47:14 +11:00
0068710740 Started on documentation and aspect ratio option 2017-11-06 19:38:31 +11:00
d206b8ab34 Merge pull request #696 from yuriykuzin/enable-speed-controls-on-vimeo
Enable speed controls using Vimeo API
2017-11-06 19:37:47 +11:00
51c011b733 Merge branch 'master' into enable-speed-controls-on-vimeo 2017-11-06 19:37:02 +11:00
5fe477340b Tab focus classname change 2017-11-05 23:20:26 +11:00
45df319823 Keyboard shortcut tweaks 2017-11-05 23:09:33 +11:00
6bebbe4153 Key listeners fix 2017-11-05 20:49:37 +11:00
374de800a4 Captions fix 2017-11-05 18:57:22 +11:00
60084a17f8 YouTube volume fix 2017-11-05 18:40:41 +11:00
4d417d0396 Vimeo fix 2017-11-05 12:04:31 +11:00
e2d8bd2b73 Captions language fix 2017-11-05 11:59:15 +11:00
1c693df00b src getter fix, local storage fix 2017-11-05 11:45:02 +11:00
8aaa932050 Cleanup 2017-11-05 01:13:00 +11:00
3cd6c2acf6 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-11-05 01:02:17 +11:00
3930ebb339 Menu design tweaks, moved logic into plugins 2017-11-05 01:02:10 +11:00
b102714067 Merge pull request #705 from friday/editorconfig
Add .editorconfig file
2017-11-05 00:49:45 +11:00
13d3037a53 Meta data and icons updated 2017-11-05 00:29:21 +11:00
1e7a60ee5c Add .editorconfig file 2017-11-04 14:16:00 +01:00
8d80f6b05d Removed log 2017-11-04 23:53:03 +11:00
3c99357f06 Vimeo fixes + small bug fixes 2017-11-04 23:18:47 +11:00
597b3fe0db Retrofit PRs 2017-11-04 22:41:18 +11:00
a757acad15 Merge pull request #577 from brandon-pereira/master
fix for svg4everybody conflicting with plyr
2017-11-04 22:25:27 +11:00
7913cf837f Merge branch 'master' into master 2017-11-04 22:25:17 +11:00
e98d356ede Merge branch 'master' into master 2017-11-04 22:22:49 +11:00
c9c20345c0 Merge pull request #613 from vers-one/master
Fix for handling explicitly set duration of audio
2017-11-04 22:19:47 +11:00
aca06be7ac Merge branch 'master' into master 2017-11-04 22:19:39 +11:00
550cc6a19b Merge branch 'master' into master 2017-11-04 22:19:17 +11:00
e44e93c117 Merge pull request #617 from oemueller/patch-1
[bugfix] target is null exception
2017-11-04 22:17:00 +11:00
a06a8150ef Merge pull request #649 from waltercruz/master
Adding title to the player html template
2017-11-04 22:16:28 +11:00
e787934ee6 Merge branch 'master' into master 2017-11-04 22:16:16 +11:00
be4b5ef4db Merge branch 'master' into master 2017-11-04 22:14:55 +11:00
55c5b8b07e Typo 2017-11-04 21:32:19 +11:00
a924d40620 Merge pull request #654 from jjui/patch-1
Enable start/stop toggle on whole element
2017-11-04 21:29:25 +11:00
0074e77a7c Update plyr.scss 2017-11-04 21:28:38 +11:00
f3141690ee Update plyr.less 2017-11-04 21:28:17 +11:00
7ac5b0e18b Merge pull request #678 from StudyTube/use-url-as-input-for-vimeo-player
Add URL as a possible input option to Vimeo player
2017-11-04 21:24:55 +11:00
d920de2a25 Small tweaks 2017-11-04 21:19:02 +11:00
5abf7e9535 Merge pull request #703 from gurupras/early-listener-fix
Allow Plyr.setup event listeners to be set up as separate event listeners
2017-11-04 18:15:03 +11:00
650ee2c18f Merge pull request #700 from ashkanhosseini/master
fix #684 memory leaks issues after destroy.
2017-11-04 18:11:35 +11:00
069c8093ae Small bug fixes 2017-11-04 14:45:06 +11:00
1cc2930dc0 ES6-ified 2017-11-04 14:25:28 +11:00
95734cf7cc Allow setup event listeners to be set up as separate event listeners
rather than in-conjunction with defaultListener

This allows the setup listeners to do things like
preventDefault()/stopImmediatePropagation() and have them work
2017-11-01 04:15:03 -04:00
3d50936b47 Split LESS into more granular files, Vimeo fixes 2017-10-28 20:14:33 +11:00
7234e2f5a3 fix #684 memory leaks issues after destroy. 2017-10-28 00:09:40 +03:00
dd9d5c8898 Linting and minor changes 2017-10-27 15:06:16 +11:00
e06b689f5c Enable speed controls using Vimeo API 2017-10-26 17:11:19 +03:00
71db66d802 Error pages 2017-10-26 20:59:40 +11:00
959b5a20e3 Inlined SVGs, fixed build 2017-10-26 00:10:56 +11:00
9de5c0cf39 Built JS 2017-10-26 00:00:15 +11:00
1a3868a6c6 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-10-25 23:59:59 +11:00
378aa159b8 Docs/demo refresh 2017-10-25 23:59:53 +11:00
6876a9163d Merge pull request #694 from friday/fix/volume-steps
Restore volume stepping via key up/down
2017-10-25 23:59:15 +11:00
34d28a69ad Merge pull request #693 from friday/fix/instanceof-undefined
v3: Add instanceof wrapper to avoid TypeErrors
2017-10-25 23:54:10 +11:00
0715d3ae67 Restore up/down arrow behavior from v2 (10 steps between min and max instead of one) 2017-10-25 14:25:10 +02:00
542095f5eb Add instanceof wrapper that avoids TypeError when second argument is undefined 2017-10-25 14:10:38 +02:00
57517a9dcc Fullscreen improvements 2017-10-18 23:55:53 +11:00
751708ff23 Fix 2017-10-18 19:47:03 +11:00
fcf944788a Cleanup 2017-10-18 12:47:54 +11:00
36a84c5c2d Removed extra <progress> for populating lower fill on range inputs 2017-10-18 12:43:10 +11:00
4392abfc49 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
2017-10-08 19:12:55 +11:00
3238b6a36a Fullscreen fix 2017-10-08 19:12:25 +11:00
18a5edef0e Linting 2017-10-08 19:10:04 +11:00
1b3a6b340d Add URL as a possible input option to Vimeo player
This is reasonable to processing correctly private Vimeo links, like https://vimeo.com/25345658/fe46e209ac - that contain not only vimeo id, but also private key
2017-10-06 14:41:57 +03:00
b3759e966d Version bump 2017-10-05 22:47:27 +11:00
34172b9aee Fullscreen bug fix (fixes #664) 2017-10-05 22:46:48 +11:00
02cb093f7b Small tweaks to focus 2017-10-03 00:00:45 +11:00
d6977473b1 Formatting 2017-10-02 21:03:51 +11:00
15cffad89e CDN URLs 2017-10-02 14:54:35 +11:00
a21ffe983c Demo fixes 2017-10-02 14:52:31 +11:00
0d96601d1a CDN URLs 2017-10-02 14:48:15 +11:00
9fb79f29fd Readme fix 2017-10-02 14:25:14 +11:00
032d73408f CDN URL updates 2017-10-02 14:18:44 +11:00
7959297a98 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-10-02 13:55:08 +11:00
6dd010ea34 Comments, small tweaks 2017-10-02 13:55:03 +11:00
f89ddb2f1b Merge pull request #669 from friday/fix/prevent-buttons-from-submitting-form
Prevent buttons from submitting form (v3)
2017-09-27 23:33:28 +10:00
2e5cc272e3 Merge pull request #670 from friday/fix/errors-without-settings-pane
Avoid errors when initiating plyr without settings (v3)
2017-09-27 23:32:25 +10:00
ebd13c63e8 Avoid multiple errors when initiating plyr without 'settings' in controls 2017-09-27 05:09:56 +02:00
ec091266aa Prevent buttons from submitting form 2017-09-27 02:34:23 +02:00
ad105877ed Merge pull request #667 from friday/fix/caption-feature-detection
Fix 'TypeError: Cannot read property 'kind' of null' in captions feat…
2017-09-27 10:03:25 +10:00
c84f666b55 Fix 'TypeError: Cannot read property 'kind' of null' in captions feature detection if video lacks captions 2017-09-27 00:24:54 +02:00
J
72412d26a5 Update plyr.less 2017-09-09 13:31:39 +02:00
J
bf02c69daa Enable start/stop toggle on whole element
Related to issue https://github.com/sampotts/plyr/issues/587
2017-09-09 13:25:22 +02:00
2bba1f30e2 Fixing Vimeo captions, WIP on settings menu, prettier and VS code settings 2017-09-03 14:36:55 +10:00
01f50f03f1 dding title to the player html template (build) 2017-08-28 10:32:56 -03:00
f50f0eb63d Adding title to the player html template 2017-08-28 10:19:29 -03:00
2196665495 Cleanup 2017-08-16 13:30:27 +10:00
0148c76c30 ESLint, comments, quality and speed menus 2017-08-16 00:36:23 +10:00
3fe0c7c84b Update readme.md 2017-08-13 21:07:22 +10:00
18b2cbb2e9 Small tweak 2017-08-13 13:54:16 +10:00
f6a7555a1d Work on quality control 2017-08-06 21:00:31 +10:00
f7144dc0cb Fix bug when switching sources 2017-08-06 13:42:37 +10:00
a9957211f4 Work on playback rate changes 2017-07-31 09:58:30 +10:00
7b60e473fa Small tweaks 2017-07-23 21:13:11 +10:00
b114a6f42e [bugfix] target is null exception
when using 'play-large' without a 'play' button, the statement target = target[0]; caused an error. So it is necessary to check the value of target before accessing [0]
2017-07-12 22:17:25 +02:00
dfc516baf6 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-07-09 17:17:26 +10:00
b0d3bcd15a Notes 2017-07-09 17:17:21 +10:00
03c3960e93 Tweaks 2017-07-09 17:14:42 +10:00
d0ceefe827 Gulp build 2017-07-06 23:41:34 -04:00
e756664750 Fix for https://github.com/sampotts/plyr/issues/601 2017-07-06 23:28:31 -04:00
21f444ed61 Tweaks 2017-06-12 09:26:01 +07:00
40b4f8e840 Tweaks 2017-06-04 22:39:21 +10:00
bc61dac4c5 Tidy up, fixing destroy 2017-06-04 18:30:37 +10:00
3605989226 Warning 2017-06-04 15:51:37 +10:00
a49d77afab Source change fix 2017-06-04 15:50:47 +10:00
7daa08c32f Change to proper constructor + prototypes 2017-06-04 15:27:37 +10:00
aaab9ad082 Seperated reduce motion check 2017-05-22 09:07:14 +10:00
86c9004183 More work on the menu 2017-05-22 08:55:45 +10:00
857dfe838c Menu toggle 2017-05-21 17:50:07 +10:00
6828bc00f8 Delete notes.md 2017-05-16 10:31:07 +10:00
e1a19faf26 URL updates 2017-05-16 10:20:58 +10:00
e95c81cc5b Update bower.json 2017-05-16 10:17:57 +10:00
f3bda17fa0 Merge branch 'master' into develop
# Conflicts:
#	demo/dist/demo.js
#	demo/src/js/main.js
#	dist/plyr.js
#	notes.md
#	src/js/plyr.js
2017-05-16 10:17:04 +10:00
c0eee58732 Plugin versions 2017-05-16 10:14:01 +10:00
bcf88cd024 Vimeo demo bug fix + repo move 2017-05-16 10:08:59 +10:00
966c3d875d Single instance only 2017-05-16 09:58:53 +10:00
fe9383bed5 Cleanup 2017-05-09 21:30:09 +10:00
2c31770030 Update notes 2017-05-09 00:20:22 +10:00
8d3e160166 Adding support for playsinline 2017-05-09 00:18:19 +10:00
408cfe5b97 Added support for passive event listeners 2017-05-08 23:07:19 +10:00
279fee3595 Bug fixes 2017-05-07 20:17:36 +10:00
28dddfcff2 Vimeo captions working 2017-05-07 20:07:08 +10:00
9e2580ec6a Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
2017-05-07 19:04:05 +10:00
aba5a9dc0e Utils object, work on Vimeo captions 2017-05-07 19:03:48 +10:00
d27a0b3855 updated to check for global namespace 2017-05-01 16:53:54 -06:00
a575aa25ac fix for svg4everybody conflicting with plyr 2017-05-01 15:50:54 -06:00
9999828505 Merge pull request #574 from mapmelabs/youtube-loop
Implement loop in YouTube
2017-04-28 13:12:31 +10:00
b04301c211 Implement loop in YouTube 2017-04-27 11:53:53 -03:00
97157efcfa More work on Vimeo captions 2017-04-26 00:24:42 +10:00
3756476809 Merge branch 'develop' of github.com:Selz/plyr into develop 2017-04-25 22:39:21 +10:00
b3d798eeee Gulp tweak 2017-04-25 22:39:14 +10:00
b499622eb8 Started on Vimeo captions 2017-04-25 20:45:25 +10:00
04b8d0bac8 Tweaks 2017-04-25 19:31:34 +10:00
8ea4cbd942 Tidy up 2017-04-25 18:43:09 +10:00
7889ae1126 Built JS 2017-04-25 18:37:56 +10:00
897d49ad87 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-04-25 18:37:36 +10:00
1960d35d8b More work on menus and tidy up 2017-04-25 18:37:31 +10:00
0b7d0dedf6 Merge pull request #545 from silverwind/blank-mp4
Add blank.mp4 to dist files
2017-04-25 12:40:20 +10:00
948e2cc66c Merge branch 'develop' into blank-mp4 2017-04-25 12:40:13 +10:00
396b816a1f Merge pull request #563 from stormrockwell/master
removed vimeo id parsing because it is not necessary
2017-04-25 12:37:43 +10:00
0a58cbfd59 Merge branch 'develop' into master 2017-04-25 12:37:35 +10:00
bbe4b7e565 Notes 2017-04-22 14:55:03 +10:00
2ecca2cbe3 removed vimeo id parsing because it is not necessary 2017-04-19 15:16:03 -04:00
f5d80fa888 Notes 2017-04-19 22:40:32 +10:00
f2bbe4eb12 Looping menu (WIP) 2017-04-19 22:34:52 +10:00
60e51b7077 Added notes 2017-04-19 21:33:23 +10:00
01d1ac9ab9 Toggle menu 2017-04-17 21:05:35 +10:00
9b2396f5ff Airplay and PiP 2017-04-17 01:28:52 +10:00
da15980b8a ID fix and styling fix 2017-04-17 00:32:41 +10:00
452678867f More work on menus 2017-04-16 20:38:45 +10:00
ff2d27f8e5 Cleanup events and references 2017-04-16 13:00:57 +10:00
546cdaad92 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
2017-04-15 23:22:35 +10:00
5d38497f07 Formatting 2017-04-15 23:22:14 +10:00
808a3af3cf Formatting 2017-04-15 23:21:18 +10:00
889712761a Merge branch 'develop' of github.com:Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	src/js/plyr.js
2017-03-29 09:05:25 +11:00
46fab972a0 Namespacing 2017-03-29 09:03:33 +11:00
535c046b62 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	src/js/plyr.js
2017-03-28 09:30:37 +11:00
55dfab8394 Tweaks 2017-03-28 09:19:58 +11:00
18bf4d84b9 Menu creation (WIP) 2017-03-28 00:12:33 +11:00
ef1d73df15 Controls rewrite 2017-03-27 00:25:41 +11:00
684107da5c WIP on new controls creation 2017-03-26 21:32:41 +11:00
2a97a9a9c5 Add blank.mp4 to dist files
For a complete "offline" experience, it's necessary to self-host the
file, so include it in the dist files for that purpose.
2017-03-25 10:58:36 +01:00
ca12343b16 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-03-25 18:44:17 +11:00
a974fbf31c Notes 2017-03-25 18:44:10 +11:00
6eaf33ec6c Merge pull request #520 from platformpurple/feature/subtitles
Feature/subtitles
2017-03-25 18:43:45 +11:00
33fef7faee Merge branch 'develop' into feature/subtitles 2017-03-25 18:43:36 +11:00
b083bf4297 Merge branch 'master' into develop
# Conflicts:
#	dist/plyr.css
#	dist/plyr.js
#	src/js/plyr.js
#	src/less/plyr.less
#	src/scss/plyr.scss
2017-03-25 11:24:38 +11:00
4957e4d80c Version bump to 2.0.12 2017-03-25 10:58:41 +11:00
605f8a8249 Changelog update 2017-03-25 10:57:52 +11:00
7490a7e7f3 Merge pull request #468 from jronallo/captions-listener
allow for setting additional listener for captions
2017-03-25 10:51:54 +11:00
f32527feb4 Merge pull request #504 from ShimShamSam/master
Add blankUrl option to set the URL for blank.mp4
2017-03-25 10:50:55 +11:00
ebc7ed538a Manual merge of #509 2017-03-25 10:49:04 +11:00
a60868bfe8 Clean up 2017-03-25 10:47:37 +11:00
4a2866d05f Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
2017-03-25 10:37:21 +11:00
1a05f352c7 Code formatting 2017-03-25 10:37:00 +11:00
ba27de0669 Merge pull request #510 from platformpurple/feature/loop-events
Added key bindings for loop
2017-03-25 10:35:15 +11:00
e21e7054ce Merge branch 'develop' into feature/loop-events 2017-03-25 10:35:08 +11:00
bc323e905c Manual merge of #543 PR 2017-03-25 10:27:03 +11:00
afa1fe9d5f Merge pull request #514 from platformpurple/improvement/loop-color-area
Improvement/loop color area
2017-03-25 10:22:10 +11:00
2444efc834 Merge branch 'develop' into improvement/loop-color-area 2017-03-25 10:22:02 +11:00
7b6257ff20 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
2017-03-25 10:09:14 +11:00
13973125fb Loop variable naming 2017-03-25 10:08:39 +11:00
ddf9104349 Merge pull request #511 from platformpurple/develop
Fix on typo
2017-03-25 10:07:07 +11:00
47b53fffb4 remove commented out code 2017-03-24 17:29:48 +02:00
9ba3753f80 toggle quality options 2017-03-24 17:25:09 +02:00
a353194fe1 make config.captions.selectedIndex number 2017-03-22 15:50:56 +02:00
c205c47509 set captionExists to true if selectedIndex is bigger than capt source 2017-03-22 13:38:08 +02:00
9c867edfbc fix data typo in language button 2017-03-18 01:59:20 +02:00
9919d80782 check if captions exists 2017-03-15 17:18:34 +02:00
a971850d26 Update readme.md 2017-03-09 15:35:29 +11:00
8464ffe6ad Conflicts and tracks array on config 2017-02-24 16:38:00 +02:00
6537225c61 Subtitles tracks 2017-02-24 16:32:17 +02:00
dcbf59bd89 Colored strip indicating looped area 2017-02-15 17:41:04 +02:00
21d478d396 Add looping key bindings to allowed array 2017-02-14 17:15:27 +02:00
054c243416 Fix on typo and gulp 2017-02-14 16:44:29 +02:00
d57e453541 Fix on typo 2017-02-14 16:42:40 +02:00
3f45ef4908 Removed console 2017-02-14 16:29:05 +02:00
1fd742464d Added key bindings for loop 2017-02-14 16:04:58 +02:00
382397407f Generated dist files 2017-02-12 00:15:21 -05:00
82809235b9 Documented the blankUrl option 2017-02-11 23:59:14 -05:00
7d5c507f16 Allow configuration of the blank.mp4 URL
This is useful for people behind firewalls who need to host a blank mp4 themselves.
2017-02-11 23:50:49 -05:00
bc139f16a7 Update readme.md 2017-02-10 11:21:20 +11:00
f6f3284dc5 Update readme.md 2017-02-10 11:19:37 +11:00
f4d2f1856f Update readme.md 2017-02-09 23:38:52 +11:00
871e19ed92 Update readme.md 2017-02-09 23:37:29 +11:00
9c599884a8 Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	src/js/plyr.js
2017-02-05 11:36:53 +11:00
cbef45841c Menu work 2017-02-05 11:34:33 +11:00
96d9f302c2 Merge pull request #490 from platformpurple/develop
Playback speed and looping controls
2017-02-05 11:31:58 +11:00
afcdf17a72 Removed not needed functions + reverted demo.js to previous 2017-02-03 14:52:38 +02:00
1bcdbe4c00 Refactored loop event buttons + on 'No Loop' cleared displayed timing 2017-02-02 11:38:08 +02:00
45c0ded88c Loop functionality 2017-02-01 19:16:39 +02:00
ea30ad9494 Playback speed control settings 2017-01-31 17:21:33 +02:00
7608615702 Merge branch 'develop' of https://github.com/Selz/plyr into develop 2017-01-11 20:14:48 +11:00
cc5f37392f ToDo notes, code tidy up 2017-01-11 20:14:41 +11:00
413fcbcb8c Merge branch 'develop' of github.com:Selz/plyr into develop
# Conflicts:
#	dist/plyr.js
#	src/js/plyr.js
2017-01-10 21:47:49 +11:00
c49d0a99e4 JsBeautifier 2017-01-10 21:43:28 +11:00
5a0cb9289c JsBeautifier in use, syntax tweaks 2017-01-10 20:22:18 +11:00
72998c4c41 Merge pull request #437 from tv2/playground
Fixed typo
2017-01-10 20:21:01 +11:00
da48be495b Merge pull request #477 from iamrobert/patch-1
Update index.html
2017-01-10 14:51:40 +11:00
c843ed3460 Update index.html
Just missing the closing </li>
2017-01-10 11:50:26 +08:00
4232aa9ea4 Update issue_template.md 2017-01-10 09:47:46 +11:00
f6612fa78c Update issue_template.md 2017-01-10 09:45:41 +11:00
bce1d983cb Merge pull request #457 from amowu/patch-3
fix: Firefox can not display captions
2017-01-04 08:45:40 +11:00
047fc43f8f Merge pull request #458 from amowu/patch-4
fix: parse webVTT time NaN
2017-01-04 08:45:06 +11:00
96ae866baa allow for setting additional listener for captions 2016-12-26 17:06:59 -05:00
0cbfc9252c fix: parse webVTT time NaN 2016-12-16 14:32:04 +08:00
d02e57bdb3 fix: Firefox can not display captions 2016-12-15 16:38:46 +08:00
a1d5bccac2 Fixed typo 2016-11-30 08:54:24 +01:00
a9bb8afca4 v2.0.11 2016-11-27 20:18:30 +11:00
28cd92b708 Merge branch 'master' of https://github.com/Selz/plyr 2016-11-27 20:15:19 +11:00
a44c7ecc3a Fix for iPad and YouTube issues 2016-11-27 20:15:15 +11:00
663c1fb627 Merge pull request #427 from amowu/patch-2
Fix cannot scroll after destroyed
2016-11-27 20:09:49 +11:00
a7969b8e9e Fix cannot scroll after destroyed
In single page application, switch router when plyr is fullscreen mode, page cannot scroll, because `document.body.style.overflow` is `hidden`
2016-11-24 11:21:43 +08:00
4ca3f030cc Merge pull request #419 from amowu/patch-1
refactor: miss prefix
2016-11-18 09:48:40 +11:00
5214c5d55d Merge pull request #425 from sebastiancarlsson/cleanup-called-twice-bug
fixes bug where cleanUp would be called twice
2016-11-18 09:40:34 +11:00
3e6ed7b69b fixes bug where cleanUp would be called twice 2016-11-17 15:43:26 +01:00
7d2a8d343a Update 2016-11-16 09:41:06 +11:00
03aacf483f Update readme.md 2016-11-16 09:40:09 +11:00
02bc8c8445 refactor: miss prefix
fix _fullscreen.prefix can't work
2016-11-10 16:27:10 +08:00
7965b82cca Quality WIP 2016-11-06 21:47:44 +11:00
bae04a492a Merge branch 'master' into develop
# Conflicts:
#	demo/index.html
#	dist/plyr.js
2016-11-06 15:55:55 +11:00
73a1391f2f Update readme.md 2016-11-02 18:45:37 +11:00
660ff0434d Cache bust 2016-11-02 18:29:38 +11:00
94208dce76 v2.0.10
- Added seek event fixes for Vimeo and YouTube (fixes #409)
- Added support for embed URLs rather than ID only (fixes #345)
2016-11-02 18:14:06 +11:00
efe54fbba4 Added seek event fixes (fixes #409), Added support for URLs (fixes #345) 2016-11-02 18:09:40 +11:00
10561d6c83 Merge pull request #408 from gurupras/urlparser
Added logic to parse youtube video id
2016-11-02 16:59:28 +11:00
72404e77e5 PiP 2016-11-01 12:00:16 +11:00
0ef87f93a6 Added logic to parse youtube video id
This commit is targetted at solving issue#345 and adds
    functionality to parse youtube video IDs from various
    types of youtube video URLs.

    Other embed types like vimeo/soundcloud can be extended by
    following a similar structure as implemented in this commit.
2016-10-30 00:49:17 -04:00
Sam
c7310c21fb Fullscreen fix, styling 2016-10-24 00:11:47 +11:00
Sam
ed9e27b64f Merge branch 'develop' of https://github.com/Selz/plyr into develop 2016-10-23 21:42:18 +11:00
801ed63be5 Work on PiP 2016-10-23 21:21:42 +11:00
Sam
2aa9b267ab Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	src/js/plyr.js
2016-10-23 21:16:49 +11:00
Sam
90338d9600 Tweaks 2016-10-23 21:15:19 +11:00
5cecf16d4f Merge branch 'master' into develop
# Conflicts:
#	demo/dist/demo.js
#	dist/plyr.css
#	dist/plyr.js
2016-10-23 15:27:28 +11:00
a3ed309342 v2.0.9
- Temporary patch for the YouTube API issues with `getDuration()` (relates to #374)
2016-10-23 15:18:53 +11:00
08b4c09b89 Temporary patch for the YouTube API issues with getDuration() (relates to #374) 2016-10-23 15:18:08 +11:00
a509a5e1bc v2.0.8
- Added `isPaused()` API method (thanks to @darrena092)
- Allowed `.on()` API method to be chainable (thanks to @gurupras) (fixes #357)
- Improved the "awful" rendering of captions on small screens in fullscreen mode (fixes #390)
- Fix for Firefox VTT compatibility (thanks to @magourex)
- Fix for Firefox Developer Edition blank video due to `-webkit-mask-image` issue (fixes #392)
- Added Issue and PR templates with the aim of reducing duplicate or duff issues
2016-10-23 13:48:05 +11:00
bec10e7836 Fix for caption size (fixes #390), Fix for Firefox Dev (fixes #392) 2016-10-23 13:35:49 +11:00
f9a184800b Add Issue and PR templates 2016-10-23 13:34:44 +11:00
f428c3ff8b Manually merged #395 2016-10-23 12:19:39 +11:00
97eb40a8e3 Merge pull request #389 from darrena092/master
Added .isPaused().
2016-10-23 11:51:10 +11:00
8211631950 Gulp build done. 2016-10-06 15:43:41 +01:00
a17cbb0e8b Added .isPaused to API 2016-10-06 15:33:49 +01:00
21a30f1b6f Fixed for array passed to _on and _off 2016-10-03 17:16:05 +11:00
Sam
613c45ede9 Merge branch 'master' into develop 2016-10-01 22:18:40 +10:00
Sam
8743c6a08d Code style 2016-10-01 22:18:01 +10:00
8098be6f93 Merge pull request #379 from magourex/master
Support WebVTT with CRLF or LF Line separator with firefox #378
2016-10-01 22:10:35 +10:00
Sam
eba0831538 Manually merged #385 2016-10-01 22:07:01 +10:00
1c73c9fbfe Update plyr.js 2016-09-27 10:57:35 +02:00
Sam
1ad103c29f Menu animation 2016-09-26 23:21:57 +10:00
Sam
ca37b4c9ac Menu 2016-09-25 23:54:49 +10:00
d3bbf09d12 More menu work 2016-09-25 20:01:58 +10:00
f608317eec Merge branch 'develop' of https://github.com/Selz/plyr into develop
# Conflicts:
#	dist/plyr.css
#	dist/plyr.js
2016-09-14 15:22:41 +10:00
c8b9c9bcaa Expanded 2016-09-13 23:52:25 +10:00
Sam
d638cdcdd3 Started on PIP support 2016-09-13 00:16:27 +10:00
46f82a4af3 Added quality in menu 2016-09-07 22:18:05 +10:00
978606edb4 CSS path 2016-09-06 23:54:04 +10:00
Sam
42a2642d40 Icon fix 2016-09-06 23:50:59 +10:00
Sam
37840c3844 Working on settings menu 2016-09-06 23:48:09 +10:00
28335ef3d8 Merge pull request #356 from amowu/feature/add-playback-speed
Add playback speed
2016-09-06 20:46:00 +10:00
78ec5b3322 refactor: sorting speed 2016-09-05 22:56:19 +08:00
ac7e1ab299 test: add speed-up button 2016-09-05 18:00:11 +08:00
ea60fd0f45 docs: update readme 2016-08-31 17:18:44 +08:00
c291e8c5d9 refactor: check config.speeds format 2016-08-31 15:42:01 +08:00
f0ac542a7f refactor: add localStorage supporting 2016-08-31 15:34:44 +08:00
a412085785 feat: add playback speed button 2016-08-31 15:11:29 +08:00
151 changed files with 42712 additions and 8276 deletions

10
.editorconfig Normal file
View File

@ -0,0 +1,10 @@
# See editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

41
.eslintrc.json Normal file
View File

@ -0,0 +1,41 @@
{
"parser": "babel-eslint",
"extends": ["airbnb-base", "prettier"],
"env": {
"browser": true,
"es6": true
},
"globals": { "Plyr": false, "jQuery": false },
"rules": {
"no-const-assign": 1,
"no-shadow": 0,
"no-this-before-super": 1,
"no-undef": 1,
"no-unreachable": 1,
"no-unused-vars": 1,
"constructor-super": 1,
"valid-typeof": 1,
"indent": [2, 4, { "SwitchCase": 1 }],
"quotes": [2, "single", "avoid-escape"],
"semi": [2, "always"],
"eqeqeq": [2, "always"],
"one-var": [2, "never"],
"comma-dangle": [2, "always-multiline"],
"no-restricted-globals": [
"error",
{
"name": "event",
"message": "Use local parameter instead."
},
{
"name": "error",
"message": "Use local parameter instead."
}
],
"array-bracket-newline": [2, { "minItems": 2 }],
"array-element-newline": [2, { "minItems": 2 }]
},
"parserOptions": {
"sourceType": "module"
}
}

17
.github/issue_template.md vendored Normal file
View File

@ -0,0 +1,17 @@
<!---
Please use this issue template as it makes replicating and fixing the issue easier!
--->
### Expected behaviour
### Actual behaviour
### Environment
- Browser:
- Version:
- Operating System:
- Version:
### Steps to reproduce
-

8
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,8 @@
### Link to related issue (if applicable)
### Sumary of proposed changes
### Task list
- [ ] Tested on [supported browsers](https://github.com/sampotts/plyr#browser-support)
- [ ] Gulp build completed

14
.gitignore vendored
View File

@ -1,11 +1,11 @@
node_modules
*.sublime-project
*.sublime-workspace
.DS_Store
aws.json
docs/index.dev.html
*.mp4
index-dev.html
notes.txt
*.vtt
docs/index.dev.php
!dist/blank.mp4
index-*.html
npm-debug.log
*.webm
/package-lock.json
.idea/

View File

View File

@ -1,55 +0,0 @@
{
// Settings
"passfail" : false, // Stop on first error.
"maxerr" : 100, // Maximum error before stopping.
// Predefined globals whom JSHint will ignore.
"browser" : true, // Standard browser globals e.g. `window`, `document`.
"node" : false,
"rhino" : false,
"couch" : false,
"wsh" : false, // Windows Scripting Host.
"jquery" : false,
// Development.
"debug" : true, // Allow debugger statements e.g. browser breakpoints.
"devel" : true, // Allow developments statements e.g. `console.log();`.
// ECMAScript 5.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : false, // Allow global "use strict" (also enables 'strict').
// The Good Parts.
"asi" : true, // Tolerate Automatic Semicolon Insertion (no semicolons).
"laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : true, // Require {} for every new block or scope.
"eqeqeq" : true, // Require triple equals i.e. `===`.
"eqnull" : false, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : false, // Prohipit variable use before definition.
"loopfunc" : true, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
"scripturl" : true, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.
// Personal styling preferences.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : true, // Prohibit use of empty blocks.
"nonew" : true, // Prohibit use of constructors for side-effects.
"nomen" : true, // Prohibit use of initial or trailing underbars in names.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"white" : true, // Check against strict whitespace and indentation rules.
"indent" : 4 // Specify indentation spacing
}

4
.npmignore Normal file
View File

@ -0,0 +1,4 @@
demo
.github
.vscode
*.code-workspace

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"useTabs": false,
"tabWidth": 4,
"printWidth": 160,
"singleQuote": true,
"trailingComma": "all"
}

24
.stylelintrc.json Normal file
View File

@ -0,0 +1,24 @@
{
"plugins": ["stylelint-selector-bem-pattern", "stylelint-scss"],
"extends": ["stylelint-config-recommended", "stylelint-config-sass-guidelines", "stylelint-config-prettier"],
"rules": {
"selector-class-pattern": null,
"selector-no-qualifying-type": [
true,
{
"ignore": ["attribute", "class"]
}
],
"indentation": 4,
"string-quotes": "single",
"max-nesting-depth": 2,
"plugin/selector-bem-pattern": {
"preset": "bem",
"componentName": "(([a-z0-9]+(?!-$)-?)+)",
"componentSelectors": {
"initial": "\\.{componentName}(((__|--)(([a-z0-9\\[\\]'=]+(?!-$)-?)+))+)?$"
},
"ignoreSelectors": [".*\\.has-.*", ".*\\.is-.*"]
}
}
}

12
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
"dbaeumer.vscode-eslint",
"wix.vscode-import-cost",
"esbenp.prettier-vscode",
"shinnn.stylelint",
"wayou.vscode-todo-highlight"
]
}

1
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -6,10 +6,10 @@
"Audio",
"Video",
"HTML5 Audio",
"HTml5 Video"
"HTML5 Video"
],
"authors": [
"Sam Potts <me@sampotts.me>"
"Sam Potts <sam@potts.es>"
],
"dependencies": {},
"main": [
@ -27,7 +27,7 @@
],
"repository": {
"type": "git",
"url": "git://github.com/selz/plyr.git"
"url": "git://github.com/sampotts/plyr.git"
},
"license": "MIT"
}
}

View File

@ -1,24 +1,20 @@
{
"plyr": {
"less": {
"plyr.css": ["src/less/plyr.less"]
},
"scss": {
"plyr.css": ["src/scss/plyr.scss"]
"sass": {
"plyr.css": "src/sass/plyr.scss"
},
"js": {
"plyr.js": ["src/js/plyr.js"]
"plyr.js": "src/js/plyr.js",
"plyr.polyfilled.js": "src/js/plyr.polyfilled.js"
}
},
"demo": {
"less": {
"demo.css": ["demo/src/less/demo.less"]
"sass": {
"demo.css": "demo/src/sass/bundles/demo.scss",
"error.css": "demo/src/sass/bundles/error.csss"
},
"js": {
"demo.js": [
"demo/src/js/lib/classlist.js",
"demo/src/js/main.js"
]
"demo.js": "demo/src/js/demo.js"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@ You need to add several placeholders to your html template that are replaced whe
- `{id}` - the dynamically generated ID for the player (for form controls)
- `{seektime}` - the seek time specified in options for fast forward and rewind
- `{title}` - the title of your media, if specified
You can include only the controls you need when specifying custom html.

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

4037
demo/dist/demo.js vendored

File diff suppressed because it is too large Load Diff

1
demo/dist/demo.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
demo/dist/demo.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
demo/dist/demo.min.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1
demo/dist/demo.svg vendored
View File

@ -1 +0,0 @@
<?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="icon-github" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 .2c-4.4 0-8 3.6-8 8 0 3.5 2.3 6.5 5.5 7.6.4.1.5-.2.5-.4V14c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.3 1.9.9 2.3.7.1-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8.6-.2 1.3-.3 2-.3s1.4.1 2 .3c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.6.8 1.3.8 2.1 0 3.1-1.9 3.7-3.7 3.9.3.4.6.9.6 1.6v2.2c0 .2.1.5.6.4 3.2-1.1 5.5-4.1 5.5-7.6-.1-4.4-3.7-8-8.1-8z"/></symbol><symbol id="icon-twitter" viewBox="0 0 16 16"><title>Twitter</title><path d="M16 3c-.6.3-1.2.4-1.9.5.7-.4 1.2-1 1.4-1.8-.6.4-1.3.6-2.1.8-.6-.6-1.5-1-2.4-1-1.7 0-3.2 1.5-3.2 3.3 0 .3 0 .5.1.7-2.7-.1-5.2-1.4-6.8-3.4-.3.5-.4 1-.4 1.7 0 1.1.6 2.1 1.5 2.7-.5 0-1-.2-1.5-.4C.7 7.7 1.8 9 3.3 9.3c-.3.1-.6.1-.9.1-.2 0-.4 0-.6-.1.4 1.3 1.6 2.3 3.1 2.3-1.1.9-2.5 1.4-4.1 1.4H0c1.5.9 3.2 1.5 5 1.5 6 0 9.3-5 9.3-9.3v-.4C15 4.3 15.6 3.7 16 3z"/></symbol><symbol id="icon-vimeo" viewBox="0 0 16 16"><path d="M16 4.3c-.1 1.6-1.2 3.7-3.3 6.4-2.2 2.8-4 4.2-5.5 4.2-.9 0-1.7-.9-2.4-2.6C4 9.9 3.4 5 2 5c-.1 0-.5.3-1.2.8l-.8-1c.8-.7 3.5-3.4 4.7-3.5 1.2-.1 2 .7 2.3 2.5.3 2 .8 6.1 1.8 6.1.9 0 2.5-3.4 2.6-4 .1-.9-.3-1.9-2.3-1.1.8-2.6 2.3-3.8 4.5-3.8 1.7.1 2.5 1.2 2.4 3.3z"/></symbol><symbol id="icon-youtube" viewBox="0 0 16 16"><path d="M15.8 4.8c-.2-1.3-.8-2.2-2.2-2.4C11.4 2 8 2 8 2s-3.4 0-5.6.4C1 2.6.3 3.5.2 4.8 0 6.1 0 8 0 8s0 1.9.2 3.2c.2 1.3.8 2.2 2.2 2.4C4.6 14 8 14 8 14s3.4 0 5.6-.4c1.4-.3 2-1.1 2.2-2.4C16 9.9 16 8 16 8s0-1.9-.2-3.2zM6 11V5l5 3-5 3z"/></symbol></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,18 +1,25 @@
<!doctype html>
<html lang="en" class="error">
<head>
<meta charset="utf-8" />
<title>Doh. Looks like something went wrong.</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/docs.css">
</head>
<body>
<main>
<h1>Doh.</h1>
<p>Looks like something went wrong.</p>
<a href="http://plyr.io" class="btn btn--primary">Back to plyr.io</a>
</main>
</body>
</html>
<head>
<meta charset="utf-8" />
<title>Doh. Looks like something went wrong.</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/error.css">
<!-- Preload -->
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-medium.woff2">
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
</head>
<body>
<main>
<h1>Doh.</h1>
<p>Looks like something went wrong.</p>
<a href="javascript:history.back()" class="button">Go back</a>
</main>
</body>
</html>

View File

@ -1,88 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Plyr - A simple HTML5 media player</title>
<meta name="description" content="A simple HTML5 media player with custom controls and WebVTT captions.">
<meta name="author" content="Sam Potts">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Styles -->
<link rel="stylesheet" href="../dist/plyr.css">
<head>
<meta charset="utf-8" />
<title>Plyr - A simple, customizable HTML5 Video, Audio, YouTube and Vimeo player</title>
<meta name="description" property="og:description" content="A simple HTML5 media player with custom controls and WebVTT captions.">
<meta name="author" content="Sam Potts">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/demo.css">
</head>
<body>
<!-- Icons -->
<link rel="icon" href="https://cdn.plyr.io/static/icons/favicon.ico">
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/16x16.png" sizes="16x16">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.plyr.io/static/icons/180x180.png">
<!-- Opengraph -->
<meta property="og:title" content="Plyr - A simple, customizable HTML5 Video, Audio, YouTube and Vimeo player">
<meta property="og:site_name" content="Plyr">
<meta property="og:url" content="https://plyr.io">
<meta property="og:image" content="https://cdn.plyr.io/static/icons/1200x630.png">
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@sam_potts">
<meta name="twitter:creator" content="@sam_potts">
<meta name="twitter:card" content="summary_large_image">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/demo.css">
<!-- Preload -->
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-medium.woff2">
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
</head>
<body>
<div class="grid">
<header>
<h1>Plyr</h1>
<p>A simple, accessible HTML5 media player by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p>
<nav>
<ul>
<li>
<a href="https://github.com/selz/plyr" target="_blank" class="btn btn--large btn--primary" data-shr-network="github">
<svg class="icon"><use xlink:href="#icon-github"/></svg>Download on GitHub
</a>
</li>
<li>
<a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn--large btn--twitter" data-shr-network="twitter">
<svg class="icon"><use xlink:href="#icon-twitter"/></svg>Tweet
</a>
</li>
</ul>
</nav>
<p>A simple, accessible and customisable media player for
<button type="button" class="faux-link" data-source="video">
<svg class="icon">
<title>HTML5</title>
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
</svg>Video</button>,
<button type="button" class="faux-link" data-source="audio">
<svg class="icon">
<title>HTML5</title>
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
</svg>Audio</button>,
<button type="button" class="faux-link" data-source="youtube">
<svg class="icon" role="presentation">
<title>YouTube</title>
<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
M6,11V5l5,3L6,11z"></path>
</svg>YouTube</button> and
<button type="button" class="faux-link" data-source="vimeo">
<svg class="icon" role="presentation">
<title>Vimeo</title>
<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"></path>
</svg>Vimeo</button>
</p>
<p>Premium video monitization from
<a href="https://vi.ai/publisher-video-monetization/?aid=plyrio" target="_blank" class="no-border">
<img src="https://cdn.plyr.io/static/vi-logo-24x24.svg" alt="ai.vi">
<span class="sr-only">ai.vi</span>
</a>
</p>
<div class="call-to-action">
<span class="button--with-count">
<a href="https://github.com/sampotts/plyr" target="_blank" class="button" data-shr-network="github">
<svg class="icon" role="presentation">
<title>GitHub</title>
<path d="M8,0.2c-4.4,0-8,3.6-8,8c0,3.5,2.3,6.5,5.5,7.6
C5.9,15.9,6,15.6,6,15.4c0-0.2,0-0.7,0-1.4C3.8,14.5,3.3,13,3.3,13c-0.4-0.9-0.9-1.2-0.9-1.2c-0.7-0.5,0.1-0.5,0.1-0.5
c0.8,0.1,1.2,0.8,1.2,0.8C4.4,13.4,5.6,13,6,12.8c0.1-0.5,0.3-0.9,0.5-1.1c-1.8-0.2-3.6-0.9-3.6-4c0-0.9,0.3-1.6,0.8-2.1
c-0.1-0.2-0.4-1,0.1-2.1c0,0,0.7-0.2,2.2,0.8c0.6-0.2,1.3-0.3,2-0.3c0.7,0,1.4,0.1,2,0.3c1.5-1,2.2-0.8,2.2-0.8
c0.4,1.1,0.2,1.9,0.1,2.1c0.5,0.6,0.8,1.3,0.8,2.1c0,3.1-1.9,3.7-3.7,3.9C9.7,12,10,12.5,10,13.2c0,1.1,0,1.9,0,2.2
c0,0.2,0.1,0.5,0.6,0.4c3.2-1.1,5.5-4.1,5.5-7.6C16,3.8,12.4,0.2,8,0.2z"></path>
</svg>
Download on GitHub
</a>
</span>
</div>
</header>
<main role="main" id="main">
<nav class="btn__bar">
<ul>
<li class="active">
<button type="button" class="btn" data-source="video">Video</button>
</li>
<li>
<button type="button" class="btn" data-source="audio">Audio</button>
</li>
<li>
<button type="button" class="btn btn--youtube" data-source="youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</button>
</li>
<li>
<button type="button" class="btn btn--vimeo" data-source="vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</button>
</li>
</ul>
</nav>
<section>
<video poster="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg" controls crossorigin>
<!-- Video files -->
<source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4" type="video/mp4">
<source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm" type="video/webm">
<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-HD.mp4" type="video/mp4">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.webm" type="video/webm">
<!-- Text track file -->
<track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.en.vtt" default>
<!-- Text track file -->
<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.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.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-HD.mp4" download>Download</a>
</video>
<ul>
<li class="plyr__cite plyr__cite--video"><small><a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> &copy; Brainfarm</small></li>
<li class="plyr__cite plyr__cite--audio"><small><a href="http://www.kishibashi.com/" target="_blank">Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;</a> &copy; Kishi Bashi</small></li>
<li class="plyr__cite plyr__cite--youtube"><small><a href="https://www.youtube.com/watch?v=bTqVqk7FSmY" target="_blank">View From A Blue Moon</a> on <span class="color--youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</span></small>
<li class="plyr__cite plyr__cite--vimeo"><small><a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on <span class="color--vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</span></small>
</ul>
</section>
<ul>
<li class="plyr__cite plyr__cite--video" hidden>
<small>
<svg class="icon">
<title>HTML5</title>
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
</svg>
<a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> &copy; Brainfarm
</small>
</li>
<li class="plyr__cite plyr__cite--audio" hidden>
<small>
<svg class="icon" title="HTML5">
<title>HTML5</title>
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
</svg>
<a href="http://www.kishibashi.com/" target="_blank">Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;</a> &copy; Kishi Bashi
</small>
</li>
<li class="plyr__cite plyr__cite--youtube" hidden>
<small>
<a href="https://www.youtube.com/watch?v=bTqVqk7FSmY" target="_blank">View From A Blue Moon</a> on&nbsp;
<span class="color--youtube">
<svg class="icon" role="presentation">
<title>YouTube</title>
<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
M6,11V5l5,3L6,11z"></path>
</svg>YouTube
</span>
</small>
</li>
<li class="plyr__cite plyr__cite--vimeo" hidden>
<small>
<a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on&nbsp;
<span class="color--vimeo">
<svg class="icon" role="presentation">
<title>Vimeo</title>
<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"></path>
</svg>Vimeo
</span>
</small>
</li>
</ul>
</main>
</div>
<!-- Plyr core script -->
<script src="../dist/plyr.js"></script>
<aside>
<svg class="icon">
<title>Twitter</title>
<path d="M16,3c-0.6,0.3-1.2,0.4-1.9,0.5c0.7-0.4,1.2-1,1.4-1.8c-0.6,0.4-1.3,0.6-2.1,0.8c-0.6-0.6-1.5-1-2.4-1
C9.3,1.5,7.8,3,7.8,4.8c0,0.3,0,0.5,0.1,0.7C5.2,5.4,2.7,4.1,1.1,2.1c-0.3,0.5-0.4,1-0.4,1.7c0,1.1,0.6,2.1,1.5,2.7
c-0.5,0-1-0.2-1.5-0.4c0,0,0,0,0,0c0,1.6,1.1,2.9,2.6,3.2C3,9.4,2.7,9.4,2.4,9.4c-0.2,0-0.4,0-0.6-0.1c0.4,1.3,1.6,2.3,3.1,2.3
c-1.1,0.9-2.5,1.4-4.1,1.4c-0.3,0-0.5,0-0.8,0c1.5,0.9,3.2,1.5,5,1.5c6,0,9.3-5,9.3-9.3c0-0.1,0-0.3,0-0.4C15,4.3,15.6,3.7,16,3z"></path>
</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>
</p>
</aside>
<!-- Docs script -->
<script src="dist/demo.js"></script>
<!-- Polyfills -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Array.prototype.includes,CustomEvent" crossorigin="anonymous"></script>
<!-- Rangetouch to fix <input type="range"> on touch devices (see https://rangetouch.com) -->
<script src="https://cdn.rangetouch.com/0.0.9/rangetouch.js" async></script>
<!-- Plyr core script -->
<script src="../dist/plyr.js" crossorigin="anonymous"></script>
<!-- Sharing libary (https://shr.one) -->
<script src="https://cdn.shr.one/0.1.9/shr.js"></script>
<script>if(window.shr) { window.shr.setup({ count: { classname: 'btn__count' } }); }</script>
</body>
</html>
<!-- Sharing libary (https://shr.one) -->
<script src="https://cdn.shr.one/1.0.1/shr.js" crossorigin="anonymous"></script>
<!-- Rangetouch to fix <input type="range"> on touch devices (see https://rangetouch.com) -->
<script src="https://cdn.rangetouch.com/1.0.1/rangetouch.js" async crossorigin="anonymous"></script>
<!-- Docs script -->
<script src="dist/demo.js" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,29 @@
WEBVTT FILE
1
00:00:09.500 --> 00:00:12.000
The ocean floor rises 5 miles to the shores
2
00:00:12.001 --> 00:00:16.500
of what people call, the seven mile miracle
3
00:00:25.500 --> 00:00:28.000
What would it be like to be born on this island?
4
00:00:32.500 --> 00:00:34.500
To grow up on these shores
5
00:00:37.500 --> 00:00:40.000
To witness this water, every day
6
00:00:43.500 --> 00:00:46.000
You're about to meet someone, who did
7
00:02:45.500 --> 00:02:49.000
This is a film about John John Florence

View File

@ -0,0 +1,29 @@
WEBVTT FILE
1
00:00:09.500 --> 00:00:12.000
Le fond de l'océan monte 5 miles des rives
2
00:00:12.001 --> 00:00:16.500
de ce que les gens appellent le miracle de sept mile
3
00:00:25.500 --> 00:00:28.000
Que serait-il d'être né sur cette île?
4
00:00:32.500 --> 00:00:34.500
Pour grandir sur ces rivages
5
00:00:37.500 --> 00:00:40.000
Pour assister à cette eau, tous les jours
6
00:00:43.500 --> 00:00:46.000
Vous êtes sur le point de rencontrer quelqu'un, qui ne
7
00:02:45.500 --> 00:02:49.000
Ceci est un film sur John John Florence

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

279
demo/src/js/demo.js Normal file
View File

@ -0,0 +1,279 @@
// ==========================================================================
// Plyr.io demo
// This code is purely for the https://plyr.io website
// Please see readme.md in the root or github.com/sampotts/plyr
// ==========================================================================
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();
}
document.addEventListener('DOMContentLoaded', () => {
Raven.context(() => {
if (window.shr) {
window.shr.setup({
count: {
classname: 'button__count',
},
});
}
// Setup tab focus
const tabClassName = 'tab-focus';
// Remove class on blur
document.addEventListener('focusout', event => {
event.target.classList.remove(tabClassName);
});
// Add classname to tabbed elements
document.addEventListener('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(() => {
document.activeElement.classList.add(tabClassName);
}, 0);
});
// Setup the player
const player = new Plyr('#player', {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',
keyboard: {
global: true,
},
tooltips: {
controls: true,
},
/* controls: [
'play-large',
'restart',
'rewind',
'play',
'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
], */
captions: {
active: true,
},
keys: {
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
},
ads: {
enabled: true,
publisherId: '918848828995742',
},
/* listeners: {
seek: () => false,
}, */
});
// Expose for tinkering in the console
window.player = player;
// Setup type toggle
const buttons = document.querySelectorAll('[data-source]');
const types = {
video: 'video',
audio: 'audio',
youtube: 'youtube',
vimeo: 'vimeo',
};
let currentType = window.location.hash.replace('#', '');
const 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-HD.mp4',
type: 'video/mp4',
}],
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',
title: 'View From A Blue Moon',
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(button => 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(cite => {
cite.setAttribute('hidden', '');
});
document.querySelector(`.plyr__cite--${type}`).removeAttribute('hidden');
}
// Bind to each button
Array.from(buttons).forEach(button => {
button.addEventListener('click', () => {
const type = button.getAttribute('data-source');
newSource(type);
if (historySupport) {
window.history.pushState({ type }, '', `#${type}`);
}
});
});
// List for backwards/forwards
window.addEventListener('popstate', event => {
if (event.state && 'type' in event.state) {
newSource(event.state.type);
}
});
// On load
if (historySupport) {
const 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);
}
}
});
});
// 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);
};
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 */
})();

View File

@ -1,237 +0,0 @@
/*
* classList.js: Cross-browser full element.classList implementation.
* 1.1.20150312
*
* By Eli Grey, http://eligrey.com
* License: Dedicated to the public domain.
* See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
if ("document" in self) {
// Full polyfill for browsers with no classList support
if (!("classList" in document.createElement("_"))) {
(function (view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = view.Element[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.setAttribute("class", this.toString());
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
;
do {
token = tokens[i] + "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
, index
;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (index !== -1) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function (token, force) {
token += "";
var
result = this.contains(token)
, method = result ?
force !== true && "remove"
:
force !== false && "add"
;
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
} else {
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function () {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function(method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function(token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function(token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
testElement = null;
}());
}
}

View File

@ -1,185 +0,0 @@
// ==========================================================================
// Plyr.io demo
// This code is purely for the plyr.io website
// Please see readme.md in the root or github.com/selz/plyr
// ==========================================================================
/*global plyr*/
// General functions
;(function() {
//document.body.addEventListener('ready', function(event) { console.log(event); });
// Setup the player
var instances = plyr.setup({
debug: true,
title: 'Video demo',
iconUrl: '../dist/plyr.svg',
tooltips: {
controls: true
},
captions: {
defaultActive: true
}
});
plyr.loadSprite('dist/demo.svg');
// Plyr returns an array regardless
var player = instances[0];
// Setup type toggle
var buttons = document.querySelectorAll('[data-source]'),
types = {
video: 'video',
audio: 'audio',
youtube: 'youtube',
vimeo: 'vimeo'
},
currentType = window.location.hash.replace('#', ''),
historySupport = (window.history && window.history.pushState);
// Bind to each button
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].addEventListener('click', function() {
var type = this.getAttribute('data-source');
newSource(type);
if (historySupport) {
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) {
history.replaceState({ 'type': currentType }, '', (video ? '' : '#' + currentType));
}
// If it's not video, load the source
if(currentType !== types.video) {
newSource(currentType, true);
}
}
// Toggle class on an element
function toggleClass(element, className, state) {
if (element) {
if (element.classList) {
element.classList[state ? 'add' : 'remove'](className);
}
else {
var name = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + className + ' ', '');
element.className = name + (state ? ' ' + 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.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4',
type: 'video/mp4'
},
{
src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm',
type: 'video/webm'
}],
poster: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg',
tracks: [{
kind: 'captions',
label: 'English',
srclang:'en',
src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
default: true
}]
});
break;
case types.audio:
player.source({
type: 'audio',
title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
sources: [{
src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
type: 'audio/mp3'
},
{
src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
type: 'audio/ogg'
}]
});
break;
case types.youtube:
player.source({
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: 'bTqVqk7FSmY',
type: 'youtube'
}]
});
break;
case types.vimeo:
player.source({
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: '143418951',
type: 'vimeo'
}]
});
break;
}
// Set the current type for next time
currentType = type;
// Remove active classes
for (var x = buttons.length - 1; x >= 0; x--) {
toggleClass(buttons[x].parentElement, 'active', false);
}
// Set active on parent
toggleClass(document.querySelector('[data-source="'+ type +'"]').parentElement, 'active', true);
}
})();
// Google analytics
// For demo site (http://[www.]plyr.io) only
if(document.domain.indexOf('plyr.io') > -1) {
(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','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-40881672-11', 'auto');
ga('send', 'pageview');
}

View File

@ -1,48 +0,0 @@
// ==========================================================================
// Base layout
// ==========================================================================
// BORDER-BOX ALL THE THINGS!
// http://paulirish.com/2012/box-sizing-border-box-ftw/
*, *::after, *::before {
box-sizing: border-box;
}
// Hidden
[hidden] {
display: none;
}
// Base
html {
height: 100%;
background: @body-background fixed;
}
body {
margin: 0;
padding: (@padding-base / 2);
}
// Header
header {
padding: @padding-base;
margin-bottom: @padding-base;
p {
.font-size(18);
}
@media (min-width: @screen-sm) {
padding-top: (@padding-base * 3);
padding-bottom: (@padding-base * 3);
}
}
// Sections
section {
max-width: @example-width-video;
margin: 0 auto @padding-base;
@media (min-width: @screen-sm) {
margin-bottom: (@padding-base * 2);
}
}

View File

@ -1,172 +0,0 @@
// ==========================================================================
// Buttons
// ==========================================================================
nav {
ul {
list-style: none;
margin: 0;
padding: 0;
font-size: 0;
}
li {
display: inline-block;
margin-top: (@padding-base / 2);
.font-size();
white-space: nowrap;
}
li + li {
margin-left: @padding-base;
}
}
// Tabs
.btn__bar {
position: relative;
margin: 0 auto @padding-base;
max-width: @example-width-video;
white-space: nowrap;
&::before {
content: "";
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: @gray-lighter;
}
ul {
position: relative;
z-index: 1;
display: inline-block;
user-select: none;
}
li {
margin: 0;
&:first-child .btn {
border-radius: 4px 0 0 4px;
}
&:last-child .btn {
border-radius: 0 4px 4px 0;
}
& + li .btn {
margin-left: -1px;
}
&.active .btn {
&:extend(.btn--primary);
box-shadow: inset 0 1px 1px rgba(0,0,0, .2);
position: relative;
z-index: 1;
.icon {
color: inherit;
}
}
&.active + li .btn:hover {
z-index: 0;
}
}
.btn {
position: relative;
display: block;
border-radius: 0;
&:hover,
&:focus {
z-index: 1;
}
}
@media (min-width: 560px) {
margin-bottom: (@padding-base * 2);
}
}
// Shared
.btn,
.btn__count {
display: inline-block;
vertical-align: middle;
border-radius: @border-radius-base;
user-select: none;
font-weight: @font-weight-bold;
}
// Buttons
.btn {
padding: (@padding-base / 2) ((@padding-base / 2) + 2);
background: linear-gradient(lighten(@off-white, 2%), darken(@off-white, 3%));
border: 1px solid @gray-light;
box-shadow: 0 1px 1px rgba(0,0,0, .05);
text-shadow: 0 1px 1px #fff;
color: @gray;
transition: background .1s ease, color .1s ease;
.font-size(@font-size-small);
&:hover,
&:focus {
border-color: darken(@gray-light, 8%);
color: @gray;
outline: 0;
}
}
// Sizes
.btn--large {
padding: (@padding-base / 2) @padding-base;
.font-size();
}
// Styles
.btn--primary {
background-image: linear-gradient(@link-color, darken(@link-color, 5%));
background-color: @link-color;
border-color: darken(@link-color, 10%);
box-shadow: 0 1px 1px rgba(0,0,0, .15);
text-shadow: 0 1px 1px rgba(0,0,0, .1);
color: #fff;
&:hover,
&:focus {
color: #fff;
border-color: darken(@link-color, 20%);
}
}
.btn--youtube .icon {
color: @color-youtube;
}
.btn--vimeo .icon {
color: @color-vimeo;
}
.btn--twitter .icon {
color: @color-twitter;
}
// Count bubble
.btn__count {
position: relative;
margin-left: (@padding-base / 2);
padding: (@padding-base / 2) (@padding-base * .75);
background: #fff;
border: 1px solid @gray-light;
&::before {
content: "";
position: absolute;
display: block;
width: @arrow-size;
height: @arrow-size;
left: 1px;
top: 50%;
margin-top: -(@arrow-size / 2);
background: inherit;
border: inherit;
border-width: 1px 0 0 1px;
transform: rotate(-45deg) translate(-50%, -50%);
}
}

View File

@ -1,75 +0,0 @@
// ==========================================================================
// Typography
// ==========================================================================
// Base
html {
font-size: 100%;
}
body {
font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.5;
text-align: center;
color: @gray;
font-weight: @font-weight-base;
.font-smoothing();
}
// Headings
h1,
h2 {
letter-spacing: -.025em;
color: @brand-primary;
margin: 0 0 (@padding-base / 2);
line-height: 1.2;
font-weight: @font-weight-bold;
}
h1 {
.font-size(@font-size-h1);
}
// Paragraph and small
p,
small {
margin: 0 0 @padding-base;
}
small {
display: block;
padding: 0 (@padding-base / 2);
.font-size(@font-size-small);
}
// Lists
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
// Links
a {
text-decoration: none;
color: @link-color;
border-bottom: 1px dotted currentColor;
transition: background .3s ease, color .3s ease, border .3s ease;
&:hover,
&:focus {
color: @gray-dark;
border-bottom-color: rgba(0,0,0,0);
}
&:focus {
.tab-focus();
}
&.logo {
border: 0;
}
}
.color--vimeo {
color: @color-vimeo;
}
.color--youtube {
color: @color-youtube;
}

View File

@ -1,26 +0,0 @@
// ==========================================================================
// Plyr.io Demo Page
// ==========================================================================
// CSS Reset
@import "lib/normalize.less";
// Mixins
@import "lib/mixins.less";
// Variables
@import "variables.less";
// Animation
@import "lib/animation.less";
// Type
@import "lib/fontface.less";
@import "components/type.less";
// Components
@import "components/base.less";
@import "components/icons.less";
@import "components/buttons.less";
@import "components/error.less";
@import "components/examples.less";

View File

@ -1,18 +0,0 @@
// ==========================================================================
// Fonts
// ==========================================================================
@font-face {
font-family: "Avenir";
src: url("//cdn.plyr.io/fonts/avenir-medium.woff2") format("woff2"),
url("//cdn.plyr.io/fonts/avenir-medium.woff") format("woff");
font-style: normal;
font-weight: @font-weight-base;
}
@font-face {
font-family: "Avenir";
src: url("//cdn.plyr.io/fonts/avenir-bold.woff2") format("woff2"),
url("//cdn.plyr.io/fonts/avenir-bold.woff") format("woff");
font-style: normal;
font-weight: @font-weight-bold;
}

View File

@ -1,41 +0,0 @@
// ==========================================================================
// Mixins
// ==========================================================================
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/
// ---------------------------------------
.clearfix() {
zoom: 1;
&:before,
&:after { content: ""; display: table; }
&:after { clear: both; }
}
// Webkit-style focus
// ---------------------------------------
.tab-focus() {
// Default
outline: thin dotted @gray-dark;
// Webkit
outline-offset: 1px;
}
// Use rems for font sizing
// Leave <body> at 100%/16px
// ---------------------------------------
.font-size(@font-size: 16){
@rem: round((@font-size / 16), 3);
font-size: (@font-size * 1px);
font-size: ~"@{rem}rem";
}
// Font smoothing
// ---------------------------------------
.font-smoothing(@mode: on) when (@mode = on) {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
.font-smoothing(@mode: on) when (@mode = off) {
-moz-osx-font-smoothing: auto;
-webkit-font-smoothing: subpixel-antialiased;
}

View File

@ -1,406 +0,0 @@
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9.
* Hide the `template` element in IE, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background: transparent;
}
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Correct font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre-wrap;
}
/**
* Set consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Correct font family not being inherited in all browsers.
* 2. Correct font size not being inherited in all browsers.
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -1,48 +0,0 @@
// ==========================================================================
// Variables
// ==========================================================================
// Colors
@gray-dark: #343f4a;
@gray: #55646b;
@gray-light: #cbd0d3;
@gray-lighter: #dbe3e8;
@off-white: #f2f5f7;
@brand-primary: #3498db;
@brand-secondary: #02BD9B;
// Brands
@color-twitter: #4BAAF4;
@color-youtube: #cc181e;
@color-vimeo: #19b7ed;
// Base
@body-background: @off-white; //linear-gradient(to left top, @brand-secondary, @brand-primary);
// Type
@font-size-base: 16;
@font-size-small: 14;
@font-size-h1: 64;
@font-weight-base: 500;
@font-weight-bold: 700;
// Elements
@link-color: @brand-primary;
@padding-base: 20px;
@arrow-size: 8px;
// Icons
@icon-size: 18px;
// Breakpoints
@screen-sm: 480px;
@screen-md: 768px;
// Radii
@border-radius-base: 4px;
@border-radius-large: 6px;
// Examples
@example-width-audio: 520px;
@example-width-video: 1200px;

View File

@ -0,0 +1,46 @@
// ==========================================================================
// Plyr.io Demo Page
// ==========================================================================
@charset 'UTF-8';
// Settings
@import '../settings/breakpoints';
@import '../settings/colors';
@import '../settings/cosmetic';
@import '../settings/icons';
@import '../settings/layout';
@import '../settings/plyr';
@import '../settings/spacing';
@import '../settings/type';
// Libs
@import '../lib/fontface';
@import '../lib/animation';
@import '../lib/mixins';
@import '../lib/normalize';
@import '../lib/reset';
// Layout
@import '../layout/core';
@import '../layout/grid';
// Type
@import '../type/base';
@import '../type/headings';
// Components
@import '../components/buttons';
@import '../components/header';
@import '../components/icons';
@import '../components/links';
@import '../components/lists';
@import '../components/media';
@import '../components/navigation';
@import '../components/players';
// Plyr
@import '../../../../src/sass/plyr';
// Utils
@import '../utilities/cosmetic';
@import '../utilities/hidden';

View File

@ -0,0 +1,29 @@
// ==========================================================================
// Plyr.io Error Page
// ==========================================================================
@charset 'UTF-8';
// Libs
@import '../lib/fontface';
@import '../lib/mixins';
@import '../lib/normalize';
@import '../lib/reset';
// Settings
@import '../settings/colors';
@import '../settings/cosmetic';
@import '../settings/icons';
@import '../settings/layout';
@import '../settings/spacing';
@import '../settings/type';
// Layout
@import '../layout/error';
// Type
@import '../type/base';
@import '../type/headings';
// Components
@import '../components/buttons';
@import '../components/links';

View File

@ -0,0 +1,83 @@
// ==========================================================================
// Buttons
// ==========================================================================
// Shared
.button,
.button__count {
align-items: center;
background: $color-button-background;
border: 0;
border-radius: $border-radius-base;
box-shadow: 0 1px 1px rgba(#000, 0.1);
color: $color-button-text;
display: inline-flex;
padding: ($spacing-base * 0.75);
position: relative;
text-shadow: none;
user-select: none;
vertical-align: middle;
}
// Buttons
.button {
font-weight: $font-weight-bold;
padding-left: $spacing-base;
padding-right: $spacing-base;
transition: all 0.2s ease;
&:hover,
&:focus {
color: $gray-dark;
// Remove the underline/border
&::after {
display: none;
}
}
&:hover {
box-shadow: 0 2px 2px rgba(#000, 0.1);
transform: translateY(-1px);
}
&:focus {
outline: 0;
}
&.tab-focus {
@include tab-focus();
}
&:active {
transform: translateY(1px);
}
}
// Button group
.button--with-count {
display: inline-flex;
.button .icon {
flex-shrink: 0;
}
}
// Count bubble
.button__count {
animation: fadein 0.2s ease;
margin-left: ($spacing-base / 2);
&::before {
border: $arrow-size solid transparent;
border-left-width: 0;
border-right-color: $color-button-background;
content: '';
height: 0;
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
width: 0;
}
}

View File

@ -0,0 +1,19 @@
// ==========================================================================
// Header
// ==========================================================================
header {
padding-bottom: $spacing-base;
text-align: center;
.call-to-action {
margin-top: ($spacing-base * 1.5);
}
@media only screen and (min-width: $screen-md) {
margin-right: ($spacing-base * 3);
max-width: 360px;
padding-bottom: ($spacing-base * 2);
text-align: left;
}
}

View File

@ -4,23 +4,20 @@
// Base size icon styles
.icon {
fill: currentColor;
width: @icon-size;
height: @icon-size;
vertical-align: -3px;
fill: currentColor;
height: $icon-size;
vertical-align: -3px;
width: $icon-size;
}
// Within elements
a svg,
button svg,
label svg {
pointer-events: none;
pointer-events: none;
}
a .icon,
.btn .icon {
margin-right: (@padding-base / 2);
}
.btn:not(.btn-large) .icon {
width: (@icon-size - 2);
height: (@icon-size - 2);
margin-right: floor($spacing-base / 3);
}

View File

@ -0,0 +1,49 @@
// ==========================================================================
// Links
// ==========================================================================
// Make a <button> look like an <a>
button.faux-link {
@extend a; // stylelint-disable-line
@include cancel-button-styles();
}
// Links
a {
border-bottom: 1px dotted currentColor;
color: $color-link;
font-weight: $font-weight-bold;
position: relative;
text-decoration: none;
transition: all 0.2s ease;
&::after {
background: currentColor;
content: '';
height: 1px;
left: 50%;
position: absolute;
top: 100%;
transform: translateX(-50%);
transition: width 0.2s ease;
width: 0;
}
&:hover,
&:focus {
border-bottom-color: transparent;
outline: 0;
&::after {
width: 100%;
}
}
&.tab-focus {
@include tab-focus();
}
&.no-border::after {
display: none;
}
}

View File

@ -0,0 +1,11 @@
// ==========================================================================
// Lists
// ==========================================================================
// Lists
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}

View File

@ -0,0 +1,10 @@
// ==========================================================================
// Basic media
// ==========================================================================
img,
video,
audio {
max-width: 100%;
vertical-align: middle;
}

View File

@ -0,0 +1,9 @@
// ==========================================================================
// Navigation
// ==========================================================================
nav {
display: flex;
justify-content: center;
margin-bottom: $spacing-base;
}

View File

@ -10,31 +10,34 @@ video {
// Example players
.plyr {
margin: 0 auto;
border-radius: @border-radius-large;
}
.plyr--audio {
max-width: @example-width-audio;
border-radius: $border-radius-base;
box-shadow: 0 2px 5px rgba(#000, 0.2);
margin: $spacing-base auto;
&.plyr--audio {
max-width: 480px;
}
}
.plyr__video-wrapper::after {
content: "";
border: 1px solid rgba(#000, 0.15);
border-radius: inherit;
bottom: 0;
content: '';
left: 0;
pointer-events: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border: 1px solid fade(#000, 15%);
border-radius: inherit;
top: 0;
}
// Style full supported player
.plyr__cite {
display: none;
margin-top: @padding-base;
margin-top: $spacing-base;
.icon {
margin-right: (@padding-base / 4);
margin-right: ceil($spacing-base / 6);
}
}

View File

@ -0,0 +1,64 @@
// ==========================================================================
// Core
// ==========================================================================
html,
body {
display: flex;
width: 100%;
}
html {
background: $page-background;
background-attachment: fixed;
height: 100%;
}
body {
align-items: center;
display: flex;
flex-direction: column;
min-height: 100%;
}
.grid {
flex: 1;
overflow: auto;
}
main {
margin: auto;
padding-bottom: 1px; // Collapsing margins
text-align: center;
}
aside {
align-items: center;
background: #fff;
color: $gray;
display: flex;
flex-shrink: 0;
justify-content: center;
padding: ($spacing-base * 0.75);
position: relative;
text-align: center;
text-shadow: none;
width: 100%;
.icon {
fill: $color-twitter;
margin-right: ($spacing-base / 2);
}
p {
margin: 0;
}
a {
color: $color-twitter;
&.tab-focus {
@include tab-focus($color-twitter);
}
}
}

View File

@ -7,13 +7,24 @@ html.error,
.error body {
height: 100%;
}
.error body {
width: 100%;
display: table;
table-layout: fixed;
html.error {
background: $page-background;
background-attachment: fixed;
}
.error main {
display: table-cell;
.error body {
align-items: center;
display: flex;
width: 100%;
vertical-align: middle;
}
}
.error main {
padding: $spacing-base;
text-align: center;
width: 100%;
p {
@include font-size($font-size-large);
}
}

View File

@ -0,0 +1,19 @@
// ==========================================================================
// Super basic grid
// ==========================================================================
.grid {
margin: 0 auto;
padding: $spacing-base;
@media only screen and (min-width: $screen-md) {
align-items: center;
display: flex;
max-width: $container-max-width;
width: 100%;
> * {
flex: 1;
}
}
}

View File

@ -3,7 +3,11 @@
// ==========================================================================
// Fade
@keyframes fade-in {
0% { opacity: 0 }
100% { opacity: 1 }
}
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -0,0 +1,45 @@
// ==========================================================================
// Fonts
// ==========================================================================
@font-face {
font-display: swap;
font-family: 'Gordita';
font-style: normal;
font-weight: $font-weight-light;
src: url('https://cdn.plyr.io/static/fonts/gordita-light.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-light.woff') format('woff');
}
@font-face {
font-display: swap;
font-family: 'Gordita';
font-style: normal;
font-weight: $font-weight-regular;
src: url('https://cdn.plyr.io/static/fonts/gordita-regular.woff2') format('woff2'),
url('https://cdn.plyr.io/static/fonts/gordita-regular.woff') format('woff');
}
@font-face {
font-display: swap;
font-family: 'Gordita';
font-style: normal;
font-weight: $font-weight-medium;
src: url('https://cdn.plyr.io/static/fonts/gordita-medium.woff2') format('woff2'),
url('https://cdn.plyr.io/static/fonts/gordita-medium.woff') format('woff');
}
@font-face {
font-display: swap;
font-family: 'Gordita';
font-style: normal;
font-weight: $font-weight-bold;
src: url('https://cdn.plyr.io/static/fonts/gordita-bold.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-bold.woff') format('woff');
}
@font-face {
font-display: swap;
font-family: 'Gordita';
font-style: normal;
font-weight: $font-weight-black;
src: url('https://cdn.plyr.io/static/fonts/gordita-black.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-black.woff') format('woff');
}

View File

@ -0,0 +1,54 @@
// ==========================================================================
// Mixins
// ==========================================================================
// Convert a <button> into an <a>
// ---------------------------------------
@mixin cancel-button-styles() {
background: transparent;
border: 0;
border-radius: 0;
cursor: pointer;
font: inherit;
line-height: $line-height-base;
margin: 0;
padding: 0;
position: relative;
text-align: inherit;
text-shadow: inherit;
-moz-user-select: text; // stylelint-disable-line
vertical-align: baseline;
width: auto;
}
// Nicer focus styles
// ---------------------------------------
@mixin tab-focus($color: $tab-focus-default-color) {
box-shadow: 0 0 0 3px rgba($color, 0.35);
outline: 0;
}
// Use rems for font sizing
// Leave <body> at 100%/16px
// ---------------------------------------
@function calculate-rem($size) {
$rem: $size / 16;
@return #{$rem}rem;
}
@mixin font-size($size: 16) {
font-size: $size * 1px; // Fallback in px
font-size: calculate-rem($size);
}
// Font smoothing
// ---------------------------------------
@mixin font-smoothing($enabled: true) {
@if $enabled {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
} @else {
-moz-osx-font-smoothing: auto;
-webkit-font-smoothing: subpixel-antialiased;
}
}

450
demo/src/sass/lib/normalize.scss vendored Normal file
View File

@ -0,0 +1,450 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main {
/* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type='button']:-moz-focusring,
[type='reset']:-moz-focusring,
[type='submit']:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type='checkbox'],
[type='radio'] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type='search']::-webkit-search-cancel-button,
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details,
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}

View File

@ -0,0 +1,11 @@
// ==========================================================================
// Resets
// ==========================================================================
// BORDER-BOX ALL THE THINGS!
// http://paulirish.com/2012/box-sizing-border-box-ftw/
*,
*::after,
*::before {
box-sizing: border-box;
}

View File

@ -0,0 +1,6 @@
// ==========================================================================
// Breakpoints
// ==========================================================================
$screen-sm: 480px;
$screen-md: 768px;

View File

@ -0,0 +1,32 @@
// ==========================================================================
// Colors
// ==========================================================================
// Greyscale
$gray-dark: #343f4a;
$gray: #55646b;
$gray-light: #cbd0d3;
$gray-lighter: #dbe3e8;
$off-white: #f2f5f7;
// Text
$color-text: #fff;
// Plyr
$color-brand-primary: #1aafff;
// Brands
$color-twitter: #4baaf4;
$color-youtube: #cc181e;
$color-vimeo: #19b7ed;
// Elements
$color-link: #fff;
$color-background: $color-brand-primary;
// Buttons
$color-button-background: #fff;
$color-button-text: $gray;
// Focus
$tab-focus-default-color: #fff;

View File

@ -0,0 +1,12 @@
// ==========================================================================
// Misc cosmetic
// ==========================================================================
// Button count arrow size
$arrow-size: 5px;
// Radii
$border-radius-base: 4px;
// Background
$page-background: linear-gradient(to left top, lighten($color-background, 10%), darken($color-background, 20%));

View File

@ -0,0 +1,5 @@
// ==========================================================================
// Icons
// ==========================================================================
$icon-size: 16px;

View File

@ -0,0 +1,5 @@
// ==========================================================================
// Layout
// ==========================================================================
$container-max-width: 1280px;

View File

@ -0,0 +1,18 @@
// ==========================================================================
// Plyr Settings
// ==========================================================================
// Font
$plyr-font-family: inherit;
// Sizes
$plyr-font-size-base: 13px;
$plyr-font-size-small: 12px;
$plyr-font-size-time: 11px;
$plyr-font-size-badges: 9px;
// Captions
$plyr-font-size-captions-base: $plyr-font-size-base;
$plyr-font-size-captions-small: $plyr-font-size-small;
$plyr-font-size-captions-medium: 18px;
$plyr-font-size-captions-large: 21px;

View File

@ -0,0 +1,5 @@
// ==========================================================================
// Colors
// ==========================================================================
$spacing-base: 20px;

View File

@ -0,0 +1,20 @@
// ==========================================================================
// Typography
// ==========================================================================
$font-sans-serif: 'Gordita', 'Avenir', 'Helvetica Neue', sans-serif;
$font-size-base: 15;
$font-size-small: 13;
$font-size-large: 18;
$font-size-h1: 64;
$font-weight-light: 300;
$font-weight-regular: 400;
$font-weight-medium: 500;
$font-weight-bold: 600;
$font-weight-black: 900;
$line-height-base: 1.75;
$letter-spacing-headings: -0.025em;

View File

@ -0,0 +1,35 @@
// ==========================================================================
// Base
// ==========================================================================
// Set to 100% for rem sizing
html {
font-size: 100%;
}
body {
@include font-smoothing();
@include font-size($font-size-base);
color: $color-text;
font-family: $font-sans-serif;
font-weight: $font-weight-medium;
line-height: $line-height-base;
text-shadow: 0 1px 1px rgba(#000, 0.15);
}
button,
input,
select,
textarea {
font: inherit;
}
p,
small {
margin: 0 0 $spacing-base;
}
small {
@include font-size($font-size-small);
display: block;
}

View File

@ -0,0 +1,10 @@
// ==========================================================================
// Headings
// ==========================================================================
h1 {
@include font-size($font-size-h1);
font-weight: $font-weight-bold;
letter-spacing: $letter-spacing-headings;
margin: 0 0 ($spacing-base / 2);
}

View File

@ -0,0 +1,7 @@
// ==========================================================================
// Misc cosmetic
// ==========================================================================
.no-border {
border: 0;
}

View File

@ -0,0 +1,20 @@
// ==========================================================================
// Hidden
// ==========================================================================
[hidden] {
display: none;
}
// Hide only visually, but have it available for screen readers: h5bp.com/v
.sr-only {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
opacity: 0.001;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8,0.2c-4.4,0-8,3.6-8,8c0,3.5,2.3,6.5,5.5,7.6
C5.9,15.9,6,15.6,6,15.4c0-0.2,0-0.7,0-1.4C3.8,14.5,3.3,13,3.3,13c-0.4-0.9-0.9-1.2-0.9-1.2c-0.7-0.5,0.1-0.5,0.1-0.5
c0.8,0.1,1.2,0.8,1.2,0.8C4.4,13.4,5.6,13,6,12.8c0.1-0.5,0.3-0.9,0.5-1.1c-1.8-0.2-3.6-0.9-3.6-4c0-0.9,0.3-1.6,0.8-2.1
c-0.1-0.2-0.4-1,0.1-2.1c0,0,0.7-0.2,2.2,0.8c0.6-0.2,1.3-0.3,2-0.3c0.7,0,1.4,0.1,2,0.3c1.5-1,2.2-0.8,2.2-0.8
c0.4,1.1,0.2,1.9,0.1,2.1c0.5,0.6,0.8,1.3,0.8,2.1c0,3.1-1.9,3.7-3.7,3.9C9.7,12,10,12.5,10,13.2c0,1.1,0,1.9,0,2.2
c0,0.2,0.1,0.5,0.6,0.4c3.2-1.1,5.5-4.1,5.5-7.6C16,3.8,12.4,0.2,8,0.2z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<title>Twitter</title>
<path d="M16,3c-0.6,0.3-1.2,0.4-1.9,0.5c0.7-0.4,1.2-1,1.4-1.8c-0.6,0.4-1.3,0.6-2.1,0.8c-0.6-0.6-1.5-1-2.4-1
C9.3,1.5,7.8,3,7.8,4.8c0,0.3,0,0.5,0.1,0.7C5.2,5.4,2.7,4.1,1.1,2.1c-0.3,0.5-0.4,1-0.4,1.7c0,1.1,0.6,2.1,1.5,2.7
c-0.5,0-1-0.2-1.5-0.4c0,0,0,0,0,0c0,1.6,1.1,2.9,2.6,3.2C3,9.4,2.7,9.4,2.4,9.4c-0.2,0-0.4,0-0.6-0.1c0.4,1.3,1.6,2.3,3.1,2.3
c-1.1,0.9-2.5,1.4-4.1,1.4c-0.3,0-0.5,0-0.8,0c1.5,0.9,3.2,1.5,5,1.5c6,0,9.3-5,9.3-9.3c0-0.1,0-0.3,0-0.4C15,4.3,15.6,3.7,16,3z"/>
</svg>

Before

Width:  |  Height:  |  Size: 981 B

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"/>
</svg>

Before

Width:  |  Height:  |  Size: 779 B

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
M6,11V5l5,3L6,11z"/>
</svg>

Before

Width:  |  Height:  |  Size: 739 B

BIN
dist/blank.mp4 vendored Normal file

Binary file not shown.

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

7732
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

1
dist/plyr.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/plyr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/plyr.min.js.map vendored Normal file

File diff suppressed because one or more lines are too long

12918
dist/plyr.polyfilled.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/plyr.polyfilled.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/plyr.polyfilled.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/plyr.polyfilled.min.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/plyr.svg vendored
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-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-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-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" 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>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -1,337 +1,412 @@
// ==========================================================================
// Gulp build script
// ==========================================================================
/*global require, __dirname,Buffer*/
/*jshint -W079 */
/* global require, __dirname */
/* eslint no-console: "off" */
var fs = require("fs"),
path = require("path"),
gulp = require("gulp"),
gutil = require("gulp-util"),
concat = require("gulp-concat"),
uglify = require("gulp-uglify"),
less = require("gulp-less"),
sass = require("gulp-sass"),
cleanCSS = require("gulp-clean-css"),
run = require("run-sequence"),
prefix = require("gulp-autoprefixer"),
svgstore = require("gulp-svgstore"),
svgmin = require("gulp-svgmin"),
rename = require("gulp-rename"),
s3 = require("gulp-s3"),
replace = require("gulp-replace"),
open = require("gulp-open"),
size = require("gulp-size"),
through = require("through2");
const del = require('del');
const path = require('path');
const gulp = require('gulp');
const gutil = require('gulp-util');
const concat = require('gulp-concat');
const filter = require('gulp-filter');
const sass = require('gulp-sass');
const cleancss = require('gulp-clean-css');
const run = require('run-sequence');
const prefix = require('gulp-autoprefixer');
const gitbranch = require('git-branch');
const svgstore = require('gulp-svgstore');
const svgmin = require('gulp-svgmin');
const rename = require('gulp-rename');
const s3 = require('gulp-s3');
const replace = require('gulp-replace');
const open = require('gulp-open');
const size = require('gulp-size');
const rollup = require('gulp-better-rollup');
const babel = require('rollup-plugin-babel');
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');
var root = __dirname,
paths = {
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
const root = __dirname;
const paths = {
plyr: {
// Source paths
src: {
less: path.join(root, "src/less/**/*"),
scss: path.join(root, "src/scss/**/*"),
js: path.join(root, "src/js/**/*"),
sprite: path.join(root, "src/sprite/*.svg")
sass: path.join(root, 'src/sass/**/*.scss'),
js: path.join(root, 'src/js/**/*'),
sprite: path.join(root, 'src/sprite/*.svg'),
},
// Output paths
output: path.join(root, "dist/")
output: path.join(root, 'dist/'),
},
demo: {
// Source paths
src: {
less: path.join(root, "demo/src/less/**/*"),
js: path.join(root, "demo/src/js/**/*"),
sprite: path.join(root, "demo/src/sprite/**/*")
sass: path.join(root, 'demo/src/sass/**/*.scss'),
js: path.join(root, 'demo/src/js/**/*'),
},
// Output paths
output: path.join(root, "demo/dist/"),
output: path.join(root, 'demo/dist/'),
// Demo
root: path.join(root, "demo/")
root: path.join(root, 'demo/'),
},
upload: [path.join(root, "dist/**"), path.join(root, "demo/dist/**")]
},
upload: [
path.join(root, `dist/*${minSuffix}.*`),
path.join(root, 'dist/*.css'),
path.join(root, 'dist/*.svg'),
path.join(root, `demo/dist/*${minSuffix}.*`),
path.join(root, 'demo/dist/*.css'),
],
};
// Task arrays
tasks = {
less: [],
scss: [],
js: [],
sprite: []
},
const tasks = {
sass: [],
js: [],
sprite: [],
clean: ['clean'],
};
// Fetch bundles from JSON
bundles = loadJSON(path.join(root, "bundles.json")),
package = loadJSON(path.join(root, "package.json"));
// Size plugin
const sizeOptions = { showFiles: true, gzip: true };
// Load json
function loadJSON(path) {
try {
return JSON.parse(fs.readFileSync(path));
}
catch(err) {
return {};
}
}
// Browserlist
const browsers = ['> 1%'];
// Create a file from a string
// http://stackoverflow.com/questions/23230569/how-do-you-create-a-file-from-a-string-in-gulp
function createFile(filename, string) {
var src = require('stream').Readable({
objectMode: true
});
src._read = function () {
this.push(new gutil.File({
cwd: "",
base: "",
path: filename,
contents: new Buffer(string),
// stats also required for some functions
// https://nodejs.org/api/fs.html#fs_class_fs_stats
stat: {
size: string.length
}
}));
this.push(null);
}
return src
}
// Babel config
const babelrc = {
presets: [[
'env',
{
targets: {
browsers,
},
useBuiltIns: true,
modules: false,
},
]],
plugins: ['external-helpers'],
babelrc: false,
exclude: 'node_modules/**',
};
var build = {
js: function (files, bundle) {
for (var key in files) {
(function(key) {
var name = "js-" + key;
tasks.js.push(name);
// Clean out /dist
gulp.task('clean', () => {
const dirs = [
paths.plyr.output,
paths.demo.output,
].map(dir => path.join(dir, '**/*'));
gulp.task(name, function () {
return gulp
.src(bundles[bundle].js[key])
.pipe(concat(key))
.pipe(uglify())
.pipe(gulp.dest(paths[bundle].output));
});
})(key);
}
// Don't delete the mp4
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
del(dirs);
});
const build = {
js(files, bundle, options) {
Object.keys(files).forEach(key => {
const name = `js:${key}`;
tasks.js.push(name);
const { output } = paths[bundle];
gulp.task(name, () =>
gulp
.src(bundles[bundle].js[key])
.pipe(sourcemaps.init())
.pipe(concat(key))
.pipe(
rollup(
{
plugins: [
resolve(),
commonjs(),
babel(babelrc),
],
},
options,
),
)
.pipe(sourcemaps.write(''))
.pipe(gulp.dest(output))
.pipe(filter('**/*.js'))
.pipe(uglify())
.pipe(size(sizeOptions))
.pipe(rename({ suffix: minSuffix }))
.pipe(sourcemaps.write(''))
.pipe(gulp.dest(output)),
);
});
},
less: function(files, bundle) {
for (var key in files) {
(function (key) {
var name = "less-" + key;
tasks.less.push(name);
sass(files, bundle) {
Object.keys(files).forEach(key => {
const name = `sass:${key}`;
tasks.sass.push(name);
gulp.task(name, function () {
return gulp
.src(bundles[bundle].less[key])
.pipe(less())
.on("error", gutil.log)
.pipe(concat(key))
.pipe(prefix(["last 2 versions"], { cascade: true }))
.pipe(cleanCSS())
.pipe(gulp.dest(paths[bundle].output));
});
})(key);
}
gulp.task(name, () =>
gulp
.src(bundles[bundle].sass[key])
.pipe(sass())
.on('error', gutil.log)
.pipe(concat(key))
.pipe(prefix(browsers, { cascade: false }))
.pipe(cleancss())
.pipe(size(sizeOptions))
.pipe(gulp.dest(paths[bundle].output)),
);
});
},
scss: function(files, bundle) {
for (var key in files) {
(function (key) {
var name = "scss-" + key;
tasks.scss.push(name);
gulp.task(name, function () {
return gulp
.src(bundles[bundle].scss[key])
.pipe(sass())
.on("error", gutil.log)
.pipe(concat(key))
.pipe(prefix(["last 2 versions"], { cascade: true }))
.pipe(cleanCSS())
.pipe(gulp.dest(paths[bundle].output));
});
})(key);
}
},
sprite: function(bundle) {
var name = "sprite-" + bundle;
sprite(bundle) {
const name = `svg:sprite:${bundle}`;
tasks.sprite.push(name);
// Process Icons
gulp.task(name, function () {
return gulp
gulp.task(name, () =>
gulp
.src(paths[bundle].src.sprite)
.pipe(svgmin({
plugins: [{
removeDesc: true
}]
}))
.pipe(
svgmin({
plugins: [{
removeDesc: true,
}],
}),
)
.pipe(svgstore())
.pipe(rename({ basename: bundle }))
.pipe(gulp.dest(paths[bundle].output));
});
}
.pipe(size(sizeOptions))
.pipe(gulp.dest(paths[bundle].output)),
);
},
};
// Plyr core files
build.js(bundles.plyr.js, "plyr");
build.less(bundles.plyr.less, "plyr");
build.scss(bundles.plyr.scss, "plyr");
build.sprite("plyr");
build.js(bundles.plyr.js, 'plyr', { name: 'Plyr', format: 'umd' });
build.sass(bundles.plyr.sass, 'plyr');
build.sprite('plyr');
// Demo files
build.less(bundles.demo.less, "demo");
build.js(bundles.demo.js, "demo");
build.sprite("demo");
build.sass(bundles.demo.sass, 'demo');
build.js(bundles.demo.js, 'demo', { format: 'iife' });
// Build all JS
gulp.task("js", function(){
gulp.task('js', () => {
run(tasks.js);
});
// Build SCSS (for testing, default is LESS)
gulp.task("scss", function(){
run(tasks.scss);
});
// Watch for file changes
gulp.task("watch", function () {
gulp.task('watch', () => {
// Plyr core
gulp.watch(paths.plyr.src.js, tasks.js);
gulp.watch(paths.plyr.src.less, tasks.less);
gulp.watch(paths.plyr.src.sass, tasks.sass);
gulp.watch(paths.plyr.src.sprite, tasks.sprite);
// Demo
gulp.watch(paths.demo.src.js, tasks.js);
gulp.watch(paths.demo.src.less, tasks.less);
gulp.watch(paths.demo.src.sprite, tasks.sprite);
gulp.watch(paths.demo.src.sass, tasks.sass);
});
// Default gulp task
gulp.task("default", function(){
run(tasks.js, tasks.less, tasks.sprite, "watch");
gulp.task('default', () => {
run(tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'watch');
});
// Publish a version to CDN and demo
// --------------------------------------------
// Some options
var aws = loadJSON(path.join(root, "aws.json")),
version = package.version,
maxAge = 31536000, // seconds 1 year
options = {
cdn: {
headers: {
"Cache-Control": "max-age=" + maxAge,
"Vary": "Accept-Encoding"
}
},
demo: {
headers: {
"Cache-Control": "no-cache, no-store, must-revalidate, max-age=0",
"Vary": "Accept-Encoding"
}
},
symlinks: function(version, filename) {
return {
headers: {
// http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
"x-amz-website-redirect-location": "/" + version + "/" + filename,
"Cache-Control": "no-cache, no-store, must-revalidate, max-age=0"
}
}
}
};
// If aws is setup
if("cdn" in aws) {
var regex = "(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:\.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:\.[\\da-z\\-]+)*)?",
cdnpath = new RegExp(aws.cdn.bucket + "\/" + regex, "gi"),
semver = new RegExp("v" + regex, "gi"),
localPath = new RegExp("(\.\.\/)?dist", "gi"),
versionPath = "https://" + aws.cdn.bucket + "/" + version;
if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
const { version } = pkg;
// Get branch info
const branch = {
current: gitbranch.sync(),
master: 'master',
beta: 'beta',
};
const allowed = [
branch.master,
branch.beta,
];
const maxAge = 31536000; // 1 year
const options = {
cdn: {
headers: {
'Cache-Control': `max-age=${maxAge}`,
Vary: 'Accept-Encoding',
},
},
demo: {
uploadPath: branch.current === branch.beta ? 'beta/' : null,
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
Vary: 'Accept-Encoding',
},
},
symlinks(ver, filename) {
return {
headers: {
// http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
'x-amz-website-redirect-location': `/${ver}/${filename}`,
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
},
};
},
};
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');
gulp.task('version', () => {
console.log(`Updating versions to '${version}'...`);
// Replace versioned URLs in source
const files = [
'plyr.js',
'plyr.polyfilled.js',
'defaults.js',
];
gulp
.src(files.map(file => path.join(root, `src/js/${file}`)))
.pipe(replace(semver, `v${version}`))
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
.pipe(gulp.dest(path.join(root, 'src/js/')));
});
// 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})`);
return null;
}
console.log(`Uploading '${version}' to ${aws.cdn.domain}...`);
// Upload to CDN
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
}),
)
// Remove min suffix from source map URL
.pipe(replace(/sourceMappingURL=([\w-?.]+)/, (match, p1) => `sourceMappingURL=${p1.replace(minSuffix, '')}`))
.pipe(
size({
showFiles: true,
gzip: true,
}),
)
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.cdn, options.cdn))
);
});
// Publish to demo bucket
gulp.task('demo', () => {
if (!allowed.includes(branch.current)) {
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
return null;
}
console.log(`Uploading '${version}' demo to ${aws.demo.domain}...`);
// Replace versioned files in readme.md
gulp
.src([`${root}/readme.md`])
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
.pipe(gulp.dest(root));
// Replace local file paths with remote paths in demo HTML
// e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
const index = `${paths.demo.root}index.html`;
const error = `${paths.demo.root}error.html`;
const pages = [index];
if (branch.current === branch.master) {
pages.push(error);
}
gulp
.src(pages)
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.demo, options.demo));
// Only update CDN for master (prod)
if (branch.current !== branch.master) {
return null;
}
// Upload error.html to cdn (as well as demo site)
return gulp
.src([error])
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.cdn, options.demo));
});
// Update symlinks for latest
/* gulp.task("symlinks", function () {
console.log("Updating symlinks...");
return gulp.src(paths.upload)
.pipe(through.obj(function (chunk, enc, callback) {
if (chunk.stat.isFile()) {
// Get the filename
var filename = chunk.path.split("/").reverse()[0];
// Create the 0 byte redirect files to upload
createFile(filename, "")
.pipe(rename(function (path) {
path.dirname = path.dirname.replace(".", "latest");
}))
// Upload to S3 with correct headers
.pipe(s3(aws.cdn, options.symlinks(version, filename)));
}
callback(null, chunk);
}));
}); */
// 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}`,
}),
);
});
// Do everything
gulp.task('publish', () => {
run('version', tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'cdn', 'demo');
});
}
// Publish version to CDN bucket
gulp.task("cdn", function () {
console.log("Uploading " + version + " to " + aws.cdn.bucket + "...");
// Upload to CDN
return gulp.src(paths.upload)
.pipe(size({
showFiles: true,
gzip: true
}))
.pipe(rename(function (path) {
path.dirname = path.dirname.replace(".", version);
}))
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.cdn, options.cdn));
});
// Publish to demo bucket
gulp.task("demo", function () {
console.log("Uploading " + version + " demo to " + aws.demo.bucket + "...");
// Replace versioned files in readme.md
gulp.src([root + "/readme.md"])
.pipe(replace(cdnpath, aws.cdn.bucket + "/" + version))
.pipe(gulp.dest(root));
// Replace versioned files in plyr.js
gulp.src(path.join(root, "src/js/plyr.js"))
.pipe(replace(semver, "v" + version))
.pipe(replace(cdnpath, aws.cdn.bucket + "/" + version))
.pipe(gulp.dest(path.join(root, "src/js/")));
// Replace local file paths with remote paths in demo HTML
// e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
gulp.src([paths.demo.root + "*.html"])
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.demo, options.demo));
// Upload error.html to cdn (as well as demo site)
return gulp.src([paths.demo.root + "error.html"])
.pipe(replace(localPath, versionPath))
.pipe(s3(aws.cdn, options.demo));
});
// Open the demo site to check it's sweet
gulp.task("symlinks", function () {
console.log("Updating symlinks...");
return gulp.src(paths.upload)
.pipe(through.obj(function (chunk, enc, callback) {
if (chunk.stat.isFile()) {
// Get the filename
var filename = chunk.path.split("/").reverse()[0];
// Create the 0 byte redirect files to upload
createFile(filename, "")
.pipe(rename(function (path) {
path.dirname = path.dirname.replace(".", "latest");
}))
// Upload to S3 with correct headers
.pipe(s3(aws.cdn, options.symlinks(version, filename)));
}
callback(null, chunk);
}));
});
// Open the demo site to check it's sweet
gulp.task("open", function () {
console.log("Opening " + aws.demo.bucket + "...");
// 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.bucket
}));
});
// Do everything
gulp.task("publish", function () {
run(tasks.js, tasks.less, tasks.sprite, "cdn", "demo", "symlinks");
});

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 Selz.com
Copyright (c) 2017 Sam Potts
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,51 +1,74 @@
{
"name": "plyr",
"version": "2.0.7",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "http://plyr.io",
"main": "src/js/plyr.js",
"dependencies": {},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-clean-css": "^2.0.6",
"gulp-concat": "^2.3.3",
"gulp-less": "^3.0.5",
"gulp-open": "^2.0.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.3",
"gulp-s3": "^0.3.0",
"gulp-sass": "^2.3.1",
"gulp-size": "^2.1.0",
"gulp-svgmin": "^1.2.2",
"gulp-svgstore": "^5.0.5",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"run-sequence": "^1.1.5",
"through2": "^2.0.1"
},
"keywords": [
"HTML5 Video",
"HTML5 Audio",
"Media Player",
"DASH",
"Shaka",
"WordPress",
"HLS"
],
"repository": {
"type": "git",
"url": "git://github.com/selz/plyr.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/selz/plyr/issues"
},
"directories": {
"doc": "readme.md"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Sam Potts <sam@selz.com>"
"name": "plyr",
"version": "3.0.6",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"main": "./dist/plyr.js",
"browser": "./dist/plyr.min.js",
"sass": "./src/sass/plyr.scss",
"style": "./dist/plyr.css",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.2",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.6.1",
"del": "^3.0.0",
"eslint": "^4.19.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.9.0",
"git-branch": "^2.0.1",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^5.0.0",
"gulp-better-rollup": "^3.0.0",
"gulp-clean-css": "^3.9.3",
"gulp-concat": "^2.6.1",
"gulp-filter": "^5.1.0",
"gulp-open": "^3.0.0",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.6.1",
"gulp-s3": "^0.11.0",
"gulp-sass": "^3.1.0",
"gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-svgmin": "^1.2.4",
"gulp-svgstore": "^6.1.1",
"gulp-uglify-es": "^1.0.1",
"gulp-util": "^3.0.8",
"prettier-eslint": "^8.8.1",
"prettier-stylelint": "^0.4.2",
"rollup-plugin-babel": "^3.0.3",
"rollup-plugin-commonjs": "^8.4.1",
"rollup-plugin-node-resolve": "^3.2.0",
"run-sequence": "^2.2.1",
"stylelint": "^9.1.3",
"stylelint-config-prettier": "^3.0.4",
"stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.0.0",
"stylelint-order": "^0.8.1",
"stylelint-scss": "^2.5.0",
"stylelint-selector-bem-pattern": "^2.0.0"
},
"keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"],
"repository": {
"type": "git",
"url": "git://github.com/sampotts/plyr.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/sampotts/plyr/issues"
},
"directories": {
"doc": "readme.md"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Sam Potts <sam@potts.es>",
"dependencies": {
"babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0",
"loadjs": "^3.5.2",
"raven-js": "^3.23.3"
}
}

31
plyr.code-workspace Normal file
View File

@ -0,0 +1,31 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
// Exclude from the editor
"files.exclude": {
"**/node_modules": true
},
// Exclude from search
"search.exclude": {
"dist/": true
},
// Linting
"stylelint.enable": true,
"css.validate": false,
"scss.validate": false,
"javascript.validate.enable": false,
// Prettier
"prettier.eslintIntegration": true,
"prettier.stylelintIntegration": true,
// Formatting
"editor.tabSize": 4,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
// Trim on save
"files.trimTrailingWhitespace": true
}
}

1367
readme.md

File diff suppressed because it is too large Load Diff

231
src/js/captions.js Normal file
View File

@ -0,0 +1,231 @@
// ==========================================================================
// Plyr Captions
// TODO: Create as class
// ==========================================================================
import support from './support';
import utils from './utils';
import controls from './controls';
const captions = {
// Setup captions
setup() {
// Requires UI support
if (!this.supported.ui) {
return;
}
// Set default language if not set
const stored = this.storage.get('language');
if (!utils.is.empty(stored)) {
this.captions.language = stored;
}
if (utils.is.empty(this.captions.language)) {
this.captions.language = this.config.captions.language.toLowerCase();
}
// Set captions enabled state if not set
if (!utils.is.boolean(this.captions.active)) {
const active = this.storage.get('captions');
if (utils.is.boolean(active)) {
this.captions.active = active;
} else {
this.captions.active = this.config.captions.active;
}
}
// Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
// Clear menu and hide
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
return;
}
// Inject the container
if (!utils.is.element(this.elements.captions)) {
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
utils.insertAfter(this.elements.captions, this.elements.wrapper);
}
// Set the class hook
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
// Get tracks
const tracks = captions.getTracks.call(this);
// If no caption file exists, hide container for caption text
if (utils.is.empty(tracks)) {
return;
}
// Get browser info
const browser = utils.getBrowser();
// Fix IE captions if CORS is used
// Fetch captions and inject as blobs instead (data URIs not supported!)
if (browser.isIE && window.URL) {
const elements = this.media.querySelectorAll('track');
Array.from(elements).forEach(track => {
const src = track.getAttribute('src');
const href = utils.parseUrl(src);
if (href.hostname !== window.location.href.hostname && [
'http:',
'https:',
].includes(href.protocol)) {
utils
.fetch(src, 'blob')
.then(blob => {
track.setAttribute('src', window.URL.createObjectURL(blob));
})
.catch(() => {
utils.removeElement(track);
});
}
});
}
// Set language
captions.setLanguage.call(this);
// Enable UI
captions.show.call(this);
// Set available languages in list
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
}
},
// Set the captions language
setLanguage() {
// Setup HTML5 track rendering
if (this.isHTML5 && this.isVideo) {
captions.getTracks.call(this).forEach(track => {
// Show track
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
// Turn off native caption rendering to avoid double captions
// eslint-disable-next-line
track.mode = 'hidden';
});
// Get current track
const currentTrack = captions.getCurrentTrack.call(this);
// Check if suported kind
if (utils.is.track(currentTrack)) {
// If we change the active track while a cue is already displayed we need to update it
if (Array.from(currentTrack.activeCues || []).length) {
captions.setCue.call(this, currentTrack);
}
}
} else if (this.isVimeo && this.captions.active) {
this.embed.enableTextTrack(this.language);
}
},
// Get the tracks
getTracks() {
// Return empty array at least
if (utils.is.nullOrUndefined(this.media)) {
return [];
}
// Only get accepted kinds
return Array.from(this.media.textTracks || []).filter(track => [
'captions',
'subtitles',
].includes(track.kind));
},
// Get the current track for the current language
getCurrentTrack() {
return captions.getTracks.call(this).find(track => track.language.toLowerCase() === this.language);
},
// Display active caption if it contains text
setCue(input) {
// Get the track from the event if needed
const track = utils.is.event(input) ? input.target : input;
const { activeCues } = track;
const active = activeCues.length && activeCues[0];
const currentTrack = captions.getCurrentTrack.call(this);
// Only display current track
if (track !== currentTrack) {
return;
}
// Display a cue, if there is one
if (utils.is.cue(active)) {
captions.setText.call(this, active.getCueAsHTML());
} else {
captions.setText.call(this, null);
}
utils.dispatchEvent.call(this, this.media, 'cuechange');
},
// Set the current caption
setText(input) {
// Requires UI
if (!this.supported.ui) {
return;
}
if (utils.is.element(this.elements.captions)) {
const content = utils.createElement('span');
// Empty the container
utils.emptyElement(this.elements.captions);
// Default to empty
const caption = !utils.is.nullOrUndefined(input) ? input : '';
// Set the span content
if (utils.is.string(caption)) {
content.textContent = caption.trim();
} else {
content.appendChild(caption);
}
// Set new caption text
this.elements.captions.appendChild(content);
} else {
this.debug.warn('No captions element to render to');
}
},
// Display captions container and button (for initialization)
show() {
// If there's no caption toggle, bail
if (!utils.is.element(this.elements.buttons.captions)) {
return;
}
// Try to load the value from storage
let active = this.storage.get('captions');
// Otherwise fall back to the default config
if (!utils.is.boolean(active)) {
({ active } = this.config.captions);
} else {
this.captions.active = active;
}
if (active) {
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);
utils.toggleState(this.elements.buttons.captions, true);
}
},
};
export default captions;

28
src/js/console.js Normal file
View File

@ -0,0 +1,28 @@
// ==========================================================================
// Console wrapper
// ==========================================================================
const noop = () => {};
export default class Console {
constructor(enabled = false) {
this.enabled = window.console && enabled;
if (this.enabled) {
this.log('Debugging enabled');
}
}
get log() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.log, console) : noop;
}
get warn() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop;
}
get error() {
// eslint-disable-next-line no-console
return this.enabled ? Function.prototype.bind.call(console.error, console) : noop;
}
}

1264
src/js/controls.js vendored Normal file

File diff suppressed because it is too large Load Diff

385
src/js/defaults.js Normal file
View File

@ -0,0 +1,385 @@
// ==========================================================================
// Plyr default config
// ==========================================================================
const defaults = {
// Disable
enabled: true,
// Custom media title
title: '',
// Logging to console
debug: false,
// Auto play (if supported)
autoplay: false,
// Only allow one media playing at once (vimeo only)
autopause: true,
// Default time to skip when rewind/fast forward
seekTime: 10,
// Default volume
volume: 1,
muted: false,
// Pass a custom duration
duration: null,
// Display the media duration on load in the current time position
// If you have opted to display both duration and currentTime, this is ignored
displayDuration: true,
// Invert the current time to be a countdown
invertTime: true,
// Clicking the currentTime inverts it's value to show time left rather than elapsed
toggleInvert: true,
// Aspect ratio (for embeds)
ratio: '16:9',
// Click video container to play/pause
clickToPlay: true,
// Auto hide the controls
hideControls: true,
// Revert to poster on finish (HTML5 - will cause reload)
showPosterOnEnd: false,
// Disable the standard context menu
disableContextMenu: true,
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.0.6/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
// Quality default
quality: {
default: 'default',
options: [
'hd2160',
'hd1440',
'hd1080',
'hd720',
'large',
'medium',
'small',
'tiny',
'default',
],
},
// Set loops
loop: {
active: false,
// start: null,
// end: null,
},
// Speed default and options to display
speed: {
selected: 1,
options: [
0.5,
0.75,
1,
1.25,
1.5,
1.75,
2,
],
},
// Keyboard shortcut settings
keyboard: {
focused: true,
global: false,
},
// Display tooltips
tooltips: {
controls: false,
seek: true,
},
// Captions settings
captions: {
active: false,
language: window.navigator.language.split('-')[0],
},
// Fullscreen settings
fullscreen: {
enabled: true, // Allow fullscreen?
fallback: true, // Fallback for vintage browsers
iosNative: false, // Use the native fullscreen in iOS (disables custom controls)
},
// Local storage
storage: {
enabled: true,
key: 'plyr',
},
// Default controls
controls: [
'play-large',
// 'restart',
// 'rewind',
'play',
// 'fast-forward',
'progress',
'current-time',
'mute',
'volume',
'captions',
'settings',
'pip',
'airplay',
'fullscreen',
],
settings: [
'captions',
'quality',
'speed',
],
// Localisation
i18n: {
restart: 'Restart',
rewind: 'Rewind {seektime} secs',
play: 'Play',
pause: 'Pause',
fastForward: 'Forward {seektime} secs',
seek: 'Seek',
played: 'Played',
buffered: 'Buffered',
currentTime: 'Current time',
duration: 'Duration',
volume: 'Volume',
mute: 'Mute',
unmute: 'Unmute',
enableCaptions: 'Enable captions',
disableCaptions: 'Disable captions',
enterFullscreen: 'Enter fullscreen',
exitFullscreen: 'Exit fullscreen',
frameTitle: 'Player for {title}',
captions: 'Captions',
settings: 'Settings',
speed: 'Speed',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
end: 'End',
all: 'All',
reset: 'Reset',
disabled: 'Disabled',
advertisement: 'Ad',
},
// URLs
urls: {
vimeo: {
api: 'https://player.vimeo.com/api/player.js',
},
youtube: {
api: 'https://www.youtube.com/iframe_api',
},
googleIMA: {
api: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
},
},
// Custom control listeners
listeners: {
seek: null,
play: null,
pause: null,
restart: null,
rewind: null,
fastForward: null,
mute: null,
volume: null,
captions: null,
fullscreen: null,
pip: null,
airplay: null,
speed: null,
quality: null,
loop: null,
language: null,
},
// Events to watch and bubble
events: [
// Events to watch on HTML5 media elements and bubble
// https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events
'ended',
'progress',
'stalled',
'playing',
'waiting',
'canplay',
'canplaythrough',
'loadstart',
'loadeddata',
'loadedmetadata',
'timeupdate',
'volumechange',
'play',
'pause',
'error',
'seeking',
'seeked',
'emptied',
'ratechange',
'cuechange',
// Custom events
'enterfullscreen',
'exitfullscreen',
'captionsenabled',
'captionsdisabled',
'languagechange',
'controlshidden',
'controlsshown',
'ready',
// YouTube
'statechange',
'qualitychange',
'qualityrequested',
// Ads
'adsloaded',
'adscontentpause',
'adscontentresume',
'adstarted',
'adsmidpoint',
'adscomplete',
'adsallcomplete',
'adsimpression',
'adsclick',
],
// Selectors
// Change these to match your template if using custom HTML
selectors: {
editable: 'input, textarea, select, [contenteditable]',
container: '.plyr',
controls: {
container: null,
wrapper: '.plyr__controls',
},
labels: '[data-plyr]',
buttons: {
play: '[data-plyr="play"]',
pause: '[data-plyr="pause"]',
restart: '[data-plyr="restart"]',
rewind: '[data-plyr="rewind"]',
fastForward: '[data-plyr="fast-forward"]',
mute: '[data-plyr="mute"]',
captions: '[data-plyr="captions"]',
fullscreen: '[data-plyr="fullscreen"]',
pip: '[data-plyr="pip"]',
airplay: '[data-plyr="airplay"]',
settings: '[data-plyr="settings"]',
loop: '[data-plyr="loop"]',
},
inputs: {
seek: '[data-plyr="seek"]',
volume: '[data-plyr="volume"]',
speed: '[data-plyr="speed"]',
language: '[data-plyr="language"]',
quality: '[data-plyr="quality"]',
},
display: {
currentTime: '.plyr__time--current',
duration: '.plyr__time--duration',
buffer: '.plyr__progress--buffer',
played: '.plyr__progress--played',
loop: '.plyr__progress--loop',
volume: '.plyr__volume--display',
},
progress: '.plyr__progress',
captions: '.plyr__captions',
menu: {
quality: '.js-plyr__menu__list--quality',
},
},
// Class hooks added to the player in different states
classNames: {
video: 'plyr__video-wrapper',
embed: 'plyr__video-embed',
ads: 'plyr__ads',
control: 'plyr__control',
type: 'plyr--{0}',
provider: 'plyr--{0}',
stopped: 'plyr--stopped',
playing: 'plyr--playing',
loading: 'plyr--loading',
error: 'plyr--has-error',
hover: 'plyr--hover',
tooltip: 'plyr__tooltip',
cues: 'plyr__cues',
hidden: 'plyr__sr-only',
hideControls: 'plyr--hide-controls',
isIos: 'plyr--is-ios',
isTouch: 'plyr--is-touch',
uiSupported: 'plyr--full-ui',
noTransition: 'plyr--no-transition',
menu: {
value: 'plyr__menu__value',
badge: 'plyr__badge',
open: 'plyr--menu-open',
},
captions: {
enabled: 'plyr--captions-enabled',
active: 'plyr--captions-active',
},
fullscreen: {
enabled: 'plyr--fullscreen-enabled',
fallback: 'plyr--fullscreen-fallback',
},
pip: {
supported: 'plyr--pip-supported',
active: 'plyr--pip-active',
},
airplay: {
supported: 'plyr--airplay-supported',
active: 'plyr--airplay-active',
},
tabFocus: 'plyr__tab-focus',
},
// Embed attributes
attributes: {
embed: {
provider: 'data-plyr-provider',
id: 'data-plyr-embed-id',
},
},
// API keys
keys: {
google: null,
},
// Advertisements plugin
// Register for an account here: http://vi.ai/publisher-video-monetization/?aid=plyrio
ads: {
enabled: false,
publisherId: '',
},
};
export default defaults;

211
src/js/fullscreen.js Normal file
View File

@ -0,0 +1,211 @@
// ==========================================================================
// Fullscreen wrapper
// https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API#prefixing
// ==========================================================================
import utils from './utils';
const browser = utils.getBrowser();
function onChange() {
if (!this.enabled) {
return;
}
// Update toggle button
const button = this.player.elements.buttons.fullscreen;
if (utils.is.element(button)) {
utils.toggleState(button, this.active);
}
// Trigger an event
utils.dispatchEvent(this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
// Trap focus in container
if (!browser.isIos) {
utils.trapFocus.call(this.player, this.target, this.active);
}
}
function toggleFallback(toggle = false) {
// Store or restore scroll position
if (toggle) {
this.scrollPosition = {
x: window.scrollX || 0,
y: window.scrollY || 0,
};
} else {
window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
}
// Toggle scroll
document.body.style.overflow = toggle ? 'hidden' : '';
// Toggle class hook
utils.toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
// Toggle button and fire events
onChange.call(this);
}
class Fullscreen {
constructor(player) {
// Keep reference to parent
this.player = player;
// Get prefix
this.prefix = Fullscreen.prefix;
this.name = Fullscreen.name;
// Scroll position
this.scrollPosition = { x: 0, y: 0 };
// Register event listeners
// Handle event (incase user presses escape etc)
utils.on(document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => {
// TODO: Filter for target??
onChange.call(this);
});
// Fullscreen toggle on double click
utils.on(this.player.elements.container, 'dblclick', () => {
this.toggle();
});
// Prevent double click on controls bubbling up
utils.on(this.player.elements.controls, 'dblclick', event => event.stopPropagation());
// Update the UI
this.update();
}
// Determine if native supported
static get native() {
return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);
}
// Get the prefix for handlers
static get prefix() {
// No prefix
if (utils.is.function(document.exitFullscreen)) {
return false;
}
// Check for fullscreen support by vendor prefix
let value = '';
const prefixes = [
'webkit',
'moz',
'ms',
];
prefixes.some(pre => {
if (utils.is.function(document[`${pre}ExitFullscreen`]) || utils.is.function(document[`${pre}CancelFullScreen`])) {
value = pre;
return true;
}
return false;
});
return value;
}
static get name() {
return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
}
// Determine if fullscreen is enabled
get enabled() {
return (
(Fullscreen.native || this.player.config.fullscreen.fallback) &&
this.player.config.fullscreen.enabled &&
this.player.supported.ui &&
this.player.isVideo
);
}
// Get active state
get active() {
if (!this.enabled) {
return false;
}
// Fallback using classname
if (!Fullscreen.native) {
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
}
const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}${this.name}Element`];
return element === this.target;
}
// Get target element
get target() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
}
// Update UI
update() {
if (this.enabled) {
this.player.debug.log(`${Fullscreen.native ? 'Native' : 'Fallback'} fullscreen enabled`);
} else {
this.player.debug.log('Fullscreen not supported and fallback disabled');
}
// Add styling hook to show button
utils.toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.enabled);
}
// Make an element fullscreen
enter() {
if (!this.enabled) {
return;
}
// iOS native fullscreen doesn't need the request step
if (browser.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) {
this.target.webkitEnterFullscreen();
}
} else if (!Fullscreen.native) {
toggleFallback.call(this, true);
} else if (!this.prefix) {
this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.name}`]();
}
}
// Bail from fullscreen
exit() {
if (!this.enabled) {
return;
}
// iOS native fullscreen
if (browser.isIos && this.player.config.fullscreen.iosNative) {
this.target.webkitExitFullscreen();
this.player.play();
} else if (!Fullscreen.native) {
toggleFallback.call(this, false);
} else if (!this.prefix) {
document.cancelFullScreen();
} else if (!utils.is.empty(this.prefix)) {
const action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
document[`${this.prefix}${action}${this.name}`]();
}
}
// Toggle state
toggle() {
if (!this.active) {
this.enter();
} else {
this.exit();
}
}
}
export default Fullscreen;

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

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

623
src/js/listeners.js Normal file
View File

@ -0,0 +1,623 @@
// ==========================================================================
// Plyr Event Listeners
// ==========================================================================
import support from './support';
import utils from './utils';
import controls from './controls';
import ui from './ui';
// Sniff out the browser
const browser = utils.getBrowser();
class Listeners {
constructor(player) {
this.player = player;
this.lastKey = null;
this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
this.firstTouch = this.firstTouch.bind(this);
}
// Handle key presses
handleKey(event) {
const code = event.keyCode ? event.keyCode : event.which;
const pressed = event.type === 'keydown';
const repeat = pressed && code === this.lastKey;
// Bail if a modifier key is set
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
return;
}
// If the event is bubbled from the media element
// Firefox doesn't get the keycode for whatever reason
if (!utils.is.number(code)) {
return;
}
// 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);
};
// Handle the key on keydown
// Reset on keyup
if (pressed) {
// Which keycodes should we prevent default
const preventDefault = [
48,
49,
50,
51,
52,
53,
54,
56,
57,
32,
75,
38,
40,
77,
39,
37,
70,
67,
73,
76,
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 = utils.getFocusElement();
if (utils.is.element(focused) && utils.matches(focused, this.player.config.selectors.editable)) {
return;
}
// If the code is found prevent default (e.g. prevent scrolling for arrows)
if (preventDefault.includes(code)) {
event.preventDefault();
event.stopPropagation();
}
switch (code) {
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
// 0-9
if (!repeat) {
seekByKey();
}
break;
case 32:
case 75:
// Space and K key
if (!repeat) {
this.player.togglePlay();
}
break;
case 38:
// Arrow up
this.player.increaseVolume(0.1);
break;
case 40:
// Arrow down
this.player.decreaseVolume(0.1);
break;
case 77:
// M key
if (!repeat) {
this.player.muted = !this.player.muted;
}
break;
case 39:
// Arrow forward
this.player.fastForward();
break;
case 37:
// Arrow back
this.player.rewind();
break;
case 70:
// F key
this.player.fullscreen.toggle();
break;
case 67:
// C key
if (!repeat) {
this.player.toggleCaptions();
}
break;
case 76:
// L key
this.player.loop = !this.player.loop;
break;
/* case 73:
this.setLoop('start');
break;
case 76:
this.setLoop();
break;
case 79:
this.setLoop('end');
break; */
default:
break;
}
// Escape is handle natively when in full screen
// So we only need to worry about non native
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle();
}
// Store last code for next cycle
this.lastKey = code;
} else {
this.lastKey = null;
}
}
// Toggle menu
toggleMenu(event) {
controls.toggleMenu.call(this.player, event);
}
// Device is touch enabled
firstTouch() {
this.player.touch = true;
// Add touch class
utils.toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
// Clean up
utils.off(document.body, 'touchstart', this.firstTouch);
}
// Global window & document listeners
global(toggle = true) {
// Keyboard shortcuts
if (this.player.config.keyboard.global) {
utils.toggleListener(window, 'keydown keyup', this.handleKey, toggle, false);
}
// Click anywhere closes menu
utils.toggleListener(document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
utils.on(document.body, 'touchstart', this.firstTouch);
}
// Container listeners
container() {
// Keyboard shortcuts
if (!this.player.config.keyboard.global && this.player.config.keyboard.focused) {
utils.on(this.player.elements.container, 'keydown keyup', this.handleKey, false);
}
// Detect tab focus
// Remove class on blur/focusout
utils.on(this.player.elements.container, 'focusout', event => {
utils.toggleClass(event.target, this.player.config.classNames.tabFocus, false);
});
// Add classname to tabbed elements
utils.on(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(() => {
utils.toggleClass(utils.getFocusElement(), this.player.config.classNames.tabFocus, true);
}, 0);
});
// Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) {
// Toggle controls on mouse events and entering fullscreen
utils.on(this.player.elements.container, 'mouseenter mouseleave mousemove touchstart touchend touchmove enterfullscreen exitfullscreen', event => {
this.player.toggleControls(event);
});
}
}
// Listen for media events
media() {
// Time change on media
utils.on(this.player.media, 'timeupdate seeking', event => ui.timeUpdate.call(this.player, event));
// Display duration
utils.on(this.player.media, 'durationchange loadedmetadata', event => ui.durationUpdate.call(this.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
utils.on(this.player.media, 'loadeddata', () => {
utils.toggleHidden(this.player.elements.volume, !this.player.hasAudio);
utils.toggleHidden(this.player.elements.buttons.mute, !this.player.hasAudio);
});
// Handle the media finishing
utils.on(this.player.media, 'ended', () => {
// Show poster on end
if (this.player.isHTML5 && this.player.isVideo && this.player.config.showPosterOnEnd) {
// Restart
this.player.restart();
// Re-load media
this.player.media.load();
}
});
// Check for buffer progress
utils.on(this.player.media, 'progress playing', event => ui.updateProgress.call(this.player, event));
// Handle native mute
utils.on(this.player.media, 'volumechange', event => ui.updateVolume.call(this.player, event));
// Handle native play/pause
utils.on(this.player.media, 'playing play pause ended emptied', event => ui.checkPlaying.call(this.player, event));
// Loading
utils.on(this.player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this.player, event));
// Check if media failed to load
// utils.on(this.player.media, 'play', event => ui.checkFailed.call(this.player, event));
// Click video
if (this.player.supported.ui && this.player.config.clickToPlay && !this.player.isAudio) {
// Re-fetch the wrapper
const wrapper = utils.getElement.call(this.player, `.${this.player.config.classNames.video}`);
// Bail if there's no wrapper (this should never happen)
if (!utils.is.element(wrapper)) {
return;
}
// On click play, pause ore restart
utils.on(wrapper, 'click', () => {
// Touch devices will just show controls (if we're hiding controls)
if (this.player.config.hideControls && this.player.touch && !this.player.paused) {
return;
}
if (this.player.paused) {
this.player.play();
} else if (this.player.ended) {
this.player.restart();
this.player.play();
} else {
this.player.pause();
}
});
}
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
utils.on(
this.player.media,
'contextmenu',
event => {
event.preventDefault();
},
false,
);
}
// Volume change
utils.on(this.player.media, 'volumechange', () => {
// Save to storage
this.player.storage.set({ volume: this.player.volume, muted: this.player.muted });
});
// Speed change
utils.on(this.player.media, 'ratechange', () => {
// Update UI
controls.updateSetting.call(this.player, 'speed');
// Save to storage
this.player.storage.set({ speed: this.player.speed });
});
// Quality change
utils.on(this.player.media, 'qualitychange', () => {
// Update UI
controls.updateSetting.call(this.player, 'quality');
// Save to storage
this.player.storage.set({ quality: this.player.quality });
});
// Caption language change
utils.on(this.player.media, 'languagechange', () => {
// Update UI
controls.updateSetting.call(this.player, 'captions');
// Save to storage
this.player.storage.set({ language: this.player.language });
});
// Captions toggle
utils.on(this.player.media, 'captionsenabled captionsdisabled', () => {
// Update UI
controls.updateSetting.call(this.player, 'captions');
// Save to storage
this.player.storage.set({ captions: this.player.captions.active });
});
// Proxy events to container
// Bubble up key events for Edge
utils.on(this.player.media, this.player.config.events.concat([
'keyup',
'keydown',
]).join(' '), event => {
let detail = {};
// Get error details from media
if (event.type === 'error') {
detail = this.player.media.error;
}
utils.dispatchEvent.call(this.player, this.player.elements.container, event.type, true, detail);
});
}
// Listen for control events
controls() {
// 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 = utils.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 && utils.is.function(defaultHandler)) {
defaultHandler.call(this.player, event);
}
};
// Trigger custom and default handlers
const on = (element, type, defaultHandler, customHandlerKey, passive = true) => {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = utils.is.function(customHandler);
utils.on(element, type, event => proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler);
};
// Play/pause toggle
on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play');
// Pause
on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
// Rewind
on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
// Rewind
on(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward');
// Mute toggle
on(
this.player.elements.buttons.mute,
'click',
() => {
this.player.muted = !this.player.muted;
},
'mute',
);
// Captions toggle
on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions);
// Fullscreen toggle
on(
this.player.elements.buttons.fullscreen,
'click',
() => {
this.player.fullscreen.toggle();
},
'fullscreen',
);
// Picture-in-Picture
on(
this.player.elements.buttons.pip,
'click',
() => {
this.player.pip = 'toggle';
},
'pip',
);
// Airplay
on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
// Settings menu
on(this.player.elements.buttons.settings, 'click', event => {
controls.toggleMenu.call(this.player, event);
});
// Settings menu
on(this.player.elements.settings.form, 'click', event => {
event.stopPropagation();
// Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, this.player.config.selectors.inputs.language)) {
proxy(
event,
() => {
this.player.language = event.target.value;
},
'language',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) {
proxy(
event,
() => {
this.player.quality = event.target.value;
},
'quality',
);
} else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) {
proxy(
event,
() => {
this.player.speed = parseFloat(event.target.value);
},
'speed',
);
} else {
controls.showTab.call(this.player, event);
}
});
// Seek
on(
this.player.elements.inputs.seek,
inputEvent,
event => {
this.player.currentTime = event.target.value / event.target.max * this.player.duration;
},
'seek',
);
// Current time invert
// Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) {
on(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;
ui.timeUpdate.call(this.player);
});
}
// Volume
on(
this.player.elements.inputs.volume,
inputEvent,
event => {
this.player.volume = event.target.value;
},
'volume',
);
// Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => {
controls.updateRangeFill.call(this.player, event.target);
});
}
// Seek tooltip
on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event));
// Toggle controls visibility based on mouse movement
if (this.player.config.hideControls) {
// Watch for cursor over controls so they don't hide when trying to interact
on(this.player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
});
// Watch for cursor over controls so they don't hide when trying to interact
on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
this.player.elements.controls.pressed = [
'mousedown',
'touchstart',
].includes(event.type);
});
// Focus in/out on controls
on(this.player.elements.controls, 'focusin focusout', event => {
this.player.toggleControls(event);
});
}
// Mouse wheel for volume
on(
this.player.elements.inputs.volume,
'wheel',
event => {
// Detect "natural" scroll - suppored on OS X Safari only
// Other browsers on OS X will be inverted until support improves
const inverted = event.webkitDirectionInvertedFromDevice;
const step = 1 / 50;
let direction = 0;
// Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) {
if (inverted) {
this.player.decreaseVolume(step);
direction = -1;
} else {
this.player.increaseVolume(step);
direction = 1;
}
}
// Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) {
if (inverted) {
this.player.increaseVolume(step);
direction = 1;
} else {
this.player.decreaseVolume(step);
direction = -1;
}
}
// Don't break page scrolling at max and min
if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) {
event.preventDefault();
}
},
'volume',
false,
);
}
// Reset on destroy
clear() {
this.global(false);
}
}
export default Listeners;

106
src/js/media.js Normal file
View File

@ -0,0 +1,106 @@
// ==========================================================================
// Plyr Media
// ==========================================================================
import support from './support';
import utils from './utils';
import youtube from './plugins/youtube';
import vimeo from './plugins/vimeo';
import ui from './ui';
// Sniff out the browser
const browser = utils.getBrowser();
const media = {
// Setup media
setup() {
// If there's no media, bail
if (!this.media) {
this.debug.warn('No media element found!');
return;
}
// Add type class
utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true);
// Add provider class
utils.toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true);
// Add video class for embeds
// This will require changes if audio embeds are added
if (this.isEmbed) {
utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', 'video'), true);
}
if (this.supported.ui) {
// Check for picture-in-picture support
utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
// Check for airplay support
utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
// If there's no autoplay attribute, assume the video is stopped and add state class
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);
// Add iOS class
utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
// Add touch class
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
}
// Inject the player wrapper
if (this.isVideo) {
// Create the wrapper div
this.elements.wrapper = utils.createElement('div', {
class: this.config.classNames.video,
});
// Wrap the video in a container
utils.wrap(this.media, this.elements.wrapper);
}
if (this.isEmbed) {
switch (this.provider) {
case 'youtube':
youtube.setup.call(this);
break;
case 'vimeo':
vimeo.setup.call(this);
break;
default:
break;
}
} else if (this.isHTML5) {
ui.setTitle.call(this);
}
},
// Cancel current network requests
// See https://github.com/sampotts/plyr/issues/174
cancelRequests() {
if (!this.isHTML5) {
return;
}
// Remove child sources
utils.removeElement(this.media.querySelectorAll('source'));
// Set blank video src attribute
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
// Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection
this.media.setAttribute('src', this.config.blankVideo);
// Load the new empty source
// This will cancel existing requests
// See https://github.com/sampotts/plyr/issues/174
this.media.load();
// Debugging
this.debug.log('Cancelled network requests');
},
};
export default media;

575
src/js/plugins/ads.js Normal file
View File

@ -0,0 +1,575 @@
// ==========================================================================
// Advertisement plugin using Google IMA HTML5 SDK
// Create an account with our ad partner, vi here:
// https://www.vi.ai/publisher-video-monetization/
// ==========================================================================
/* global google */
import utils from '../utils';
import i18n from '../i18n';
class Ads {
/**
* Ads constructor.
* @param {object} player
* @return {Ads}
*/
constructor(player) {
this.player = player;
this.publisherId = player.config.ads.publisherId;
this.enabled = player.isHTML5 && player.isVideo && player.config.ads.enabled && utils.is.string(this.publisherId) && this.publisherId.length;
this.playing = false;
this.initialized = false;
this.elements = {
container: null,
displayContainer: null,
};
this.manager = null;
this.loader = null;
this.cuePoints = null;
this.events = {};
this.safetyTimer = null;
this.countdownTimer = null;
// Setup a promise to resolve when the IMA manager is ready
this.managerPromise = new Promise((resolve, reject) => {
// The ad is loaded and ready
this.on('loaded', resolve);
// Ads failed
this.on('error', reject);
});
this.load();
}
/**
* Load the IMA SDK
*/
load() {
if (this.enabled) {
// Check if the Google IMA3 SDK is loaded or load it ourselves
if (!utils.is.object(window.google) || !utils.is.object(window.google.ima)) {
utils
.loadScript(this.player.config.urls.googleIMA.api)
.then(() => {
this.ready();
})
.catch(() => {
// Script failed to load or is blocked
this.trigger('error', new Error('Google IMA SDK failed to load'));
});
} else {
this.ready();
}
}
}
/**
* Get the ads instance ready
*/
ready() {
// Start ticking our safety timer. If the whole advertisement
// thing doesn't resolve within our set time; we bail
this.startSafetyTimer(12000, 'ready()');
// Clear the safety timer
this.managerPromise.then(() => {
this.clearSafetyTimer('onAdsManagerLoaded()');
});
// Set listeners on the Plyr instance
this.listeners();
// Setup the IMA SDK
this.setupIMA();
}
// Build the default tag URL
get tagUrl() {
const params = {
AV_PUBLISHERID: '58c25bb0073ef448b1087ad6',
AV_CHANNELID: '5a0458dc28a06145e4519d21',
AV_URL: location.hostname,
cb: Date.now(),
AV_WIDTH: 640,
AV_HEIGHT: 480,
AV_CDIM2: this.publisherId,
};
const base = 'https://go.aniview.com/api/adserver6/vast/';
return `${base}?${utils.buildUrlParams(params)}`;
}
/**
* In order for the SDK to display ads for our video, we need to tell it where to put them,
* so here we define our ad container. This div is set up to render on top of the video player.
* Using the code below, we tell the SDK to render ads within that div. We also provide a
* handle to the content video player - the SDK will poll the current time of our player to
* properly place mid-rolls. After we create the ad display container, we initialize it. On
* mobile devices, this initialization is done as the result of a user action.
*/
setupIMA() {
// Create the container for our advertisements
this.elements.container = utils.createElement('div', {
class: this.player.config.classNames.ads,
});
this.player.elements.container.appendChild(this.elements.container);
// So we can run VPAID2
google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);
// Set language
google.ima.settings.setLocale(this.player.config.ads.language);
// We assume the adContainer is the video container of the plyr element
// that will house the ads
this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container);
// Request video ads to be pre-loaded
this.requestAds();
}
/**
* Request advertisements
*/
requestAds() {
const { container } = this.player.elements;
try {
// Create ads loader
this.loader = new google.ima.AdsLoader(this.elements.displayContainer);
// Listen and respond to ads loaded and error events
this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, event => this.onAdsManagerLoaded(event), false);
this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false);
// Request video ads
const request = new google.ima.AdsRequest();
request.adTagUrl = this.tagUrl;
// Specify the linear and nonlinear slot sizes. This helps the SDK
// to select the correct creative if multiple are returned
request.linearAdSlotWidth = container.offsetWidth;
request.linearAdSlotHeight = container.offsetHeight;
request.nonLinearAdSlotWidth = container.offsetWidth;
request.nonLinearAdSlotHeight = container.offsetHeight;
// We only overlay ads as we only support video.
request.forceNonLinearFullSlot = false;
this.loader.requestAds(request);
} catch (e) {
this.onAdError(e);
}
}
/**
* Update the ad countdown
* @param {boolean} start
*/
pollCountdown(start = false) {
if (!start) {
clearInterval(this.countdownTimer);
this.elements.container.removeAttribute('data-badge-text');
return;
}
const update = () => {
const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0));
const label = `${i18n.get('advertisement', this.player.config)} - ${time}`;
this.elements.container.setAttribute('data-badge-text', label);
};
this.countdownTimer = setInterval(update, 100);
}
/**
* This method is called whenever the ads are ready inside the AdDisplayContainer
* @param {Event} adsManagerLoadedEvent
*/
onAdsManagerLoaded(event) {
// Get the ads manager
const settings = new google.ima.AdsRenderingSettings();
// Tell the SDK to save and restore content video state on our behalf
settings.restoreCustomPlaybackStateOnAdBreakComplete = true;
settings.enablePreloading = true;
// The SDK is polling currentTime on the contentPlayback. And needs a duration
// so it can determine when to start the mid- and post-roll
this.manager = event.getAdsManager(this.player, settings);
// Get the cue points for any mid-rolls by filtering out the pre- and post-roll
this.cuePoints = this.manager.getCuePoints();
// Add advertisement cue's within the time line if available
this.cuePoints.forEach(cuePoint => {
if (cuePoint !== 0 && cuePoint !== -1 && cuePoint < this.player.duration) {
const seekElement = this.player.elements.progress;
if (seekElement) {
const cuePercentage = 100 / this.player.duration * cuePoint;
const cue = utils.createElement('span', {
class: this.player.config.classNames.cues,
});
cue.style.left = `${cuePercentage.toString()}%`;
seekElement.appendChild(cue);
}
}
});
// Get skippable state
// TODO: Skip button
// this.manager.getAdSkippableState();
// Set volume to match player
this.manager.setVolume(this.player.volume);
// Add listeners to the required events
// Advertisement error events
this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error));
// Advertisement regular events
Object.keys(google.ima.AdEvent.Type).forEach(type => {
this.manager.addEventListener(google.ima.AdEvent.Type[type], event => this.onAdEvent(event));
});
// Resolve our adsManager
this.trigger('loaded');
}
/**
* This is where all the event handling takes place. Retrieve the ad from the event. Some
* events (e.g. ALL_ADS_COMPLETED) don't have the ad object associated
* https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type
* @param {Event} event
*/
onAdEvent(event) {
const { container } = this.player.elements;
// Retrieve the ad from the event. Some events (e.g. ALL_ADS_COMPLETED)
// don't have ad object associated
const ad = event.getAd();
// Proxy event
const dispatchEvent = type => {
const event = `ads${type.replace(/_/g, '').toLowerCase()}`;
utils.dispatchEvent.call(this.player, this.player.media, event);
};
switch (event.type) {
case google.ima.AdEvent.Type.LOADED:
// This is the first event sent for an ad - it is possible to determine whether the
// ad is a video ad or an overlay
this.trigger('loaded');
// Bubble event
dispatchEvent(event.type);
// Start countdown
this.pollCountdown(true);
if (!ad.isLinear()) {
// Position AdDisplayContainer correctly for overlay
ad.width = container.offsetWidth;
ad.height = container.offsetHeight;
}
// console.info('Ad type: ' + event.getAd().getAdPodInfo().getPodIndex());
// console.info('Ad time: ' + event.getAd().getAdPodInfo().getTimeOffset());
break;
case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
// All ads for the current videos are done. We can now request new advertisements
// in case the video is re-played
// Fire event
dispatchEvent(event.type);
// TODO: Example for what happens when a next video in a playlist would be loaded.
// So here we load a new video when all ads are done.
// Then we load new ads within a new adsManager. When the video
// Is started - after - the ads are loaded, then we get ads.
// You can also easily test cancelling and reloading by running
// player.ads.cancel() and player.ads.play from the console I guess.
// this.player.source = {
// type: 'video',
// title: 'View From A Blue Moon',
// sources: [{
// src:
// 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4', type:
// 'video/mp4', }], 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', }, ],
// };
// TODO: So there is still this thing where a video should only be allowed to start
// playing when the IMA SDK is ready or has failed
this.loadAds();
break;
case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:
// This event indicates the ad has started - the video player can adjust the UI,
// for example display a pause button and remaining time. Fired when content should
// be paused. This usually happens right before an ad is about to cover the content
dispatchEvent(event.type);
this.pauseContent();
break;
case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED:
// This event indicates the ad has finished - the video player can perform
// appropriate UI actions, such as removing the timer for remaining time detection.
// Fired when content should be resumed. This usually happens when an ad finishes
// or collapses
dispatchEvent(event.type);
this.pollCountdown();
this.resumeContent();
break;
case google.ima.AdEvent.Type.STARTED:
case google.ima.AdEvent.Type.MIDPOINT:
case google.ima.AdEvent.Type.COMPLETE:
case google.ima.AdEvent.Type.IMPRESSION:
case google.ima.AdEvent.Type.CLICK:
dispatchEvent(event.type);
break;
default:
break;
}
}
/**
* Any ad error handling comes through here
* @param {Event} event
*/
onAdError(event) {
this.cancel();
this.player.debug.warn('Ads error', event);
}
/**
* Setup hooks for Plyr and window events. This ensures
* the mid- and post-roll launch at the correct time. And
* resize the advertisement when the player resizes
*/
listeners() {
const { container } = this.player.elements;
let time;
// Add listeners to the required events
this.player.on('ended', () => {
this.loader.contentComplete();
});
this.player.on('seeking', () => {
time = this.player.currentTime;
return time;
});
this.player.on('seeked', () => {
const seekedTime = this.player.currentTime;
this.cuePoints.forEach((cuePoint, index) => {
if (time < cuePoint && cuePoint < seekedTime) {
this.manager.discardAdBreak();
this.cuePoints.splice(index, 1);
}
});
});
// Listen to the resizing of the window. And resize ad accordingly
// TODO: eventually implement ResizeObserver
window.addEventListener('resize', () => {
this.manager.resize(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);
});
}
/**
* Initialize the adsManager and start playing advertisements
*/
play() {
const { container } = this.player.elements;
if (!this.managerPromise) {
this.resumeContent();
}
// Play the requested advertisement whenever the adsManager is ready
this.managerPromise
.then(() => {
// Initialize the container. Must be done via a user action on mobile devices
this.elements.displayContainer.initialize();
try {
if (!this.initialized) {
// Initialize the ads manager. Ad rules playlist will start at this time
this.manager.init(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);
// Call play to start showing the ad. Single video and overlay ads will
// start at this time; the call will be ignored for ad rules
this.manager.start();
}
this.initialized = true;
} catch (adError) {
// An error may be thrown if there was a problem with the
// VAST response
this.onAdError(adError);
}
})
.catch(() => {});
}
/**
* Resume our video
*/
resumeContent() {
// Hide the advertisement container
this.elements.container.style.zIndex = '';
// Ad is stopped
this.playing = false;
// Play our video
if (this.player.currentTime < this.player.duration) {
this.player.play();
}
}
/**
* Pause our video
*/
pauseContent() {
// Show the advertisement container
this.elements.container.style.zIndex = 3;
// Ad is playing.
this.playing = true;
// Pause our video.
this.player.pause();
}
/**
* Destroy the adsManager so we can grab new ads after this. If we don't then we're not
* allowed to call new ads based on google policies, as they interpret this as an accidental
* video requests. https://developers.google.com/interactive-
* media-ads/docs/sdks/android/faq#8
*/
cancel() {
// Pause our video
if (this.initialized) {
this.resumeContent();
}
// Tell our instance that we're done for now
this.trigger('error');
// Re-create our adsManager
this.loadAds();
}
/**
* Re-create our adsManager
*/
loadAds() {
// Tell our adsManager to go bye bye
this.managerPromise
.then(() => {
// Destroy our adsManager
if (this.manager) {
this.manager.destroy();
}
// Re-set our adsManager promises
this.managerPromise = new Promise(resolve => {
this.on('loaded', resolve);
this.player.debug.log(this.manager);
});
// Now request some new advertisements
this.requestAds();
})
.catch(() => {});
}
/**
* Handles callbacks after an ad event was invoked
* @param {string} event - Event type
*/
trigger(event, ...args) {
const handlers = this.events[event];
if (utils.is.array(handlers)) {
handlers.forEach(handler => {
if (utils.is.function(handler)) {
handler.apply(this, args);
}
});
}
}
/**
* Add event listeners
* @param {string} event - Event type
* @param {function} callback - Callback for when event occurs
* @return {Ads}
*/
on(event, callback) {
if (!utils.is.array(this.events[event])) {
this.events[event] = [];
}
this.events[event].push(callback);
return this;
}
/**
* Setup a safety timer for when the ad network doesn't respond for whatever reason.
* The advertisement has 12 seconds to get its things together. We stop this timer when the
* advertisement is playing, or when a user action is required to start, then we clear the
* timer on ad ready
* @param {number} time
* @param {string} from
*/
startSafetyTimer(time, from) {
this.player.debug.log(`Safety timer invoked from: ${from}`);
this.safetyTimer = setTimeout(() => {
this.cancel();
this.clearSafetyTimer('startSafetyTimer()');
}, time);
}
/**
* Clear our safety timer(s)
* @param {string} from
*/
clearSafetyTimer(from) {
if (!utils.is.nullOrUndefined(this.safetyTimer)) {
this.player.debug.log(`Safety timer cleared from: ${from}`);
clearTimeout(this.safetyTimer);
this.safetyTimer = null;
}
}
}
export default Ads;

321
src/js/plugins/vimeo.js Normal file
View File

@ -0,0 +1,321 @@
// ==========================================================================
// Vimeo plugin
// ==========================================================================
import utils from './../utils';
import captions from './../captions';
import ui from './../ui';
const vimeo = {
setup() {
// Add embed class for responsive
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
// Set intial ratio
vimeo.setAspectRatio.call(this);
// Load the API if not already
if (!utils.is.object(window.Vimeo)) {
utils
.loadScript(this.config.urls.vimeo.api)
.then(() => {
vimeo.ready.call(this);
})
.catch(error => {
this.debug.warn('Vimeo API failed to load', error);
});
} else {
vimeo.ready.call(this);
}
},
// Set aspect ratio
// For Vimeo we have an extra 300% height <div> to hide the standard controls and UI
setAspectRatio(input) {
const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
const padding = 100 / ratio[0] * ratio[1];
const height = 240;
const offset = (height - padding) / (height / 50);
this.elements.wrapper.style.paddingBottom = `${padding}%`;
this.media.style.transform = `translateY(-${offset}%)`;
},
// API Ready
ready() {
const player = this;
// Get Vimeo params for the iframe
const options = {
loop: player.config.loop.active,
autoplay: player.autoplay,
byline: false,
portrait: false,
title: false,
speed: true,
transparent: 0,
gesture: 'media',
};
const params = utils.buildUrlParams(options);
// Get the source URL or ID
let source = player.media.getAttribute('src');
// Get from <div> if needed
if (utils.is.empty(source)) {
source = player.media.getAttribute(this.config.attributes.embed.id);
}
const id = utils.parseVimeoId(source);
// Build an iframe
const iframe = utils.createElement('iframe');
const src = `https://player.vimeo.com/video/${id}?${params}`;
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
iframe.setAttribute('allow', 'autoplay');
// Inject the package
const wrapper = utils.createElement('div');
wrapper.appendChild(iframe);
player.media = utils.replaceElement(wrapper, player.media);
// Setup instance
// https://github.com/vimeo/player.js
player.embed = new window.Vimeo.Player(iframe);
player.media.paused = true;
player.media.currentTime = 0;
// Create a faux HTML5 API using the Vimeo API
player.media.play = () => {
player.embed.play().then(() => {
player.media.paused = false;
});
};
player.media.pause = () => {
player.embed.pause().then(() => {
player.media.paused = true;
});
};
player.media.stop = () => {
player.pause();
player.currentTime = 0;
};
// Seeking
let { currentTime } = player.media;
Object.defineProperty(player.media, 'currentTime', {
get() {
return currentTime;
},
set(time) {
// Get current paused state
// Vimeo will automatically play on seek
const { paused } = player.media;
// Set seeking flag
player.media.seeking = true;
// Trigger seeking
utils.dispatchEvent.call(player, player.media, 'seeking');
// Seek after events
player.embed.setCurrentTime(time);
// Restore pause state
if (paused) {
player.pause();
}
},
});
// Playback speed
let speed = player.config.speed.selected;
Object.defineProperty(player.media, 'playbackRate', {
get() {
return speed;
},
set(input) {
player.embed.setPlaybackRate(input).then(() => {
speed = input;
utils.dispatchEvent.call(player, player.media, 'ratechange');
});
},
});
// Volume
let { volume } = player.config;
Object.defineProperty(player.media, 'volume', {
get() {
return volume;
},
set(input) {
player.embed.setVolume(input).then(() => {
volume = input;
utils.dispatchEvent.call(player, player.media, 'volumechange');
});
},
});
// Muted
let { muted } = player.config;
Object.defineProperty(player.media, 'muted', {
get() {
return muted;
},
set(input) {
const toggle = utils.is.boolean(input) ? input : false;
player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => {
muted = toggle;
utils.dispatchEvent.call(player, player.media, 'volumechange');
});
},
});
// Loop
let { loop } = player.config;
Object.defineProperty(player.media, 'loop', {
get() {
return loop;
},
set(input) {
const toggle = utils.is.boolean(input) ? input : player.config.loop.active;
player.embed.setLoop(toggle).then(() => {
loop = toggle;
});
},
});
// Source
let currentSrc;
player.embed.getVideoUrl().then(value => {
currentSrc = value;
});
Object.defineProperty(player.media, 'currentSrc', {
get() {
return currentSrc;
},
});
// Ended
Object.defineProperty(player.media, 'ended', {
get() {
return player.currentTime === player.duration;
},
});
// Set aspect ratio based on video size
Promise.all([
player.embed.getVideoWidth(),
player.embed.getVideoHeight(),
]).then(dimensions => {
const ratio = utils.getAspectRatio(dimensions[0], dimensions[1]);
vimeo.setAspectRatio.call(this, ratio);
});
// Set autopause
player.embed.setAutopause(player.config.autopause).then(state => {
player.config.autopause = state;
});
// Get title
player.embed.getVideoTitle().then(title => {
player.config.title = title;
ui.setTitle.call(this);
});
// Get current time
player.embed.getCurrentTime().then(value => {
currentTime = value;
utils.dispatchEvent.call(player, player.media, 'timeupdate');
});
// Get duration
player.embed.getDuration().then(value => {
player.media.duration = value;
utils.dispatchEvent.call(player, player.media, 'durationchange');
});
// Get captions
player.embed.getTextTracks().then(tracks => {
player.media.textTracks = tracks;
captions.setup.call(player);
});
player.embed.on('cuechange', data => {
let cue = null;
if (data.cues.length) {
cue = utils.stripHTML(data.cues[0].text);
}
captions.setText.call(player, cue);
});
player.embed.on('loaded', () => {
if (utils.is.element(player.embed.element) && player.supported.ui) {
const frame = player.embed.element;
// Fix keyboard focus issues
// https://github.com/sampotts/plyr/issues/317
frame.setAttribute('tabindex', -1);
}
});
player.embed.on('play', () => {
// Only fire play if paused before
if (player.media.paused) {
utils.dispatchEvent.call(player, player.media, 'play');
}
player.media.paused = false;
utils.dispatchEvent.call(player, player.media, 'playing');
});
player.embed.on('pause', () => {
player.media.paused = true;
utils.dispatchEvent.call(player, player.media, 'pause');
});
player.embed.on('timeupdate', data => {
player.media.seeking = false;
currentTime = data.seconds;
utils.dispatchEvent.call(player, player.media, 'timeupdate');
});
player.embed.on('progress', data => {
player.media.buffered = data.percent;
utils.dispatchEvent.call(player, player.media, 'progress');
// Check all loaded
if (parseInt(data.percent, 10) === 1) {
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
}
});
player.embed.on('seeked', () => {
player.media.seeking = false;
utils.dispatchEvent.call(player, player.media, 'seeked');
utils.dispatchEvent.call(player, player.media, 'play');
});
player.embed.on('ended', () => {
player.media.paused = true;
utils.dispatchEvent.call(player, player.media, 'ended');
});
player.embed.on('error', detail => {
player.media.error = detail;
utils.dispatchEvent.call(player, player.media, 'error');
});
// Rebuild UI
setTimeout(() => ui.build.call(player), 0);
},
};
export default vimeo;

427
src/js/plugins/youtube.js Normal file
View File

@ -0,0 +1,427 @@
// ==========================================================================
// YouTube plugin
// ==========================================================================
import utils from './../utils';
import controls from './../controls';
import ui from './../ui';
const youtube = {
setup() {
// Add embed class for responsive
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
// Set aspect ratio
youtube.setAspectRatio.call(this);
// Setup API
if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) {
youtube.ready.call(this);
} else {
// Load the API
utils.loadScript(this.config.urls.youtube.api).catch(error => {
this.debug.warn('YouTube API failed to load', error);
});
// Setup callback for the API
// YouTube has it's own system of course...
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
// Add to queue
window.onYouTubeReadyCallbacks.push(() => {
youtube.ready.call(this);
});
// Set callback to process queue
window.onYouTubeIframeAPIReady = () => {
window.onYouTubeReadyCallbacks.forEach(callback => {
callback();
});
};
}
},
// Get the media title
getTitle(videoId) {
// Try via undocumented API method first
// This method disappears now and then though...
// https://github.com/sampotts/plyr/issues/709
if (utils.is.function(this.embed.getVideoData)) {
const { title } = this.embed.getVideoData();
if (utils.is.empty(title)) {
this.config.title = title;
ui.setTitle.call(this);
return;
}
}
// Or via Google API
const key = this.config.keys.google;
if (utils.is.string(key) && !utils.is.empty(key)) {
const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;
utils
.fetch(url)
.then(result => {
if (utils.is.object(result)) {
this.config.title = result.items[0].snippet.title;
ui.setTitle.call(this);
}
})
.catch(() => {});
}
},
// Set aspect ratio
setAspectRatio() {
const ratio = this.config.ratio.split(':');
this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`;
},
// API ready
ready() {
const player = this;
// Ignore already setup (race condition)
const currentId = player.media.getAttribute('id');
if (!utils.is.empty(currentId) && currentId.startsWith('youtube-')) {
return;
}
// Get the source URL or ID
let source = player.media.getAttribute('src');
// Get from <div> if needed
if (utils.is.empty(source)) {
source = player.media.getAttribute(this.config.attributes.embed.id);
}
// Replace the <iframe> with a <div> due to YouTube API issues
const videoId = utils.parseYouTubeId(source);
const id = utils.generateId(player.provider);
const container = utils.createElement('div', { id });
player.media = utils.replaceElement(container, player.media);
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
player.embed = new window.YT.Player(id, {
videoId,
playerVars: {
autoplay: player.config.autoplay ? 1 : 0, // Autoplay
controls: player.supported.ui ? 0 : 1, // Only show controls if not fully supported
rel: 0, // No related vids
showinfo: 0, // Hide info
iv_load_policy: 3, // Hide annotations
modestbranding: 1, // Hide logos as much as possible (they still show one in the corner when paused)
disablekb: 1, // Disable keyboard as we handle it
playsinline: 1, // Allow iOS inline playback
// Tracking for stats
// origin: window ? `${window.location.protocol}//${window.location.host}` : null,
widget_referrer: window ? window.location.href : null,
// Captions are flaky on YouTube
cc_load_policy: player.captions.active ? 1 : 0,
cc_lang_pref: player.config.captions.language,
},
events: {
onError(event) {
// If we've already fired an error, don't do it again
// YouTube fires onError twice
if (utils.is.object(player.media.error)) {
return;
}
const detail = {
code: event.data,
};
// Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
switch (event.data) {
case 2:
detail.message =
'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.';
break;
case 5:
detail.message =
'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';
break;
case 100:
detail.message =
'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.';
break;
case 101:
case 150:
detail.message = 'The owner of the requested video does not allow it to be played in embedded players.';
break;
default:
detail.message = 'An unknown error occured';
break;
}
player.media.error = detail;
utils.dispatchEvent.call(player, player.media, 'error');
},
onPlaybackQualityChange(event) {
// Get the instance
const instance = event.target;
// Get current quality
player.media.quality = instance.getPlaybackQuality();
utils.dispatchEvent.call(player, player.media, 'qualitychange');
},
onPlaybackRateChange(event) {
// Get the instance
const instance = event.target;
// Get current speed
player.media.playbackRate = instance.getPlaybackRate();
utils.dispatchEvent.call(player, player.media, 'ratechange');
},
onReady(event) {
// Get the instance
const instance = event.target;
// Get the title
youtube.getTitle.call(player, videoId);
// Create a faux HTML5 API using the YouTube API
player.media.play = () => {
instance.playVideo();
};
player.media.pause = () => {
instance.pauseVideo();
};
player.media.stop = () => {
instance.stopVideo();
};
player.media.duration = instance.getDuration();
player.media.paused = true;
// Seeking
player.media.currentTime = 0;
Object.defineProperty(player.media, 'currentTime', {
get() {
return Number(instance.getCurrentTime());
},
set(time) {
// Set seeking flag
player.media.seeking = true;
// Trigger seeking
utils.dispatchEvent.call(player, player.media, 'seeking');
// Seek after events sent
instance.seekTo(time);
},
});
// Playback speed
Object.defineProperty(player.media, 'playbackRate', {
get() {
return instance.getPlaybackRate();
},
set(input) {
instance.setPlaybackRate(input);
},
});
// Quality
Object.defineProperty(player.media, 'quality', {
get() {
return instance.getPlaybackQuality();
},
set(input) {
// Trigger request event
utils.dispatchEvent.call(player, player.media, 'qualityrequested', false, {
quality: input,
});
instance.setPlaybackQuality(input);
},
});
// Volume
let { volume } = player.config;
Object.defineProperty(player.media, 'volume', {
get() {
return volume;
},
set(input) {
volume = input;
instance.setVolume(volume * 100);
utils.dispatchEvent.call(player, player.media, 'volumechange');
},
});
// Muted
let { muted } = player.config;
Object.defineProperty(player.media, 'muted', {
get() {
return muted;
},
set(input) {
const toggle = utils.is.boolean(input) ? input : muted;
muted = toggle;
instance[toggle ? 'mute' : 'unMute']();
utils.dispatchEvent.call(player, player.media, 'volumechange');
},
});
// Source
Object.defineProperty(player.media, 'currentSrc', {
get() {
return instance.getVideoUrl();
},
});
// Ended
Object.defineProperty(player.media, 'ended', {
get() {
return player.currentTime === player.duration;
},
});
// Get available speeds
player.options.speed = instance.getAvailablePlaybackRates();
// Set the tabindex to avoid focus entering iframe
if (player.supported.ui) {
player.media.setAttribute('tabindex', -1);
}
utils.dispatchEvent.call(player, player.media, 'timeupdate');
utils.dispatchEvent.call(player, player.media, 'durationchange');
// Reset timer
clearInterval(player.timers.buffering);
// Setup buffering
player.timers.buffering = setInterval(() => {
// Get loaded % from YouTube
player.media.buffered = instance.getVideoLoadedFraction();
// Trigger progress only when we actually buffer something
if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {
utils.dispatchEvent.call(player, player.media, 'progress');
}
// Set last buffer point
player.media.lastBuffered = player.media.buffered;
// Bail if we're at 100%
if (player.media.buffered === 1) {
clearInterval(player.timers.buffering);
// Trigger event
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
}
}, 200);
// Rebuild UI
setTimeout(() => ui.build.call(player), 50);
},
onStateChange(event) {
// Get the instance
const instance = event.target;
// Reset timer
clearInterval(player.timers.playing);
// Handle events
// -1 Unstarted
// 0 Ended
// 1 Playing
// 2 Paused
// 3 Buffering
// 5 Video cued
switch (event.data) {
case -1:
// Update scrubber
utils.dispatchEvent.call(player, player.media, 'timeupdate');
// Get loaded % from YouTube
player.media.buffered = instance.getVideoLoadedFraction();
utils.dispatchEvent.call(player, player.media, 'progress');
break;
case 0:
player.media.paused = true;
// YouTube doesn't support loop for a single video, so mimick it.
if (player.media.loop) {
// YouTube needs a call to `stopVideo` before playing again
instance.stopVideo();
instance.playVideo();
} else {
utils.dispatchEvent.call(player, player.media, 'ended');
}
break;
case 1:
// If we were seeking, fire seeked event
if (player.media.seeking) {
utils.dispatchEvent.call(player, player.media, 'seeked');
}
player.media.seeking = false;
// Only fire play if paused before
if (player.media.paused) {
utils.dispatchEvent.call(player, player.media, 'play');
}
player.media.paused = false;
utils.dispatchEvent.call(player, player.media, 'playing');
// Poll to get playback progress
player.timers.playing = setInterval(() => {
utils.dispatchEvent.call(player, player.media, 'timeupdate');
}, 50);
// Check duration again due to YouTube bug
// https://github.com/sampotts/plyr/issues/374
// https://code.google.com/p/gdata-issues/issues/detail?id=8690
if (player.media.duration !== instance.getDuration()) {
player.media.duration = instance.getDuration();
utils.dispatchEvent.call(player, player.media, 'durationchange');
}
// Get quality
controls.setQualityMenu.call(player, instance.getAvailableQualityLevels());
break;
case 2:
player.media.paused = true;
utils.dispatchEvent.call(player, player.media, 'pause');
break;
default:
break;
}
utils.dispatchEvent.call(player, player.elements.container, 'statechange', false, {
code: event.data,
});
},
},
});
},
};
export default youtube;

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More