Comparar commits

...

25 Commits

Autor SHA1 Mensagem Data
Gary Katsevman 8f67b4fc54 6.1.0 2017-05-15 14:35:23 -04:00
Gary Katsevman cb6005eca4 fix: only disable user-selection on sliders (#4354) 2017-05-15 12:04:42 -04:00
Alex Barstow 8d80a5846e feat: add 'playsinline' player option (#4348)
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-12 16:39:37 -04:00
Gary Katsevman 35df35143f feat: deprecate firstplay event (#4353) 2017-05-12 16:38:48 -04:00
Martin Bachwerk 3087830ed7 fix: Only update text track mode if changed (#4298) 2017-05-12 16:35:39 -04:00
Garrett 483e5a2ca5 feat: Add getVideoPlaybackQuality API (#4338)
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-12 15:22:02 -04:00
Gary Katsevman 3dcfa9568a fix: TextTrackButton on Safari and iOS (#4350)
On Safari and iOS, we use native text tracks, so, during text track
button creation, textTracks() is populated. However, `this.kinds_` isn't
necessarily set and ends up throwing.

Instead, we want to move the fallback for `this.kinds_` from the
constructor and right above the usage of `this.kinds_`.
2017-05-12 14:51:12 -04:00
greenkeeper[bot] b5c60f339b fix(package): update global to version 4.3.2 (#4291)
https://greenkeeper.io/
2017-05-11 17:52:30 -04:00
mister-ben 03bab83dde fix: prevent dupe events on enabled ClickableComponents (#4316)
Prevent ClickableComponents re-adding event listeners each time enabled() is called.

* Keeps track of enabled state (this.enabled_)
* enable() doesn't do anything if the component is enabled, so the event handlers are not re-added

Fixes #4312
2017-05-11 17:32:20 -04:00
Owen Edwards f773c473f9 chore: Fix examples and docs and some links (#4279)
* Fix the doc/example/elephantsdream/index.html file, add an index of doc/examples, and update CDN links to use video.js v5.19.
* Add poster from CDN to Elephants Dream examples.
2017-05-11 17:16:12 -04:00
mister-ben ae423df4f5 feat: Add a version class to the player (#4320)
Adds `vjs-v6` class so you can target that version of Video.js.
2017-05-11 17:15:12 -04:00
Pat O'Neill 0a19cf0d6a feat: Add 'beforepluginsetup' event and named plugin setup events (e.g. 'pluginsetup:foo') (#4255)
This adds a beforepluginsetup event as well as beforepluginsetup:$name and pluginsetup:$name events.

The drive behind this is improving the ability for people to make cross-plugin dependencies in a more robust manner.
2017-05-11 17:13:22 -04:00
Stéphane Roucheray da1d8613d7 chore: typo soruce -> source (#4307) 2017-05-11 17:00:59 -04:00
mister-ben da0f1ee681 feat: remove playbackRate blacklist for recent Android Chrome (#4321)
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-11 16:59:22 -04:00
Dave Kiss cff2e503ef docs(react-guide): Use a React component as a VJS component (#4287) 2017-05-11 16:33:28 -04:00
Brandon Casey 561d5ddf76 fix a comment typo (#4293) 2017-04-19 23:56:07 -04:00
Gary Katsevman d6e56e9222 6.0.1 2017-04-13 16:58:11 -04:00
Gary Katsevman 7490a499ce chore: add slack travis notifications (#4282) 2017-04-13 12:23:31 -04:00
Matthew Neil 1ea00419c9 fix: set IE_VERSION correctly for IE11 (#4281)
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:28:03 -04:00
Aaron Chamberlain 230743ecb1 docs: add a Webpack usage guide (#4261) 2017-04-12 18:16:41 -04:00
Garrett 39fd73f781 docs: remove mentions of bower support (#4274)
Since video.js 6 no longer officially supports bower (#4012), remove some old mentions in the docs of bower.
2017-04-12 18:15:54 -04:00
Gary Katsevman 92e5d9fb5a fix: techOrder names can be camelCased. (#4277)
In the new middleware work, the way that new sources were loaded was refactored. We also recently made techs and components work either TitleCased or camelcased. There was one comparison that didn't do the proper check and cause the tech to be reloaded, even if the two techs were the same.
2017-04-12 17:17:33 -04:00
Dennis JongHyuck Won 083f643e63 chore: gitignore all npm-debug.log.* (#4252) 2017-04-07 13:41:30 -04:00
Alex Barstow 02721c7c03 docs(component): Replace VolumeMenuButton with VolumePanel in component tree (#4267)
Video.js 6 has switch to using the VolumePanel instead of the VolumeMenuButton.

Fixes #4266
2017-04-07 13:39:57 -04:00
Gary Katsevman c20ca5cc97 chore(changelog): Update CHANGELOG with v5 changes (#4257) 2017-04-07 13:38:39 -04:00
37 arquivos alterados com 926 adições e 142 exclusões
+1 -1
Ver Arquivo
@@ -10,7 +10,7 @@ test/*.map
.s3config.json
node_modules
npm-debug.log
npm-debug.log*
sandbox/*
!sandbox/*.example
+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=
+131
Ver Arquivo
@@ -1,3 +1,52 @@
<a name="6.1.0"></a>
# [6.1.0](https://github.com/videojs/video.js/compare/v6.0.1...v6.1.0) (2017-05-15)
### Features
* Add 'beforepluginsetup' event and named plugin setup events (e.g. 'pluginsetup:foo') ([#4255](https://github.com/videojs/video.js/issues/4255)) ([0a19cf0](https://github.com/videojs/video.js/commit/0a19cf0))
* add 'playsinline' player option ([#4348](https://github.com/videojs/video.js/issues/4348)) ([8d80a58](https://github.com/videojs/video.js/commit/8d80a58))
* Add a version class to the player ([#4320](https://github.com/videojs/video.js/issues/4320)) ([ae423df](https://github.com/videojs/video.js/commit/ae423df))
* Add getVideoPlaybackQuality API ([#4338](https://github.com/videojs/video.js/issues/4338)) ([483e5a2](https://github.com/videojs/video.js/commit/483e5a2))
* deprecate firstplay event ([#4353](https://github.com/videojs/video.js/issues/4353)) ([35df351](https://github.com/videojs/video.js/commit/35df351))
* remove playbackRate blacklist for recent Android Chrome ([#4321](https://github.com/videojs/video.js/issues/4321)) ([da0f1ee](https://github.com/videojs/video.js/commit/da0f1ee))
### Bug Fixes
* **package:** update global to version 4.3.2 ([#4291](https://github.com/videojs/video.js/issues/4291)) ([b5c60f3](https://github.com/videojs/video.js/commit/b5c60f3))
* only disable user-selection on sliders ([#4354](https://github.com/videojs/video.js/issues/4354)) ([cb6005e](https://github.com/videojs/video.js/commit/cb6005e))
* Only update text track mode if changed ([#4298](https://github.com/videojs/video.js/issues/4298)) ([3087830](https://github.com/videojs/video.js/commit/3087830))
* prevent dupe events on enabled ClickableComponents ([#4316](https://github.com/videojs/video.js/issues/4316)) ([03bab83](https://github.com/videojs/video.js/commit/03bab83)), closes [#4312](https://github.com/videojs/video.js/issues/4312)
* TextTrackButton on Safari and iOS ([#4350](https://github.com/videojs/video.js/issues/4350)) ([3dcfa95](https://github.com/videojs/video.js/commit/3dcfa95))
### Chores
* Fix examples and docs and some links ([#4279](https://github.com/videojs/video.js/issues/4279)) ([f773c47](https://github.com/videojs/video.js/commit/f773c47))
* typo soruce -> source ([#4307](https://github.com/videojs/video.js/issues/4307)) ([da1d861](https://github.com/videojs/video.js/commit/da1d861))
### Documentation
* **react-guide:** Use a React component as a VJS component ([#4287](https://github.com/videojs/video.js/issues/4287)) ([cff2e50](https://github.com/videojs/video.js/commit/cff2e50))
<a name="6.0.1"></a>
## [6.0.1](https://github.com/videojs/video.js/compare/v6.0.0...v6.0.1) (2017-04-13)
### Bug Fixes
* set IE_VERSION correctly for IE11 ([#4281](https://github.com/videojs/video.js/issues/4281)) ([1ea0041](https://github.com/videojs/video.js/commit/1ea0041)), closes [#4278](https://github.com/videojs/video.js/issues/4278)
* techOrder names can be camelCased. ([#4277](https://github.com/videojs/video.js/issues/4277)) ([92e5d9f](https://github.com/videojs/video.js/commit/92e5d9f))
### Chores
* **changelog:** Update CHANGELOG with v5 changes ([#4257](https://github.com/videojs/video.js/issues/4257)) ([c20ca5c](https://github.com/videojs/video.js/commit/c20ca5c))
* add slack travis notifications ([#4282](https://github.com/videojs/video.js/issues/4282)) ([7490a49](https://github.com/videojs/video.js/commit/7490a49))
* gitignore all npm-debug.log.* ([#4252](https://github.com/videojs/video.js/issues/4252)) ([083f643](https://github.com/videojs/video.js/commit/083f643))
### Documentation
* **component:** Replace VolumeMenuButton with VolumePanel in component tree ([#4267](https://github.com/videojs/video.js/issues/4267)) ([02721c7](https://github.com/videojs/video.js/commit/02721c7)), closes [#4266](https://github.com/videojs/video.js/issues/4266)
* add a Webpack usage guide ([#4261](https://github.com/videojs/video.js/issues/4261)) ([230743e](https://github.com/videojs/video.js/commit/230743e))
* remove mentions of bower support ([#4274](https://github.com/videojs/video.js/issues/4274)) ([39fd73f](https://github.com/videojs/video.js/commit/39fd73f))
<a name="6.0.0"></a>
# [6.0.0](https://github.com/videojs/video.js/compare/v5.16.0...v6.0.0) (2017-04-03)
@@ -183,6 +232,88 @@
* button component will always use a button element.
* `play()` no longer returns the player object but instead the native Promise or nothing.
<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)
+2 -1
Ver Arquivo
@@ -261,7 +261,8 @@ git reset --hard upstream/master
Releasing video.js is partially automated through [`conrib.json`](/contrib.json) scripts. To do a release, you need a couple of things: npm access, GitHub personal access token.
Releases in video.js are done on npm and bower and GitHub and eventually posted on the CDN. This is the instruction for the npm/bower/GitHub releases.
Releases in video.js are done on npm and GitHub and eventually posted on the CDN. These
are the instructions for the npm/GitHub releases.
When we do a release, we release it as a `next` tag on npm first and then at least a week later, we promote this release to `latest` on npm.
+3 -3
Ver Arquivo
@@ -22,11 +22,11 @@
Thanks to the awesome folks over at [Fastly][fastly], there's a free, CDN hosted version of Video.js that anyone can use. Add these tags to your document's `<head>`:
```html
<link href="//vjs.zencdn.net/5.11/video-js.min.css" rel="stylesheet">
<script src="//vjs.zencdn.net/5.11/video.min.js"></script>
<link href="//vjs.zencdn.net/5.19/video-js.min.css" rel="stylesheet">
<script src="//vjs.zencdn.net/5.19/video.min.js"></script>
```
> For the latest URLs, check out the [Getting Started][getting-started] page on our website.
> For the latest version of video.js and URLs to use, check out the [Getting Started][getting-started] page on our website.
Next, using Video.js is as simple as creating a `<video>` element, but with an additional `data-setup` attribute. At a minimum, this attribute must have a value of `'{}'`, but it can include any Video.js [options][options] - just make sure it contains valid JSON!
+11 -15
Ver Arquivo
@@ -1,32 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Video.js Text Descriptions, Chapters &amp; Captions Example</title>
<link href="../../video-js.css" rel="stylesheet" type="text/css">
<script src="../../video.js"></script>
<!-- Set the location of the flash SWF -->
<script>
videojs.setGlobalOptions({
flash: {
swf: '../../video-js.swf'
}
});
</script>
<link href="http://vjs.zencdn.net/5.19/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/ie8/1.1/videojs-ie8.min.js"></script>
<script src="http://vjs.zencdn.net/5.19/video.js"></script>
</head>
<body>
<p style="background-color:#eee; border: 1px solid #777; padding: 10px; font-size: .8em; line-height: 1.5em; font-family: Verdana, sans-serif;">This page demonstrates a text descriptions track (intended primarily for blind and visually impaired consumers of visual media)</p>
<!-- NOTE: we have to disable native Text Track support for the HTML5 tech,
since even HTML5 video players with native Text Track support
don't currently support 'description' text tracks in any
useful way! -->
useful way! Currently this means that iOS will not display
ANY text tracks -->
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="360"
data-setup='{ "html5" : { "nativeTextTracks" : false } }'>
data-setup='{ "html5" : { "nativeTextTracks" : false } }'
poster="http://d2zihajmogu5jn.cloudfront.net/elephantsdream/poster.png">
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.mp4" type="video/mp4">
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.ogg" type="video/ogg">
@@ -42,5 +36,7 @@
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
</body>
</html>
+18
Ver Arquivo
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Index of video.js examples</title>
</head>
<body>
<h1>Index of video.js examples</h1>
<ul>
<li><a href="simple-embed">Video.js HTML5 video player simple example</a></li>
<li><a href="elephantsdream">Elephants Dream video with text descriptions, chapters &amp; captions example</a></li>
</ul>
</body>
</html>
+4 -5
Ver Arquivo
@@ -1,15 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Video.js | HTML5 Video Player</title>
<link href="http://vjs.zencdn.net/5.0.2/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/ie8/1.1.0/videojs-ie8.min.js"></script>
<script src="http://vjs.zencdn.net/5.0.2/video.js"></script>
<link href="http://vjs.zencdn.net/5.19/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/ie8/1.1/videojs-ie8.min.js"></script>
<script src="http://vjs.zencdn.net/5.19/video.js"></script>
</head>
<body>
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264" poster="http://vjs.zencdn.net/v/oceans.png" data-setup="{}">
@@ -22,6 +20,7 @@
<!-- Tracks need an ending tag thanks to IE9 -->
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
</body>
</html>
+1 -1
Ver Arquivo
@@ -292,7 +292,7 @@ Player
├── BigPlayButton
├─┬ ControlBar
│ ├── PlayToggle
│ ├── VolumeMenuButton
│ ├── VolumePanel
│ ├── CurrentTimeDisplay (hidden by default)
│ ├── TimeDivider (hidden by default)
│ ├── DurationDisplay (hidden by default)
+52 -49
Ver Arquivo
@@ -59,13 +59,15 @@ video.js is an extendable framework/library around the native video element. It
## Q: How do I install video.js?
Currently video.js can be installed using bower, npm, serving a release file from
Currently video.js can be installed using npm, serving a release file from
a github tag, or even using a CDN hosted version. For information on doing any of those
see the [install guide][install-guide].
## Q: Is video.js on bower?
Yes! See the [install guide][install-guide] for more information.
Versions prior to video.js 6 do support bower, however, as of video.js 6, bower is no
longer officially supported. Please see https://github.com/videojs/video.js/issues/4012
for more information.
## Q: What do video.js version numbers mean?
@@ -266,67 +268,68 @@ Yes! Please [submit an issue or open a pull request][pr-issue-question] if this
Yes! Please [submit an issue or open a pull request][pr-issue-question] if this does not work.
Be sure to use `require('!style-loader!css-loader!video.js/dist/video-js.css')` to inject video.js CSS.
We have a short guide that deals with small configurations that you will need to do. [Webpack and Videojs Configuration][webpack-guide].
## Q: Does video.js work with react?
Yes! See [ReactJS integration example][react-guide].
[reduced-test-case]: https://css-tricks.com/reduced-test-cases/
[react-guide]: /docs/guides/react.md
[plugin-guide]: /docs/guides/plugins.md
[install-guide]: http://videojs.com/getting-started/
[troubleshooting]: /docs/guides/troubleshooting.md
[video-tracks]: /docs/guides/video-tracks.md
[audio-tracks]: /docs/guides/audio-tracks.md
[text-tracks]: /docs/guides/text-tracks.md
[debug-guide]: /docs/guides/debugging.md
[pr-issue-question]: #q-i-think-i-found-a-bug-with-videojs-or-i-want-to-add-a-feature-what-should-i-do
[hls]: http://github.com/videojs/videojs-contrib-hls
[flash]: https://github.com/videojs/videojs-flash
[dash]: http://github.com/videojs/videojs-contrib-dash
[eme]: https://github.com/videojs/videojs-contrib-eme
[generator]: https://github.com/videojs/generator-videojs-plugin
[youtube]: https://github.com/videojs/videojs-youtube
[vimeo]: https://github.com/videojs/videojs-vimeo
[ads]: https://github.com/videojs/videojs-contrib-ads
[pr-template]: http://github.com/videojs/video.js/blob/master/.github/PULL_REQUEST_TEMPLATE.md
[issue-template]: http://github.com/videojs/video.js/blob/master/.github/ISSUE_TEMPLATE.md
[plugin-list]: http://videojs.com/plugins
[skins-list]: https://github.com/videojs/video.js/wiki/Skins
[audio-tracks]: /docs/guides/audio-tracks.md
[contributing-issues]: http://github.com/videojs/video.js/blob/master/CONTRIBUTING.md#filing-issues
[contributing-prs]: http://github.com/videojs/video.js/blob/master/CONTRIBUTING.md#contributing-code
[dash]: http://github.com/videojs/videojs-contrib-dash
[debug-guide]: /docs/guides/debugging.md
[eme]: https://github.com/videojs/videojs-contrib-eme
[flash]: https://github.com/videojs/videojs-flash
[generator]: https://github.com/videojs/generator-videojs-plugin
[hls]: http://github.com/videojs/videojs-contrib-hls
[install-guide]: http://videojs.com/getting-started/
[issue-template]: http://github.com/videojs/video.js/blob/master/.github/ISSUE_TEMPLATE.md
[npm-keywords]: https://docs.npmjs.com/files/package.json#keywords
[plugin-guide]: /docs/guides/plugins.md
[plugin-list]: http://videojs.com/plugins
[pr-issue-question]: #q-i-think-i-found-a-bug-with-videojs-or-i-want-to-add-a-feature-what-should-i-do
[pr-template]: http://github.com/videojs/video.js/blob/master/.github/PULL_REQUEST_TEMPLATE.md
[react-guide]: /docs/guides/react.md
[reduced-test-case]: https://css-tricks.com/reduced-test-cases/
[semver]: http://semver.org/
[skins-list]: https://github.com/videojs/video.js/wiki/Skins
[starter-example]: http://jsbin.com/axedog/edit?html,output
[text-tracks]: /docs/guides/text-tracks.md
[troubleshooting]: /docs/guides/troubleshooting.md
[video-tracks]: /docs/guides/video-tracks.md
[vimeo]: https://github.com/videojs/videojs-vimeo
[vjs-issues]: https://github.com/videojs/video.js/issues
[vjs-prs]: https://github.com/videojs/video.js/pulls
[npm-keywords]: https://docs.npmjs.com/files/package.json#keywords
[semver]: http://semver.org/
[starter-example]: http://jsbin.com/axedog/edit?html,output
[webpack-guide]: /docs/guides/webpack.md
[youtube]: https://github.com/videojs/videojs-youtube
+21
Ver Arquivo
@@ -176,6 +176,14 @@ Like components, advanced plugins offer an implementation of events. This includ
By offering a built-in events system, advanced plugins offer a wider range of options for code structure with a pattern familiar to most web developers.
##### Extra Event Data
All events triggered by plugins include an additional data object as a second argument. This object has three properties:
- `name`: The name of the plugin (e.g. `"examplePlugin"`) as a string.
- `plugin`: The plugin constructor (e.g. `ExamplePlugin`).
- `instance`: The plugin constructor instance.
#### Statefulness
A new concept introduced for advanced plugins is _statefulness_. This is similar to React components' `state` property and `setState` method.
@@ -307,6 +315,19 @@ player.examplePlugin({customClass: 'example-class'});
These two methods are functionally identical - use whichever you prefer!
### Plugin Setup Events
Occasionally, a use-case arises where some code needs to wait for a plugin to be initialized. As of Video.js 6, this can be achieved by listening for `pluginsetup` events on the player.
For any given plugin initialization, there are four events to be aware of:
- `beforepluginsetup`: Triggered immediately before any plugin is initialized.
- `beforepluginsetup:examplePlugin` Triggered immediately before the `examplePlugin` is initialized.
- `pluginsetup`: Triggered after any plugin is initialized.
- `pluginsetup:examplePlugin`: Triggered after he `examplePlugin` is initialized.
These events work for both basic and advanced plugins. They are triggered on the player and each includes an object of [extra event data](#extra-event-data) as a second argument to its listeners.
## References
* [Player API][api-player]
+91
Ver Arquivo
@@ -54,3 +54,94 @@ return <VideoPlayer { ...videoJsOptions } />
Dont forget to include the video.js CSS, located at `video.js/dist/video-js.css`.
[options]: /docs/guides/options.md
## Using a React Component as a Video JS Component
```jsx
/**
* EpisodeList.js
*
* This is just a plain ol' React component.
* the vjsComponent methods, player methods etc. are available via
* the vjsComponent prop (`this.props.vjsComponent`)
*/
import React, { Component, PropTypes } from 'react';
class EpisodeList extends Component {
render() {
return (
<div>
<h1>{this.props.body}</h1>
</div>
);
}
}
/**
* vjsEpisodeList.js
*
* Here is where we register a Video JS Component and
* mount the React component to it when the player is ready.
*/
import EpisodeList from './EpisodeList';
import ReactDOM from 'react-dom';
import videojs from 'video.js';
const vjsComponent = videojs.getComponent('Component');
class vjsEpisodeList extends vjsComponent {
constructor(player, options) {
super(player, options);
/* Bind the current class context to the mount method */
this.mount = this.mount.bind(this);
/* When player is ready, call method to mount React component */
player.ready(() => {
this.mount();
});
}
/**
* We will render out the React EpisodeList component into the DOM element
* generated automatically by the VideoJS createEl() method.
*
* We fetch that generated element using `this.el()`, a method provided by the
* vjsComponent class that this class is extending.
*/
mount() {
ReactDOM.render(<EpisodeList vjsComponent={this} body="Episodes" />, this.el() );
}
}
/**
* Make sure to register the vjsComponent so Video JS knows it exists
*/
vjsComponent.registerComponent('vjsEpisodeList', vjsEpisodeList);
export default vjsEpisodeList;
/**
* VideoPlayer.js
* Check the above example for how to integrate the rest of this class.
*/
// ...
componentDidMount() {
// instantiate video.js
this.player = videojs(this.videoNode, this.props, function onPlayerReady() {
console.log('onPlayerReady', this)
});
/**
* Fetch the controlBar component and add the new vjsEpisodeList component as a child
* You can pass options here if desired in the second object.
*/
this.player.getChild('controlBar').addChild('vjsEpisodeList', {});
}
// ...
```
+1 -1
Ver Arquivo
@@ -14,7 +14,7 @@
## Getting Video.js
Video.js is officially available via CDN, npm, and Bower.
Video.js is officially available via CDN and npm.
Video.js works out of the box with not only HTML `<script>` and `<link>` tags, but also all major bundlers/packagers/builders, such as Browserify, Node, WebPack, etc.
+37
Ver Arquivo
@@ -0,0 +1,37 @@
# Using Webpack with Video.js
video.js, and the playback technologies such as videojs-contrib-hls all work in a Webpack based build environment. Here are several configuration changes specific to Webpack that will get you up and running.
## Video.js CSS:
To add the CSS that the player requires, simply add
`require('!style-loader!css-loader!video.js/dist/video-js.css')` to the file where the player is also included or initialized.
## Handling .eot files in Webpack
In addition to this, you may run into a problem where Webpack does not know how to load .eot files required for IE8 support by default. This can be solved by installing the file-loader and url-loader packages. Install them by running:
`npm install --save-dev file-loader url-loader`
With both packages installed, simply add the following to you webpack.config file in the 'loaders' section:
```
{
loader: 'url-loader?limit=100000',
test: /\.(png|woff|woff2|eot|ttf|svg)$/
}
```
## Using Webpack with videojs-contrib-hls
Import the HLS library with a line such as:
`import * as HLS from 'videojs-contrib-hls';`
In order to use the tech, we must also introduce webworkers with the package 'webworkify-webpack-dropin', run:
`npm install --save-dev webworkify-webpack-dropin`
To utilize this in your page, simply create an alias in your webpack.config.js file with:
```
resolve: {
alias: {
webworkify: 'webworkify-webpack-dropin'
}
}
```
Source maps that use the 'eval' tag are not compatible with webworkify, so this may need to be changed also. Source maps such as 'cheap-eval-module-source-map' should be changed to 'cheap-source-map' or anything else that fits your build without using 'eval' source maps.
+3 -3
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": "6.0.0",
"version": "6.1.0",
"main": "./es5/video.js",
"style": "./dist/video-js.css",
"copyright": "Copyright Brightcove, Inc. <https://www.brightcove.com/>",
@@ -42,7 +42,7 @@
},
"dependencies": {
"babel-runtime": "^6.9.2",
"global": "4.3.0",
"global": "4.3.2",
"safe-json-parse": "4.0.0",
"tsml": "1.0.1",
"videojs-font": "2.0.0",
@@ -126,7 +126,7 @@
"tui-jsdoc-template": "^1.1.0",
"uglify-js": "~2.8.8",
"videojs-doc-generator": "0.0.1",
"videojs-flash": "^1.0.0-RC.0",
"videojs-flash": "^1.1.0",
"videojs-standard": "^6.0.1",
"webpack": "^2.3.0"
},
+7 -2
Ver Arquivo
@@ -18,12 +18,16 @@
</head>
<body>
<!-- NOTE: we have to disable native Text Track support for the HTML5 tech,
since even HTML5 video players with native Text Track support
don't currently support 'description' text tracks in any
useful way! -->
useful way! Currently this means that iOS will not display
ANY text tracks -->
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="360"
data-setup='{ "html5" : { "nativeTextTracks" : false } }'>
data-setup='{ "html5" : { "nativeTextTracks" : false } }'
poster="http://d2zihajmogu5jn.cloudfront.net/elephantsdream/poster.png">
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.mp4" type="video/mp4">
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.ogg" type="video/ogg">
@@ -39,5 +43,6 @@
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
</body>
</html>
-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; }
+2
Ver Arquivo
@@ -4,6 +4,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);
}
+12 -10
Ver Arquivo
@@ -141,28 +141,30 @@ class ClickableComponent extends Component {
* Enable this `Component`s element.
*/
enable() {
this.removeClass('vjs-disabled');
this.el_.setAttribute('aria-disabled', 'false');
if (typeof this.tabIndex_ !== 'undefined') {
this.el_.setAttribute('tabIndex', this.tabIndex_);
if (!this.enabled_) {
this.enabled_ = true;
this.removeClass('vjs-disabled');
this.el_.setAttribute('aria-disabled', 'false');
if (typeof this.tabIndex_ !== 'undefined') {
this.el_.setAttribute('tabIndex', this.tabIndex_);
}
this.on(['tap', 'click'], this.handleClick);
this.on('focus', this.handleFocus);
this.on('blur', this.handleBlur);
}
this.on('tap', this.handleClick);
this.on('click', this.handleClick);
this.on('focus', this.handleFocus);
this.on('blur', this.handleBlur);
}
/**
* Disable this `Component`s element.
*/
disable() {
this.enabled_ = false;
this.addClass('vjs-disabled');
this.el_.setAttribute('aria-disabled', 'true');
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);
}
+1 -1
Ver Arquivo
@@ -40,7 +40,7 @@ class Component {
*
* @param {Object} [options]
* The key/value store of player options.
#
*
* @param {Object[]} [options.children]
* An array of children objects to intialize this component with. Children objects have
* a name property that will be used if more than one component of the same type needs to be
@@ -26,10 +26,6 @@ class TextTrackButton extends TrackButton {
options.tracks = player.textTracks();
super(player, options);
if (!Array.isArray(this.kinds_)) {
this.kinds_ = [this.kind_];
}
}
/**
@@ -61,6 +57,10 @@ class TextTrackButton extends TrackButton {
const tracks = this.player_.textTracks();
if (!Array.isArray(this.kinds_)) {
this.kinds_ = [this.kind_];
}
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];
@@ -101,8 +101,10 @@ class TextTrackMenuItem extends MenuItem {
const track = tracks[i];
if (track === this.track && (kinds.indexOf(track.kind) > -1)) {
track.mode = 'showing';
} else {
if (track.mode !== 'showing') {
track.mode = 'showing';
}
} else if (track.mode !== 'disabled') {
track.mode = 'disabled';
}
}
+51 -4
Ver Arquivo
@@ -14,7 +14,7 @@ import * as Fn from './utils/fn.js';
import * as Guid from './utils/guid.js';
import * as browser from './utils/browser.js';
import log from './utils/log.js';
import toTitleCase from './utils/to-title-case.js';
import toTitleCase, { titleCaseEquals } from './utils/to-title-case.js';
import { createTimeRange } from './utils/time-ranges.js';
import { bufferedPercent } from './utils/buffer.js';
import * as stylesheet from './utils/stylesheet.js';
@@ -442,6 +442,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);
@@ -855,6 +860,7 @@ class Player extends Component {
'playerId': this.id(),
'techId': `${this.id()}_${titleTechName}_api`,
'autoplay': this.options_.autoplay,
'playsinline': this.options_.playsinline,
'preload': this.options_.preload,
'loop': this.options_.loop,
'muted': this.options_.muted,
@@ -1282,7 +1288,8 @@ class Player extends Component {
*
* @fires Player#firstplay
* @listens Tech#firstplay
* @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated
* @deprecated As of 6.0 firstplay event is deprecated.
* @deprecated As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
* @private
*/
handleTechFirstPlay_() {
@@ -1300,6 +1307,7 @@ class Player extends Component {
* reason to prevent playback, use `myPlayer.one('play');` instead.
*
* @event Player#firstplay
* @deprecated As of 6.0 firstplay event is deprecated.
* @type {EventTarget~Event}
*/
this.trigger('firstplay');
@@ -2326,7 +2334,7 @@ class Player extends Component {
return true;
}
if (sourceTech.tech !== this.techName_) {
if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
this.changingSrc_ = true;
// load this technology with the chosen source
@@ -2466,6 +2474,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.
*
@@ -2528,7 +2561,7 @@ class Player extends Component {
* asynchronous way. We want the poster component to use this
* poster source so that it covers up the tech's controls.
* (YouTube's play button). However we only want to use this
* soruce if the player user hasn't set a poster through
* source if the player user hasn't set a poster through
* the normal APIs.
*
* @fires Player#posterchange
@@ -2995,6 +3028,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
*
+63 -8
Ver Arquivo
@@ -75,6 +75,27 @@ const markPluginAsActive = (player, name) => {
player[PLUGIN_CACHE_KEY][name] = true;
};
/**
* Triggers a pair of plugin setup events.
*
* @private
* @param {Player} player
* A Video.js player instance.
*
* @param {Plugin~PluginEventHash} hash
* A plugin event hash.
*
* @param {Boolean} [before]
* If true, prefixes the event name with "before". In other words,
* use this to trigger "beforepluginsetup" instead of "pluginsetup".
*/
const triggerSetupEvent = (player, hash, before) => {
const eventName = (before ? 'before' : '') + 'pluginsetup';
player.trigger(eventName, hash);
player.trigger(eventName + ':' + hash.name, hash);
};
/**
* Takes a basic plugin function and returns a wrapper function which marks
* on the player that the plugin has been activated.
@@ -91,15 +112,20 @@ const markPluginAsActive = (player, name) => {
*/
const createBasicPlugin = function(name, plugin) {
const basicPluginWrapper = function() {
// We trigger the "beforepluginsetup" and "pluginsetup" events on the player
// regardless, but we want the hash to be consistent with the hash provided
// for advanced plugins.
//
// The only potentially counter-intuitive thing here is the `instance` in
// the "pluginsetup" event is the value returned by the `plugin` function.
triggerSetupEvent(this, {name, plugin, instance: null}, true);
const instance = plugin.apply(this, arguments);
markPluginAsActive(this, name);
triggerSetupEvent(this, {name, plugin, instance});
// We trigger the "pluginsetup" event on the player regardless, but we want
// the hash to be consistent with the hash provided for advanced plugins.
// The only potentially counter-intuitive thing here is the `instance` is the
// value returned by the `plugin` function.
this.trigger('pluginsetup', {name, plugin, instance});
return instance;
};
@@ -133,11 +159,15 @@ const createPluginFactory = (name, PluginSubClass) => {
PluginSubClass.prototype.name = name;
return function(...args) {
triggerSetupEvent(this, {name, plugin: PluginSubClass, instance: null}, true);
const instance = new PluginSubClass(...[this, ...args]);
// The plugin is replaced by a function that returns the current instance.
this[name] = () => instance;
triggerSetupEvent(this, instance.getEventHash());
return instance;
};
};
@@ -147,7 +177,10 @@ const createPluginFactory = (name, PluginSubClass) => {
*
* @mixes module:evented~EventedMixin
* @mixes module:stateful~StatefulMixin
* @fires Player#beforepluginsetup
* @fires Player#beforepluginsetup:$name
* @fires Player#pluginsetup
* @fires Player#pluginsetup:$name
* @listens Player#dispose
* @throws {Error}
* If attempting to instantiate the base {@link Plugin} class
@@ -164,12 +197,12 @@ class Plugin {
* A Video.js player instance.
*/
constructor(player) {
this.player = player;
if (this.constructor === Plugin) {
throw new Error('Plugin must be sub-classed; not directly instantiated.');
}
this.player = player;
// Make this object evented, but remove the added `trigger` method so we
// use the prototype version instead.
evented(this);
@@ -184,7 +217,6 @@ class Plugin {
// If the player is disposed, dispose the plugin.
player.on('dispose', this.dispose);
player.trigger('pluginsetup', this.getEventHash());
}
/**
@@ -432,6 +464,21 @@ Player.prototype.hasPlugin = function(name) {
export default Plugin;
/**
* Signals that a plugin is about to be set up on a player.
*
* @event Player#beforepluginsetup
* @type {Plugin~PluginEventHash}
*/
/**
* Signals that a plugin is about to be set up on a player - by name. The name
* is the name of the plugin.
*
* @event Player#beforepluginsetup:$name
* @type {Plugin~PluginEventHash}
*/
/**
* Signals that a plugin has just been set up on a player.
*
@@ -439,6 +486,14 @@ export default Plugin;
* @type {Plugin~PluginEventHash}
*/
/**
* Signals that a plugin has just been set up on a player - by name. The name
* is the name of the plugin.
*
* @event Player#pluginsetup:$name
* @type {Plugin~PluginEventHash}
*/
/**
* @typedef {Object} Plugin~PluginEventHash
*
+73 -2
Ver Arquivo
@@ -234,7 +234,7 @@ class Html5 extends Tech {
}
// Update specific tag settings, in case they were overridden
const settingsAttrs = ['autoplay', 'preload', 'loop', 'muted'];
const settingsAttrs = ['autoplay', 'preload', 'loop', 'muted', 'playsinline'];
for (let i = settingsAttrs.length - 1; i >= 0; i--) {
const attr = settingsAttrs[i];
@@ -667,6 +667,77 @@ class Html5 extends Tech {
}
}
}
/**
* 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() {
return this.el_.hasAttribute('playsinline');
}
/**
* 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}
*/
setPlaysinline(value) {
if (value) {
this.el_.setAttribute('playsinline', 'playsinline');
} else {
this.el_.removeAttribute('playsinline');
}
}
/**
* 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 ---------------------------------------------------- */
@@ -761,7 +832,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
+29
Ver Arquivo
@@ -721,6 +721,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`.
*
@@ -728,6 +743,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.
*
+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;
+16
Ver Arquivo
@@ -21,3 +21,19 @@ function toTitleCase(string) {
}
export default toTitleCase;
/**
* Compares the TitleCase versions of the two strings for equality.
*
* @param {string} str1
* The first string to compare
*
* @param {string} str2
* The second string to compare
*
* @return {boolean}
* Whether the TitleCase versions of the strings are equal
*/
export function titleCaseEquals(str1, str2) {
return toTitleCase(str1) === toTitleCase(str2);
}
+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();
});
+58
Ver Arquivo
@@ -791,6 +791,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');
@@ -1564,6 +1593,26 @@ QUnit.test('src selects tech based on middleware', function(assert) {
});
QUnit.test('src_ does not call loadTech is name is titleCaseEquals', function(assert) {
let loadTechCalled = 0;
const playerProxy = {
selectSource() {
return {
tech: 'html5'
};
},
techName_: 'Html5',
ready() {},
loadTech_() {
loadTechCalled++;
}
};
Player.prototype.src_.call(playerProxy);
assert.equal(loadTechCalled, 0, 'loadTech was not called');
});
QUnit.test('options: plugins', function(assert) {
const optionsSpy = sinon.spy();
@@ -1597,3 +1646,12 @@ QUnit.test('options: plugins', function(assert) {
player.dispose();
Plugin.deregisterPlugin('foo');
});
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();
});
+23 -12
Ver Arquivo
@@ -67,24 +67,35 @@ QUnit.test('setup', function(assert) {
);
});
QUnit.test('"pluginsetup" event', function(assert) {
QUnit.test('all "pluginsetup" events', function(assert) {
const setupSpy = sinon.spy();
const events = [
'beforepluginsetup',
'beforepluginsetup:mock',
'pluginsetup',
'pluginsetup:mock'
];
this.player.on('pluginsetup', setupSpy);
this.player.on(events, setupSpy);
const instance = this.player.mock();
const event = setupSpy.firstCall.args[0];
const hash = setupSpy.firstCall.args[1];
assert.strictEqual(setupSpy.callCount, 1, 'the "pluginsetup" event was triggered');
assert.strictEqual(event.type, 'pluginsetup', 'the event has the correct type');
assert.strictEqual(event.target, this.player.el_, 'the event has the correct target');
events.forEach((type, i) => {
const event = setupSpy.getCall(i).args[0];
const hash = setupSpy.getCall(i).args[1];
assert.deepEqual(hash, {
name: 'mock',
instance,
plugin: this.MockPlugin
}, 'the event hash object is correct');
assert.strictEqual(event.type, type, `the "${type}" event was triggered`);
assert.strictEqual(event.target, this.player.el_, 'the event has the correct target');
assert.deepEqual(hash, {
name: 'mock',
// The "before" events have a `null` instance and the others have the
// return value of the plugin factory.
instance: i < 2 ? null : instance,
plugin: this.MockPlugin
}, 'the event hash object is correct');
});
});
QUnit.test('defaultState static property is used to populate state', function(assert) {
+23 -11
Ver Arquivo
@@ -47,23 +47,35 @@ QUnit.test('setup', function(assert) {
assert.ok(this.player.hasPlugin('basic'), 'player has the plugin available');
});
QUnit.test('"pluginsetup" event', function(assert) {
QUnit.test('all "pluginsetup" events', function(assert) {
const setupSpy = sinon.spy();
const events = [
'beforepluginsetup',
'beforepluginsetup:basic',
'pluginsetup',
'pluginsetup:basic'
];
this.player.on('pluginsetup', setupSpy);
this.player.on(events, setupSpy);
const instance = this.player.basic();
const event = setupSpy.firstCall.args[0];
const hash = setupSpy.firstCall.args[1];
assert.strictEqual(setupSpy.callCount, 1, 'the "pluginsetup" event was triggered');
assert.strictEqual(event.type, 'pluginsetup', 'the event has the correct type');
events.forEach((type, i) => {
const event = setupSpy.getCall(i).args[0];
const hash = setupSpy.getCall(i).args[1];
assert.deepEqual(hash, {
name: 'basic',
instance,
plugin: this.basic
}, 'the event hash object is correct');
assert.strictEqual(event.type, type, `the "${type}" event was triggered`);
assert.strictEqual(event.target, this.player.el_, 'the event has the correct target');
assert.deepEqual(hash, {
name: 'basic',
// The "before" events have a `null` instance and the others have the
// return value of the plugin factory.
instance: i < 2 ? null : instance,
plugin: this.basic
}, 'the event hash object is correct');
});
});
QUnit.test('properties are copied', function(assert) {
+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();
});
+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__) {
@@ -100,6 +113,32 @@ QUnit.test('test defaultPlaybackRate', function(assert) {
assert.strictEqual(tech.defaultPlaybackRate(), 0.75, 'can be changed from the API');
});
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('test volume', function(assert) {
if (!Html5.canControlVolume()) {
assert.ok(true, 'Volume is not supported');
@@ -665,3 +704,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;
});
+6
Ver Arquivo
@@ -601,3 +601,9 @@ QUnit.test('setSource after previous setSource should dispose source handler onc
assert.equal(disposeCount, 2, 'did dispose for third setSource');
});
QUnit.test('returns an empty object for getVideoPlaybackQuality', function(assert) {
const tech = new Tech();
assert.deepEqual(tech.getVideoPlaybackQuality(), {}, 'returns an empty object');
});
+13 -1
Ver Arquivo
@@ -1,5 +1,5 @@
/* eslint-env qunit */
import toTitleCase from '../../../src/js/utils/to-title-case.js';
import toTitleCase, { titleCaseEquals } from '../../../src/js/utils/to-title-case.js';
QUnit.module('to-title-case');
@@ -8,3 +8,15 @@ QUnit.test('should make a string start with an uppercase letter', function(asser
assert.ok(foo === 'Bar');
});
QUnit.test('titleCaseEquals compares whether the TitleCase of two strings is equal', function(assert) {
assert.ok(titleCaseEquals('foo', 'foo'), 'foo equals foo');
assert.ok(titleCaseEquals('foo', 'Foo'), 'foo equals Foo');
assert.ok(titleCaseEquals('Foo', 'foo'), 'Foo equals foo');
assert.ok(titleCaseEquals('Foo', 'Foo'), 'Foo equals Foo');
assert.ok(titleCaseEquals('fooBar', 'fooBar'), 'fooBar equals fooBar');
assert.notOk(titleCaseEquals('fooBAR', 'fooBar'), 'fooBAR does not equal fooBar');
assert.notOk(titleCaseEquals('foobar', 'fooBar'), 'foobar does not equal fooBar');
assert.notOk(titleCaseEquals('fooBar', 'FOOBAR'), 'fooBar does not equal fooBAR');
});