Comparar commits
25 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 8f67b4fc54 | |||
| cb6005eca4 | |||
| 8d80a5846e | |||
| 35df35143f | |||
| 3087830ed7 | |||
| 483e5a2ca5 | |||
| 3dcfa9568a | |||
| b5c60f339b | |||
| 03bab83dde | |||
| f773c473f9 | |||
| ae423df4f5 | |||
| 0a19cf0d6a | |||
| da1d8613d7 | |||
| da0f1ee681 | |||
| cff2e503ef | |||
| 561d5ddf76 | |||
| d6e56e9222 | |||
| 7490a499ce | |||
| 1ea00419c9 | |||
| 230743ecb1 | |||
| 39fd73f781 | |||
| 92e5d9fb5a | |||
| 083f643e63 | |||
| 02721c7c03 | |||
| c20ca5cc97 |
+1
-1
@@ -10,7 +10,7 @@ test/*.map
|
||||
.s3config.json
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
npm-debug.log*
|
||||
|
||||
sandbox/*
|
||||
!sandbox/*.example
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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!
|
||||
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<title>Video.js Text Descriptions, Chapters & 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>
|
||||
|
||||
@@ -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 & captions example</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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>
|
||||
|
||||
@@ -292,7 +292,7 @@ Player
|
||||
├── BigPlayButton
|
||||
├─┬ ControlBar
|
||||
│ ├── PlayToggle
|
||||
│ ├── VolumeMenuButton
|
||||
│ ├── VolumePanel
|
||||
│ ├── CurrentTimeDisplay (hidden by default)
|
||||
│ ├── TimeDivider (hidden by default)
|
||||
│ ├── DurationDisplay (hidden by default)
|
||||
|
||||
+52
-49
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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', {});
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário