Comparar commits

...

64 Commits

Autor SHA1 Mensagem Data
Pat O'Neill 66c0cf5ac6 v5.20.4 2017-10-02 17:07:07 -04:00
Gary Katsevman 0f8de5204e fix: Make boolean attributes set and check both the associated property and the attribute (#4631)
Make boolean attributes like muted set the property video.muted = {{boolean}} and the attribute video.setAttribute('muted', 'muted') when changing the value.
Also, in html5's createEl, we were setting/updating attributes for various properties but we were only setting the attributes and not also the properties but also autoplay was happening first rather than last which caused autoplay to fail because muted and playsinline weren't necessary set by that time.

This is the 5.x version of #4562.

Fix #4351.
2017-10-02 16:08:52 -04:00
Gary Katsevman 23c7f263c6 v5.20.3 2017-08-16 17:40:06 -04:00
Alex Barstow 424d021517 fix: tracks are disabled and cuepoints are cleared in iOS native player (#4569)
In the HTML5 tech, when we go fullscreen to the native player, listen for change events and restore previous mode state for the metadata text tracks.
This is 5.x version of #4496.
2017-08-16 12:04:49 -04:00
Gary Katsevman 935ec1e854 chore(CHANGELOG): update CHANGELOG for 5.20.2 (#4529) 2017-07-28 16:58:39 -04:00
Gary Katsevman 08aad15c04 v5.20.2 2017-07-26 16:58:21 -04:00
Gary Katsevman d90c94584d fix: only change focus from BPB if not a mouse click (#4523) 2017-07-26 16:09:40 -04:00
marguinbc b314268c2b fix: adjust volume ranges so muted(true) and vol=0 do not use the same icons (#4425) 2017-07-26 15:41:03 -04:00
Gary Katsevman a145dd287f chore(package): update to swf 5.4.1 (#4516) 2017-07-26 15:40:09 -04:00
Gary Katsevman 9ca9989494 chore: add a release script to makes things easier (#4517) 2017-07-26 15:37:19 -04:00
Alex Barstow 2576eda2f7 fix: player.duration() should return NaN if duration is not known (#4456) 2017-07-06 13:47:27 +10:00
mister-ben 8599c8ede0 fix: Use passive event listeners for touchstart/touchmove (#4445)
If passive event listening is supported, we should use it.

5.x version of #4440.

Fixes #4432.
2017-07-05 09:53:55 +10:00
mister-ben c17c003771 fix: Safari picture-in-picture triggers fullscreenchange (#4442)
When picture-in-picture mode is entered on Safari, `webkitbeginfullscreen` is triggered which results in a proxied `fullscreenchange` event and adding the fullscreen class to the player. That causes the tech element to collapse to zero height so that the "this video is playing in picture in picture" placeholder is not visible.

As with #4437 for v6, on `webkitbeginfullscreen` check whether the presentation mode is picture-in-picture' before proxying a `fullscreenchange` event.
2017-07-05 09:52:45 +10:00
Gary Katsevman a1763dcc56 fix: Only update text track mode if changed (#4368) 2017-05-18 13:12:18 -04:00
Gary Katsevman 3f7e215e7b chore(package): update videojs-vtt.js to 0.12.4 (#4364) 2017-05-16 16:51:00 -04:00
Gary Katsevman 0d725dfec7 chore: move imports up so bundlers wont trip up (#4363) 2017-05-16 11:39:53 -04:00
Gary Katsevman f194ca56eb v5.20.1 2017-05-15 13:57:32 -04:00
Gary Katsevman 0d6072078d fix: only disable user-selection on sliders (#4355) 2017-05-15 12:04:53 -04:00
mister-ben 3faea9fc63 fix: prevent dupe events on enabled ClickableComponents (#4357)
Like #4316 for v6, this prevents multiple click handlers being added if an already enabled component is enabled. In the PR for six, ClickableComponent was tracking this.enabled_ but this is incompatible with v5 on a MenuButton which also changes the state of this.enabled_.
Removes event handlers in enable() before adding them.
2017-05-15 11:33:01 -04:00
Gary Katsevman 69179673e1 v5.20.0 2017-05-11 11:25:38 -04:00
Gary Katsevman 823f6c7a47 test: fix playsinline test for IE8 (#4347)
This test is more of an integration test and uses the html5 tech to make
sure the setter and getter work properly. This causes issues with IE8
which doesn't work well with the Html5 tech.

Instead, we wrap the test in an if that checks whether Html5 is
supported.
2017-05-10 17:08:14 -04:00
Alex Barstow 946f84b4de feat: add 'playsinline' player option (#4325)
Video.js players can accept a number of standard <video> element options (autoplay, muted, loop, etc), but not currently playsinline, which is now part of the [HTML spec](https://html.spec.whatwg.org/multipage/embedded-content.html#attr-video-playsinline). We should add it to the list of <video> attributes that can be provided to the player as options.
2017-05-10 15:55:39 -04:00
Gary Katsevman 6cf7327fd4 chore(package): pin karma to 1.3.0 for IE8 support (#4340)
* chore: pin karma to 1.3.0 for IE8 support
* remove function assertion that breaks IE8
2017-05-09 15:47:23 -04:00
Gary Katsevman ab110ff191 chore: add slack travis notifications (#4339) 2017-05-09 14:54:16 -04:00
Garrett c970474e10 feat: Add getVideoPlaybackQuality API (#4286)
Provides a tech getter for getVideoPlaybackQuality as specified by the W3C's Media Playback Quality API: https://wicg.github.io/media-playback-quality/.
2017-05-09 14:32:40 -04:00
mister-ben b855bfb9de feat: Add a version class to the player (#4335)
Adds `vjs-v5` class so you can target that version of Video.js.
2017-05-09 14:18:01 -04:00
mister-ben 3fe22e93b0 feat: remove playbackRate blacklist for recent Android Chrome (#4336)
Android Chrome now supports playbackRate properly, so removes the blacklist added in #3246 for newer Chrome versions.
Adds `browser.CHROME_VERSION` as a necessary evil.
No longer blacklists for Chrome 58+ -- this could possibly be fixed since 52, but 58 is all I've been able to test on and most users should keep Chrome up to date.
2017-05-09 14:16:12 -04:00
Gary Katsevman eb05ff7f00 v5.19.2 2017-04-13 17:09:30 -04:00
Matthew Neil 207730ed6f fix: set IE_VERSION correctly for IE11 (#4280)
videojs.browser.IE_VERSION is null in IE11 because IE11 uses a different user agent string than other IE versions

Fixes #4278
2017-04-13 11:27:51 -04:00
Gary Katsevman 969d32551b v5.19.1 2017-03-27 15:29:19 -04:00
Matthew Neil ad770fb196 chore(package): update videojs-vtt.js to 0.12.3 (#4223) 2017-03-24 14:39:05 -04:00
Brandon Casey 3974944007 fix: removeCue should work with native passed in cue (#4209) 2017-03-23 14:32:01 -04:00
Brandon Casey 465392215c fix: not showing default text tracks over video (#4217)
This allows default tracks or tracks with a `mode`of `showing` to be seen
when they are added (as we would expect). Rather than only being seen
after the first `modechange` event (aka a selection from the menu). This is done by watching for `loadstart` on Player.
2017-03-23 14:30:50 -04:00
Gary Katsevman 6cf4ef8545 remove incorrect 5.19.0 changelog entry 2017-03-15 14:53:06 -04:00
Gary Katsevman c1ed22c39f fix CHANGELOG 2017-03-15 14:51:37 -04:00
Gary Katsevman aa7e94ba10 v5.19.0 2017-03-15 14:47:26 -04:00
Gary Katsevman c069655bc5 fix: make sure audio track hides with one item (#4203) 2017-03-15 11:27:59 -04:00
Gary Katsevman 398c6e9dd0 fix: make load progress buffered regions height 100% (#4191) 2017-03-13 11:53:01 -04:00
Brandon Casey 4ec3b56b45 feat: Make pause on open optional for ModalDialog via options (#4187)
* The new option is called pauseOnOpen, and defaults to true
* default pauseOnOpen to false in the `ErrorDisplay`
2017-03-13 11:27:00 -04:00
Gary Katsevman d2b4f1c5b9 v5.18.4 2017-03-08 11:29:38 -05:00
Gary Katsevman ad86eec723 fix(vttjs): wait till tech el in DOM before loading vttjs (#4176) 2017-03-08 11:27:44 -05:00
Gary Katsevman ed378986a5 v5.18.3 2017-03-06 12:14:42 -05:00
Gary Katsevman d2180b0ef8 v5.18.2 2017-03-03 17:20:50 -05:00
Gary Katsevman 841d135db8 fix(cues): only copy cue props that don't exist (#4146) 2017-03-03 17:07:28 -05:00
Gary Katsevman 5d91a878fb v5.18.1 2017-03-03 16:17:20 -05:00
Matthew Neil 2bcfe2156d chore(package): update video-js-swf to 5.3.0 (#4161) 2017-03-03 16:02:04 -05:00
Justin Anastos 4367c69752 fix(MenuButton): Unify behavior of showing/hiding (#3993)
Implement a `hideThreshold` property that defaults to 1 so
descendants can override it if necessary. Right now the only
descendant that will override will be `CaptionsButton` because
video.js adds a "captions settings" for emulated text tracks.
2017-03-02 15:24:17 -05:00
Gary Katsevman 072c27746f fix: make sure that cues copy over their id (#4154) 2017-03-02 15:22:48 -05:00
Gary Katsevman 288edd1473 fix(playback rate menu): playback rate menu items should be selectable (#4150) 2017-03-02 14:55:31 -05:00
Gary Katsevman b1ca34494d chore(build): lint errors only and silence webpack (#4153) 2017-03-02 14:48:13 -05:00
Brandon Casey e7d4b47744 fix: cue-points with a startTime of 0 (#4148)
Previously timeupdate would fire before the video was playing, and the tech was not ready. This caused issues when preload was set to auto, because cuechange would fire before the video was even started for cues with a startTime of 0.

Wait for tech to be ready before watching for timeupdate
update unit tests to use TechFaker
Add a unit test to verify that we wait for Tech to be ready.
2017-03-02 12:10:45 -05:00
Gary Katsevman de08669d6c fix(cues): only copy cue props that don't exist (#4146) 2017-03-02 11:16:32 -05:00
Gary Katsevman 5c3be8bf7e v5.18.0 2017-02-27 14:09:52 -05:00
Gary Katsevman d838cd8c33 v5.17.0 2017-02-27 14:07:04 -05:00
Gary Katsevman 3d4aebc633 feat: update videojs-vtt.js and wrap native cues in TextTrack (#4131)
Update videojs-vtt.js and don't auto-export its versions of VTTCue globally.
When our TextTrack object is given a cue, if it's a native cue, wrap it in our emulated vttjs.VTTCue.

Fixes #4093.
2017-02-27 13:51:18 -05:00
Gary Katsevman dcc615a28d feat: focus play toggle from Big Play Btn on play (#4132)
If the control bar and playtoggle exist, focus the playtoggle after
triggering play via the keyboard from the Big Play Button.
If the control bar isn't available, then focus the player element.
Adds focus() and blur() on components.

This is a port of #4018.

Fixes #2729 for the 5.x branch.
2017-02-27 12:09:49 -05:00
Gary Katsevman fa97309a78 fix: addChild instance names should be toTitleCased (#4117) 2017-02-22 14:00:17 -05:00
Brandon Casey b5727a62da fix: synchronously shim vtt.js when possible (#4082) 2017-02-21 12:03:36 -05:00
forbesjo ce19ed5294 fix: make mergeOptions behave the same across browsers (#4090)
* Make an object util function `keys` return an empty array for non objects
* Use that function in object utils instead of `Object.assign`
2017-02-16 16:55:37 -05:00
Brian Wells c02c6c63dd fix(sass): import path has cwd once again (#4076) 2017-02-15 15:44:17 -05:00
Gary Katsevman dab893bb5a chore(package): update swf to 5.2.0 (#4040) 2017-02-07 16:33:11 -05:00
Owen Edwards 91874a3f7e fix: remove title attribute on menu items, fixes #3699 (#4009) 2017-02-07 15:59:35 -05:00
Owen Edwards daad4923c4 chore: change accessibility test in grunt.js to remove unnecessary warning message. (#4008) 2017-02-07 15:59:15 -05:00
Pat O'Neill bca44c0f17 fix: Patch a memory leak caused by un-removed track listener(s). (#3975) 2017-02-07 15:58:25 -05:00
46 arquivos alterados com 1348 adições e 257 exclusões
+2
Ver Arquivo
@@ -13,6 +13,8 @@ notifications:
on_success: never
webhooks:
- http://pam.videojs.com/savage/travis
slack:
secure: LrF8K6mCYWlUt6SvdbGHazyQZSk/opKoiB/wgoGYaGc9+3wYXkVexY0WkO1m6wBKhUqXRAMVMFszr1wqKgdcxtItmFMMj8HqTLI1MVqgKqYX4Ux3CnEHJQiwxIk0aVL7lHLsZTXV/2Y0QIOYmAnCrgy46klETrk0ZuXf5okpu2Q=
env:
global:
- secure: K6JpKwMkfNaJix3Bb0tLjVMzHMJgtBXdd/dvfw1BMb9DCBpd81PqXbDs7yXCddUxnUPTBPxZCrQgWsw71Wn+qEoIG5MU3uOT5A2rBbx/yZonVAGv5ed/9w0xk0OzO383CmPMFqwqtp9YmdmqGjQBkYXVXJjTvNTOAExFSdhO+3U=
+153
Ver Arquivo
@@ -1,3 +1,156 @@
<a name="5.20.4"></a>
## [5.20.4](https://github.com/videojs/video.js/compare/v5.20.3...v5.20.4) (2017-10-02)
### Bug Fixes
* Make boolean attributes set and check both the associated property and the attribute ([#4631](https://github.com/videojs/video.js/issues/4631)) ([0f8de52](https://github.com/videojs/video.js/commit/0f8de52)), closes [#4351](https://github.com/videojs/video.js/issues/4351)
<a name="5.20.3"></a>
## [5.20.3](https://github.com/videojs/video.js/compare/v5.20.2...v5.20.3) (2017-08-16)
### Bug Fixes
* tracks are disabled and cuepoints are cleared in iOS native player ([#4569](https://github.com/videojs/video.js/issues/4569)) ([424d021](https://github.com/videojs/video.js/commit/424d021))
### Chores
* **CHANGELOG:** update CHANGELOG for 5.20.2 ([#4529](https://github.com/videojs/video.js/issues/4529)) ([935ec1e](https://github.com/videojs/video.js/commit/935ec1e))
<a name="5.20.2"></a>
## [5.20.2](https://github.com/videojs/video.js/compare/v5.20.1...v5.20.2) (2017-07-26)
### Bug Fixes
* adjust volume ranges so muted(true) and vol=0 do not use the same icons ([#4425](https://github.com/videojs/video.js/issues/4425)) ([b314268](https://github.com/videojs/video.js/commit/b314268))
* only change focus from BPB if not a mouse click ([#4523](https://github.com/videojs/video.js/issues/4523)) ([d90c945](https://github.com/videojs/video.js/commit/d90c945))
* Only update text track mode if changed ([#4368](https://github.com/videojs/video.js/issues/4368)) ([a1763dc](https://github.com/videojs/video.js/commit/a1763dc))
* player.duration() should return NaN if duration is not known ([#4456](https://github.com/videojs/video.js/issues/4456)) ([2576eda](https://github.com/videojs/video.js/commit/2576eda))
* Safari picture-in-picture triggers fullscreenchange ([#4442](https://github.com/videojs/video.js/issues/4442)) ([c17c003](https://github.com/videojs/video.js/commit/c17c003))
* Use passive event listeners for touchstart/touchmove ([#4445](https://github.com/videojs/video.js/issues/4445)) ([8599c8e](https://github.com/videojs/video.js/commit/8599c8e)), closes [#4432](https://github.com/videojs/video.js/issues/4432)
### Chores
* add a release script to makes things easier ([#4517](https://github.com/videojs/video.js/issues/4517)) ([9ca9989](https://github.com/videojs/video.js/commit/9ca9989))
* move imports up so bundlers wont trip up ([#4363](https://github.com/videojs/video.js/issues/4363)) ([0d725df](https://github.com/videojs/video.js/commit/0d725df))
* **package:** update to swf 5.4.1 ([#4516](https://github.com/videojs/video.js/issues/4516)) ([a145dd2](https://github.com/videojs/video.js/commit/a145dd2))
* **package:** update videojs-vtt.js to 0.12.4 ([#4364](https://github.com/videojs/video.js/issues/4364)) ([3f7e215](https://github.com/videojs/video.js/commit/3f7e215))
<a name="5.20.1"></a>
## [5.20.1](https://github.com/videojs/video.js/compare/v5.20.0...v5.20.1) (2017-05-15)
### Bug Fixes
* only disable user-selection on sliders ([#4355](https://github.com/videojs/video.js/issues/4355)) ([0d60720](https://github.com/videojs/video.js/commit/0d60720))
* prevent dupe events on enabled ClickableComponents ([#4357](https://github.com/videojs/video.js/issues/4357)) ([3faea9f](https://github.com/videojs/video.js/commit/3faea9f))
<a name="5.20.0"></a>
# [5.20.0](https://github.com/videojs/video.js/compare/v5.19.2...v5.20.0) (2017-05-11)
### Features
* add 'playsinline' player option ([#4325](https://github.com/videojs/video.js/issues/4325)) ([946f84b](https://github.com/videojs/video.js/commit/946f84b))
* Add a version class to the player ([#4335](https://github.com/videojs/video.js/issues/4335)) ([b855bfb](https://github.com/videojs/video.js/commit/b855bfb))
* Add getVideoPlaybackQuality API ([#4286](https://github.com/videojs/video.js/issues/4286)) ([c970474](https://github.com/videojs/video.js/commit/c970474))
* remove playbackRate blacklist for recent Android Chrome ([#4336](https://github.com/videojs/video.js/issues/4336)) ([3fe22e9](https://github.com/videojs/video.js/commit/3fe22e9))
### Chores
* add slack travis notifications ([#4339](https://github.com/videojs/video.js/issues/4339)) ([ab110ff](https://github.com/videojs/video.js/commit/ab110ff))
* **package:** pin karma to 1.3.0 for IE8 support ([#4340](https://github.com/videojs/video.js/issues/4340)) ([6cf7327](https://github.com/videojs/video.js/commit/6cf7327))
### Tests
* fix playsinline test for IE8 ([#4347](https://github.com/videojs/video.js/issues/4347)) ([823f6c7](https://github.com/videojs/video.js/commit/823f6c7))
<a name="5.19.2"></a>
## [5.19.2](https://github.com/videojs/video.js/compare/v5.19.1...v5.19.2) (2017-04-13)
### Bug Fixes
* set IE_VERSION correctly for IE11 ([#4280](https://github.com/videojs/video.js/issues/4280)) ([207730e](https://github.com/videojs/video.js/commit/207730e)), closes [#4278](https://github.com/videojs/video.js/issues/4278)
<a name="5.19.1"></a>
## [5.19.1](https://github.com/videojs/video.js/compare/v5.19.0...v5.19.1) (2017-03-27)
### Bug Fixes
* not showing default text tracks over video ([#4217](https://github.com/videojs/video.js/issues/4217)) ([4653922](https://github.com/videojs/video.js/commit/4653922))
* removeCue should work with native passed in cue ([#4209](https://github.com/videojs/video.js/issues/4209)) ([3974944](https://github.com/videojs/video.js/commit/3974944))
### Chores
* **package:** update videojs-vtt.js to 0.12.3 ([#4223](https://github.com/videojs/video.js/issues/4223)) ([ad770fb](https://github.com/videojs/video.js/commit/ad770fb))
<a name="5.19.0"></a>
# [5.19.0](https://github.com/videojs/video.js/compare/v5.18.4...v5.19.0) (2017-03-15)
### Features
* Make pause on open optional for ModalDialog via options ([#4187](https://github.com/videojs/video.js/issues/4187)) ([4ec3b56](https://github.com/videojs/video.js/commit/4ec3b56))
### Bug Fixes
* make load progress buffered regions height 100% ([#4191](https://github.com/videojs/video.js/issues/4191)) ([398c6e9](https://github.com/videojs/video.js/commit/398c6e9))
* make sure audio track hides with one item ([#4203](https://github.com/videojs/video.js/issues/4203)) ([c069655](https://github.com/videojs/video.js/commit/c069655))
<a name="5.18.4"></a>
## [5.18.4](https://github.com/videojs/video.js/compare/v5.18.3...v5.18.4) (2017-03-08)
### Bug Fixes
* **vttjs:** wait till tech el in DOM before loading vttjs ([#4176](https://github.com/videojs/video.js/issues/4176)) ([ad86eec](https://github.com/videojs/video.js/commit/ad86eec))
<a name="5.18.3"></a>
## [5.18.3](https://github.com/videojs/video.js/compare/v5.18.2...v5.18.3) (2017-03-06)
<a name="5.18.1"></a>
## [5.18.1](https://github.com/videojs/video.js/compare/v5.18.0...v5.18.1) (2017-03-03)
### Bug Fixes
* **cues:** only copy cue props that don't exist ([#4146](https://github.com/videojs/video.js/issues/4146)) ([de08669](https://github.com/videojs/video.js/commit/de08669))
* cue-points with a startTime of 0 ([#4148](https://github.com/videojs/video.js/issues/4148)) ([e7d4b47](https://github.com/videojs/video.js/commit/e7d4b47))
* make sure that cues copy over their id ([#4154](https://github.com/videojs/video.js/issues/4154)) ([072c277](https://github.com/videojs/video.js/commit/072c277))
* **MenuButton:** Unify behavior of showing/hiding ([#3993](https://github.com/videojs/video.js/issues/3993)) ([4367c69](https://github.com/videojs/video.js/commit/4367c69))
* **playback rate menu:** playback rate menu items should be selectable ([#4150](https://github.com/videojs/video.js/issues/4150)) ([288edd1](https://github.com/videojs/video.js/commit/288edd1))
### Chores
* **build:** lint errors only and silence webpack ([#4153](https://github.com/videojs/video.js/issues/4153)) ([b1ca344](https://github.com/videojs/video.js/commit/b1ca344))
* **package:** update video-js-swf to 5.3.0 ([#4161](https://github.com/videojs/video.js/issues/4161)) ([2bcfe21](https://github.com/videojs/video.js/commit/2bcfe21))
<a name="5.18.0"></a>
# [5.18.0](https://github.com/videojs/video.js/compare/v5.17.0...v5.18.0) (2017-02-27)
### Features
* focus play toggle from Big Play Btn on play ([#4132](https://github.com/videojs/video.js/issues/4132)) ([dcc615a](https://github.com/videojs/video.js/commit/dcc615a)), closes [#2729](https://github.com/videojs/video.js/issues/2729)
* update videojs-vtt.js and wrap native cues in TextTrack ([#4131](https://github.com/videojs/video.js/issues/4131)) ([3d4aebc](https://github.com/videojs/video.js/commit/3d4aebc)), closes [#4093](https://github.com/videojs/video.js/issues/4093)
### Bug Fixes
* **sass:** import path has cwd once again ([#4076](https://github.com/videojs/video.js/issues/4076)) ([c02c6c6](https://github.com/videojs/video.js/commit/c02c6c6))
* addChild instance names should be toTitleCased ([#4117](https://github.com/videojs/video.js/issues/4117)) ([fa97309](https://github.com/videojs/video.js/commit/fa97309))
* make mergeOptions behave the same across browsers ([#4090](https://github.com/videojs/video.js/issues/4090)) ([ce19ed5](https://github.com/videojs/video.js/commit/ce19ed5))
* synchronously shim vtt.js when possible ([#4082](https://github.com/videojs/video.js/issues/4082)) ([b5727a6](https://github.com/videojs/video.js/commit/b5727a6))
<a name="5.17.0"></a>
# [5.17.0](https://github.com/videojs/video.js/compare/v5.16.0...v5.17.0) (2017-02-07)
### Bug Fixes
* Patch a memory leak caused by un-removed track listener(s). ([#3975](https://github.com/videojs/video.js/issues/3975)) ([bca44c0](https://github.com/videojs/video.js/commit/bca44c0))
* remove title attribute on menu items, fixes [#3699](https://github.com/videojs/video.js/issues/3699) ([#4009](https://github.com/videojs/video.js/issues/4009)) ([91874a3](https://github.com/videojs/video.js/commit/91874a3))
### Chores
* change accessibility test in grunt.js to remove unnecessary warning message. ([#4008](https://github.com/videojs/video.js/issues/4008)) ([daad492](https://github.com/videojs/video.js/commit/daad492))
* **package:** update swf to 5.2.0 ([#4040](https://github.com/videojs/video.js/issues/4040)) ([dab893b](https://github.com/videojs/video.js/commit/dab893b))
### Documentation
* minor fix to currentTime() comment: "setting" not "getting" ([#3944](https://github.com/videojs/video.js/issues/3944)) ([6578ed9](https://github.com/videojs/video.js/commit/6578ed9))
<a name="5.16.0"></a>
# [5.16.0](https://github.com/videojs/video.js/compare/v5.15.1...v5.16.0) (2017-01-12)
Arquivo executável
+59
Ver Arquivo
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
echo "Release Type?"
read -rp "> " rtype
echo grunt version:${rtype}
grunt version:${rtype}
version=$(./build/bin/version)
echo New version is $version
# echo grunt chg-release:$version
# grunt chg-release:$version
npm run changelog
echo git commit -am "v$version"
git commit -am "v$version"
echo git checkout temp-release-branch
git checkout -b temp-release-branch
echo grunt dist
grunt dist
echo git add -f dist
git add -f dist
echo git commit -m "v$version dist"
git commit -m "v$version dist"
echo git tag -a "v$version" -m "v$version"
git tag -a "v$version" -m "v$version"
echo git show
git show
read -p "publish? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Nn]$ ]]
then
exit 0
fi
echo git push upstream
git push upstream --tags
git push origin --tags
echo npm publish --tag next-5
npm publish --tag next-5
echo grunt github-release:prereleease
grunt github-release:prerelease
echo git checkout -
git checkout -
echo git branch -D temp-release-branch
git branch -D temp-release-branch
+4 -4
Ver Arquivo
@@ -450,7 +450,7 @@ module.exports = function(grunt) {
}
},
lint: {
command: 'npm run lint',
command: 'npm run lint -- --errors',
options: {
preferLocal: true
}
@@ -468,7 +468,7 @@ module.exports = function(grunt) {
}
},
webpack: {
command: 'webpack test/require/webpack.js build/temp/webpack.js',
command: 'webpack --hide-modules test/require/webpack.js build/temp/webpack.js',
options: {
preferLocal: true
}
@@ -483,8 +483,8 @@ module.exports = function(grunt) {
error: true
},
ignore: [
// Ignore the warning about needing <optgroup> elements
'WCAG2AA.Principle1.Guideline1_3.1_3_1.H85.2'
// Ignore warning about contrast of the "vjs-no-js" fallback link
'WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.BgImage'
]
},
+1 -1
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "video.js",
"description": "An HTML5 and Flash video player with a common API and skin for both.",
"version": "5.16.0",
"version": "5.20.4",
"keywords": [
"videojs",
"html5",
+4 -4
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "video.js",
"description": "An HTML5 and Flash video player with a common API and skin for both.",
"version": "5.16.0",
"version": "5.20.4",
"main": "./es5/video.js",
"style": "./dist/video-js.css",
"copyright": "Copyright Brightcove, Inc. <https://www.brightcove.com/>",
@@ -41,8 +41,8 @@
"tsml": "1.0.1",
"videojs-font": "2.0.0",
"videojs-ie8": "1.1.2",
"videojs-swf": "5.1.0",
"videojs-vtt.js": "0.12.1",
"videojs-swf": "5.4.1",
"videojs-vtt.js": "0.12.4",
"xhr": "2.2.2"
},
"devDependencies": {
@@ -91,7 +91,7 @@
"grunt-zip": "0.17.1",
"istanbul": "^0.4.5",
"jsdoc": "^3.4.2",
"karma": "^1.2.0",
"karma": "1.3.0",
"karma-browserify": "^5.1.0",
"karma-browserstack-launcher": "^1.0.1",
"karma-chrome-launcher": "^2.0.0",
-2
Ver Arquivo
@@ -21,8 +21,6 @@
// Avoiding helvetica: issue #376
font-family: $text-font-family;
@include user-select(none);
// Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when
// checking fullScreenEnabled.
&:-moz-full-screen { position: absolute; }
+1 -1
Ver Arquivo
@@ -54,7 +54,7 @@
.video-js .vjs-progress-holder .vjs-load-progress div {
position: absolute;
display: block;
height: 0.3em;
height: 100%;
margin: 0;
padding: 0;
// updated by javascript during playback
+2
Ver Arquivo
@@ -5,6 +5,8 @@
padding: 0;
margin: 0 0.45em 0 0.45em;
@include user-select(none);
@include background-color-with-alpha($secondary-background-color, $secondary-background-transparency);
}
+41 -1
Ver Arquivo
@@ -11,6 +11,13 @@ import Component from './component.js';
* @extends Button
*/
class BigPlayButton extends Button {
constructor(player, options) {
super(player, options);
this.mouseused_ = false;
this.on('mousedown', this.handleMouseDown);
}
/**
* Builds the default DOM `className`.
@@ -34,7 +41,40 @@ class BigPlayButton extends Button {
* @listens click
*/
handleClick(event) {
this.player_.play();
const playPromise = this.player_.play();
// exit early if clicked via the mouse
if (this.mouseused_ && event.clientX && event.clientY) {
return;
}
const cb = this.player_.getChild('controlBar');
const playToggle = cb && cb.getChild('playToggle');
if (!playToggle) {
this.player_.focus();
return;
}
const playFocus = () => playToggle.focus();
if (playPromise && playPromise.then) {
const ignoreRejectedPlayPromise = () => {};
playPromise.then(playFocus, ignoreRejectedPlayPromise);
} else {
this.setTimeout(playFocus, 1);
}
}
handleKeyPress(event) {
this.mouseused_ = false;
super.handleKeyPress(event);
}
handleMouseDown(event) {
this.mouseused_ = true;
}
}
+10 -5
Ver Arquivo
@@ -121,7 +121,11 @@ class ClickableComponent extends Component {
this.controlText_ = text;
this.controlTextEl_.innerHTML = localizedText;
el.setAttribute('title', localizedText);
if (!this.nonIconControl) {
// Set title attribute if only an icon is shown
el.setAttribute('title', localizedText);
}
return this;
}
@@ -148,8 +152,10 @@ class ClickableComponent extends Component {
if (typeof this.tabIndex_ !== 'undefined') {
this.el_.setAttribute('tabIndex', this.tabIndex_);
}
this.on('tap', this.handleClick);
this.on('click', this.handleClick);
this.off(['tap', 'click'], this.handleClick);
this.off('focus', this.handleFocus);
this.off('blur', this.handleBlur);
this.on(['tap', 'click'], this.handleClick);
this.on('focus', this.handleFocus);
this.on('blur', this.handleBlur);
return this;
@@ -167,8 +173,7 @@ class ClickableComponent extends Component {
if (typeof this.tabIndex_ !== 'undefined') {
this.el_.removeAttribute('tabIndex');
}
this.off('tap', this.handleClick);
this.off('click', this.handleClick);
this.off(['tap', 'click'], this.handleClick);
this.off('focus', this.handleFocus);
this.off('blur', this.handleBlur);
return this;
+15 -1
Ver Arquivo
@@ -392,7 +392,7 @@ class Component {
// If a name wasn't used to create the component, check if we can use the
// name function of the component
componentName = componentName || (component.name && component.name());
componentName = componentName || (component.name && toTitleCase(component.name()));
if (componentName) {
this.childNameIndex_[componentName] = component;
@@ -1232,6 +1232,20 @@ class Component {
return this.currentDimension('height');
}
/**
* Set the focus to this component
*/
focus() {
this.el_.focus();
}
/**
* Remove the focus from this component
*/
blur() {
this.el_.blur();
}
/**
* Emit a 'tap' events when touch event support gets detected. This gets used to
* support toggling the controls through a tap on the video. They get enabled
@@ -49,6 +49,9 @@ class AudioTrackButton extends TrackButton {
* An array of menu items
*/
createItems(items = []) {
// if there's only one audio track, there no point in showing it
this.hideThreshold_ = 1;
const tracks = this.player_.audioTracks && this.player_.audioTracks();
if (!tracks) {
+1 -1
Ver Arquivo
@@ -81,7 +81,7 @@ class MuteToggle extends Button {
const vol = this.player_.volume();
let level = 3;
if (vol === 0 || this.player_.muted()) {
if (this.player_.muted()) {
level = 0;
} else if (vol < 0.33) {
level = 1;
@@ -27,6 +27,8 @@ class PlaybackRateMenuItem extends MenuItem {
// Modify options for parent MenuItem class's init.
options.label = label;
options.selected = rate === 1;
options.selectable = true;
super(player, options);
this.label = label;
@@ -39,33 +39,6 @@ class CaptionsButton extends TextTrackButton {
return `vjs-captions-button ${super.buildCSSClass()}`;
}
/**
* Update caption menu items
*
* @param {EventTarget~Event} [event]
* The `addtrack` or `removetrack` event that caused this function to be
* called.
*
* @listens TextTrackList#addtrack
* @listens TextTrackList#removetrack
*/
update(event) {
let threshold = 2;
super.update();
// if native, then threshold is 1 because no settings button
if (this.player().tech_ && this.player().tech_.featuresNativeTextTracks) {
threshold = 1;
}
if (this.items && this.items.length > threshold) {
this.show();
} else {
this.hide();
}
}
/**
* Create caption menu items
*
@@ -77,6 +50,8 @@ class CaptionsButton extends TextTrackButton {
if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.kind_}));
this.hideThreshold_ += 1;
}
return super.createItems(items);
@@ -40,6 +40,7 @@ class TextTrackButton extends TrackButton {
createItems(items = []) {
// Add an OFF menu item to turn all tracks off
items.push(new OffTextTrackMenuItem(this.player_, {kind: this.kind_}));
this.hideThreshold_ += 1;
const tracks = this.player_.textTracks();
@@ -38,6 +38,7 @@ class TextTrackMenuItem extends MenuItem {
if (tracks) {
const changeHandler = Fn.bind(this, this.handleTracksChange);
player.on(['loadstart', 'texttrackchange'], changeHandler);
tracks.addEventListener('change', changeHandler);
this.on('dispose', function() {
tracks.removeEventListener('change', changeHandler);
@@ -102,8 +103,10 @@ class TextTrackMenuItem extends MenuItem {
}
if (track === this.track) {
track.mode = 'showing';
} else {
if (track.mode !== 'showing') {
track.mode = 'showing';
}
} else if (track.mode !== 'disabled') {
track.mode = 'disabled';
}
}
+1
Ver Arquivo
@@ -58,6 +58,7 @@ class ErrorDisplay extends ModalDialog {
* @private
*/
ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
pauseOnOpen: false,
fillAlways: true,
temporary: false,
uncloseable: true
+14 -2
Ver Arquivo
@@ -58,9 +58,9 @@ class MenuButton extends ClickableComponent {
this.buttonPressed_ = false;
this.el_.setAttribute('aria-expanded', 'false');
if (this.items && this.items.length === 0) {
if (this.items && this.items.length <= this.hideThreshold_) {
this.hide();
} else if (this.items && this.items.length > 1) {
} else {
this.show();
}
}
@@ -74,6 +74,16 @@ class MenuButton extends ClickableComponent {
createMenu() {
const menu = new Menu(this.player_);
/**
* Hide the menu if the number of items is less than or equal to this threshold. This defaults
* to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
* it here because every time we run `createMenu` we need to reset the value.
*
* @protected
* @type {Number}
*/
this.hideThreshold_ = 0;
// Add a title list item to the top
if (this.options_.title) {
const title = Dom.createEl('li', {
@@ -82,6 +92,8 @@ class MenuButton extends ClickableComponent {
tabIndex: -1
});
this.hideThreshold_ += 1;
menu.children_.unshift(title);
Dom.insertElFirst(title, menu.contentEl());
}
+3
Ver Arquivo
@@ -54,6 +54,9 @@ class MenuItem extends ClickableComponent {
* The element that gets created.
*/
createEl(type, props, attrs) {
// The control is textual, not just an icon
this.nonIconControl = true;
return super.createEl('li', assign({
className: 'vjs-menu-item',
innerHTML: this.localize(this.options_.label),
+3 -2
Ver Arquivo
@@ -180,7 +180,7 @@ class ModalDialog extends Component {
// playing state.
this.wasPlaying_ = !player.paused();
if (this.wasPlaying_) {
if (this.options_.pauseOnOpen && this.wasPlaying_) {
player.pause();
}
@@ -243,7 +243,7 @@ class ModalDialog extends Component {
this.trigger('beforemodalclose');
this.opened_ = false;
if (this.wasPlaying_) {
if (this.wasPlaying_ && this.options_.pauseOnOpen) {
player.play();
}
@@ -427,6 +427,7 @@ class ModalDialog extends Component {
* @private
*/
ModalDialog.prototype.options_ = {
pauseOnOpen: true,
temporary: true
};
+51 -2
Ver Arquivo
@@ -413,6 +413,11 @@ class Player extends Component {
// Make player easily findable by ID
Player.players[this.id_] = this;
// Add a major version class to aid css in plugins
const majorVersion = require('../../package.json').version.split('.')[0];
this.addClass(`vjs-v${majorVersion}`);
// When the player is first initialized, trigger activity so components
// like the control bar show themselves if needed
this.userActive(true);
@@ -481,6 +486,9 @@ class Player extends Component {
el = this.el_ = super.createEl('div');
}
// set tabindex to -1 so we could focus on the player element
tag.setAttribute('tabindex', '-1');
// Remove width/height attrs from tag so CSS can make it 100% width/height
tag.removeAttribute('width');
tag.removeAttribute('height');
@@ -819,6 +827,7 @@ class Player extends Component {
'textTracks': this.textTracks_,
'audioTracks': this.audioTracks_,
'autoplay': this.options_.autoplay,
'playsinline': this.options_.playsinline,
'preload': this.options_.preload,
'loop': this.options_.loop,
'muted': this.options_.muted,
@@ -1685,10 +1694,11 @@ class Player extends Component {
*/
duration(seconds) {
if (seconds === undefined) {
return this.cache_.duration || 0;
// return NaN if the duration is not known
return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
}
seconds = parseFloat(seconds) || 0;
seconds = parseFloat(seconds);
// Standardize on Inifity for signaling video is live
if (seconds < 0) {
@@ -2377,6 +2387,31 @@ class Player extends Component {
return this.techGet_('autoplay', value);
}
/**
* Set or unset the playsinline attribute.
* Playsinline tells the browser that non-fullscreen playback is preferred.
*
* @param {boolean} [value]
* - true means that we should try to play inline by default
* - false means that we should use the browser's default playback mode,
* which in most cases is inline. iOS Safari is a notable exception
* and plays fullscreen by default.
*
* @return {string|Player}
* - the current value of playsinline
* - the player when setting
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
playsinline(value) {
if (value !== undefined) {
this.techCall_('setPlaysinline', value);
this.options_.playsinline = value;
return this;
}
return this.techGet_('playsinline');
}
/**
* Get or set the loop attribute on the video element.
*
@@ -2985,6 +3020,20 @@ class Player extends Component {
}
}
/**
* Gets available media playback quality metrics as specified by the W3C's Media
* Playback Quality API.
*
* @see [Spec]{@link https://wicg.github.io/media-playback-quality}
*
* @return {Object|undefined}
* An object with supported media playback quality metrics or undefined if there
* is no tech or the tech does not support it.
*/
getVideoPlaybackQuality() {
return this.techGet_('getVideoPlaybackQuality');
}
/**
* Get video width
*
+23
Ver Arquivo
@@ -341,6 +341,29 @@ class Flash extends Tech {
return false;
}
/**
* Gets available media playback quality metrics as specified by the W3C's Media
* Playback Quality API.
*
* @see [Spec]{@link https://wicg.github.io/media-playback-quality}
*
* @return {Object}
* An object with supported media playback quality metrics
*/
getVideoPlaybackQuality() {
const videoPlaybackQuality = this.el_.vjs_getProperty('getVideoPlaybackQuality');
if (window.performance && typeof window.performance.now === 'function') {
videoPlaybackQuality.creationTime = window.performance.now();
} else if (window.performance &&
window.performance.timing &&
typeof window.performance.timing.navigationStart === 'number') {
videoPlaybackQuality.creationTime =
window.Date.now() - window.performance.timing.navigationStart;
}
return videoPlaybackQuality;
}
}
// Create setters and getters for attributes
+316 -125
Ver Arquivo
@@ -131,6 +131,9 @@ class Html5 extends Tech {
this.proxyNativeTextTracks_();
}
// prevent iOS Safari from disabling metadata text tracks during native playback
this.restoreMetadataTracksInIOSNativePlayer_();
// Determine if native controls should be used
// Our goal should be to get the custom controls on mobile solid everywhere
// so we can remove this all together. Right now this will block custom
@@ -173,6 +176,72 @@ class Html5 extends Tech {
super.dispose();
}
/**
* When a captions track is enabled in the iOS Safari native player, all other
* tracks are disabled (including metadata tracks), which nulls all of their
* associated cue points. This will restore metadata tracks to their pre-fullscreen
* state in those cases so that cue points are not needlessly lost.
*
* @private
*/
restoreMetadataTracksInIOSNativePlayer_() {
const textTracks = this.textTracks();
let metadataTracksPreFullscreenState;
// captures a snapshot of every metadata track's current state
const takeMetadataTrackSnapshot = () => {
metadataTracksPreFullscreenState = [];
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'metadata') {
metadataTracksPreFullscreenState.push({
track,
storedMode: track.mode
});
}
}
};
// snapshot each metadata track's initial state, and update the snapshot
// each time there is a track 'change' event
takeMetadataTrackSnapshot();
textTracks.addEventListener('change', takeMetadataTrackSnapshot);
const restoreTrackMode = () => {
for (let i = 0; i < metadataTracksPreFullscreenState.length; i++) {
const storedTrack = metadataTracksPreFullscreenState[i];
if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
storedTrack.track.mode = storedTrack.storedMode;
}
}
// we only want this handler to be executed on the first 'change' event
textTracks.removeEventListener('change', restoreTrackMode);
};
// when we enter fullscreen playback, stop updating the snapshot and
// restore all track modes to their pre-fullscreen state
this.on('webkitbeginfullscreen', () => {
textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
// remove the listener before adding it just in case it wasn't previously removed
textTracks.removeEventListener('change', restoreTrackMode);
textTracks.addEventListener('change', restoreTrackMode);
});
// start updating the snapshot again after leaving fullscreen
this.on('webkitendfullscreen', () => {
// remove the listener before adding it just in case it wasn't previously removed
textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
textTracks.addEventListener('change', takeMetadataTrackSnapshot);
// remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
textTracks.removeEventListener('change', restoreTrackMode);
});
}
/**
* Create the `Html5` Tech's DOM element.
*
@@ -222,17 +291,27 @@ class Html5 extends Tech {
el.playerId = this.options_.playerId;
}
if (typeof this.options_.preload !== 'undefined') {
Dom.setAttribute(el, 'preload', this.options_.preload);
}
// Update specific tag settings, in case they were overridden
const settingsAttrs = ['autoplay', 'preload', 'loop', 'muted'];
// `autoplay` has to be *last* so that `muted` and `playsinline` are present
// when iOS/Safari or other browsers attempt to autoplay.
const settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
for (let i = settingsAttrs.length - 1; i >= 0; i--) {
const attr = settingsAttrs[i];
const overwriteAttrs = {};
const value = this.options_[attr];
if (typeof this.options_[attr] !== 'undefined') {
overwriteAttrs[attr] = this.options_[attr];
if (typeof value !== 'undefined') {
if (value) {
Dom.setAttribute(el, attr, attr);
} else {
Dom.removeAttribute(el, attr);
}
el[attr] = value;
}
Dom.setElAttributes(el, overwriteAttrs);
}
return el;
@@ -557,9 +636,12 @@ class Html5 extends Tech {
};
const beginFn = function() {
this.one('webkitendfullscreen', endFn);
if ('webkitPresentationMode' in this.el_ &&
this.el_.webkitPresentationMode !== 'picture-in-picture') {
this.one('webkitendfullscreen', endFn);
this.trigger('fullscreenchange', { isFullscreen: true });
this.trigger('fullscreenchange', { isFullscreen: true });
}
};
this.on('webkitbeginfullscreen', beginFn);
@@ -794,6 +876,40 @@ class Html5 extends Tech {
}
}
}
/**
* Gets available media playback quality metrics as specified by the W3C's Media
* Playback Quality API.
*
* @see [Spec]{@link https://wicg.github.io/media-playback-quality}
*
* @return {Object}
* An object with supported media playback quality metrics
*/
getVideoPlaybackQuality() {
if (typeof this.el().getVideoPlaybackQuality === 'function') {
return this.el().getVideoPlaybackQuality();
}
const videoPlaybackQuality = {};
if (typeof this.el().webkitDroppedFrameCount !== 'undefined' &&
typeof this.el().webkitDecodedFrameCount !== 'undefined') {
videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
}
if (window.performance && typeof window.performance.now === 'function') {
videoPlaybackQuality.creationTime = window.performance.now();
} else if (window.performance &&
window.performance.timing &&
typeof window.performance.timing.navigationStart === 'number') {
videoPlaybackQuality.creationTime =
window.Date.now() - window.performance.timing.navigationStart;
}
return videoPlaybackQuality;
}
}
/* HTML5 Support Testing ---------------------------------------------------- */
@@ -865,7 +981,7 @@ Html5.canControlVolume = function() {
Html5.canControlPlaybackRate = function() {
// Playback rate API is implemented in Android Chrome, but doesn't do anything
// https://github.com/videojs/video.js/issues/3180
if (browser.IS_ANDROID && browser.IS_CHROME) {
if (browser.IS_ANDROID && browser.IS_CHROME && browser.CHROME_VERSION < 58) {
return false;
}
// IE will error if Windows Media Player not installed #3315
@@ -1122,7 +1238,195 @@ Html5.resetMediaElement = function(el) {
};
/* Native HTML5 element property wrapping ----------------------------------- */
// Wrap native boolean attributes with getters that check both property and attribute
// The list is as followed:
// muted, defaultMuted, autoplay, controls, loop, playsinline
[
/**
* Get the value of `muted` from the media element. `muted` indicates
* that the volume for the media should be set to silent. This does not actually change
* the `volume` attribute.
*
* @method Html5#muted
* @return {boolean}
* - True if the value of `volume` should be ignored and the audio set to silent.
* - False if the value of `volume` should be used.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'muted',
/**
* Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
* whether the media should start muted or not. Only changes the default state of the
* media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
* current state.
*
* @method Html5#defaultMuted
* @return {boolean}
* - The value of `defaultMuted` from the media element.
* - True indicates that the media should start muted.
* - False indicates that the media should not start muted
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'defaultMuted',
/**
* Get the value of `autoplay` from the media element. `autoplay` indicates
* that the media should start to play as soon as the page is ready.
*
* @method Html5#autoplay
* @return {boolean}
* - The value of `autoplay` from the media element.
* - True indicates that the media should start as soon as the page loads.
* - False indicates that the media should not start as soon as the page loads.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Get the value of `controls` from the media element. `controls` indicates
* whether the native media controls should be shown or hidden.
*
* @method Html5#controls
* @return {boolean}
* - The value of `controls` from the media element.
* - True indicates that native controls should be showing.
* - False indicates that native controls should be hidden.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
*/
'controls',
/**
* Get the value of `loop` from the media element. `loop` indicates
* that the media should return to the start of the media and continue playing once
* it reaches the end.
*
* @method Html5#loop
* @return {boolean}
* - The value of `loop` from the media element.
* - True indicates that playback should seek back to start once
* the end of a media is reached.
* - False indicates that playback should not loop back to the start when the
* end of the media is reached.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop',
/**
* Get the value of `playsinline` from the media element. `playsinline` indicates
* to the browser that non-fullscreen playback is preferred when fullscreen
* playback is the native default, such as in iOS Safari.
*
* @method Html5#playsinline
* @return {boolean}
* - The value of `playsinline` from the media element.
* - True indicates that the media should play inline.
* - False indicates that the media should not play inline.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
'playsinline'
].forEach(function(prop) {
Html5.prototype[prop] = function() {
return this.el_[prop] || this.el_.hasAttribute(prop);
};
});
// Wrap native boolean attributes with setters that set both property and attribute
// The list is as followed:
// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
// setControls is special-cased above
[
/**
* Set the value of `muted` on the media element. `muted` indicates that the current
* audio level should be silent.
*
* @method Html5#setMuted
* @param {boolean} muted
* - True if the audio should be set to silent
* - False otherwise
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'muted',
/**
* Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
* audio level should be silent, but will only effect the muted level on intial playback..
*
* @method Html5.prototype.setDefaultMuted
* @param {boolean} defaultMuted
* - True if the audio should be set to silent
* - False otherwise
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'defaultMuted',
/**
* Set the value of `autoplay` on the media element. `autoplay` indicates
* that the media should start to play as soon as the page is ready.
*
* @method Html5#setAutoplay
* @param {boolean} autoplay
* - True indicates that the media should start as soon as the page loads.
* - False indicates that the media should not start as soon as the page loads.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Set the value of `loop` on the media element. `loop` indicates
* that the media should return to the start of the media and continue playing once
* it reaches the end.
*
* @method Html5#setLoop
* @param {boolean} loop
* - True indicates that playback should seek back to start once
* the end of a media is reached.
* - False indicates that playback should not loop back to the start when the
* end of the media is reached.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop',
/**
* Set the value of `playsinline` from the media element. `playsinline` indicates
* to the browser that non-fullscreen playback is preferred when fullscreen
* playback is the native default, such as in iOS Safari.
*
* @method Html5#setPlaysinline
* @param {boolean} playsinline
* - True indicates that the media should play inline.
* - False indicates that the media should not play inline.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
'playsinline'
].forEach(function(prop) {
Html5.prototype['set' + toTitleCase(prop)] = function(v) {
this.el_[prop] = v;
if (v) {
this.el_.setAttribute(prop, prop);
} else {
this.el_.removeAttribute(prop);
}
};
});
// Wrap native properties with a getter
// The list is as followed
// paused, currentTime, buffered, volume, poster, preload, error, seeking
// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
// readyState, videoWidth, videoHeight
[
/**
* Get the value of `paused` from the media element. `paused` indicates whether the media element
@@ -1174,20 +1478,6 @@ Html5.resetMediaElement = function(el) {
*/
'volume',
/**
* Get the value of `muted` from the media element. `muted` indicates
* that the volume for the media should be set to silent. This does not actually change
* the `volume` attribute.
*
* @method Html5#muted
* @return {boolean}
* - True if the value of `volume` should be ignored and the audio set to silent.
* - False if the value of `volume` should be used.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'muted',
/**
* Get the value of `poster` from the media element. `poster` indicates
* that the url of an image file that can/will be shown when no media data is available.
@@ -1220,51 +1510,6 @@ Html5.resetMediaElement = function(el) {
*/
'preload',
/**
* Get the value of `autoplay` from the media element. `autoplay` indicates
* that the media should start to play as soon as the page is ready.
*
* @method Html5#autoplay
* @return {boolean}
* - The value of `autoplay` from the media element.
* - True indicates that the media should start as soon as the page loads.
* - False indicates that the media should not start as soon as the page loads.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Get the value of `controls` from the media element. `controls` indicates
* whether the native media controls should be shown or hidden.
*
* @method Html5#controls
* @return {boolean}
* - The value of `controls` from the media element.
* - True indicates that native controls should be showing.
* - False indicates that native controls should be hidden.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
*/
'controls',
/**
* Get the value of `loop` from the media element. `loop` indicates
* that the media should return to the start of the media and continue playing once
* it reaches the end.
*
* @method Html5#loop
* @return {boolean}
* - The value of `loop` from the media element.
* - True indicates that playback should seek back to start once
* the end of a media is reached.
* - False indicates that playback should not loop back to the start when the
* end of the media is reached.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop',
/**
* Get the value of the `error` from the media element. `error` indicates any
* MediaError that may have occured during playback. If error returns null there is no
@@ -1320,22 +1565,6 @@ Html5.resetMediaElement = function(el) {
*/
'ended',
/**
* Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
* whether the media should start muted or not. Only changes the default state of the
* media. `muted` and `defaultMuted` can have different values. `muted` indicates the
* current state.
*
* @method Html5#defaultMuted
* @return {boolean}
* - The value of `defaultMuted` from the media element.
* - True indicates that the media should start muted.
* - False indicates that the media should not start muted
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'defaultMuted',
/**
* Get the value of `playbackRate` from the media element. `playbackRate` indicates
* the rate at which the media is currently playing back. Examples:
@@ -1433,6 +1662,8 @@ Html5.resetMediaElement = function(el) {
// Wrap native properties with a setter in this format:
// set + toTitleCase(name)
// The list is as follows:
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
[
/**
* Set the value of `volume` on the media element. `volume` indicates the current
@@ -1447,19 +1678,6 @@ Html5.resetMediaElement = function(el) {
*/
'volume',
/**
* Set the value of `muted` on the media element. `muted` indicates the current
* audio level should be silent.
*
* @method Html5#setMuted
* @param {boolean} muted
* - True if the audio should be set to silent
* - False otherwise
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'muted',
/**
* Set the value of `src` on the media element. `src` indicates the current
* {@link Tech~SourceObject} for the media.
@@ -1504,35 +1722,6 @@ Html5.resetMediaElement = function(el) {
*/
'preload',
/**
* Set the value of `autoplay` on the media element. `autoplay` indicates
* that the media should start to play as soon as the page is ready.
*
* @method Html5#setAutoplay
* @param {boolean} autoplay
* - True indicates that the media should start as soon as the page loads.
* - False indicates that the media should not start as soon as the page loads.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Set the value of `loop` on the media element. `loop` indicates
* that the media should return to the start of the media and continue playing once
* it reaches the end.
*
* @method Html5#setLoop
* @param {boolean} loop
* - True indicates that playback should seek back to start once
* the end of a media is reached.
* - False indicates that playback should not loop back to the start when the
* end of the media is reached.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop',
/**
* Set the value of `playbackRate` on the media element. `playbackRate` indicates
* the rate at which the media should play back. Examples:
@@ -1554,6 +1743,8 @@ Html5.resetMediaElement = function(el) {
});
// wrap native functions with a function
// The list is as follows:
// pause, load play
[
/**
* A wrapper around the media elements `pause` function. This will call the `HTML5`
+60 -15
Ver Arquivo
@@ -529,16 +529,20 @@ class Tech extends Component {
* @fires Tech#vttjserror
*/
addWebVttScript_() {
if (!window.WebVTT && this.el().parentNode !== null && this.el().parentNode !== undefined) {
if (window.WebVTT) {
return;
}
// Initially, Tech.el_ is a child of a dummy-div wait until the Component system
// signals that the Tech is ready at which point Tech.el_ is part of the DOM
// before inserting the WebVTT script
if (document.body.contains(this.el())) {
const vtt = require('videojs-vtt.js');
// load via require if available and vtt.js script location was not passed in
// as an option. novtt builds will turn the above require call into an empty object
// which will cause this if check to always fail.
if (!this.options_['vtt.js'] && isPlain(vtt) && Object.keys(vtt).length > 0) {
Object.keys(vtt).forEach(function(k) {
window[k] = vtt[k];
});
this.trigger('vttjsloaded');
return;
}
@@ -547,7 +551,7 @@ class Tech extends Component {
// passed in
const script = document.createElement('script');
script.src = this.options_['vtt.js'] || 'https://cdn.rawgit.com/gkatsev/vtt.js/vjs-v0.12.1/dist/vtt.min.js';
script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
script.onload = () => {
/**
* Fired when vtt.js is loaded.
@@ -574,7 +578,10 @@ class Tech extends Component {
// we don't overwrite the injected window.WebVTT if it loads right away
window.WebVTT = true;
this.el().parentNode.appendChild(script);
} else {
this.ready(this.addWebVttScript_);
}
}
/**
@@ -589,20 +596,17 @@ class Tech extends Component {
return;
}
this.remoteTextTracks().on('addtrack', (e) => {
this.textTracks().addTrack_(e.track);
});
const remoteTracks = this.remoteTextTracks();
const handleAddTrack = (e) => tracks.addTrack_(e.track);
const handleRemoveTrack = (e) => tracks.removeTrack_(e.track);
this.remoteTextTracks().on('removetrack', (e) => {
this.textTracks().removeTrack_(e.track);
});
remoteTracks.on('addtrack', handleAddTrack);
remoteTracks.on('removetrack', handleRemoveTrack);
// Initially, Tech.el_ is a child of a dummy-div wait until the Component system
// signals that the Tech is ready at which point Tech.el_ is part of the DOM
// before inserting the WebVTT script
this.on('ready', this.addWebVttScript_);
this.addWebVttScript_();
const updateDisplay = () => this.trigger('texttrackchange');
const textTracksChanges = () => {
updateDisplay();
@@ -618,9 +622,21 @@ class Tech extends Component {
textTracksChanges();
tracks.addEventListener('change', textTracksChanges);
tracks.addEventListener('addtrack', textTracksChanges);
tracks.addEventListener('removetrack', textTracksChanges);
this.on('dispose', function() {
remoteTracks.off('addtrack', handleAddTrack);
remoteTracks.off('removetrack', handleRemoveTrack);
tracks.removeEventListener('change', textTracksChanges);
tracks.removeEventListener('addtrack', textTracksChanges);
tracks.removeEventListener('removetrack', textTracksChanges);
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];
track.removeEventListener('cuechange', updateDisplay);
}
});
}
@@ -789,6 +805,21 @@ class Tech extends Component {
this.autoRemoteTextTracks_.removeTrack_(track);
}
/**
* Gets available media playback quality metrics as specified by the W3C's Media
* Playback Quality API.
*
* @see [Spec]{@link https://wicg.github.io/media-playback-quality}
*
* @return {Object}
* An object with supported media playback quality metrics
*
* @abstract
*/
getVideoPlaybackQuality() {
return {};
}
/**
* A method to set a poster from a `Tech`.
*
@@ -796,6 +827,20 @@ class Tech extends Component {
*/
setPoster() {}
/**
* A method to check for the presence of the 'playsinine' <video> attribute.
*
* @abstract
*/
playsinline() {}
/**
* A method to set or unset the 'playsinine' <video> attribute.
*
* @abstract
*/
setPlaysinline() {}
/*
* Check if the tech can support the given mime-type.
*
+28 -11
Ver Arquivo
@@ -198,7 +198,9 @@ class TextTrack extends Track {
});
if (mode !== 'disabled') {
tt.tech_.on('timeupdate', timeupdateHandler);
tt.tech_.ready(() => {
tt.tech_.on('timeupdate', timeupdateHandler);
}, true);
}
/**
@@ -232,7 +234,9 @@ class TextTrack extends Track {
}
mode = newMode;
if (mode === 'showing') {
this.tech_.on('timeupdate', timeupdateHandler);
this.tech_.ready(() => {
this.tech_.on('timeupdate', timeupdateHandler);
}, true);
}
/**
* An event that fires when mode changes on this track. This allows
@@ -328,7 +332,23 @@ class TextTrack extends Track {
* @param {TextTrack~Cue} cue
* The cue to add to our internal list
*/
addCue(cue) {
addCue(originalCue) {
let cue = originalCue;
if (window.vttjs && !(originalCue instanceof window.vttjs.VTTCue)) {
cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
for (const prop in originalCue) {
if (!(prop in cue)) {
cue[prop] = originalCue[prop];
}
}
// make sure that `id` is copied over
cue.id = originalCue.id;
cue.originalCue_ = originalCue;
}
const tracks = this.tech_.textTracks();
if (tracks) {
@@ -350,20 +370,17 @@ class TextTrack extends Track {
* The cue to remove from our internal list
*/
removeCue(removeCue) {
let removed = false;
let i = this.cues_.length;
for (let i = 0, l = this.cues_.length; i < l; i++) {
while (i--) {
const cue = this.cues_[i];
if (cue === removeCue) {
if (cue === removeCue || (cue.originalCue_ && cue.originalCue_ === removeCue)) {
this.cues_.splice(i, 1);
removed = true;
this.cues.setCues_(this.cues_);
break;
}
}
if (removed) {
this.cues.setCues_(this.cues_);
}
}
}
+19 -3
Ver Arquivo
@@ -62,10 +62,26 @@ export const IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebki
export const IS_FIREFOX = (/Firefox/i).test(USER_AGENT);
export const IS_EDGE = (/Edge/i).test(USER_AGENT);
export const IS_CHROME = !IS_EDGE && (/Chrome/i).test(USER_AGENT);
export const CHROME_VERSION = (function() {
const match = USER_AGENT.match(/Chrome\/(\d+)/);
if (match && match[1]) {
return parseFloat(match[1]);
}
return null;
}());
export const IS_IE8 = (/MSIE\s8\.0/).test(USER_AGENT);
export const IE_VERSION = (function(result) {
return result && parseFloat(result[1]);
}((/MSIE\s(\d+)\.\d/).exec(USER_AGENT)));
export const IE_VERSION = (function() {
const result = (/MSIE\s(\d+)\.\d/).exec(USER_AGENT);
let version = result && parseFloat(result[1]);
if (!version && (/Trident\/7.0/i).test(USER_AGENT) && (/rv:11.0/).test(USER_AGENT)) {
// IE 11 has a different user agent string than other IE versions
version = 11.0;
}
return version;
}());
export const IS_SAFARI = (/Safari/i).test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
export const IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
+1 -1
Ver Arquivo
@@ -485,7 +485,7 @@ export function getElAttributes(tag) {
// known boolean attributes
// we can check for matching boolean properties, but older browsers
// won't know about HTML5 boolean attributes that we still read from
const knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ',';
const knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
if (tag && tag.attributes && tag.attributes.length > 0) {
const attrs = tag.attributes;
+34 -1
Ver Arquivo
@@ -202,6 +202,33 @@ export function fixEvent(event) {
return event;
}
/**
* Whether passive event listeners are supported
*/
let _supportsPassive = false;
(function() {
try {
const opts = Object.defineProperty({}, 'passive', {
get() {
_supportsPassive = true;
}
});
window.addEventListener('test', null, opts);
} catch (e) {
// disregard
}
})();
/**
* Touch events Chrome expects to be passive
*/
const passiveEvents = [
'touchstart',
'touchmove'
];
/**
* Add an event listener to element
* It stores the handler function in a separate cache object
@@ -273,7 +300,13 @@ export function on(elem, type, fn) {
if (data.handlers[type].length === 1) {
if (elem.addEventListener) {
elem.addEventListener(type, data.dispatcher, false);
let options = false;
if (_supportsPassive &&
passiveEvents.indexOf(type) > -1) {
options = {passive: true};
}
elem.addEventListener(type, data.dispatcher, options);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, data.dispatcher);
}
+18 -2
Ver Arquivo
@@ -30,6 +30,22 @@
*/
const toString = Object.prototype.toString;
/**
* Get the keys of an Object
*
* @param {Object}
* The Object to get the keys from
*
* @return {string[]}
* An array of the keys from the object. Returns an empty array if the
* object passed in was invalid or had no keys.
*
* @private
*/
const keys = function(object) {
return isObject(object) ? Object.keys(object) : [];
};
/**
* Array-like iteration for objects.
*
@@ -40,7 +56,7 @@ const toString = Object.prototype.toString;
* The callback function which is called for each key in the object.
*/
export function each(object, fn) {
Object.keys(object).forEach(key => fn(object[key], key));
keys(object).forEach(key => fn(object[key], key));
}
/**
@@ -61,7 +77,7 @@ export function each(object, fn) {
* The final accumulated value.
*/
export function reduce(object, fn, initial = 0) {
return Object.keys(object).reduce(
return keys(object).reduce(
(accum, key) => fn(accum, object[key], key), initial);
}
+2 -2
Ver Arquivo
@@ -7,6 +7,8 @@
import window from 'global/window';
import document from 'global/document';
import * as browser from './utils/browser.js';
import * as Dom from './utils/dom.js';
import * as setup from './setup';
import * as stylesheet from './utils/stylesheet.js';
import Component from './component';
@@ -23,8 +25,6 @@ import VideoTrack from './tracks/video-track.js';
import { createTimeRanges } from './utils/time-ranges.js';
import formatTime from './utils/format-time.js';
import log from './utils/log.js';
import * as Dom from './utils/dom.js';
import * as browser from './utils/browser.js';
import * as Url from './utils/url.js';
import {isObject} from './utils/obj';
import computedStyle from './utils/computed-style.js';
+1
Ver Arquivo
@@ -59,6 +59,7 @@ QUnit.test('should be able to access expected player API methods', function(asse
assert.ok(player.userActive, 'userActive exists');
assert.ok(player.usingNativeControls, 'usingNativeControls exists');
assert.ok(player.isFullscreen, 'isFullscreen exists');
assert.ok(player.getVideoPlaybackQuality, 'getVideoPlaybackQuality exists');
// Track methods
assert.ok(player.audioTracks, 'audioTracks exists');
+22
Ver Arquivo
@@ -71,3 +71,25 @@ QUnit.test('handleClick should not be triggered when disabled', function() {
testClickableComponent.dispose();
player.dispose();
});
QUnit.test('handleClick should not be triggered more than once when enabled', function() {
let clicks = 0;
class TestClickableComponent extends ClickableComponent {
handleClick() {
clicks++;
}
}
const player = TestHelpers.makePlayer({});
const testClickableComponent = new TestClickableComponent(player);
const el = testClickableComponent.el();
testClickableComponent.enable();
// Click should still be handled just once
Events.trigger(el, 'click');
QUnit.equal(clicks, 1, 'no additional click handler when already enabled ClickableComponent has been enabled again');
testClickableComponent.dispose();
player.dispose();
});
+13
Ver Arquivo
@@ -99,6 +99,19 @@ QUnit.test('addChild should throw if the child does not exist', function(assert)
});
QUnit.test('addChild with instance should allow getting child correctly', function(assert) {
const comp = new Component(getFakePlayer());
const comp2 = new Component(getFakePlayer());
comp2.name = function() {
return 'foo';
};
comp.addChild(comp2);
assert.ok(comp.getChild('foo'), 'we can get child with camelCase');
assert.ok(comp.getChild('Foo'), 'we can get child with TitleCase');
});
QUnit.test('should add a child component with title case name', function(assert) {
const comp = new Component(getFakePlayer());
+29
Ver Arquivo
@@ -208,6 +208,35 @@ QUnit.test('open() pauses playback, close() resumes', function(assert) {
assert.strictEqual(playSpy.callCount, 1, 'player is resumed when the modal closes');
});
QUnit.test('open() does not pause, close() does not play() with pauseOnOpen set to false', function(assert) {
const playSpy = sinon.spy();
const pauseSpy = sinon.spy();
// don't pause the video on modal open
this.modal.options_.pauseOnOpen = false;
// Quick and dirty; make it looks like the player is playing.
this.player.paused = function() {
return false;
};
this.player.play = function() {
playSpy();
};
this.player.pause = function() {
pauseSpy();
};
this.modal.open();
assert.expect(2);
assert.strictEqual(pauseSpy.callCount, 0, 'player remains playing when the modal opens');
this.modal.close();
assert.strictEqual(playSpy.callCount, 0, 'player is resumed when the modal closes');
});
QUnit.test('open() hides controls, close() shows controls', function(assert) {
this.modal.open();
+66
Ver Arquivo
@@ -830,6 +830,35 @@ QUnit.test('should restore attributes from the original video tag when creating
assert.equal(el.getAttribute('webkit-playsinline'), '', 'webkit-playsinline attribute was set properly');
});
if (Html5.isSupported()) {
QUnit.test('player.playsinline() should be able to get/set playsinline attribute', function(assert) {
assert.expect(5);
const video = document.createElement('video');
const player = TestHelpers.makePlayer({techOrder: ['html5']}, video);
// test setter
assert.ok(!player.tech_.el().hasAttribute('playsinline'), 'playsinline has not yet been added');
player.playsinline(true);
assert.ok(player.tech_.el().hasAttribute('playsinline'), 'playsinline attribute added');
player.playsinline(false);
assert.ok(!player.tech_.el().hasAttribute('playsinline'), 'playsinline attribute removed');
// test getter
player.tech_.el().setAttribute('playsinline', 'playsinline');
assert.ok(player.playsinline(), 'correctly detects playsinline attribute');
player.tech_.el().removeAttribute('playsinline');
assert.ok(!player.playsinline(), 'correctly detects absence of playsinline attribute');
});
}
QUnit.test('if tag exists and movingMediaElementInDOM, re-use the tag', function(assert) {
// simulate attributes stored from the original tag
const tag = Dom.createEl('video');
@@ -1387,3 +1416,40 @@ QUnit.test('should not allow to register custom player when any player has been
// reset the Player to the original value;
videojs.registerComponent('Player', Player);
});
QUnit.test('should add a class with major version', function(assert) {
const majorVersion = require('../../package.json').version.split('.')[0];
const player = TestHelpers.makePlayer();
assert.ok(player.hasClass('vjs-v' + majorVersion), 'the version class should be added to the player');
player.dispose();
});
QUnit.test('player.duration() returns NaN if player.cache_.duration is undefined', function(assert) {
const player = TestHelpers.makePlayer();
player.cache_.duration = undefined;
assert.ok(Number.isNaN(player.duration()), 'returned NaN for unkown duration');
});
QUnit.test('player.duration() returns player.cache_.duration if it is defined', function(assert) {
const player = TestHelpers.makePlayer();
player.cache_.duration = 200;
assert.equal(player.duration(), 200, 'returned correct integer duration');
player.cache_.duration = 942;
assert.equal(player.duration(), 942, 'returned correct integer duration');
});
QUnit.test('player.duration() sets the value of player.cache_.duration', function(assert) {
const player = TestHelpers.makePlayer();
// set an arbitrary initial cached duration value for testing the setter functionality
player.cache_.duration = 1;
player.duration(NaN);
assert.ok(Number.isNaN(player.duration()), 'duration() set and get NaN duration value');
player.duration(200);
assert.equal(player.duration(), 200, 'duration() set and get integer duration value');
});
+2 -1
Ver Arquivo
@@ -7,12 +7,13 @@ QUnit.test('should set options from data-setup even if autoSetup is not called b
const el = TestHelpers.makeTag();
el.setAttribute('data-setup',
'{"controls": true, "autoplay": false, "preload": "auto"}');
'{"controls": true, "autoplay": false, "preload": "auto", "playsinline": true}');
const player = TestHelpers.makePlayer({}, el);
assert.ok(player.options_.controls === true);
assert.ok(player.options_.autoplay === false);
assert.ok(player.options_.preload === 'auto');
assert.ok(player.options_.playsinline === true);
player.dispose();
});
+76
Ver Arquivo
@@ -2,6 +2,7 @@
import Flash from '../../../src/js/tech/flash.js';
import { createTimeRange } from '../../../src/js/utils/time-ranges.js';
import document from 'global/document';
import window from 'global/window';
import sinon from 'sinon';
// fake out the <object> interaction but leave all the other logic intact
@@ -261,3 +262,78 @@ QUnit.test('duration returns NaN, Infinity or duration according to the HTML sta
'duration returns duration property when readyState' +
' and duration property are both higher than 0');
});
QUnit.test('getVideoPlaybackQuality API exists', function(assert) {
const propertyCalls = [];
const videoPlaybackQuality = { test: 'test' };
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_getProperty(attr) {
propertyCalls.push(attr);
return videoPlaybackQuality;
}
/* eslint-enable camelcase */
}
};
assert.deepEqual(Flash.prototype.getVideoPlaybackQuality.call(mockFlash),
videoPlaybackQuality,
'called to get property from flash');
assert.equal(propertyCalls.length, 1, 'only one property call');
assert.equal(propertyCalls[0],
'getVideoPlaybackQuality',
'called for getVideoPlaybackQuality');
});
QUnit.test('getVideoPlaybackQuality uses best available creationTime', function(assert) {
const origPerformance = window.performance;
const origDate = window.Date;
const videoPlaybackQuality = {};
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_getProperty(attr) {
return videoPlaybackQuality;
}
/* eslint-enable camelcase */
}
};
window.performance = void 0;
assert.notOk(Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
'no creationTime when no performance API available');
window.performance = {
timing: {}
};
assert.notOk(Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
'no creationTime when performance API insufficient');
window.performance = {
now: () => 4
};
assert.equal(Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
4,
'creationTime is performance.now when available');
window.Date = {
now: () => 10
};
window.performance = {
timing: {
navigationStart: 3
}
};
assert.equal(Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
7,
'creationTime uses Date.now() - navigationStart when available');
window.performance.now = () => 4;
assert.equal(Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
4,
'creationTime prioritizes performance.now when available');
window.Date = origDate;
window.performance = origPerformance;
});
+119
Ver Arquivo
@@ -50,6 +50,19 @@ QUnit.module('HTML5', {
}
});
QUnit.test('should be able to set playsinline attribute', function(assert) {
assert.expect(2);
tech.createEl();
tech.setPlaysinline(true);
assert.ok(tech.el().hasAttribute('playsinline'), 'playsinline attribute was added');
tech.setPlaysinline(false);
assert.ok(!tech.el().hasAttribute('playsinline'), 'playsinline attribute was removed');
});
QUnit.test('should detect whether the volume can be changed', function(assert) {
if (!{}.__defineSetter__) {
@@ -84,6 +97,32 @@ QUnit.test('test playbackRate', function(assert) {
assert.strictEqual(tech.playbackRate(), 0.75);
});
QUnit.test('blacklist playbackRate support on older verisons of Chrome on Android', function(assert) {
if (!Html5.canControlPlaybackRate()) {
assert.ok(true, 'playbackRate is not supported');
return;
}
// Reset playbackrate - Firefox's rounding of playbackRate causes the rate not to change in canControlPlaybackRate() after a few instances
Html5.TEST_VID.playbackRate = 1;
const oldIsAndroid = browser.IS_ANDROID;
const oldIsChrome = browser.IS_CHROME;
const oldChromeVersion = browser.CHROME_VERSION;
browser.IS_ANDROID = true;
browser.IS_CHROME = true;
browser.CHROME_VERSION = 50;
assert.strictEqual(Html5.canControlPlaybackRate(), false, 'canControlPlaybackRate should return false on older Chrome');
browser.CHROME_VERSION = 58;
assert.strictEqual(Html5.canControlPlaybackRate(), true, 'canControlPlaybackRate should return true on newer Chrome');
browser.IS_ANDROID = oldIsAndroid;
browser.IS_CHROME = oldIsChrome;
browser.CHROME_VERSION = oldChromeVersion;
});
QUnit.test('should export played', function(assert) {
tech.createEl();
assert.deepEqual(tech.played(), tech.el().played, 'returns the played attribute');
@@ -641,3 +680,83 @@ test('When Android Chrome reports Infinity duration with currentTime 0, return N
browser.IS_CHROME = oldIsChrome;
tech.el_ = oldEl;
});
QUnit.test('supports getting available media playback quality metrics', function(assert) {
const origPerformance = window.performance;
const origDate = window.Date;
const oldEl = tech.el_;
const videoPlaybackQuality = {
creationTime: 1,
corruptedVideoFrames: 2,
droppedVideoFrames: 3,
totalVideoFrames: 5
};
tech.el_ = {
getVideoPlaybackQuality: () => videoPlaybackQuality
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
videoPlaybackQuality,
'uses native implementation when supported');
tech.el_ = {
webkitDroppedFrameCount: 1,
webkitDecodedFrameCount: 2
};
window.performance = {
now: () => 4
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
{ droppedVideoFrames: 1, totalVideoFrames: 2, creationTime: 4 },
'uses webkit prefixed metrics and performance.now when supported');
tech.el_ = {
webkitDroppedFrameCount: 1,
webkitDecodedFrameCount: 2
};
window.Date = {
now: () => 10
};
window.performance = {
timing: {
navigationStart: 3
}
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
{ droppedVideoFrames: 1, totalVideoFrames: 2, creationTime: 7 },
'uses webkit prefixed metrics and Date.now() - navigationStart when ' +
'supported');
tech.el_ = {};
window.performance = void 0;
assert.deepEqual(tech.getVideoPlaybackQuality(), {}, 'empty object when not supported');
window.performance = {
now: () => 5
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
{ creationTime: 5 },
'only creation time when it\'s the only piece available');
window.performance = {
timing: {
navigationStart: 3
}
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
{ creationTime: 7 },
'only creation time when it\'s the only piece available');
tech.el_ = {
getVideoPlaybackQuality: () => videoPlaybackQuality,
webkitDroppedFrameCount: 1,
webkitDecodedFrameCount: 2
};
assert.deepEqual(tech.getVideoPlaybackQuality(),
videoPlaybackQuality,
'prefers native implementation when supported');
tech.el_ = oldEl;
window.performance = origPerformance;
window.Date = origDate;
});
+4 -1
Ver Arquivo
@@ -9,7 +9,10 @@ class TechFaker extends Tech {
constructor(options, handleReady) {
super(options, handleReady);
this.triggerReady();
if (!options || options.autoReady !== false) {
this.triggerReady();
}
}
createEl() {
+6
Ver Arquivo
@@ -631,3 +631,9 @@ QUnit.test('setSource after previous setSource should dispose source handler onc
});
QUnit.test('returns an empty object for getVideoPlaybackQuality', function(assert) {
const tech = new Tech();
assert.deepEqual(tech.getVideoPlaybackQuality(), {}, 'returns an empty object');
});
+12 -10
Ver Arquivo
@@ -1,14 +1,16 @@
/* eslint-env qunit */
import HTMLTrackElement from '../../../src/js/tracks/html-track-element.js';
import TechFaker from '../tech/tech-faker';
const defaultTech = {
textTracks() {},
on() {},
off() {},
currentTime() {}
};
QUnit.module('HTML Track Element');
QUnit.module('HTML Track Element', {
beforeEach() {
this.tech = new TechFaker();
},
afterEach() {
this.tech.dispose();
this.tech = null;
}
});
QUnit.test('html track element requires a tech', function(assert) {
assert.throws(
@@ -31,7 +33,7 @@ QUnit.test('can create a html track element with various properties', function(a
label,
language,
src,
tech: defaultTech
tech: this.tech
});
assert.equal(typeof htmlTrackElement.default, 'undefined', 'we have a default');
@@ -45,7 +47,7 @@ QUnit.test('can create a html track element with various properties', function(a
QUnit.test('defaults when items not provided', function(assert) {
const htmlTrackElement = new HTMLTrackElement({
tech: defaultTech
tech: this.tech
});
assert.equal(typeof htmlTrackElement.default, 'undefined', 'we have a default');
+2 -5
Ver Arquivo
@@ -2,6 +2,7 @@
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
import TextTrack from '../../../src/js/tracks/text-track.js';
import EventTarget from '../../../src/js/event-target.js';
import TechFaker from '../tech/tech-faker.js';
QUnit.module('Text Track List');
QUnit.test('trigger "change" event when "modechange" is fired on a track', function(assert) {
@@ -23,11 +24,7 @@ QUnit.test('trigger "change" event when "modechange" is fired on a track', funct
});
QUnit.test('trigger "change" event when mode changes on a TextTrack', function(assert) {
const tt = new TextTrack({
tech: {
on() {}
}
});
const tt = new TextTrack({tech: new TechFaker()});
const ttl = new TextTrackList([tt]);
let changes = 0;
const changeHandler = function() {
+110 -25
Ver Arquivo
@@ -9,14 +9,16 @@ import proxyquireify from 'proxyquireify';
import sinon from 'sinon';
const proxyquire = proxyquireify(require);
const defaultTech = {
textTracks() {},
on() {},
off() {},
currentTime() {}
};
QUnit.module('Text Track');
QUnit.module('Text Track', {
beforeEach() {
this.tech = new TechFaker();
},
afterEach() {
this.tech.dispose();
this.tech = null;
}
});
// do baseline track testing
TrackBaseline(TextTrack, {
@@ -25,7 +27,7 @@ TrackBaseline(TextTrack, {
mode: 'disabled',
label: 'English',
language: 'en',
tech: defaultTech
tech: new TechFaker()
});
QUnit.test('requires a tech', function(assert) {
@@ -39,7 +41,7 @@ QUnit.test('can create a TextTrack with a mode property', function(assert) {
const mode = 'disabled';
const tt = new TextTrack({
mode,
tech: defaultTech
tech: this.tech
});
assert.equal(tt.mode, mode, 'we have a mode');
@@ -47,7 +49,7 @@ QUnit.test('can create a TextTrack with a mode property', function(assert) {
QUnit.test('defaults when items not provided', function(assert) {
const tt = new TextTrack({
tech: TechFaker
tech: this.tech
});
assert.equal(tt.kind, 'subtitles', 'kind defaulted to subtitles');
@@ -58,7 +60,7 @@ QUnit.test('defaults when items not provided', function(assert) {
QUnit.test('kind can only be one of several options, defaults to subtitles', function(assert) {
let tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'foo'
});
@@ -66,35 +68,35 @@ QUnit.test('kind can only be one of several options, defaults to subtitles', fun
assert.notEqual(tt.kind, 'foo', 'the kind is set to subtitles, not foo');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'subtitles'
});
assert.equal(tt.kind, 'subtitles', 'the kind is set to subtitles');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'captions'
});
assert.equal(tt.kind, 'captions', 'the kind is set to captions');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'descriptions'
});
assert.equal(tt.kind, 'descriptions', 'the kind is set to descriptions');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'chapters'
});
assert.equal(tt.kind, 'chapters', 'the kind is set to chapters');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
kind: 'metadata'
});
@@ -103,7 +105,7 @@ QUnit.test('kind can only be one of several options, defaults to subtitles', fun
QUnit.test('mode can only be one of several options, defaults to disabled', function(assert) {
let tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
mode: 'foo'
});
@@ -111,21 +113,21 @@ QUnit.test('mode can only be one of several options, defaults to disabled', func
assert.notEqual(tt.mode, 'foo', 'the mode is set to disabld, not foo');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
mode: 'disabled'
});
assert.equal(tt.mode, 'disabled', 'the mode is set to disabled');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
mode: 'hidden'
});
assert.equal(tt.mode, 'hidden', 'the mode is set to hidden');
tt = new TextTrack({
tech: defaultTech,
tech: this.tech,
mode: 'showing'
});
@@ -136,7 +138,7 @@ QUnit.test('cue and activeCues are read only', function(assert) {
const mode = 'disabled';
const tt = new TextTrack({
mode,
tech: defaultTech
tech: this.tech
});
tt.cues = 'foo';
@@ -148,7 +150,7 @@ QUnit.test('cue and activeCues are read only', function(assert) {
QUnit.test('mode can only be set to a few options', function(assert) {
const tt = new TextTrack({
tech: defaultTech
tech: this.tech
});
tt.mode = 'foo';
@@ -173,7 +175,7 @@ QUnit.test('mode can only be set to a few options', function(assert) {
QUnit.test('cues and activeCues return a TextTrackCueList', function(assert) {
const tt = new TextTrack({
tech: defaultTech
tech: this.tech
});
assert.ok(tt.cues.getCueById, 'cues are a TextTrackCueList');
@@ -182,7 +184,7 @@ QUnit.test('cues and activeCues return a TextTrackCueList', function(assert) {
QUnit.test('cues can be added and removed from a TextTrack', function(assert) {
const tt = new TextTrack({
tech: defaultTech
tech: this.tech
});
const cues = tt.cues;
@@ -203,6 +205,46 @@ QUnit.test('cues can be added and removed from a TextTrack', function(assert) {
assert.equal(cues.length, 3, 'we now have 3 cues');
});
QUnit.test('original cue can be used to remove cue from cues list', function(assert) {
const tt = new TextTrack({
tech: this.tech
});
const Cue = window.VTTCue ||
window.vttjs && window.vttjs.VTTCue ||
window.TextTrackCue;
const cue1 = new Cue(0, 1, 'some-cue');
assert.equal(tt.cues.length, 0, 'start with zero cues');
tt.addCue(cue1);
assert.equal(tt.cues.length, 1, 'we have one cue');
tt.removeCue(cue1);
assert.equal(tt.cues.length, 0, 'we have removed cue1');
});
QUnit.test('can only remove one cue at a time', function(assert) {
const tt = new TextTrack({
tech: this.tech
});
const Cue = window.VTTCue ||
window.vttjs && window.vttjs.VTTCue ||
window.TextTrackCue;
const cue1 = new Cue(0, 1, 'some-cue');
assert.equal(tt.cues.length, 0, 'start with zero cues');
tt.addCue(cue1);
tt.addCue(cue1);
assert.equal(tt.cues.length, 2, 'we have two cues');
tt.removeCue(cue1);
assert.equal(tt.cues.length, 1, 'we have removed one instance of cue1');
tt.removeCue(cue1);
assert.equal(tt.cues.length, 0, 'we have removed the other instance of cue1');
});
QUnit.test('fires cuechange when cues become active and inactive', function(assert) {
const player = TestHelpers.makePlayer();
let changes = 0;
@@ -242,6 +284,49 @@ QUnit.test('fires cuechange when cues become active and inactive', function(asse
player.dispose();
});
QUnit.test('does not fire cuechange before Tech is ready', function(assert) {
const done = assert.async();
const player = TestHelpers.makePlayer({techfaker: {autoReady: false}});
let changes = 0;
const tt = new TextTrack({
tech: player.tech_,
mode: 'showing'
});
const cuechangeHandler = function() {
changes++;
};
tt.addCue({
id: '1',
startTime: 0,
endTime: 5
});
tt.oncuechange = cuechangeHandler;
tt.addEventListener('cuechange', cuechangeHandler);
player.tech_.currentTime = function() {
return 0;
};
player.tech_.trigger('timeupdate');
assert.equal(changes, 0, 'a cuechange event is not triggered');
player.tech_.on('ready', function() {
player.tech_.currentTime = function() {
return 0.2;
};
player.tech_.trigger('timeupdate');
assert.equal(changes, 2, 'a cuechange event trigger addEventListener and oncuechange');
player.dispose();
done();
});
player.tech_.triggerReady();
});
QUnit.test('tracks are parsed if vttjs is loaded', function(assert) {
const clock = sinon.useFakeTimers();
const oldVTT = window.WebVTT;
@@ -270,7 +355,7 @@ QUnit.test('tracks are parsed if vttjs is loaded', function(assert) {
/* eslint-disable no-unused-vars */
const tt = new TextTrack_({
tech: defaultTech,
tech: this.tech,
src: 'http://example.com'
});
/* eslint-enable no-unused-vars */
+7
Ver Arquivo
@@ -2,6 +2,7 @@
import mergeOptions from '../../../src/js/utils/merge-options.js';
QUnit.module('merge-options');
QUnit.test('should merge options objects', function(assert) {
const ob1 = {
a: true,
@@ -27,3 +28,9 @@ QUnit.test('should merge options objects', function(assert) {
d: true
}, 'options objects merged correctly');
});
QUnit.test('should ignore non-objects', function(assert) {
const obj = { a: 1 };
assert.deepEqual(mergeOptions(obj, true), obj, 'ignored non-object input');
});