Comparar commits
231 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 7bd5839537 | |||
| 3c28e188b0 | |||
| 133087ebf5 | |||
| 5bde16a1bc | |||
| cb42fcfb02 | |||
| 5b8b41e546 | |||
| f87ada1ee0 | |||
| 19b429bddc | |||
| c8f5e3a3fc | |||
| 22aade1b61 | |||
| e34335b012 | |||
| 866a3f37d8 | |||
| 37a6811fb6 | |||
| 03529163b6 | |||
| 2e2ac6f870 | |||
| 4979ea78d5 | |||
| 3585af0fe2 | |||
| d7d7cfeb9f | |||
| 4c3b60c3b6 | |||
| c2545ddb6d | |||
| 05b39fe281 | |||
| 7b0d738e8f | |||
| 8e7d8cc959 | |||
| 8888e2b5b5 | |||
| 1463e50f7b | |||
| a0ba8e2360 | |||
| 924fb279ee | |||
| e642295468 | |||
| de3945db15 | |||
| 58f2349302 | |||
| e3424f6a4d | |||
| 34aab3f357 | |||
| b387437aed | |||
| 524f868e31 | |||
| 1ba1f5aabd | |||
| 49bed07039 | |||
| 7bafcc2a55 | |||
| 2037e18235 | |||
| 8d1653aebc | |||
| 8f07f5d57c | |||
| 0bba3196d8 | |||
| 091bdf9261 | |||
| 29ffbfbc87 | |||
| 57af15ce8b | |||
| ce6acc832a | |||
| 8622b2648e | |||
| 73b6316f3b | |||
| 844e4f0107 | |||
| b07143d276 | |||
| e12bedbb45 | |||
| c340dbcccd | |||
| 20141202c1 | |||
| 5377ffc176 | |||
| f8aed4dc32 | |||
| 6578ed98ac | |||
| bb9b710d95 | |||
| 305e5ea192 | |||
| 94fd5c12c8 | |||
| 2ceed0a4eb | |||
| bbe82530aa | |||
| d290db1765 | |||
| f35de1c98d | |||
| 02da69741a | |||
| 0ce7cd4fe3 | |||
| d120ea29b7 | |||
| c239bd5c5a | |||
| b5472145bf | |||
| 9ec55879a1 | |||
| 83d453b498 | |||
| e5a240a631 | |||
| 9c74116578 | |||
| b914c76247 | |||
| 26d4e7b0bf | |||
| ac0b03f2f7 | |||
| b7c384eb5b | |||
| 4fd902229f | |||
| 42507f8c11 | |||
| e92db4f407 | |||
| 22cf3dd935 | |||
| eb389c57c0 | |||
| 26789e7470 | |||
| 642ad4b5cf | |||
| 0493f54d6f | |||
| d20e9ce128 | |||
| 74530d8b3a | |||
| a7ffa34b7b | |||
| 3f724f9349 | |||
| 559297a376 | |||
| 87cd26d958 | |||
| 98b80df0d0 | |||
| 83cbeec424 | |||
| 7985c63541 | |||
| 214e01c885 | |||
| 5ec46b0372 | |||
| 014c6b89e6 | |||
| 766580af2b | |||
| 881cfcb346 | |||
| bed19be9cd | |||
| bfcb9e2fb5 | |||
| 761b877626 | |||
| 8f16de2778 | |||
| 54ff1f97b8 | |||
| 1d5562d656 | |||
| c9022795fd | |||
| ba3cf1724f | |||
| 15ce37e45d | |||
| eb2093e16e | |||
| cfc3ed7f0f | |||
| 1a0b2812ce | |||
| d24fe409e8 | |||
| b4ebd9ba00 | |||
| 9b9f89e5b3 | |||
| d69551ac80 | |||
| c5d1152456 | |||
| 41bd855e19 | |||
| de25d751b9 | |||
| 406c203f17 | |||
| eb5e78bb88 | |||
| 430be94e7f | |||
| 72fcb6c659 | |||
| 252bcee959 | |||
| 6b477bb737 | |||
| 23f0fa05cb | |||
| 2a05633159 | |||
| 3fc9b83958 | |||
| 50f8ad214c | |||
| f05a9271b8 | |||
| 996507744f | |||
| b6d521f472 | |||
| 4859bb9e3d | |||
| 45ffa810fb | |||
| e9e5b5f782 | |||
| 8d51235b30 | |||
| 77357b1cd2 | |||
| 11a096d60f | |||
| 5f42130b82 | |||
| 6889e925b4 | |||
| b2c5b2a412 | |||
| fb74c71ba6 | |||
| 74cddcad73 | |||
| 2e720afb65 | |||
| 8f7eb121bb | |||
| d11fd50a63 | |||
| 726367abc5 | |||
| 9702618c02 | |||
| 685404d018 | |||
| 202da2d468 | |||
| 028559ccb0 | |||
| de1b363470 | |||
| 9d77268f76 | |||
| ed59531f78 | |||
| 2988f6ae53 | |||
| f599ef4c51 | |||
| 254683b5cd | |||
| 9e20386a94 | |||
| fe760a44d7 | |||
| 49e29bac85 | |||
| ac0329f875 | |||
| 7b9574bb19 | |||
| c51f22680f | |||
| 66922a818e | |||
| 16c855931b | |||
| 8845bd36c1 | |||
| e932061024 | |||
| 44ec0e4e75 | |||
| d6e49a4095 | |||
| d4e89d2adc | |||
| 920c54a65c | |||
| 7d85f27a8e | |||
| ae3e27788d | |||
| e479f8c34c | |||
| 6878c21c68 | |||
| dd2aff00a8 | |||
| c545acdaea | |||
| 54e3db5d34 | |||
| 2032b17943 | |||
| ca022981e0 | |||
| 816291e7b5 | |||
| 4063f96bb5 | |||
| 280ecd49d7 | |||
| eb0efd499c | |||
| 08c7f4e1d4 | |||
| 86f0830502 | |||
| 5fdcd4602b | |||
| 5f95e413e3 | |||
| 057588f6c7 | |||
| 7cf2de30ac | |||
| 3a859f97d0 | |||
| 0d0ff90ca1 | |||
| b4265a8468 | |||
| fac52046f3 | |||
| 83b372c0d4 | |||
| daab267736 | |||
| 8beab2791a | |||
| 2d9d6b9e46 | |||
| 94f22bb995 | |||
| 0b10b69775 | |||
| f37383784d | |||
| af6beb21c3 | |||
| ebf7718f1b | |||
| 80851ea7ca | |||
| c154518df4 | |||
| b90f04fc3b | |||
| 141ecf221b | |||
| c859d8e104 | |||
| f787cbad18 | |||
| 46058ddb0f | |||
| 955185379f | |||
| d58be409a6 | |||
| 773b5ab1c1 | |||
| 6385d1d429 | |||
| 4ae370c1a2 | |||
| 72c44daaf3 | |||
| b3e4e95f9c | |||
| ab82bf0008 | |||
| 1bb40b8d14 | |||
| f2a21d4aa2 | |||
| c15d64adf5 | |||
| a0e9318862 | |||
| 56fc3356dc | |||
| f947ed7791 | |||
| 1ff9f381a2 | |||
| d86d4b2222 | |||
| 052c2ce1a9 | |||
| 4f6cb03add | |||
| 485524882e | |||
| 945711855a | |||
| 272d5eed83 | |||
| 86068a5b45 | |||
| e85c1c0391 | |||
| c89b75699e |
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": [ "es3", ["es2015", {"loose": true}] ],
|
||||
"plugins": ["inline-json"]
|
||||
}
|
||||
@@ -4,7 +4,6 @@ Include a [reduced test case](https://css-tricks.com/reduced-test-cases/), we ha
|
||||
|
||||
## Steps to reproduce
|
||||
Explain in detail the exact steps necessary to reproduce the issue.
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
@@ -3,13 +3,13 @@ Please describe the change as necessary.
|
||||
If it's a feature or enhancement please be as detailed as possible.
|
||||
If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail.
|
||||
|
||||
|
||||
## Specific Changes proposed
|
||||
Please list the specific changes involved in this pull request.
|
||||
|
||||
## Requirements Checklist
|
||||
- [ ] Feature implemented / Bug fixed
|
||||
- [ ] If necessary, more likely in a feature request than a bug fix
|
||||
- [ ] Change has been verified in an actual browser (Chome, Firefox, IE)
|
||||
- [ ] Unit Tests updated or fixed
|
||||
- [ ] Docs/guides updated
|
||||
- [ ] Example created ([starter template on JSBin](http://jsbin.com/axedog/edit?html,output))
|
||||
@@ -28,5 +28,6 @@ test/coverage/*
|
||||
.sass-cache
|
||||
|
||||
dist/*
|
||||
es5/*
|
||||
|
||||
.idea/
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"source": {
|
||||
"include": [ "src/js/" ],
|
||||
"includePattern": ".js$"
|
||||
},
|
||||
"opts": {
|
||||
"destination": "docs/api",
|
||||
"readme": "docs/index.md",
|
||||
"template": "node_modules/tui-jsdoc-template",
|
||||
"package": "package.json",
|
||||
"recurse": true,
|
||||
"tutorials": "docs/guides",
|
||||
"encoding": "utf8"
|
||||
},
|
||||
"templates": {
|
||||
"default": {
|
||||
"staticFiles": {
|
||||
"include": ["build/docs/"]
|
||||
}
|
||||
},
|
||||
"logo": {
|
||||
"url": "http://videojs.com/img/logo.png",
|
||||
"height": "30px",
|
||||
"width": "214px"
|
||||
},
|
||||
"name": "Video.js Documentation",
|
||||
"tabNames": {
|
||||
"tutorials": "Guides"
|
||||
},
|
||||
"footerText": "<span class='copyright'><a href='http://videojs.com'>Video.js</a> is a free and open source HTML5 video player. © <a href='https://brightcove.com' target='_blank'>Brightcove, Inc</a>. <a href='https://github.com/videojs/video.js/blob/master/LICENSE' class='button blue' target='_blank'>View license</a></span> <ul class='other-links'><li><a href='http://videojs.com' class='button white'><i class='fa fa-external-link'></i> Video.js</a></li> <li><a href='https://twitter.com/videojs' class='button white' target='_blank'><i class='fa fa-twitter'></i> @videojs</a></li> <li><a href='http://github.com/videojs/video.js' class='button white' target='_blank'><i class='fa fa-github-alt'></i> Source</a></li> </ul>",
|
||||
"css": [
|
||||
"styles/videojs.css",
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css"
|
||||
]
|
||||
},
|
||||
"plugins": ["plugins/markdown"],
|
||||
"markdown": {
|
||||
"tags": ["example"]
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"evil" : true,
|
||||
"validthis": true,
|
||||
"node" : true,
|
||||
"debug" : true,
|
||||
"boss" : true,
|
||||
"expr" : true,
|
||||
"eqnull" : true,
|
||||
"quotmark" : "single",
|
||||
"sub" : true,
|
||||
"trailing" : true,
|
||||
"undef" : true,
|
||||
"laxbreak" : true,
|
||||
"esnext" : true,
|
||||
"eqeqeq" : true,
|
||||
"predef" : [
|
||||
"_V_",
|
||||
"goog",
|
||||
"console",
|
||||
|
||||
"require",
|
||||
"define",
|
||||
"module",
|
||||
"exports",
|
||||
"process",
|
||||
|
||||
"DOMException",
|
||||
|
||||
"q",
|
||||
"asyncTest",
|
||||
"deepEqual",
|
||||
"equal",
|
||||
"expect",
|
||||
"module",
|
||||
"notDeepEqual",
|
||||
"notEqual",
|
||||
"notStrictEqual",
|
||||
"ok",
|
||||
"throws",
|
||||
"QUnit",
|
||||
"raises",
|
||||
"start",
|
||||
"stop",
|
||||
"strictEqual",
|
||||
"test",
|
||||
"throws",
|
||||
"sinon"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
# Exclude everything but the contents of the dist directory.
|
||||
**/*
|
||||
!dist/**
|
||||
!es5/**
|
||||
!src/css/**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
CHANGELOG.md
|
||||
@@ -0,0 +1,84 @@
|
||||
var remarkrc = {
|
||||
settings: {
|
||||
bullet: '*',
|
||||
fence: '`',
|
||||
strong: '*',
|
||||
emphasis: '_',
|
||||
listItemIndent: 1,
|
||||
incrementListMarker: false
|
||||
},
|
||||
plugins: {
|
||||
'toc': {
|
||||
tight: true
|
||||
},
|
||||
}
|
||||
};
|
||||
var args = process.argv;
|
||||
|
||||
// only lint in non-output mode
|
||||
if (args.indexOf('-o') === -1 && args.indexOf('--output') === -1) {
|
||||
remarkrc['validate-links'] = {};
|
||||
remarkrc.plugins.lint = {
|
||||
'blockquote-indentation': ['error', 2],
|
||||
'checkbox-character-style': ['warn'],
|
||||
'checkbox-content-indent': ['error'],
|
||||
'code-block-style': ['error', 'fenced'],
|
||||
'definition-case': ['off'],
|
||||
'definition-spacing': ['error'],
|
||||
'emphasis-marker': ['error', '_'],
|
||||
'fenced-code-flag': ['error'],
|
||||
'fenced-code-marker': ['error', '`'],
|
||||
'file-extension': ['error'],
|
||||
'final-definition': ['error'],
|
||||
'final-newline': ['off'],
|
||||
'first-heading-level': ['warn', 1],
|
||||
'hard-break-spaces': ['off'],
|
||||
'heading-increment': ['error'],
|
||||
'heading-style': ['error', 'atx'],
|
||||
'link-title-style': ['warn', '"'],
|
||||
'list-item-bullet-indent': ['error'],
|
||||
'list-item-content-indent': ['warn'],
|
||||
'list-item-indent': ['error', 'space'],
|
||||
'list-item-spacing': ['off'],
|
||||
'maximum-heading-length': ['off'],
|
||||
'maximum-line-length': ['off'],
|
||||
'no-auto-link-without-protocol': ['error'],
|
||||
'no-blockquote-without-caret': ['error'],
|
||||
'no-consecutive-blank-lines': ['error'],
|
||||
'no-duplicate-definitions': ['error'],
|
||||
'no-duplicate-headings-in-section': ['error'],
|
||||
'no-duplicate-headings': ['off'],
|
||||
'no-emphasis-as-heading': ['error'],
|
||||
'no-file-name-articles': ['off'],
|
||||
'no-file-name-consecutive-dashes': ['off'],
|
||||
'no-file-name-irregular-characters': ['warn', '\\.a-zA-Z0-9-_'],
|
||||
'no-file-name-mixed-case': ['error'],
|
||||
'no-file-name-outer-dashes': ['error'],
|
||||
'no-heading-content-indent': ['error'],
|
||||
'no-heading-indent': ['error'],
|
||||
'no-heading-punctuation': ['off'],
|
||||
'no-html': ['off'],
|
||||
'no-inline-padding': ['error'],
|
||||
'no-literal-urls': ['off'],
|
||||
'no-missing-blank-lines': ['off'],
|
||||
'no-multiple-toplevel-headings': ['error'],
|
||||
'no-reference-like-url': ['error'],
|
||||
'no-shell-dollars': ['error'],
|
||||
'no-shortcut-reference-iamge': ['off'],
|
||||
'no-shortcut-reference-link': ['off'],
|
||||
'no-table-indentation': ['error'],
|
||||
'no-tabs': ['error'],
|
||||
'no-undefined-references': ['error'],
|
||||
'no-unused-definitions': ['error'],
|
||||
'ordered-list-marker-style': ['error', '.'],
|
||||
'ordered-list-marker-value': ['error', 'one'],
|
||||
'rule-style': ['error', '***'],
|
||||
'strong-marker': ['error', '*'],
|
||||
'table-cell-padding': ['warn', 'padded'],
|
||||
'table-cell-alignment': ['warn'],
|
||||
'table-pipes': ['warn'],
|
||||
'unordered-list-marker-style': ['warn', '*']
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = remarkrc;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.12
|
||||
- 4.4
|
||||
before_install:
|
||||
- export CHROME_BIN=chromium-browser
|
||||
- export DISPLAY=:99.0
|
||||
|
||||
+286
-5
@@ -1,10 +1,291 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
<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)
|
||||
|
||||
## HEAD (Unreleased)
|
||||
_(none)_
|
||||
### Features
|
||||
|
||||
--------------------
|
||||
* Show big play button on pause if specified ([#3892](https://github.com/videojs/video.js/issues/3892)) ([b547214](https://github.com/videojs/video.js/commit/b547214))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* give techs a name ([#3934](https://github.com/videojs/video.js/issues/3934)) ([94fd5c1](https://github.com/videojs/video.js/commit/94fd5c1)), closes [#1786](https://github.com/videojs/video.js/issues/1786)
|
||||
* Pause player before seeking in seek bar mousedown ([#3921](https://github.com/videojs/video.js/issues/3921)) ([2ceed0a](https://github.com/videojs/video.js/commit/2ceed0a)), closes [#3839](https://github.com/videojs/video.js/issues/3839) [#3886](https://github.com/videojs/video.js/issues/3886)
|
||||
* player el ingest when parent doesn't have `hasAttribute` method ([#3929](https://github.com/videojs/video.js/issues/3929)) ([bbe8253](https://github.com/videojs/video.js/commit/bbe8253))
|
||||
* showing custom poster with controls disabled ([#3933](https://github.com/videojs/video.js/issues/3933)) ([305e5ea](https://github.com/videojs/video.js/commit/305e5ea)), closes [#1625](https://github.com/videojs/video.js/issues/1625)
|
||||
|
||||
### Chores
|
||||
|
||||
* better dev experience ([#3896](https://github.com/videojs/video.js/issues/3896)) ([9ec5587](https://github.com/videojs/video.js/commit/9ec5587))
|
||||
* don't run tests on travis if only docs were changed ([#3908](https://github.com/videojs/video.js/issues/3908)) ([c239bd5](https://github.com/videojs/video.js/commit/c239bd5))
|
||||
* **development:** fix `npm start` file watching ([#3922](https://github.com/videojs/video.js/issues/3922)) ([02da697](https://github.com/videojs/video.js/commit/02da697))
|
||||
* **release:** add es5 folder to the tagged commit ([#3913](https://github.com/videojs/video.js/issues/3913)) ([d120ea2](https://github.com/videojs/video.js/commit/d120ea2))
|
||||
* **sass:** upgrade to latest version of grunt-sass ([#3897](https://github.com/videojs/video.js/issues/3897)) ([83d453b](https://github.com/videojs/video.js/commit/83d453b)), closes [#3692](https://github.com/videojs/video.js/issues/3692)
|
||||
* fix typo in collaborator guide ([#3931](https://github.com/videojs/video.js/issues/3931)) ([f35de1c](https://github.com/videojs/video.js/commit/f35de1c))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* require `videojs-vtt.js` via require rather than concat ([#3919](https://github.com/videojs/video.js/issues/3919)) ([d290db1](https://github.com/videojs/video.js/commit/d290db1))
|
||||
|
||||
### Documentation
|
||||
|
||||
* **faq:** add a question about autoplay ([#3898](https://github.com/videojs/video.js/issues/3898)) ([e5a240a](https://github.com/videojs/video.js/commit/e5a240a))
|
||||
* **faq:** add FAQ question about RTMP url ([#3899](https://github.com/videojs/video.js/issues/3899)) ([9c74116](https://github.com/videojs/video.js/commit/9c74116))
|
||||
* **troubleshooting:** updates to troubleshooting doc ([#3912](https://github.com/videojs/video.js/issues/3912)) ([0ce7cd4](https://github.com/videojs/video.js/commit/0ce7cd4))
|
||||
|
||||
<a name="5.15.1"></a>
|
||||
## [5.15.1](https://github.com/videojs/video.js/compare/v5.15.0...v5.15.1) (2016-12-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* extra warn logs on already initialized player references ([#3888](https://github.com/videojs/video.js/issues/3888)) ([b7c384e](https://github.com/videojs/video.js/commit/b7c384e))
|
||||
* Support require()-ing video.js ([#3889](https://github.com/videojs/video.js/issues/3889)) ([ac0b03f](https://github.com/videojs/video.js/commit/ac0b03f)), closes [#3869](https://github.com/videojs/video.js/issues/3869)
|
||||
|
||||
<a name="5.15.0"></a>
|
||||
# [5.15.0](https://github.com/videojs/video.js/compare/v5.14.1...v5.15.0) (2016-12-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **player:** ingest a player div for videojs ([#3856](https://github.com/videojs/video.js/issues/3856)) ([74530d8](https://github.com/videojs/video.js/commit/74530d8))
|
||||
* deprecate the use of `starttime` in player.js ([#3838](https://github.com/videojs/video.js/issues/3838)) ([22cf3dd](https://github.com/videojs/video.js/commit/22cf3dd))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **html5:** (un)patchCanPlayType could set native canPlayType to null ([#3863](https://github.com/videojs/video.js/issues/3863)) ([559297a](https://github.com/videojs/video.js/commit/559297a))
|
||||
* **seeking:** don't always pause in mouse down ([#3886](https://github.com/videojs/video.js/issues/3886)) ([e92db4f](https://github.com/videojs/video.js/commit/e92db4f)), closes [#3839](https://github.com/videojs/video.js/issues/3839)
|
||||
* don't emit tap events on tech when using native controls ([#3873](https://github.com/videojs/video.js/issues/3873)) ([42507f8](https://github.com/videojs/video.js/commit/42507f8))
|
||||
* remote text track deprecation warnings ([#3864](https://github.com/videojs/video.js/issues/3864)) ([a7ffa34](https://github.com/videojs/video.js/commit/a7ffa34))
|
||||
* remove vjs-seeking on src change ([#3846](https://github.com/videojs/video.js/issues/3846)) ([83cbeec](https://github.com/videojs/video.js/commit/83cbeec)), closes [#3765](https://github.com/videojs/video.js/issues/3765)
|
||||
|
||||
### Chores
|
||||
|
||||
* **docs:** Documentation Linting and TOC generation ([#3841](https://github.com/videojs/video.js/issues/3841)) ([0493f54](https://github.com/videojs/video.js/commit/0493f54))
|
||||
* **faq:** move FAQ and troubleshooting guide to docs/ ([#3883](https://github.com/videojs/video.js/issues/3883)) ([26789e7](https://github.com/videojs/video.js/commit/26789e7))
|
||||
* **package:** update dependencies (enable Greenkeeper) 🌴 ([#3777](https://github.com/videojs/video.js/issues/3777)) ([d20e9ce](https://github.com/videojs/video.js/commit/d20e9ce))
|
||||
* **videojs-standard:** update to version 6.0.1 ([#3884](https://github.com/videojs/video.js/issues/3884)) ([eb389c5](https://github.com/videojs/video.js/commit/eb389c5))
|
||||
|
||||
### Documentation
|
||||
|
||||
* move examples out of code into docs ([642ad4b](https://github.com/videojs/video.js/commit/642ad4b))
|
||||
|
||||
### Tests
|
||||
|
||||
* **hooks:** move vjs hooks QUnit module into separate file ([#3862](https://github.com/videojs/video.js/issues/3862)) ([87cd26d](https://github.com/videojs/video.js/commit/87cd26d))
|
||||
* **hooks:** remove errors logged in tests ([#3865](https://github.com/videojs/video.js/issues/3865)) ([3f724f9](https://github.com/videojs/video.js/commit/3f724f9))
|
||||
|
||||
<a name="5.14.1"></a>
|
||||
## [5.14.1](https://github.com/videojs/video.js/compare/v5.14.0...v5.14.1) (2016-12-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **throttle:** Fix error in Fn.throttle that broke MouseTimeDisplay ([#3833](https://github.com/videojs/video.js/issues/3833)) ([014c6b8](https://github.com/videojs/video.js/commit/014c6b8))
|
||||
|
||||
### Tests
|
||||
|
||||
* add Edge to browserstack tests ([#3834](https://github.com/videojs/video.js/issues/3834)) ([5ec46b0](https://github.com/videojs/video.js/commit/5ec46b0))
|
||||
* **events:** silence error logging in tests ([#3835](https://github.com/videojs/video.js/issues/3835)) ([214e01c](https://github.com/videojs/video.js/commit/214e01c))
|
||||
|
||||
<a name="5.14.0"></a>
|
||||
# [5.14.0](https://github.com/videojs/video.js/compare/v5.13.2...v5.14.0) (2016-12-02)
|
||||
|
||||
### Features
|
||||
|
||||
* Allow to use custom Player class ([#3458](https://github.com/videojs/video.js/issues/3458)) ([de25d75](https://github.com/videojs/video.js/commit/de25d75)), closes [#3335](https://github.com/videojs/video.js/issues/3335) [#3016](https://github.com/videojs/video.js/issues/3016)
|
||||
* Eliminate lodash-compat as a dependency, rewrite mergeOptions ([#3760](https://github.com/videojs/video.js/issues/3760)) ([761b877](https://github.com/videojs/video.js/commit/761b877))
|
||||
* Object Type-Detection and Replacing object.assign ([#3757](https://github.com/videojs/video.js/issues/3757)) ([8f16de2](https://github.com/videojs/video.js/commit/8f16de2))
|
||||
* Refactoring chapters button handling and fixing several issues ([#3472](https://github.com/videojs/video.js/issues/3472)) ([41bd855](https://github.com/videojs/video.js/commit/41bd855)), closes [#3447](https://github.com/videojs/video.js/issues/3447) [#3447](https://github.com/videojs/video.js/issues/3447)
|
||||
* **texttracks:** always use emulated text tracks ([#3798](https://github.com/videojs/video.js/issues/3798)) ([881cfcb](https://github.com/videojs/video.js/commit/881cfcb))
|
||||
* **tracks:** Added option to disable native tracks ([#3786](https://github.com/videojs/video.js/issues/3786)) ([9b9f89e](https://github.com/videojs/video.js/commit/9b9f89e))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **html5:** remove confusing references to player in a tech ([#3790](https://github.com/videojs/video.js/issues/3790)) ([d69551a](https://github.com/videojs/video.js/commit/d69551a))
|
||||
|
||||
### Documentation
|
||||
|
||||
* **FAQ:** add an faq ([#3805](https://github.com/videojs/video.js/issues/3805)) ([1d5562d](https://github.com/videojs/video.js/commit/1d5562d))
|
||||
* **guides:** Manual Documentation Improvements ([#3703](https://github.com/videojs/video.js/issues/3703)) ([d24fe40](https://github.com/videojs/video.js/commit/d24fe40))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 1 ([#3694](https://github.com/videojs/video.js/issues/3694)) ([1a0b281](https://github.com/videojs/video.js/commit/1a0b281))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 2 ([#3698](https://github.com/videojs/video.js/issues/3698)) ([cfc3ed7](https://github.com/videojs/video.js/commit/cfc3ed7))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 3 ([#3708](https://github.com/videojs/video.js/issues/3708)) ([eb2093e](https://github.com/videojs/video.js/commit/eb2093e))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 4 ([#3756](https://github.com/videojs/video.js/issues/3756)) ([15ce37e](https://github.com/videojs/video.js/commit/15ce37e))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 5 ([#3766](https://github.com/videojs/video.js/issues/3766)) ([ba3cf17](https://github.com/videojs/video.js/commit/ba3cf17))
|
||||
* **jsdoc:** Update the jsdoc comments to modern syntax - Part 6 ([#3771](https://github.com/videojs/video.js/issues/3771)) ([c902279](https://github.com/videojs/video.js/commit/c902279))
|
||||
* add a troubleshooting guide ([#3814](https://github.com/videojs/video.js/issues/3814)) ([54ff1f9](https://github.com/videojs/video.js/commit/54ff1f9))
|
||||
* fix typo, extends -> extend ([#3789](https://github.com/videojs/video.js/issues/3789)) ([c5d1152](https://github.com/videojs/video.js/commit/c5d1152))
|
||||
|
||||
### Tests
|
||||
|
||||
* fix tests on older IE ([#3800](https://github.com/videojs/video.js/issues/3800)) ([b4ebd9b](https://github.com/videojs/video.js/commit/b4ebd9b))
|
||||
|
||||
<a name="5.13.2"></a>
|
||||
## [5.13.2](https://github.com/videojs/video.js/compare/v5.13.1...v5.13.2) (2016-11-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **html5:** exit early on emulated tracks in html5 ([#3772](https://github.com/videojs/video.js/issues/3772)) ([252bcee](https://github.com/videojs/video.js/commit/252bcee))
|
||||
* **HtmlTrackElementList:** allow to reference by index via bracket notation ([#3776](https://github.com/videojs/video.js/issues/3776)) ([430be94](https://github.com/videojs/video.js/commit/430be94))
|
||||
|
||||
### Chores
|
||||
|
||||
* fix CHANGELOG 5.13.1 header ([23f0fa0](https://github.com/videojs/video.js/commit/23f0fa0))
|
||||
* fixup CHANGELOG for 5.13.1 release ([2a05633](https://github.com/videojs/video.js/commit/2a05633))
|
||||
* **package:** update karma-detect-browsers to version 2.2.3 ([#3770](https://github.com/videojs/video.js/issues/3770)) ([6b477bb](https://github.com/videojs/video.js/commit/6b477bb))
|
||||
* **pr_template:** add checkbox to verify changes in a browser ([#3775](https://github.com/videojs/video.js/issues/3775)) ([72fcb6c](https://github.com/videojs/video.js/commit/72fcb6c))
|
||||
|
||||
<a name="5.13.1"></a>
|
||||
# [5.13.1](https://github.com/videojs/video.js/compare/v5.12.6...v5.13.1) (2016-11-09)
|
||||
|
||||
### Features
|
||||
|
||||
* **clickable-component:** Disable interaction with disabled clickable components ([#3525](https://github.com/videojs/video.js/issues/3525)) ([de1b363](https://github.com/videojs/video.js/commit/de1b363))
|
||||
* **component:** attribute get/set/remove methods ([202da2d](https://github.com/videojs/video.js/commit/202da2d))
|
||||
* **fluid:** use default aspect ratio for fluid players if width unknown ([#3614](https://github.com/videojs/video.js/issues/3614)) ([2988f6a](https://github.com/videojs/video.js/commit/2988f6a))
|
||||
* add a safe computedStyle to videojs. ([#3664](https://github.com/videojs/video.js/issues/3664)) ([9702618](https://github.com/videojs/video.js/commit/9702618))
|
||||
* add ability to get current source object and all source objects ([#2678](https://github.com/videojs/video.js/issues/2678)) ([028559c](https://github.com/videojs/video.js/commit/028559c)), closes [#2443](https://github.com/videojs/video.js/issues/2443)
|
||||
* Components are now accessible via `camelCase` and `UpperCamelCase` ([#3439](https://github.com/videojs/video.js/issues/3439)) ([9d77268](https://github.com/videojs/video.js/commit/9d77268)), closes [#3436](https://github.com/videojs/video.js/issues/3436)
|
||||
* **lang:** update ru.json ([#3654](https://github.com/videojs/video.js/issues/3654)) ([d11fd50](https://github.com/videojs/video.js/commit/d11fd50))
|
||||
* **lang:** update uk.json ([#3675](https://github.com/videojs/video.js/issues/3675)) ([8f7eb12](https://github.com/videojs/video.js/commit/8f7eb12))
|
||||
* implement player lifecycle hooks and trigger beforesetup/setup hooks ([#3639](https://github.com/videojs/video.js/issues/3639)) ([77357b1](https://github.com/videojs/video.js/commit/77357b1))
|
||||
* option to have remoteTextTracks automatically 'garbage-collected' when sources change ([#3736](https://github.com/videojs/video.js/issues/3736)) ([f05a927](https://github.com/videojs/video.js/commit/f05a927))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow rounded value for fluid player ratio test ([#3739](https://github.com/videojs/video.js/issues/3739)) ([2e720af](https://github.com/videojs/video.js/commit/2e720af))
|
||||
* aria-live="assertive" only for descriptions ([685404d](https://github.com/videojs/video.js/commit/685404d)), closes [#3554](https://github.com/videojs/video.js/issues/3554)
|
||||
* currentDimension can return 0 for fluid player on IE ([#3738](https://github.com/videojs/video.js/issues/3738)) ([74cddca](https://github.com/videojs/video.js/commit/74cddca))
|
||||
* Suppress Infinity duration on Android Chrome before playback ([#3476](https://github.com/videojs/video.js/issues/3476)) ([ed59531](https://github.com/videojs/video.js/commit/ed59531)), closes [#3079](https://github.com/videojs/video.js/issues/3079)
|
||||
|
||||
### Chores
|
||||
|
||||
* **changelog.md:** update 5.12.6 and 5.12.3 ([#3715](https://github.com/videojs/video.js/issues/3715)) ([254683b](https://github.com/videojs/video.js/commit/254683b))
|
||||
* pin karma-detect-browsers to 2.1.0 ([#3764](https://github.com/videojs/video.js/issues/3764)) ([4859bb9](https://github.com/videojs/video.js/commit/4859bb9))
|
||||
* **package:** update grunt-accessibility to version 5.0.0 ([#3747](https://github.com/videojs/video.js/issues/3747)) ([b6d521f](https://github.com/videojs/video.js/commit/b6d521f))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **texttracksettings:** DRYer code and remove massive HTML blob ([#3679](https://github.com/videojs/video.js/issues/3679)) ([fb74c71](https://github.com/videojs/video.js/commit/fb74c71))
|
||||
* remove un-needed contructor and function overrides ([#3721](https://github.com/videojs/video.js/issues/3721)) ([6889e92](https://github.com/videojs/video.js/commit/6889e92))
|
||||
|
||||
### Documentation
|
||||
|
||||
* Change registerSourceHandler param doc from first to index ([#3737](https://github.com/videojs/video.js/issues/3737)) ([b2c5b2a](https://github.com/videojs/video.js/commit/b2c5b2a))
|
||||
* **collaborator_guide:** add collaborator guide ([#3724](https://github.com/videojs/video.js/issues/3724)) ([8d51235](https://github.com/videojs/video.js/commit/8d51235))
|
||||
* **contributing.md:** update CONTRIBUTING.md with latest info ([#3722](https://github.com/videojs/video.js/issues/3722)) ([11a096d](https://github.com/videojs/video.js/commit/11a096d))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* Dispatch Flash events asynchronously ([#3700](https://github.com/videojs/video.js/pull/3700))
|
||||
* Cache currentTime and buffered from Flash ([#3705](https://github.com/videojs/video.js/issues/3705)) ([45ffa81](https://github.com/videojs/video.js/commit/45ffa81))
|
||||
* Use ES6 rest operator and allow V8 to optimize mergeOptions ([#3743](https://github.com/videojs/video.js/issues/3743)) ([5f42130](https://github.com/videojs/video.js/commit/5f42130))
|
||||
|
||||
### Tests
|
||||
|
||||
* **dom:** fix removeElClass test in Safari 10. ([#3768](https://github.com/videojs/video.js/issues/3768)) ([9965077](https://github.com/videojs/video.js/commit/9965077))
|
||||
* **hooks:** fix hooks unit test in ie8 ([#3745](https://github.com/videojs/video.js/issues/3745)) ([e9e5b5f](https://github.com/videojs/video.js/commit/e9e5b5f))
|
||||
|
||||
<a name="5.12.6"></a>
|
||||
## [5.12.6](https://github.com/videojs/video.js/compare/v5.12.5...v5.12.6) (2016-10-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make sure that document.createElement exists before using ([#3706](https://github.com/videojs/video.js/issues/3706)) ([49e29ba](https://github.com/videojs/video.js/commit/49e29ba)), closes [#3665](https://github.com/videojs/video.js/issues/3665)
|
||||
* remove unnecessary comments from video.min.js ([#3709](https://github.com/videojs/video.js/issues/3709)) ([fe760a4](https://github.com/videojs/video.js/commit/fe760a4)), closes [#3707](https://github.com/videojs/video.js/issues/3707)
|
||||
|
||||
<a name="5.12.5"></a>
|
||||
## [5.12.5](https://github.com/videojs/video.js/compare/v5.12.4...v5.12.5) (2016-10-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move html5 source handler incantation to bottom ([#3695](https://github.com/videojs/video.js/issues/3695)) ([7b9574b](https://github.com/videojs/video.js/commit/7b9574b))
|
||||
|
||||
<a name="5.12.4"></a>
|
||||
## [5.12.4](https://github.com/videojs/video.js/compare/v5.12.3...v5.12.4) (2016-10-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* logging failing on browsers that don't always have console ([#3686](https://github.com/videojs/video.js/issues/3686)) ([e932061](https://github.com/videojs/video.js/commit/e932061))
|
||||
* Restore timeupdate/loadedmetadata listeners for duration display ([#3682](https://github.com/videojs/video.js/issues/3682)) ([44ec0e4](https://github.com/videojs/video.js/commit/44ec0e4))
|
||||
|
||||
### Chores
|
||||
|
||||
* **grunt:** fix getting changelog by switching to npm-run ([#3687](https://github.com/videojs/video.js/issues/3687)) ([8845bd3](https://github.com/videojs/video.js/commit/8845bd3)), closes [#3683](https://github.com/videojs/video.js/issues/3683)
|
||||
|
||||
### Documentation
|
||||
|
||||
* **options.md:** Remove Bad Apostrophe ([#3677](https://github.com/videojs/video.js/issues/3677)) ([16c8559](https://github.com/videojs/video.js/commit/16c8559))
|
||||
* **tech.md:** Add a note on Flash permissions in sandboxed environments ([#3684](https://github.com/videojs/video.js/issues/3684)) ([66922a8](https://github.com/videojs/video.js/commit/66922a8))
|
||||
|
||||
<a name="5.12.3"></a>
|
||||
## [5.12.3](https://github.com/videojs/video.js/compare/v5.12.2...v5.12.3) (2016-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* **lang:** add missing translations in fr.json ([280ecd4](https://github.com/videojs/video.js/commit/280ecd4))
|
||||
* **lang:** add missing translations to el.json ([eb0efd4](https://github.com/videojs/video.js/commit/eb0efd4))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **controls:** fix load progress bar never highlighting first buffered time range ([ca02298](https://github.com/videojs/video.js/commit/ca02298))
|
||||
* **css:** remove commented out css ([5fdcd46](https://github.com/videojs/video.js/commit/5fdcd46)), closes [#3587](https://github.com/videojs/video.js/issues/3587)
|
||||
* disable HLS hack on Firefox for Android ([#3586](https://github.com/videojs/video.js/issues/3586)) ([dd2aff0](https://github.com/videojs/video.js/commit/dd2aff0))
|
||||
* proxy ios webkit events into fullscreenchange ([#3644](https://github.com/videojs/video.js/issues/3644)) ([e479f8c](https://github.com/videojs/video.js/commit/e479f8c))
|
||||
* **html5:** disable manual timeupdate events on html5 tech ([#3656](https://github.com/videojs/video.js/issues/3656)) ([920c54a](https://github.com/videojs/video.js/commit/920c54a))
|
||||
|
||||
### Chores
|
||||
|
||||
* move metadata to hidden folder and update references ([86f0830](https://github.com/videojs/video.js/commit/86f0830))
|
||||
* **deps:** add the bundle-collapser browserify plugin ([816291e](https://github.com/videojs/video.js/commit/816291e))
|
||||
* **package:** remove es2015-loose since it's an option for es2015 ([#3629](https://github.com/videojs/video.js/issues/3629)) ([c545acd](https://github.com/videojs/video.js/commit/c545acd))
|
||||
* **package:** update grunt-contrib-cssmin to version 1.0.2 ([#3595](https://github.com/videojs/video.js/issues/3595)) ([54e3db5](https://github.com/videojs/video.js/commit/54e3db5))
|
||||
* **package:** update grunt-shell to version 2.0.0 ([#3642](https://github.com/videojs/video.js/issues/3642)) ([2032b17](https://github.com/videojs/video.js/commit/2032b17))
|
||||
* refactor redundant code in html5 tech ([#3593](https://github.com/videojs/video.js/issues/3593)) ([6878c21](https://github.com/videojs/video.js/commit/6878c21))
|
||||
* refactor redundant or verbose code in player.js ([#3597](https://github.com/videojs/video.js/issues/3597)) ([ae3e277](https://github.com/videojs/video.js/commit/ae3e277))
|
||||
* update CHANGELOG automation to use conventional-changelog ([#3669](https://github.com/videojs/video.js/issues/3669)) ([d4e89d2](https://github.com/videojs/video.js/commit/d4e89d2))
|
||||
* update object.assign to ^4.0.4 ([08c7f4e](https://github.com/videojs/video.js/commit/08c7f4e))
|
||||
|
||||
### Documentation
|
||||
|
||||
* fix broken links in docs index.md ([4063f96](https://github.com/videojs/video.js/commit/4063f96))
|
||||
|
||||
### Tests
|
||||
|
||||
* **a11y:** add basic accessibility testing using grunt-accessibility ([7d85f27](https://github.com/videojs/video.js/commit/7d85f27))
|
||||
|
||||
## 5.12.2 (2016-09-28)
|
||||
* Changes from 5.11.7 on the 5.12 branch
|
||||
|
||||
## 5.12.1 (2016-08-25)
|
||||
* Changes from 5.11.6 on the 5.12 branch
|
||||
|
||||
## 5.13.0 (2016-08-25)
|
||||
* Ignored release
|
||||
|
||||
## 5.12.0 (2016-08-25)
|
||||
* @misteroneill, @BrandonOCasey, and @pagarwal123 updates all the code to pass the linter ([view](https://github.com/videojs/video.js/pull/3459))
|
||||
* @misteroneill added ghooks to run linter on git push ([view](https://github.com/videojs/video.js/pull/3459))
|
||||
* @BrandonOCasey removed unused base-styles.js file ([view](https://github.com/videojs/video.js/pull/3486))
|
||||
* @erikyuzwa, @gkatsev updated CSS build to inlcude the IE8-specific CSS from a separate file instead of it being inside of sass ([view](https://github.com/videojs/video.js/pull/3380)) ([view2](https://github.com/erikyuzwa/video.js/pull/1))
|
||||
* @gkatsev added null checks around navigator.userAgent ([view](https://github.com/videojs/video.js/pull/3502))
|
||||
* greenkeeper updated karma dependencies ([view](https://github.com/videojs/video.js/pull/3523))
|
||||
* @BrandonOCasey updated language docs to link to IANA language registry ([view](https://github.com/videojs/video.js/pull/3493))
|
||||
* @gkatsev removed unused dependencies ([view](https://github.com/videojs/video.js/pull/3516))
|
||||
* @misteroneill enabled and updated videojs-standard and fixed an issue with linting ([view](https://github.com/videojs/video.js/pull/3508))
|
||||
* @misteroneill updated tests to qunit 2.0 ([view](https://github.com/videojs/video.js/pull/3509))
|
||||
* @gkatsev added slack badge to README ([view](https://github.com/videojs/video.js/pull/3527))
|
||||
* @gkatsev reverted back to qunitjs 1.x to unbreak IE8. Added es5-shim to tests ([view](https://github.com/videojs/video.js/pull/3533))
|
||||
* @gkatsev updated build system to open es5 folder for bundles and dist folder other users ([view](https://github.com/videojs/video.js/pull/3445))
|
||||
* greenkeeper updated uglify ([view](https://github.com/videojs/video.js/pull/3547))
|
||||
* greenkeeper updated grunt-concurrent ([view](https://github.com/videojs/video.js/pull/3532))
|
||||
* greenkeeper updated karma-chrome-launcher ([view](https://github.com/videojs/video.js/pull/3553))
|
||||
* @gkatsev added tests for webpack and browserify bundling and node.js requiring ([view](https://github.com/videojs/video.js/pull/3558))
|
||||
* @rlchung fixed tests that weren't disposing players when they finished ([view](https://github.com/videojs/video.js/pull/3524))
|
||||
|
||||
## 5.11.9 (2016-10-25)
|
||||
* greenkeeper updated karma dependencies ([view](https://github.com/videojs/video.js/pull/3523))
|
||||
* update to latest uglify to fix preserve comments issue. Disable screw ie8 option. ([view](https://github.com/videojs/video.js/pull/3709))
|
||||
* remove sourcemap generation ([view](https://github.com/videojs/video.js/pull/3710))
|
||||
|
||||
## 5.11.8 (2016-10-17)
|
||||
* @misteroneill restore timeupdate/loadedmetadata listeners for duration display ([view](https://github.com/videojs/video.js/pull/3682))
|
||||
|
||||
## 5.11.7 (2016-09-28)
|
||||
* @gkatsev checked throwIfWhitespace first in hasElClass ([view](https://github.com/videojs/video.js/pull/3640))
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
# Collaborator Guide
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Issues and Pull Requests](#issues-and-pull-requests)
|
||||
* [Accepting changes](#accepting-changes)
|
||||
* [Involving the TSC](#involving-the-tsc)
|
||||
* [Landing a PR](#landing-a-pr)
|
||||
* [Landing a PR manually](#landing-a-pr-manually)
|
||||
* [Landing a PR manually with several changes](#landing-a-pr-manually-with-several-changes)
|
||||
* [I just made a mistake](#i-just-made-a-mistake)
|
||||
* [I accidentally pushed a broken commit or incorrect commit to master](#i-accidentally-pushed-a-broken-commit-or-incorrect-commit-to-master)
|
||||
* [I lost changes](#i-lost-changes)
|
||||
* [I accidentally committed a broken change to master](#i-accidentally-committed-a-broken-change-to-master)
|
||||
* [video.js releases](#videojs-releases)
|
||||
* [Getting dependencies](#getting-dependencies)
|
||||
* [Install contrib](#install-contrib)
|
||||
* [npm access](#npm-access)
|
||||
* [GitHub personal access token](#github-personal-access-token)
|
||||
* [Doing a release](#doing-a-release)
|
||||
* [Doc credit](#doc-credit)
|
||||
|
||||
## Issues and Pull Requests
|
||||
|
||||
Full courtesy should always be shown in video.js projects.
|
||||
|
||||
Collaborators may manage issues they feel qualified to handle, being mindful of our guidelines.
|
||||
|
||||
Any issue and PR can be closed if they are not relevant, when in doubt leave it open for more discussion. Issues can always be re-opened if new information is made available.
|
||||
|
||||
If issues or PRs are very short and don't contain much information, ask for more by linking to the [issue][issue template] or [PR][pr template] template. There is also a [response guide](https://github.com/videojs/video.js/wiki/New-Issue-Response-Guide) if you're unsure.
|
||||
|
||||
## Accepting changes
|
||||
|
||||
Any code change in video.js should be happening through Pull Requests on GitHub. This includes core committers.
|
||||
|
||||
Before a PR is merged, it must be reviewed by at least two core committers, at least one if it comes from a core committer.
|
||||
|
||||
Feel free to @-mention a particular core committer if you know they are experts in the area that is being changed.
|
||||
|
||||
If you are unsure about the modification and cannot take responsibility for it, defer to another core committer.
|
||||
|
||||
Before merging the change, it should be left open for other core committers to comment on. At least 24 hours during a weekday, and the 48 hours on a weekend. Trivial changes or bug fixes that have been reviewed by multiple committers may be merged without delay.
|
||||
|
||||
For non-breaking changes, if there is no disagreeming between the collaborators, the PR may be landed assuming it was reviewed. If there is still disagreement, it may need to be [escalated to the TSC](#involving-the-tsc).
|
||||
|
||||
Bug fixes require a test case that fails beforehand and succeeds after. All code changes should contain tests and pass on the CI.
|
||||
|
||||
### Involving the TSC
|
||||
|
||||
A change or issue can be elevated to the TSC by assing the `tsc-agent` label. This should be done in the following scenarios:
|
||||
|
||||
* There will be a major impact on the codebase or project
|
||||
* The change is inherently controversial
|
||||
* No agreement was reached between collaborators participating in the discussion
|
||||
|
||||
The TSC will be the final arbiter when required.
|
||||
|
||||
## Landing a PR
|
||||
|
||||
Landing a PR is fairly easy given that we can use the GitHub UI for it.
|
||||
|
||||
When using the big green button on GitHub, make sure the "squash and merge" is selected -- it should be the only allowed option. If a PR has two features in it and should be merged as two separate commits, either ask the contributor to break it up into two, or follow the [manual steps](#landing-a-pr-manually).
|
||||
|
||||
The commit message should follow our [conventional changelog conventions][conventions]. They are based on the angularjs changelog conventions. The changelog is then generated from these commit messages on release.
|
||||
|
||||
The first line of the commit message -- the header and first text box on GitHub -- should be prefixed with a type and optional scope followed by a short description of the commit.
|
||||
The type is required. Two common ones are `fix` and `feat` for bug fixes and new features. Scope is optional and can be anything.
|
||||
|
||||
The body should contain extra information, potentially copied from the original comment of the PR.
|
||||
|
||||
The footer should contain things like whether this is a breaking change or what issues were fixed by this PR.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```commit
|
||||
fix(html5): a regression with html5 tech
|
||||
|
||||
This is where you'd explain what the regression is.
|
||||
|
||||
Fixes #123
|
||||
```
|
||||
|
||||
### Landing a PR manually
|
||||
|
||||
_Optional:_ ensure you're not in a weird rebase or merge state:
|
||||
|
||||
```sh
|
||||
git am --abort
|
||||
git rebase --abort
|
||||
```
|
||||
|
||||
Checkout and update the master branch:
|
||||
|
||||
```sh
|
||||
git checkout master
|
||||
git remote update
|
||||
git rebase upstream/master
|
||||
```
|
||||
|
||||
Check out the PR:
|
||||
|
||||
```sh
|
||||
git fetch upstream pull/{{PR Number}}/head:{{name of branch}}
|
||||
git checkout -t {{name of branch}}
|
||||
```
|
||||
|
||||
> For example:
|
||||
>
|
||||
> ```sh
|
||||
> git fetch upstream pull/123/head:gkatsev-html5-fix
|
||||
> git checkout -t gkatsev-html5-fix
|
||||
> ```
|
||||
|
||||
_Optional:_ If necessary, rebase against master. If you have multiple features in the PR, [landing a PR manually with several changes](#landing-a-pr-manually-with-several-changes)
|
||||
|
||||
```sh
|
||||
git rebase master
|
||||
```
|
||||
|
||||
Fix up any issues that arise from the rebase, change back to the master branch and squash merge:
|
||||
|
||||
```sh
|
||||
git checkout master
|
||||
git merge --squash --no-commit gkatsev-html5-fix
|
||||
```
|
||||
|
||||
The `--no-commit` tells git not to make a commit on your behalf. It does stage everything for you, so, you can instead it:
|
||||
|
||||
```sh
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
Now get the author from the original commit:
|
||||
|
||||
```sh
|
||||
git log -n 1 --pretty=short gkatsev-html5-fix
|
||||
```
|
||||
|
||||
Which shows:
|
||||
|
||||
```txt
|
||||
commit 433c58224f5be34480c8e067ca6c5406ba1c1e9c
|
||||
Author: Gary Katsevman <git@gkatsev.com>
|
||||
|
||||
Update TOC
|
||||
```
|
||||
|
||||
Now you can commit the change the change with the author, following our commit guidelines
|
||||
|
||||
```sh
|
||||
git commit --author "Gary Katsevman <git@gkatsev.com>"
|
||||
```
|
||||
|
||||
Now that it's committed, push to master
|
||||
|
||||
```sh
|
||||
git push upstream master
|
||||
```
|
||||
|
||||
Congratulate yourself for a job well done and the contributor for having his change landed in master.
|
||||
|
||||
#### Landing a PR manually with several changes
|
||||
|
||||
Follow the same steps as before but when you rebase against master, you want to do an interactive rebase and then squash the changes into just a few commits.
|
||||
|
||||
```sh
|
||||
git rebase -i master
|
||||
```
|
||||
|
||||
This will give you an output like the following:
|
||||
|
||||
```txt
|
||||
pick b4dc15d Update CONTRIBUTING.md with latest info
|
||||
pick 8592149 Add Dev certificate of origin
|
||||
pick 259dee6 Add grunt and doctoc npm scripts
|
||||
pick f12af12 Add conventional-changelog-videojs link
|
||||
pick ae4613a Update node's CONTRIBUTING.md url
|
||||
pick 433c582 Update TOC
|
||||
|
||||
# Rebase f599ef4..433c582 onto f599ef4 (6 command(s))
|
||||
#
|
||||
# Commands:
|
||||
# p, pick = use commit
|
||||
# r, reword = use commit, but edit the commit message
|
||||
# e, edit = use commit, but stop for amending
|
||||
# s, squash = use commit, but meld into previous commit
|
||||
# f, fixup = like "squash", but discard this commit's log message
|
||||
# x, exec = run command (the rest of the line) using shell
|
||||
# d, drop = remove commit
|
||||
#
|
||||
# These lines can be re-ordered; they are executed from top to bottom.
|
||||
#
|
||||
# If you remove a line here THAT COMMIT WILL BE LOST.
|
||||
#
|
||||
# However, if you remove everything, the rebase will be aborted.
|
||||
#
|
||||
# Note that empty commits are commented out
|
||||
```
|
||||
|
||||
Replace `pick` to `fixup` or `edit` depending on how you want the output to look. You can also re-order the commits, if necessary.
|
||||
|
||||
> `fixup` will squash the commit it's infront of up into the commit above it
|
||||
>
|
||||
> `edit` will allow you to edit the commit message before continuing
|
||||
|
||||
```txt
|
||||
edit b4dc15d Update CONTRIBUTING.md with latest info
|
||||
fixup 8592149 Add Dev certificate of origin
|
||||
fixup f12af12 Add conventional-changelog-videojs link
|
||||
fixup ae4613a Update node's CONTRIBUTING.md url
|
||||
fixup 433c582 Update TOC
|
||||
edit 259dee6 Add grunt and doctoc npm scripts
|
||||
```
|
||||
|
||||
When you get to the edit commits, git will give more information, but you'd want to run ammend the current commit while following our commit guidelines
|
||||
|
||||
```sh
|
||||
git commit --amend
|
||||
```
|
||||
|
||||
After going through and making the commits you want, you want to change back to master and then rebase the branch onto master so we get a clean history
|
||||
|
||||
```sh
|
||||
git rebase gkatsev-html5-fix
|
||||
```
|
||||
|
||||
This will put our two commits into master:
|
||||
|
||||
```txt
|
||||
b4dc15d chore(contributing.md): Update CONTRIBUTING.md with latest info <Gary Katsevman>
|
||||
259dee6 chore(package.json): Add grunt and doctoc npm scripts <Gary Katsevman>
|
||||
9e20386 v5.12.6 <Gary Katsevman>
|
||||
```
|
||||
|
||||
Now you're ready to push to master as in the normal instructions.
|
||||
|
||||
#### I just made a mistake
|
||||
|
||||
While `git` allows you to update the remote branch with a force push (`git push -f`). This is generally frowned upon since you're rewriting public history. However, if you just pushed the change and it's been less than 10 minutes since you've done with, you may force push to update the commit, assuming no one else has already pushed after you.
|
||||
|
||||
##### I accidentally pushed a broken commit or incorrect commit to master
|
||||
|
||||
Assuming no more than 10 minutes have passed, you may force-push to update or remove the commit. If someone else has already pushed to master or 10 minutes have passed, you should instead use the revert command (`git revert`) to revert the commit and then commit the proper change, or just fix it forward with a followup commit that fixes things.
|
||||
|
||||
##### I lost changes
|
||||
|
||||
Assuming that the changes were committed, even if you lost the commit in your current history does not mean that it is lost. In a lot of cases you can still recover it from the PR branch or if all else fails look at [git's reflog](https://git-scm.com/docs/git-reflog).
|
||||
|
||||
##### I accidentally committed a broken change to master
|
||||
|
||||
This is a great time to discover that something is broken. Because it hasn't been pushed to GitHub yet, it's very easy to reset the change as if nothing has happened and try again.
|
||||
|
||||
To do so, just reset the branch against master.
|
||||
|
||||
```sh
|
||||
git reset --hard upstream/master
|
||||
```
|
||||
|
||||
## video.js releases
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Getting dependencies
|
||||
|
||||
#### Install contrib
|
||||
|
||||
You can install it globally
|
||||
|
||||
```sh
|
||||
npm i -g contrib/contrib
|
||||
```
|
||||
|
||||
#### npm access
|
||||
|
||||
To see who currently has access run this:
|
||||
|
||||
```sh
|
||||
npm owner ls video.js
|
||||
```
|
||||
|
||||
If you are a core committer, you can request access to npm from one of the current owners.
|
||||
|
||||
#### GitHub personal access token
|
||||
|
||||
This is used to make a GitHub release on videojs. You can get a token from the [personal access tokens](https://github.com/settings/tokens) page.
|
||||
|
||||
After generating one, make sure to keep it safe because GitHub will not show the token for you again. A good place to save it is Lastpass Secure Notes.
|
||||
|
||||
### Doing a release
|
||||
|
||||
To do a release, check out the master branch
|
||||
|
||||
```sh
|
||||
git checkout master
|
||||
```
|
||||
|
||||
Then run the contrib command to do the next release. Don't forget to provide your GitHub token so the GitHub release goes through.
|
||||
|
||||
```sh
|
||||
VJS_GITHUB_USER=gkatsev VJS_GITHUB_TOKEN=my-personal-access-token contrib release next patch
|
||||
```
|
||||
|
||||
This makes a patch release, you can also do a `minor` and a `major` release.
|
||||
|
||||
After it's done, verify that the GitHub release has the correct changelog output.
|
||||
|
||||
## Doc credit
|
||||
|
||||
This collaborator guide was heavily inspired by [node.js's guide](https://github.com/nodejs/node/blob/master/COLLABORATOR_GUIDE.md)
|
||||
|
||||
[issue template]: /.github/ISSUE_TEMPLATE.md
|
||||
|
||||
[pr template]: /.github/PULL_REQUEST_TEMPLATE.md
|
||||
|
||||
[conventions]: https://github.com/videojs/conventional-changelog-videojs/blob/master/convention.md
|
||||
+212
-161
@@ -1,139 +1,136 @@
|
||||
CONTRIBUTING
|
||||
============
|
||||
# CONTRIBUTING
|
||||
|
||||
So you want to help out? Great! There's a number of ways you can get involved.
|
||||
|
||||
* [File and discuss issues](#filing-issues)
|
||||
* [Contribute code](#contributing-code)
|
||||
* [Build and share plugins](docs/guides/plugins.md)
|
||||
* [Answer questions on Stack Overflow](http://stackoverflow.com/questions/tagged/video.js)
|
||||
## Table of Contents
|
||||
|
||||
* [Other repositories where issues could be filed](#other-repositories-where-issues-could-be-filed)
|
||||
* [Filing issues](#filing-issues)
|
||||
* [Reporting a Bug](#reporting-a-bug)
|
||||
* [Requesting a Feature](#requesting-a-feature)
|
||||
* [Contributing code](#contributing-code)
|
||||
* [Building video.js locally](#building-videojs-locally)
|
||||
* [Forking and cloning the repository](#forking-and-cloning-the-repository)
|
||||
* [Installing local dependencies](#installing-local-dependencies)
|
||||
* [Running tests](#running-tests)
|
||||
* [Building videojs](#building-videojs)
|
||||
* [Testing Locally](#testing-locally)
|
||||
* [Sandbox test directory](#sandbox-test-directory)
|
||||
* [Running a local web server](#running-a-local-web-server)
|
||||
* [Watching source and test changes](#watching-source-and-test-changes)
|
||||
* [Making Changes](#making-changes)
|
||||
* [Step 1: Verify](#step-1-verify)
|
||||
* [Step 2: Update remote](#step-2-update-remote)
|
||||
* [Step 3: Branch](#step-3-branch)
|
||||
* [Step 4: Commit](#step-4-commit)
|
||||
* [Step 5: Test](#step-5-test)
|
||||
* [Step 6: Push](#step-6-push)
|
||||
* [Code Style Guide](#code-style-guide)
|
||||
* [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin-11)
|
||||
* [Doc Credit](#doc-credit)
|
||||
|
||||
## Other repositories where issues could be filed
|
||||
|
||||
There's also other Video.js projects where you can help. (check the [video.js org](https://github.com/videojs) for an up-to-date list of projects)
|
||||
|
||||
* [Videojs.com](https://github.com/videojs/videojs.com)
|
||||
* [Video.js flash player](https://github.com/videojs/video-js-swf)
|
||||
* [Player skin designer](https://github.com/videojs/designer)
|
||||
* [Contribflow](https://github.com/zencoder/contribflow)
|
||||
* [Videojs.com](https://github.com/videojs/videojs.com)
|
||||
* [Video.js flash player](https://github.com/videojs/videojs-flash)
|
||||
* [HLS](https://github.com/videojs/videojs-contrib-hls)
|
||||
* [DASH](https://github.com/videojs/videojs-contrib-dash)
|
||||
* [Youtube Tech](https://github.com/videojs/videojs-youtube)
|
||||
* [Vimeo Tech](https://github.com/videojs/videojs-vimeo)
|
||||
* [Ads](https://github.com/videojs/videojs-contrib-ads)
|
||||
* [Plugin generator](https://github.com/videojs/generator-videojs-plugin)
|
||||
* [Linter][linter]
|
||||
|
||||
## Filing issues
|
||||
|
||||
Filing issues
|
||||
-------------
|
||||
[GitHub Issues](https://github.com/videojs/video.js/issues) are used for all discussions around the codebase, including **bugs**, **features**, and other **enhancements**.
|
||||
|
||||
When filling out an issue, make sure to fill out the questions in the
|
||||
|
||||
### Reporting a Bug
|
||||
|
||||
**A bug is a demonstrable problem** that is caused by the code in the repository. Good bug reports are extremely helpful. Thank You!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. Use the [GitHub issue search](https://github.com/videojs/video.js/issues) — check if the issue has already been reported.
|
||||
|
||||
2. Check if the issue has already been fixed — try to reproduce it using the latest `master` branch in the repository.
|
||||
|
||||
3. Isolate the problem — **create a [reduced test case](https://css-tricks.com/reduced-test-cases/)** with a live example. You can possibly use [this JSBin example](http://jsbin.com/axedog/7/edit) as a starting point.
|
||||
1. If your issue is with a particular video.js plugin or subproject, please open an issue against that project. See [list of some potential other projects above](#other-repositories-where-issues-could-be-filed)
|
||||
1. Use the [GitHub issue search](https://github.com/videojs/video.js/issues) — check if the issue has already been reported.
|
||||
1. Check if the issue has already been fixed — try to reproduce it using the latest `master` branch in the repository.
|
||||
1. Isolate the problem — **create a [reduced test case](https://css-tricks.com/reduced-test-cases/)** with a live example. You can possibly use [this JSBin example](http://jsbin.com/axedog/edit) as a starting point -- don't forget to update it to the videojs version you use.
|
||||
1. Answer all questions in the [issue template][]. The questions in the issue template are designed to try and provide the maintainers with as much information possible to minimize back-and-forth to get the issue resolved.
|
||||
|
||||
A good bug report should be as detailed as possible, so that others won't have to follow up for the essential details.
|
||||
|
||||
Here's an example:
|
||||
|
||||
> Short yet concise Bug Summary
|
||||
>
|
||||
> Description:
|
||||
> Happens on Windows 7 and OSX. Seen with IE9, Firefox 19 OSX, Chrome 21, Flash 11.6 and 11.2
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> Expected:
|
||||
> (describe the expected outcome of the steps above)
|
||||
>
|
||||
> Actual:
|
||||
> (describe what actually happens)
|
||||
>
|
||||
> `<url>` (a link to the reduced test case, if it exists)
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
**[File a bug report](https://github.com/videojs/video.js/issues/new)**
|
||||
|
||||
### Requesting a Feature
|
||||
|
||||
1. [Check the plugin list](https://github.com/videojs/video.js/wiki/Plugins) for any plugins that may already support the feature.
|
||||
|
||||
2. [Search the issues](https://github.com/videojs/video.js/issues) for any previous requests for the same feature, and give a thumbs up or +1 on existing requests.
|
||||
|
||||
2. If no previous requests exist, create a new issue. Please be as clear as possible about why the feautre is needed and the intended use case.
|
||||
1. [Check the plugin list](http://videojs.com/plugins/) for any plugins that may already support the feature.
|
||||
1. [Search the issues](https://github.com/videojs/video.js/issues) for any previous requests for the same feature, and give a thumbs up or +1 on existing requests.
|
||||
1. If no previous requests exist, create a new issue. Please be as clear as possible about why the feautre is needed and the intended use case.
|
||||
1. Once again, be as details as possible and follow the [issue template][]
|
||||
|
||||
**[Request a feature](https://github.com/videojs/video.js/issues/new)**
|
||||
|
||||
Contributing code
|
||||
-----------------
|
||||
## Contributing code
|
||||
|
||||
To contibute code you'll need to be able to build a copy of Video.js and run tests locally. There are a few requirements before getting started.
|
||||
|
||||
- Node.js -- Video.js uses Node for build and test automation. Node is available for Windows, Mac OS X, Linux, and SunOS, as well as source code if that doesn't scare you. [Download and install Node.js](http://nodejs.org/download/)
|
||||
* Node.js
|
||||
Video.js uses Node for build and test automation. Node is available for Windows, Mac OS X, Linux, and SunOS, as well as source code if that doesn't scare you. [Download and install Node.js](http://nodejs.org/download/)
|
||||
|
||||
- grunt-cli -- Install grunt-cli globally so that you will have the correct version of grunt available for any project that needs it.
|
||||
* `grunt-cli`
|
||||
Optionally, install `grunt-cli` globally to use grunt directly. It can always be run via an npm script:
|
||||
|
||||
On Unix-based systems, you'll have to do this as a superuser:
|
||||
|
||||
```bash
|
||||
sudo npm install -g grunt-cli
|
||||
```sh
|
||||
npm run grunt
|
||||
```
|
||||
On Windows, you can just run:
|
||||
|
||||
```bash
|
||||
```sh
|
||||
npm install -g grunt-cli
|
||||
```
|
||||
|
||||
- Contribflow -- A homegrown git workflow tool for managing feature/hotfix branches and submitting pull requests. If you have your own preferred git workflow, contribflow isn't required, but the following instructions will assume you're using it.
|
||||
Depending on how you have node and npm set up, you may need to run the global install (`-g`) as a superuser by prepending `sudo`.
|
||||
|
||||
On Unix-based systems, you'll have to do this as a superuser:
|
||||
### Building video.js locally
|
||||
|
||||
```bash
|
||||
sudo npm install -g contribflow
|
||||
```
|
||||
#### Forking and cloning the repository
|
||||
|
||||
On Windows, you can just run:
|
||||
|
||||
```bash
|
||||
npm install -g contribflow
|
||||
```
|
||||
|
||||
### Building your own copy of Video.js
|
||||
|
||||
First, [fork](http://help.github.com/fork-a-repo/) the video.js git repository. At the top of every github page, there is a Fork button. Click it, and the forking process will copy Video.js into your own GitHub account.
|
||||
First, [fork](http://help.github.com/fork-a-repo/) the video.js git repository. At the top of every GitHub page, there is a Fork button. Click it, and the forking process will copy Video.js into your own GitHub account.
|
||||
|
||||
Clone your fork of the repo into your code directory
|
||||
|
||||
```bash
|
||||
```sh
|
||||
git clone https://github.com/<your-username>/video.js.git
|
||||
```
|
||||
|
||||
Navigate to the newly cloned directory
|
||||
|
||||
```bash
|
||||
```sh
|
||||
cd video.js
|
||||
```
|
||||
|
||||
Assign the original repo to a remote called "upstream"
|
||||
|
||||
```
|
||||
```sh
|
||||
git remote add upstream https://github.com/videojs/video.js.git
|
||||
```
|
||||
|
||||
>In the future, if you want to pull in updates to video.js that happened after you cloned the main repo, you can run:
|
||||
> In the future, if you want to pull in updates to video.js that happened after you cloned the main repo, you can run:
|
||||
>
|
||||
> ```bash
|
||||
> ```sh
|
||||
> git remote update
|
||||
> git checkout master
|
||||
> git pull upstream master
|
||||
> ```
|
||||
|
||||
#### Installing local dependencies
|
||||
|
||||
Install the required node.js modules using node package manager
|
||||
|
||||
```bash
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
@@ -141,135 +138,189 @@ npm install
|
||||
> `npm config set color false`
|
||||
> Note that this change takes effect when a new command prompt window is opened; the current window will not be affected.
|
||||
|
||||
Build a local copy of video.js and run tests
|
||||
#### Running tests
|
||||
|
||||
```bash
|
||||
grunt dist
|
||||
grunt test
|
||||
To run the tests all you need to do is run
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
Video.js is also configured to run tests with Karma. Karma is installed as a grunt plugin to run QUnit tests in real browsers, as opposed to simply running the tests in phantomjs, a headless browser. To run the tests with Karma:
|
||||
This will build video.js locally and run the tests using [Karma](https://karma-runner.github.io/1.0/index.html) which runs our tests in actual browsers.
|
||||
|
||||
```bash
|
||||
grunt karma:dev
|
||||
#### Building videojs
|
||||
|
||||
To build video.js, simply run
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
At this point you should have a built copy of video.js in a directory named `dist`, and all tests should be passing.
|
||||
This outputs an `es5/` and `dist/` folder. The `es5/` folder is used by bundling tools like browserify and webpack to package video.js into projects. The `dist/` folder has pre-compiled versions of video.js, including a minified version and the CSS file. This file can be included in page via a `<script></script>` tag.
|
||||
|
||||
### Making Changes
|
||||
#### Testing Locally
|
||||
|
||||
Whether you're adding something new, making something better, or fixing a bug, you'll first want to search the [GitHub issues](https://github.com/videojs/video.js/issues) and [plugins list](https://github.com/videojs/video.js/wiki/Plugins) to make sure you're aware of any previous discussion or work. If an unclaimed issue exists, claim it via a comment. If no issue exists for your change, submit one, follwing the [issue filing guidelines](#filing-issues).
|
||||
Besides running automated tests, you often want to run video.js manually and play around with things as you're developing. A few things are provided to make it easier.
|
||||
|
||||
There are two categories of changes in video.js land, features and hotfixes (Video.js follows a branching model similar to [gitflow](http://nvie.com/posts/a-successful-git-branching-model/)). Hotfixes are for urgent fixes that need to be released immediately as a patch. Features are for everything else (including non-urgent fixes). If you think you have a hotfix scenario, verify that (via comment) before starting the work. We'll focus on features here, but you can swap `hotfix` for `feature` in any command.
|
||||
#### Sandbox test directory
|
||||
|
||||
Start a new development branch
|
||||
There's a sandbox directory where you can add any file and it won't get tracked in git. To start you can copy the example index file.
|
||||
|
||||
```bash
|
||||
contrib feature start
|
||||
```
|
||||
|
||||
You'll be prompted to name the branch. After that, contrib will create the branch locally, and use git to push it up to your origin, and track it. You're now ready to start building your feature or fixing that bug! Be sure to read the [Code Style Guide](#code-style-guide).
|
||||
|
||||
While you're developing, you can ensure your changes are working by writing tests (in the `test` directory) and running `grunt test`.
|
||||
|
||||
There's also a sandbox directory where you can add any file and it won't get tracked as a change. To start you can copy the example index file and see a working version of a player (using the local source code) by loading it in a browser.
|
||||
|
||||
```bash
|
||||
```sh
|
||||
cp sandbox/index.html.example sandbox/index.html
|
||||
open sandbox/index.html
|
||||
```
|
||||
|
||||
See [the following section](#running-a-local-web-server) for how to open the page in a browser.
|
||||
|
||||
### Testing Locally
|
||||
A simple Connect server is available via the Grunt plugin. The commands below will allow you to setup a test sandbox and begin development.
|
||||
#### Running a local web server
|
||||
|
||||
```bash
|
||||
cp sandbox/index.html.example sandbox/index.html
|
||||
This ties in nicely with the sandbox directory. You can always open the `sandbox/index.html` file directly but in some cases it may not work properly.
|
||||
|
||||
> Flash files (`.swf`) that are local and loaded into a locally accessed page (file:///) will NOT run.
|
||||
> To get around this you must use a local web server.
|
||||
|
||||
To run the local webserver, you can run it in a couple of ways.
|
||||
|
||||
```sh
|
||||
grunt connect
|
||||
open http://localhost:9999/sandbox/index.html
|
||||
```
|
||||
|
||||
> NOTES regarding local testing in Chrome 21+ (as of 2013/01/01)
|
||||
> Flash files that are local and loaded into a locally accessed page (file:///) will NOT run.
|
||||
> To get around this you can do either of the following:
|
||||
>
|
||||
> 1. Do your development and testing using a local HTTP server. See Grunt commands above.
|
||||
>
|
||||
> 2. [Disable the version of Flash included with Chrome](http://helpx.adobe.com/flash-player/kb/flash-player-google-chrome.html#How_can_I_run_debugger_or_alternate_versions_of_Flash_Player_in_Google_Chrome) and enable a system-wide version of Flash instead.
|
||||
or
|
||||
|
||||
Commit and push changes as you go (using git directly). Write thorough descriptions of your changes in your commit messages.
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -av
|
||||
git push
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
> GitHub allows you to close an issue through your commit message using the [fixes](https://github.com/blog/831-issues-2-0-the-next-generation) keyword.
|
||||
The latter does some extra work which will be described in the next section.
|
||||
|
||||
#### Watching source and test changes
|
||||
|
||||
As you're developing, you want the build to re-run and update itself, and potentially re-run the tests. In addition, you want to launch a local web-server that you can open the `sandbox` directory in.
|
||||
To do so, you just need to run
|
||||
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
This sets up the local webserver using connect and then watches source files, test files, and CSS files for you and rebuilds things as they happen.
|
||||
|
||||
### Making Changes
|
||||
|
||||
#### Step 1: Verify
|
||||
|
||||
Whether you're adding something new, making something better, or fixing a bug, you'll first want to search the [GitHub issues](https://github.com/videojs/video.js/issues) and [plugins list](https://github.com/videojs/video.js/wiki/Plugins) to make sure you're aware of any previous discussion or work. If an unclaimed issue exists, claim it via a comment. If no issue exists for your change, submit one, follwing the [issue filing guidelines](#filing-issues).
|
||||
|
||||
#### Step 2: Update remote
|
||||
|
||||
Before starting work, you want to update your local repository to have all the latest changes.
|
||||
|
||||
```sh
|
||||
git remote update
|
||||
git checkout master
|
||||
git rebase upstream/master
|
||||
```
|
||||
|
||||
#### Step 3: Branch
|
||||
|
||||
You want to do your work in a separate branch.
|
||||
|
||||
```sh
|
||||
git checkout -b my-branch
|
||||
```
|
||||
|
||||
#### Step 4: Commit
|
||||
|
||||
Commit changes as you go. Write thorough descriptions of your changes in your commit messages.
|
||||
For more information see our [conventional changelog guidelines for video.js](https://github.com/videojs/conventional-changelog-videojs/blob/master/convention.md)
|
||||
Follow these guidelines:
|
||||
|
||||
1. The first line should be less than 50 characters and contain a short description of the commit.
|
||||
1. The body should contain a more detailed description. It can contain things like reasoning for the change and specifics of what changed.
|
||||
1. A footer can be added if this fixes a particular issue on GitHub.
|
||||
|
||||
```sh
|
||||
git add src/js/player.js
|
||||
git commit
|
||||
```
|
||||
|
||||
An example of the first line of a commit message: `fix: changed the footer to correctly display foo`
|
||||
|
||||
In the body of the commit message, we can talk about why we made the change. What the change entails.
|
||||
Any testing considerations or things to think about when looking at the commit. For Example:
|
||||
|
||||
```txt
|
||||
fix: one line commit explanation
|
||||
|
||||
In the body of the commit message, we can talk about why we made the change. What the change entails.
|
||||
|
||||
Any testing considerations or things to think about when looking at the commit.
|
||||
|
||||
Fixes #123. The footer can contain Fixes messages.
|
||||
```
|
||||
|
||||
> Make sure that git knows your name and email:
|
||||
>
|
||||
> ```bash
|
||||
> My commit message. fixes #123
|
||||
> Testing: (briefly describe any testing here, for example, 'unit tests and cross-browser manual tests around playback and network interruption')
|
||||
> ```sh
|
||||
> git config --global user.name "Random User"
|
||||
> git config --global user.email "random.user@example.com"
|
||||
> ```
|
||||
|
||||
### Submitting your changes
|
||||
#### Step 5: Test
|
||||
|
||||
First, thoroughly test your feature or fix, including writing tests to make sure your change doesn't get regressed in a future update. If you're fixing a bug, we recommend in addition to testing the fix itself, to do some testing around the areas that your fix has touched. For example, a brief smoketest of the player never hurts.
|
||||
Any code change should come with corresponding test changes. Especially bug fixes.
|
||||
Tests attached to bug fixes should fail before the change and succeed with it.
|
||||
|
||||
Make sure your changes are pushed to origin
|
||||
|
||||
```bash
|
||||
git push
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
Use contrib to submit a pull request (make sure you're in your feature branch)
|
||||
#### Step 6: Push
|
||||
|
||||
```bash
|
||||
contrib feature submit
|
||||
```sh
|
||||
git push origin my-branch
|
||||
```
|
||||
|
||||
You'll be prompted for title and description for the Pull Request. After that, contrib will use Git to submit your pull request to video.js.
|
||||
Then go to the [repo page](http://github.com/videojs/video.js) and click the "Pull Request" button and fill out the [pull request template](/.github/PULL_REQUEST_TEMPLATE.md)
|
||||
|
||||
You're Done! (except for cleanup.) To clean up your feature or hotfix branch:
|
||||
### Code Style Guide
|
||||
|
||||
First, checkout your feature or issue branch:
|
||||
Our javascript is linted using [videojs-standard][linter].
|
||||
|
||||
```bash
|
||||
git checkout (branchname)
|
||||
```
|
||||
## [Developer's Certificate of Origin 1.1](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#developers-certificate-of-origin-11)
|
||||
|
||||
Run this command to clean up your feature:
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
```bash
|
||||
contrib feature delete
|
||||
```
|
||||
* (a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
Run this command to clean up your bug fix:
|
||||
* (b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
```bash
|
||||
contrib hotfix delete
|
||||
```
|
||||
> PLEASE NOTE: THIS WILL DELETE YOUR LOCAL AND REMOTE COPIES OF THE FEATURE.
|
||||
> This is meant to clean up your local and remote branches, so make sure any changes you don't want to lose have been pulled into the parent project or another branch first.
|
||||
* (c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
Code Style Guide
|
||||
----------------
|
||||
Please follow [Google's JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) to the letter. If your editor supports [.editorconfig](http://editorconfig.org/#download) it will make it easier to manage differences from your own coding style.
|
||||
* (d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
|
||||
### Style examples include:
|
||||
* Two space indents.
|
||||
* Delimit strings with single-quotes `'`, not double-quotes `"`.
|
||||
* No trailing whitespace, except in markdown files where a linebreak must be forced.
|
||||
* No more than [one assignment](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) per `var` statement.
|
||||
* Prefer `if` and `else` to ["clever"](http://programmers.stackexchange.com/a/25281) uses of `? :` conditional or `||`, `&&` logical operators.
|
||||
* **When in doubt, follow the conventions you see used in the source already.**
|
||||
## Doc Credit
|
||||
|
||||
If you happen to find something in the codebase that does not follow the style guide, that's a good opportunity to make your first contribution!
|
||||
|
||||
---
|
||||
### Doc Credit
|
||||
This doc was inspired by some great contribution guide examples including [contribute.md template](https://github.com/contribute-md/contribute-md-template),
|
||||
[grunt](https://github.com/gruntjs/grunt/wiki/Contributing),
|
||||
[html5 boilerplate](https://github.com/h5bp/html5-boilerplate/blob/master/CONTRIBUTING.md),
|
||||
[jquery](https://github.com/jquery/jquery/blob/master/CONTRIBUTING.md),
|
||||
and [node.js](https://github.com/joyent/node/wiki/Contributing).
|
||||
and [node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md).
|
||||
|
||||
[issue template]: /.github/ISSUE_TEMPLATE.md
|
||||
|
||||
[linter]: https://github.com/videojs/standard
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
require('babel/register');
|
||||
require('babel-register');
|
||||
|
||||
// Need to `require` a separate Grunt file so we can use ES6 syntax via
|
||||
// Babel's require hook.
|
||||
|
||||
+94
-35
@@ -1,63 +1,122 @@
|
||||

|
||||
![Video.js logo][logo]
|
||||
|
||||
# [Video.js - HTML5 Video Player](http://videojs.com)
|
||||
[](https://travis-ci.org/videojs/video.js)
|
||||
[](https://coveralls.io/github/videojs/video.js?branch=master)
|
||||
# [Video.js - HTML5 Video Player][vjs]
|
||||
|
||||
[](https://nodei.co/npm/video.js/)
|
||||
[![Build Status][travis-icon]][travis-link]
|
||||
[![Coverage Status][coveralls-icon]][coveralls-link]
|
||||
[![Slack Status][slack-icon]][slack-link]
|
||||
|
||||
> Video.js is a web video player built from the ground up for an HTML5 world. It supports HTML5 and Flash video, as well as YouTube and Vimeo (through [plugins](https://github.com/videojs/video.js/wiki/Plugins)). It supports video playback on desktops and mobile devices. This project was started mid 2010, and the player is now used on over ~~50,000~~ ~~100,000~~ 200,000 websites.
|
||||
[![NPM][npm-icon]][npm-link]
|
||||
|
||||
## Quick start
|
||||
Thanks to the awesome folks over at [Fastly](http://www.fastly.com/), there's a free, CDN hosted version of Video.js that anyone can use.
|
||||
Also, check out the [Getting Started](http://videojs.com/getting-started/) page on our website which has the latest urls as well.
|
||||
Simply add these includes to your document's
|
||||
`<head>`:
|
||||
> Video.js is a web video player built from the ground up for an HTML5 world. It supports HTML5 and Flash video, as well as YouTube and Vimeo (through [plugins][plugins]). It supports video playback on desktops and mobile devices. This project was started mid 2010, and the player is now used on over ~~50,000~~ ~~100,000~~ ~~200,000~~ [400,000 websites][builtwith].
|
||||
|
||||
## Quick Start
|
||||
|
||||
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.8/video-js.min.css" rel="stylesheet">
|
||||
<script src="//vjs.zencdn.net/5.8/video.min.js"></script>
|
||||
<link href="//vjs.zencdn.net/5.11/video-js.min.css" rel="stylesheet">
|
||||
<script src="//vjs.zencdn.net/5.11/video.min.js"></script>
|
||||
```
|
||||
|
||||
Then, whenever you want to use Video.js you can simply use the `<video>` element as your normally would, but with an additional `data-setup` attribute containing any Video.js options. These options
|
||||
can include any Video.js option plus potential [plugin](http://videojs.com/plugins/) options, just make sure they're valid JSON!
|
||||
> For the latest URLs, 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!
|
||||
|
||||
```html
|
||||
<video id="really-cool-video" class="video-js vjs-default-skin" controls
|
||||
preload="auto" width="640" height="264" poster="really-cool-video-poster.jpg"
|
||||
data-setup='{}'>
|
||||
<source src="really-cool-video.mp4" type="video/mp4">
|
||||
<source src="really-cool-video.webm" type="video/webm">
|
||||
<video
|
||||
id="my-player"
|
||||
class="video-js"
|
||||
controls
|
||||
preload="auto"
|
||||
poster="//vjs.zencdn.net/v/oceans.png"
|
||||
data-setup='{}'>
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4"></source>
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm"></source>
|
||||
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg"></source>
|
||||
<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>
|
||||
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>
|
||||
```
|
||||
|
||||
If you don't want to use auto-setup, you can leave off the `data-setup` attribute and initialize a video element manually.
|
||||
When the page loads, Video.js will find this element and automatically setup a player in its place.
|
||||
|
||||
```javascript
|
||||
var player = videojs('really-cool-video', { /* Options */ }, function() {
|
||||
console.log('Good to go!');
|
||||
If you don't want to use automatic setup, you can leave off the `data-setup` attribute and initialize a `<video>` element manually using the `videojs` function:
|
||||
|
||||
this.play(); // if you don't trust autoplay for some reason
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
```
|
||||
|
||||
The `videojs` function also accepts an `options` object and a callback to be invoked
|
||||
when the player is ready:
|
||||
|
||||
```js
|
||||
var options = {};
|
||||
|
||||
var player = videojs('my-player', options, function onPlayerReady() {
|
||||
videojs.log('Your player is ready!');
|
||||
|
||||
// In this context, `this` is the player that was created by Video.js.
|
||||
this.play();
|
||||
|
||||
// How about an event listener?
|
||||
this.on('ended', function() {
|
||||
console.log('awww...over so soon?');
|
||||
videojs.log('Awww...over so soon?!');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
If you're ready to dive in, the [documentation](http://docs.videojs.com) is the first place to go for more information.
|
||||
If you're ready to dive in, the [Getting Started][getting-started] page and [documentation][docs] are the best places to go for more information. If you get stuck, head over to our [Slack channel][slack-link]!
|
||||
|
||||
## Contributing
|
||||
Video.js is a free and open source library, and we appreciate any help you're willing to give. Check out the [contributing guide](CONTRIBUTING.md).
|
||||
|
||||
_Video.js uses [BrowserStack](https://browserstack.com) for compatibility testing_
|
||||
## Building your own Video.js from source
|
||||
To build your own custom version read the section on [contributing code](CONTRIBUTING.md#contributing-code) and ["Building your own copy"](CONTRIBUTING.md#building-your-own-copy-of-videojs) in the contributing guide.
|
||||
## License
|
||||
Video.js is a free and open source library, and we appreciate any help you're willing to give - whether it's fixing bugs, improving documentation, or suggesting new features. Check out the [contributing guide][contributing] for more!
|
||||
|
||||
Video.js is licensed under the Apache License, Version 2.0. [View the license file](LICENSE)
|
||||
_Video.js uses [BrowserStack][browserstack] for compatibility testing._
|
||||
|
||||
## [License][license]
|
||||
|
||||
Video.js is [licensed][license] under the Apache License, Version 2.0.
|
||||
|
||||
[browserstack]: https://browserstack.com
|
||||
|
||||
[builtwith]: https://trends.builtwith.com/media/VideoJS
|
||||
|
||||
[contributing]: CONTRIBUTING.md
|
||||
|
||||
[coveralls-icon]: https://coveralls.io/repos/github/videojs/video.js/badge.svg?branch=master
|
||||
|
||||
[coveralls-link]: https://coveralls.io/github/videojs/video.js?branch=master
|
||||
|
||||
[docs]: http://docs.videojs.com
|
||||
|
||||
[fastly]: http://www.fastly.com/
|
||||
|
||||
[getting-started]: http://videojs.com/getting-started/
|
||||
|
||||
[license]: LICENSE
|
||||
|
||||
[logo]: http://videojs.com/img/logo.png
|
||||
|
||||
[npm-icon]: https://nodei.co/npm/video.js.png?downloads=true&downloadRank=true
|
||||
|
||||
[npm-link]: https://nodei.co/npm/video.js/
|
||||
|
||||
[options]: docs/guides/options.md
|
||||
|
||||
[plugins]: http://videojs.com/plugins/
|
||||
|
||||
[slack-icon]: http://slack.videojs.com/badge.svg
|
||||
|
||||
[slack-link]: http://slack.videojs.com
|
||||
|
||||
[travis-icon]: https://travis-ci.org/videojs/video.js.svg?branch=master
|
||||
|
||||
[travis-link]: https://travis-ci.org/videojs/video.js
|
||||
|
||||
[vjs]: http://videojs.com
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import sh from 'shelljs';
|
||||
import path from 'path';
|
||||
|
||||
|
||||
export default function(commit, commitRange) {
|
||||
const SINGLE_COMMIT = `git diff-tree --no-commit-id --name-only -r ${commit}`;
|
||||
const COMMIT_RANGE = `git diff --name-only ${commitRange}`;
|
||||
|
||||
let command = SINGLE_COMMIT;
|
||||
|
||||
if (commitRange) {
|
||||
command = COMMIT_RANGE
|
||||
}
|
||||
|
||||
const output = sh.exec(command, {async: false, silent: true}).stdout;
|
||||
|
||||
const files = output.split('\n').filter(Boolean);
|
||||
return files.every((file) => file.startsWith('docs') || path.extname(file) === '.md');
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#resizer,
|
||||
footer {
|
||||
background-color: #ECEEF1;
|
||||
color: #868688;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
padding: 3px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
footer .copyright {
|
||||
float: left;
|
||||
}
|
||||
|
||||
footer .other-links {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
footer .other-links li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
footer .logo {
|
||||
display: none;
|
||||
}
|
||||
+165
-105
@@ -1,8 +1,12 @@
|
||||
import {gruntCustomizer, gruntOptionsMaker} from './options-customizer.js';
|
||||
import chg from 'chg';
|
||||
import npmRun from 'npm-run';
|
||||
import isDocsOnly from './docs-only.js';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
let _ = require('lodash-compat');
|
||||
let _ = require('lodash');
|
||||
let pkg = grunt.file.readJSON('package.json');
|
||||
let license = grunt.file.read('build/license-header.txt');
|
||||
let bannerCommonData = _.pick(pkg, ['version', 'copyright']);
|
||||
@@ -16,29 +20,11 @@ module.exports = function(grunt) {
|
||||
|
||||
const browserifyGruntDefaults = {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: 'videojs'
|
||||
},
|
||||
plugin: [
|
||||
['bundle-collapser/plugin'],
|
||||
['browserify-derequire']
|
||||
],
|
||||
transform: [
|
||||
require('babelify').configure({
|
||||
sourceMapRelative: './',
|
||||
loose: ['all']
|
||||
}),
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION__',
|
||||
version: pkg.version
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION_NO_PATCH__',
|
||||
version: version.majorMinor
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__SWF_VERSION__',
|
||||
version: pkg.dependencies['videojs-swf']
|
||||
}]
|
||||
]
|
||||
};
|
||||
|
||||
@@ -47,7 +33,10 @@ module.exports = function(grunt) {
|
||||
release: {
|
||||
tag_name: 'v'+ version.full,
|
||||
name: version.full,
|
||||
body: require('chg').find(version.full).changesRaw
|
||||
body: npmRun.execSync('conventional-changelog -p videojs', {
|
||||
silent: true,
|
||||
encoding: 'utf8'
|
||||
})
|
||||
},
|
||||
},
|
||||
files: {
|
||||
@@ -111,23 +100,13 @@ module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
pkg,
|
||||
clean: {
|
||||
build: ['build/temp/*'],
|
||||
build: ['build/temp/*', 'es5'],
|
||||
dist: ['dist/*']
|
||||
},
|
||||
jshint: {
|
||||
src: {
|
||||
src: ['src/js/**/*.js', 'Gruntfile.js', 'test/unit/**/*.js'],
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
sourceMapIn: 'build/temp/video.js.map',
|
||||
sourceMapRoot: '../../src/js',
|
||||
preserveComments: 'some',
|
||||
screwIE8: false,
|
||||
mangle: true,
|
||||
compress: {
|
||||
sequences: true,
|
||||
@@ -149,9 +128,14 @@ module.exports = function(grunt) {
|
||||
},
|
||||
dist: {},
|
||||
watch: {
|
||||
novtt: {
|
||||
files: ['build/temp/video.js'],
|
||||
tasks: ['concat:novtt']
|
||||
dist: {
|
||||
files: [
|
||||
'build/temp/video.js',
|
||||
'build/temp/alt/video.novtt.js',
|
||||
'build/temp/video-js.css',
|
||||
'build/temp/alt/video-js-cdn.css'
|
||||
],
|
||||
tasks: ['copy:dist']
|
||||
},
|
||||
minify: {
|
||||
files: ['build/temp/video.js'],
|
||||
@@ -159,12 +143,8 @@ module.exports = function(grunt) {
|
||||
},
|
||||
skin: {
|
||||
files: ['src/css/**/*'],
|
||||
tasks: ['sass']
|
||||
tasks: ['skin']
|
||||
},
|
||||
jshint: {
|
||||
files: ['src/**/*', 'test/unit/**/*.js', 'Gruntfile.js'],
|
||||
tasks: 'jshint'
|
||||
}
|
||||
},
|
||||
connect: {
|
||||
dev: {
|
||||
@@ -190,6 +170,7 @@ module.exports = function(grunt) {
|
||||
swf: { cwd: 'node_modules/videojs-swf/dist/', src: 'video-js.swf', dest: 'build/temp/', expand: true, filter: 'isFile' },
|
||||
ie8: { cwd: 'node_modules/videojs-ie8/dist/', src: ['**/**'], dest: 'build/temp/ie8/', expand: true, filter: 'isFile' },
|
||||
dist: { cwd: 'build/temp/', src: ['**/**', '!test*'], dest: 'dist/', expand: true, filter: 'isFile' },
|
||||
a11y: { src: 'sandbox/descriptions.html.example', dest: 'sandbox/descriptions.test-a11y.html' }, // Can only test a file with a .html or .htm extension
|
||||
examples: { cwd: 'docs/examples/', src: ['**/**'], dest: 'dist/examples/', expand: true, filter: 'isFile' }
|
||||
},
|
||||
cssmin: {
|
||||
@@ -240,6 +221,7 @@ module.exports = function(grunt) {
|
||||
chrome_bs: { browsers: ['chrome_bs'] },
|
||||
firefox_bs: { browsers: ['firefox_bs'] },
|
||||
safari_bs: { browsers: ['safari_bs'] },
|
||||
edge_bs: { browsers: ['edge_bs'] },
|
||||
ie11_bs: { browsers: ['ie11_bs'] },
|
||||
ie10_bs: { browsers: ['ie10_bs'] },
|
||||
ie9_bs: { browsers: ['ie9_bs'] },
|
||||
@@ -281,25 +263,25 @@ module.exports = function(grunt) {
|
||||
options: {
|
||||
release: 'major'
|
||||
},
|
||||
src: ['package.json', 'component.json']
|
||||
src: ['package.json']
|
||||
},
|
||||
minor: {
|
||||
options: {
|
||||
release: 'minor'
|
||||
},
|
||||
src: ['package.json', 'component.json']
|
||||
src: ['package.json']
|
||||
},
|
||||
patch: {
|
||||
options: {
|
||||
release: 'patch'
|
||||
},
|
||||
src: ['package.json', 'component.json']
|
||||
src: ['package.json']
|
||||
},
|
||||
prerelease: {
|
||||
options: {
|
||||
release: 'prerelease'
|
||||
},
|
||||
src: ['package.json', 'component.json']
|
||||
src: ['package.json']
|
||||
},
|
||||
css: {
|
||||
options: {
|
||||
@@ -325,43 +307,61 @@ module.exports = function(grunt) {
|
||||
}
|
||||
})
|
||||
},
|
||||
babel: {
|
||||
es5: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: 'src/js/',
|
||||
src: ['**/*.js', '!base-styles.js'],
|
||||
dest: 'es5/'
|
||||
}]
|
||||
}
|
||||
},
|
||||
browserify: {
|
||||
options: browserifyGruntOptions(),
|
||||
build: {
|
||||
options: browserifyGruntOptions(),
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/video.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
options: browserifyGruntOptions({
|
||||
transform: [
|
||||
['browserify-versionify', {
|
||||
placeholder: '../node_modules/videojs-vtt.js/dist/vtt.js',
|
||||
version: 'https://cdn.rawgit.com/gkatsev/vtt.js/vjs-v0.12.1/dist/vtt.min.js'
|
||||
}],
|
||||
]
|
||||
}),
|
||||
buildnovtt: {
|
||||
options: browserifyGruntOptions({transform: [
|
||||
['aliasify', {aliases: {'videojs-vtt.js': false}}]
|
||||
]}),
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/alt/video.novtt.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
options: browserifyGruntOptions({
|
||||
watch: true,
|
||||
keepAlive: true
|
||||
},
|
||||
keepAlive: true,
|
||||
}),
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/video.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
watchnovtt: {
|
||||
options: browserifyGruntOptions({
|
||||
transform: [
|
||||
['aliasify', {aliases: {'videojs-vtt.js': false}}]
|
||||
],
|
||||
watch: true,
|
||||
keepAlive: true,
|
||||
}),
|
||||
files: {
|
||||
'build/temp/alt/video.novtt.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
options: {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: false
|
||||
verbose: true,
|
||||
standalone: false,
|
||||
transform: ['babelify']
|
||||
},
|
||||
plugin: [
|
||||
['proxyquireify/plugin']
|
||||
['proxyquireify/plugin', 'bundle-collapser/plugin']
|
||||
],
|
||||
banner: false,
|
||||
watch: true,
|
||||
@@ -375,14 +375,6 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}
|
||||
},
|
||||
exorcise: {
|
||||
build: {
|
||||
options: {},
|
||||
files: {
|
||||
'build/temp/video.js.map': ['build/temp/video.js'],
|
||||
}
|
||||
}
|
||||
},
|
||||
coveralls: {
|
||||
options: {
|
||||
// warn instead of failing when coveralls errors
|
||||
@@ -394,30 +386,36 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
novtt: {
|
||||
options: {
|
||||
separator: '\n'
|
||||
},
|
||||
src: ['build/temp/video.js'],
|
||||
dest: 'build/temp/alt/video.novtt.js'
|
||||
},
|
||||
vtt: {
|
||||
options: {
|
||||
separator: '\n',
|
||||
},
|
||||
src: ['build/temp/video.js', 'node_modules/videojs-vtt.js/dist/vtt.js'],
|
||||
dest: 'build/temp/video.js',
|
||||
options: {
|
||||
separator: '\n'
|
||||
},
|
||||
ie8_addition: {
|
||||
src: ['build/temp/video-js.css', 'src/css/ie8.css'],
|
||||
dest: 'build/temp/video-js.css'
|
||||
}
|
||||
},
|
||||
concurrent: {
|
||||
options: {
|
||||
logConcurrentOutput: true
|
||||
},
|
||||
tests: [
|
||||
'shell:babel',
|
||||
'browserify:tests'
|
||||
],
|
||||
dev: [
|
||||
'shell:babel',
|
||||
'browserify:watch',
|
||||
'browserify:watchnovtt',
|
||||
'browserify:tests',
|
||||
'watch:skin',
|
||||
'watch:dist'
|
||||
],
|
||||
// Run multiple watch tasks in parallel
|
||||
// Needed so watchify can cache intelligently
|
||||
watchAll: [
|
||||
'watch',
|
||||
'browserify:watch',
|
||||
'browserify:watchnovtt',
|
||||
'browserify:tests',
|
||||
'karma:watch'
|
||||
],
|
||||
@@ -443,6 +441,56 @@ module.exports = function(grunt) {
|
||||
src: ['build/temp/video.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
shell: {
|
||||
babel: {
|
||||
command: 'npm run babel -- --watch',
|
||||
options: {
|
||||
preferLocal: true
|
||||
}
|
||||
},
|
||||
lint: {
|
||||
command: 'npm run lint',
|
||||
options: {
|
||||
preferLocal: true
|
||||
}
|
||||
},
|
||||
noderequire: {
|
||||
command: 'node test/require/node.js',
|
||||
options: {
|
||||
failOnError: true
|
||||
}
|
||||
},
|
||||
browserify: {
|
||||
command: 'browserify test/require/browserify.js -o build/temp/browserify.js',
|
||||
options: {
|
||||
preferLocal: true
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
command: 'webpack test/require/webpack.js build/temp/webpack.js',
|
||||
options: {
|
||||
preferLocal: true
|
||||
}
|
||||
}
|
||||
},
|
||||
accessibility: {
|
||||
options: {
|
||||
accessibilityLevel: 'WCAG2AA',
|
||||
reportLevels: {
|
||||
notice: false,
|
||||
warning: true,
|
||||
error: true
|
||||
},
|
||||
ignore: [
|
||||
// Ignore the warning about needing <optgroup> elements
|
||||
'WCAG2AA.Principle1.Guideline1_3.1_3_1.H85.2'
|
||||
]
|
||||
|
||||
},
|
||||
test: {
|
||||
src: ['sandbox/descriptions.test-a11y.html']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -450,21 +498,20 @@ module.exports = function(grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
grunt.loadNpmTasks('videojs-doc-generator');
|
||||
grunt.loadNpmTasks('chg');
|
||||
grunt.loadNpmTasks('gkatsev-grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-accessibility');
|
||||
|
||||
const buildDependents = [
|
||||
grunt.registerTask('build', [
|
||||
'shell:lint',
|
||||
'clean:build',
|
||||
|
||||
'jshint',
|
||||
'babel:es5',
|
||||
'browserify:build',
|
||||
'exorcise:build',
|
||||
'concat:novtt',
|
||||
'concat:vtt',
|
||||
'browserify:buildnovtt',
|
||||
'usebanner:novtt',
|
||||
'usebanner:vtt',
|
||||
'uglify',
|
||||
|
||||
'sass',
|
||||
'skin',
|
||||
'version:css',
|
||||
'cssmin',
|
||||
|
||||
@@ -472,35 +519,48 @@ module.exports = function(grunt) {
|
||||
'copy:swf',
|
||||
'copy:ie8',
|
||||
'vjslanguages'
|
||||
];
|
||||
|
||||
grunt.registerTask('build', buildDependents);
|
||||
|
||||
grunt.registerTask(
|
||||
'build:dist',
|
||||
buildDependents.map(task => task === 'browserify:build' ? 'browserify:dist' : task)
|
||||
);
|
||||
]);
|
||||
|
||||
grunt.registerTask('dist', [
|
||||
'clean:dist',
|
||||
'build:dist',
|
||||
'build',
|
||||
'copy:dist',
|
||||
'copy:examples',
|
||||
'zip:dist'
|
||||
]);
|
||||
|
||||
grunt.registerTask('skin', ['sass']);
|
||||
grunt.registerTask('skin', ['sass', 'concat:ie8_addition']);
|
||||
|
||||
// Default task - build and test
|
||||
grunt.registerTask('default', ['test']);
|
||||
|
||||
// The test script includes coveralls only when the TRAVIS env var is set.
|
||||
grunt.registerTask('test', ['build', 'karma:defaults'].concat(process.env.TRAVIS && 'coveralls').filter(Boolean));
|
||||
grunt.registerTask('test', function() {
|
||||
const tasks = [
|
||||
'build',
|
||||
'shell:noderequire',
|
||||
'shell:browserify',
|
||||
'shell:webpack',
|
||||
'karma:defaults',
|
||||
'test-a11y'
|
||||
];
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
if (isDocsOnly(process.env.TRAVIS_COMMIT, process.env.TRAVIS_COMMIT_RANGE)) {
|
||||
grunt.log.write('Not running any tests because only docs were changed');
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.concat(process.env.TRAVIS && 'coveralls').filter(Boolean);
|
||||
}
|
||||
|
||||
grunt.task.run(tasks);
|
||||
});
|
||||
|
||||
// Run while developing
|
||||
grunt.registerTask('dev', ['build', 'connect:dev', 'concurrent:watchSandbox']);
|
||||
|
||||
grunt.registerTask('dev', ['connect:dev', 'concurrent:dev']);
|
||||
grunt.registerTask('watchAll', ['build', 'connect:dev', 'concurrent:watchAll']);
|
||||
grunt.registerTask('test-a11y', ['copy:a11y', 'accessibility']);
|
||||
|
||||
// Pick your testing, or run both in different terminals
|
||||
grunt.registerTask('test-ui', ['browserify:tests']);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash-compat';
|
||||
import _ from 'lodash';
|
||||
/**
|
||||
* Customizes _.merge behavior in `gruntOptions` to concatenate
|
||||
* arrays. This can be overridden on a per-call basis to
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('cdn-links', 'Update the version of CDN links in docs', function(){
|
||||
let doc = grunt.file.read('docs/guides/setup.md');
|
||||
let version = require('../package.json').version;
|
||||
let version = require('../../package.json').version;
|
||||
|
||||
// remove the patch version to point to the latest patch
|
||||
version = version.replace(/(\d+\.\d+)\.\d+/, '$1');
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('check-translations', 'Check that translations are up to date', function(){
|
||||
const source = require('../../lang/en.json');
|
||||
const table = require('markdown-table');
|
||||
let doc = grunt.file.read('docs/translations-needed.md');
|
||||
const tableRegex = /(<!-- START langtable -->)(.|\n)*(<!-- END langtable -->)/;
|
||||
let tableData = [['Language file', 'Missing translations']];
|
||||
|
||||
grunt.file.recurse('lang', (abspath, rootdir, subdir, filename) => {
|
||||
if (filename === 'en.json') {
|
||||
return;
|
||||
}
|
||||
const target = require(`../../${abspath}`);
|
||||
let missing = [];
|
||||
for (const string in source) {
|
||||
if (!target[string]) {
|
||||
grunt.log.writeln(`${filename} missing "${string}"`);
|
||||
missing.push(string);
|
||||
}
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
grunt.log.error(`${filename} is missing ${missing.length} translations.`);
|
||||
tableData.push([`${filename} (missing ${missing.length})`, missing[0]]);
|
||||
for (var i = 1; i < missing.length; i++) {
|
||||
tableData.push(['', missing[i]]);
|
||||
}
|
||||
} else {
|
||||
grunt.log.ok(`${filename} is up to date.`);
|
||||
tableData.push([`${filename} (Complete)`, '']);
|
||||
}
|
||||
});
|
||||
doc = doc.replace(tableRegex, `$1\n` + table(tableData) + `\n$3`);
|
||||
grunt.file.write('docs/translations-needed.md', doc);
|
||||
});
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('saucelabs', function() {
|
||||
const exec = require('child_process').exec;
|
||||
const done = this.async();
|
||||
|
||||
if (this.args[0] == 'connect') {
|
||||
exec('curl https://gist.githubusercontent.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash',
|
||||
function(error, stdout, stderr) {
|
||||
if (error) {
|
||||
grunt.log.error(error);
|
||||
return done();
|
||||
}
|
||||
|
||||
grunt.verbose.error(stderr.toString());
|
||||
grunt.verbose.writeln(stdout.toString());
|
||||
grunt.task.run(['karma:saucelabs']);
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
grunt.task.run(['karma:saucelabs']);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.11.7",
|
||||
"keywords": [
|
||||
"videojs",
|
||||
"html5",
|
||||
"flash",
|
||||
"video",
|
||||
"player"
|
||||
],
|
||||
"scripts": ["dist/video-js/video.dev.js"],
|
||||
"styles": ["dist/video-js/video-js.css"],
|
||||
"files": ["dist/video-js/video-js.swf"],
|
||||
"fonts": [
|
||||
"dist/video-js/font/vjs.eot",
|
||||
"dist/video-js/font/vjs.svg",
|
||||
"dist/video-js/font/vjs.ttf",
|
||||
"dist/video-js/font/vjs.woff"
|
||||
],
|
||||
"main": "dist/video-js/video.dev.js"
|
||||
}
|
||||
+4
-4
@@ -280,14 +280,14 @@
|
||||
[ "git checkout -b temp-release-branch master","Create a temporary branch for the dist" ],
|
||||
[ "grunt version:{{release_type}}", "Bump package versions" ],
|
||||
[ "./build/bin/version", "Return the current VJS Version from the package.json file", "version" ],
|
||||
[ "grunt chg-release:{{version}}", "Update the changelog with the new release" ],
|
||||
[ "npm run changelog", "Update the changelog with the new release" ],
|
||||
[ "git commit -am 'v{{version}}'", "Add and commit the package changes" ],
|
||||
[ "git checkout master", "Checkout the developmet branch" ],
|
||||
[ "git merge temp-release-branch", "Merge package changes into the dev brach" ],
|
||||
[ "git push upstream master", "Push the dev branch changes to the repo" ],
|
||||
[ "git checkout temp-release-branch", "Checkout the temp branch again" ],
|
||||
[ "grunt dist", "Build the dist" ],
|
||||
[ "git add dist --force", "Add the (otherwise ignored) release files" ],
|
||||
[ "git add es5 dist --force", "Add the (otherwise ignored) release files" ],
|
||||
[ "git commit -m 'v{{version}} dist'", "Commit the dist changes" ],
|
||||
[ "git tag -a v{{version}} -m 'v{{version}}'", "Tag the release" ],
|
||||
[ "git push upstream --tags", "Push the new tag to the repo" ],
|
||||
@@ -306,12 +306,12 @@
|
||||
[ "grunt test", "Run tests" ],
|
||||
[ "grunt version:{{release_type}}", "Bump package versions" ],
|
||||
[ "./build/bin/version", "Return the current VJS Version from the package.json file", "version" ],
|
||||
[ "grunt chg-release:{{version}}", "Update the changelog with the new release" ],
|
||||
[ "npm run changelog", "Update the changelog with the new release" ],
|
||||
[ "git commit -am 'v{{version}}'", "Add and commit the package changes" ],
|
||||
[ "git push upstream stable", "Push the release branch changes to the repo" ],
|
||||
[ "git checkout -b temp-release-branch stable","Create a temporary branch for the dist" ],
|
||||
[ "grunt dist", "Build the dist" ],
|
||||
[ "git add dist --force", "Add the (otherwise ignored) release files" ],
|
||||
[ "git add es5 dist --force", "Add the (otherwise ignored) release files" ],
|
||||
[ "git commit -m 'v{{version}} dist'", "Commit the dist changes" ],
|
||||
[ "git tag -a v{{version}} -m 'v{{version}}'", "Tag the release" ],
|
||||
[ "git push upstream --tags", "Push the new tag to the repo" ],
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
API
|
||||
===
|
||||
|
||||
The Video.js API allows you to interact with the video through JavaScript, whether the browser is playing the video through HTML5 video, Flash, or any other supported playback technologies.
|
||||
|
||||
Referencing the Player
|
||||
----------------------
|
||||
To use the API functions, you need access to the player object. Luckily this is easy to get. You just need to make sure your video tag has an ID. The example embed code has an ID of "example\_video_1". If you have multiple videos on one page, make sure every video tag has a unique ID.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('example_video_1');
|
||||
```
|
||||
|
||||
(If the player hasn't been initialized yet via the data-setup attribute or another method, this will also initialize the player.)
|
||||
|
||||
Wait Until the Player is Ready
|
||||
------------------------------
|
||||
The time it takes Video.js to set up the video and API will vary depending on the playback technology being used (HTML5 will often be much faster to load than Flash). For that reason we want to use the player's 'ready' function to trigger any code that requires the player's API.
|
||||
|
||||
```js
|
||||
videojs("example_video_1").ready(function(){
|
||||
var myPlayer = this;
|
||||
|
||||
// EXAMPLE: Start playing the video.
|
||||
myPlayer.play();
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
API Methods
|
||||
-----------
|
||||
Now that you have access to a ready player, you can control the video, get values, or respond to video events. The Video.js API function names follow the [HTML5 media API](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html). The main difference is that getter/setter functions are used for video properties.
|
||||
|
||||
```js
|
||||
|
||||
// setting a property on a bare HTML5 video element
|
||||
myVideoElement.currentTime = "120";
|
||||
|
||||
// setting a property on a Video.js player
|
||||
myPlayer.currentTime(120);
|
||||
|
||||
```
|
||||
|
||||
The full list of player API methods and events can be found in the [player API docs](http://docs.videojs.com/docs/api/index.html).
|
||||
+133
-45
@@ -1,69 +1,157 @@
|
||||
# Audio Tracks
|
||||
|
||||
Audio Tracks are a function of HTML5 video for providing alternative audio track selections to the user, so that a track other than the main track can be played. Video.js makes audio tracks work across all browsers. There are currently five types of tracks:
|
||||
Audio tracks are a feature of HTML5 video for providing alternate audio track selections
|
||||
to the user, so that a track other than the main track can be played. Video.js offers a
|
||||
cross-browser implementation of audio tracks.
|
||||
|
||||
- **Alternative**: alternative audio for the main video track
|
||||
- **Descriptions**: descriptions of what is happening in the video track
|
||||
- **Main**: the main audio track for this video
|
||||
- **Translation**: a translation of the main audio track
|
||||
- **Commentary**: commentary on the video, usually the director of the content talking about design choices
|
||||
## Table of Contents
|
||||
|
||||
## Missing Funtionality
|
||||
- It is currently impossible to add AudioTracks in a non-programtic way
|
||||
- Literal switching of AudioTracks for playback is not handled by video.js and must be handled by something else. video.js only stores the track representation
|
||||
* [Caveats](#caveats)
|
||||
* [Working with Audio Tracks](#working-with-audio-tracks)
|
||||
* [Add an Audio Track to the Player](#add-an-audio-track-to-the-player)
|
||||
* [Listen for a Video Track Becoming Enabled](#listen-for-a-video-track-becoming-enabled)
|
||||
* [Removing an Audio Track from the Player](#removing-an-audio-track-from-the-player)
|
||||
* [API](#api)
|
||||
* [videojs.AudioTrack](#videojsaudiotrack)
|
||||
* [id](#id)
|
||||
* [kind](#kind)
|
||||
* [label](#label)
|
||||
* [language](#language)
|
||||
* [enabled](#enabled)
|
||||
|
||||
## Adding to Video.js
|
||||
## Caveats
|
||||
|
||||
> Right now adding audio tracks in the HTML is unsupported. Audio Tracks must be added programatically.
|
||||
* It is not possible to add audio tracks through HTML like you can with text tracks.
|
||||
They must be added programmatically.
|
||||
* Video.js only stores track representations. Switching audio tracks for playback is
|
||||
_not handled by Video.js_ and must be handled elsewhere - for example,
|
||||
[videojs-contrib-hls](http://github.com/videojs/videojs-contrib-hls) handles switching
|
||||
audio tracks to support track selection through the UI.
|
||||
|
||||
You must add audio tracks [programatically](#api) for the time being.
|
||||
## Working with Audio Tracks
|
||||
|
||||
## Attributes
|
||||
Audio Track propertites and settings
|
||||
### Add an Audio Track to the Player
|
||||
|
||||
### kind
|
||||
One of the five track types listed above. Kind defaults to empty string if no kind is included, or an invalid kind is used.
|
||||
|
||||
### label
|
||||
The label for the track that will be show to the user, for example in a menu that list the different languages available for audio tracks.
|
||||
|
||||
### language
|
||||
The two-letter code (valid BCP 47 language tag) for the language of the audio track, for example "en" for English. A list of language codes is [available here](languages.md#language-codes).
|
||||
|
||||
### enabled
|
||||
If this track should be playing or not. In video.js we only allow one track to be enabled at a time. so if you enable more than one the last one to be enabled will end up being the only one.
|
||||
|
||||
## Interacting with Audio Tracks
|
||||
### Doing something when a track becomes enabled
|
||||
When a new track is enabled (other than the main track) an event is fired on the `AudioTrackList` called `change` you can listen to that event and do something with it.
|
||||
Here's an example:
|
||||
```js
|
||||
// get the current players AudioTrackList object
|
||||
let tracks = player.audioTracks();
|
||||
// Create a player.
|
||||
var player = videojs('my-player');
|
||||
|
||||
// listen to the change event
|
||||
tracks.addEventListener('change', function() {
|
||||
// Create a track object.
|
||||
var track = new videojs.AudioTrack({
|
||||
id: 'my-spanish-audio-track',
|
||||
kind: 'translation',
|
||||
label: 'Spanish',
|
||||
language: 'es'
|
||||
});
|
||||
|
||||
// print the currently enabled AudioTrack label
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
// Add the track to the player's audio track list.
|
||||
player.audioTracks().addTrack(track);
|
||||
```
|
||||
|
||||
### Listen for a Video Track Becoming Enabled
|
||||
|
||||
When a track is enabled or disabled on an `AudioTrackList`, a `change` event will be
|
||||
fired. You can listen for that event and do something with it.
|
||||
|
||||
> NOTE: The initial `AudioTrack` selection (usually the main track that is selected)
|
||||
> should not fire a `change` event.
|
||||
|
||||
```js
|
||||
// Get the current player's AudioTrackList object.
|
||||
var audioTrackList = player.audioTracks();
|
||||
|
||||
// Listen to the "change" event.
|
||||
audioTrackList.addEventListener('change', function() {
|
||||
|
||||
// Log the currently enabled AudioTrack label.
|
||||
for (var i = 0; i < audioTrackList.length; i++) {
|
||||
var track = audioTrackList[i];
|
||||
|
||||
if (track.enabled) {
|
||||
console.log(track.label);
|
||||
videojs.log(track.label);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Removing an Audio Track from the Player
|
||||
|
||||
Assuming a player already exists and has an audio track that you want to remove, you
|
||||
might do something like the following:
|
||||
|
||||
```js
|
||||
// Get the track we created in an earlier example.
|
||||
var track = player.audioTracks().getTrackById('my-spanish-audio-track');
|
||||
|
||||
// Remove it from the audio track list.
|
||||
player.audioTracks().removeTrack(track);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `player.audioTracks() -> AudioTrackList`
|
||||
This is the main interface into the audio tracks of the player.
|
||||
It returns an AudioTrackList which is an array like object that contains all the `AudioTrack` on the player.
|
||||
For more complete information, refer to the
|
||||
[Video.js API docs](http://docs.videojs.com/docs/api/index.html), specifically:
|
||||
|
||||
### `player.audioTracks().addTrack(AudioTrack)`
|
||||
Add an existing AudioTrack to the players internal list of AudioTracks.
|
||||
* `Player#audioTracks`
|
||||
* `AudioTrackList`
|
||||
* `AudioTrack`
|
||||
|
||||
### `player.audioTracks().removeTrack(AudioTrack)`
|
||||
Remove a track from the AudioTrackList currently on the player. if no track exists this will do nothing.
|
||||
### `videojs.AudioTrack`
|
||||
|
||||
This class is based on [the `AudioTrack` standard][spec-audiotrack] and can be used to
|
||||
create new audio track objects.
|
||||
|
||||
Each property below is available as an option to the `AudioTrack` constructor.
|
||||
|
||||
#### `id`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-id)
|
||||
|
||||
A unique identifier for this track. Video.js will generate one if not given.
|
||||
|
||||
#### `kind`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind)
|
||||
|
||||
Video.js supports standard `kind` values for `AudioTracks`:
|
||||
|
||||
* `"alternative"`: A possible alternative to the main track.
|
||||
* `"descriptions"`: An audio description of a video track.
|
||||
* `"main"`: The primary audio track for this video.
|
||||
* `"main-desc"`: The primary audio track, mixed with audio descriptions.
|
||||
* `"translation"`: A translated version of the main audio track.
|
||||
* `"commentary"`: Commentary on the primary audio track, e.g. a director's commentary.
|
||||
* `""` (default): No explicit kind, or the kind given by the track's metadata is not
|
||||
recognized by the user agent.
|
||||
|
||||
#### `label`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-label)
|
||||
|
||||
The label for the track that will be shown to the user. For example, in a menu that lists
|
||||
the different languages available as alternate audio tracks.
|
||||
|
||||
#### `language`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-language)
|
||||
|
||||
The valid [BCP 47](https://tools.ietf.org/html/bcp47) code for the language of the audio
|
||||
track, e.g. `"en"` for English or `"es"` for Spanish.
|
||||
|
||||
For supported language translations, please see the [languages folder (/lang)](https://github.com/videojs/video.js/tree/master/lang)
|
||||
located in the Video.js root and refer to the [languages guide](languages.md) for more
|
||||
information on languages in Video.js.
|
||||
|
||||
#### `enabled`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-enabled)
|
||||
|
||||
Whether or not this track should be playing.
|
||||
|
||||
In Video.js, we only allow one track to be enabled at a time; so, if you enable more
|
||||
than one, the last one to be enabled will end up being the only one. While the spec
|
||||
allows for more than one track to be enabled, Safari and most implementations only allow
|
||||
one audio track to be enabled at a time.
|
||||
|
||||
[spec-audiotrack]: https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack
|
||||
|
||||
+315
-64
@@ -1,86 +1,337 @@
|
||||
Components
|
||||
===
|
||||
The Video.js player is built on top of a simple, custom UI components architecture. The player class and all control classes inherit from the `Component` class, or a subclass of `Component`.
|
||||
# Components
|
||||
|
||||
The architecture of the Video.js player is centered around components. The `Player` class and all classes representing player controls and other UI elements inherit from the `Component` class. This architecture makes it easy to construct the user interface of the Video.js player in a tree-like structure that mirrors the DOM.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [What is a Component?](#what-is-a-component)
|
||||
* [Creating a Component](#creating-a-component)
|
||||
* [Component Children](#component-children)
|
||||
* [Basic Example](#basic-example)
|
||||
* [Using Options](#using-options)
|
||||
* [Event Listening](#event-listening)
|
||||
* [Using on](#using-on)
|
||||
* [Using off](#using-off)
|
||||
* [Using one](#using-one)
|
||||
* [Using trigger](#using-trigger)
|
||||
* [Default Component Tree](#default-component-tree)
|
||||
* [Specific Component Details](#specific-component-details)
|
||||
* [Volume Panel](#volume-panel)
|
||||
* [Text Track Settings](#text-track-settings)
|
||||
|
||||
## What is a Component?
|
||||
|
||||
A component is a JavaScript object that has the following features:
|
||||
|
||||
* An associated DOM element.
|
||||
* An association to a `Player` object.
|
||||
* The ability to manage any number of child components.
|
||||
* The ability to listen for and trigger events.
|
||||
* A lifecycle of initialization and disposal.
|
||||
|
||||
For more specifics on the programmatic interface of a component, see [the component API docs](http://docs.videojs.com/docs/api/component.html).
|
||||
|
||||
## Creating a Component
|
||||
|
||||
Video.js components can be inherited and registered with Video.js to add new features and UI to the player.
|
||||
|
||||
For a working example, [we have a JSBin](http://jsbin.com/vobacas/edit?html,css,js,output) demonstrating the creation of a component for displaying a title across the top of the player.
|
||||
|
||||
In addition, there are a couple methods worth recognizing:
|
||||
|
||||
* `videojs.getComponent(String name)`: Retrieves component constructors from Video.js.
|
||||
* `videojs.registerComponent(String name, Function Comp)`: Registers component constructors with Video.js.
|
||||
* `videojs.extend(Function component, Object properties)`: Provides prototype inheritance. Can be used to extend a component's constructor, returning a new constructor with the given properties.
|
||||
|
||||
Creation:
|
||||
|
||||
```js
|
||||
videojs.registerComponent('Control', videojs.extends(Component));
|
||||
videojs.registerComponent('Button', videojs.extends(videojs.getComponent('Control')));
|
||||
videojs.registerComponent('PlayToggle', videojs.extends(videojs.getComponent('Button')));
|
||||
// adding a button to the player
|
||||
var player = videojs('some-video-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var button = new Component(player);
|
||||
|
||||
console.log(button.el());
|
||||
```
|
||||
|
||||
The UI component architecture makes it easier to add child components to a parent component and build up an entire user interface, like the controls for the Video.js player.
|
||||
|
||||
```js
|
||||
// Adding a new control to the player
|
||||
myPlayer.addChild('BigPlayButton');
|
||||
```
|
||||
|
||||
Every component has an associated DOM element, and when you add a child component, it inserts the element of that child into the element of the parent.
|
||||
|
||||
```js
|
||||
myPlayer.addChild('BigPlayButton');
|
||||
```
|
||||
|
||||
Results in:
|
||||
The above code will output
|
||||
|
||||
```html
|
||||
<!-- Player Element -->
|
||||
<div class="video-js">
|
||||
<!-- BigPlayButton Element -->
|
||||
<div class="vjs-big-play-button"></div>
|
||||
</div>
|
||||
<div class="video-js">
|
||||
<div class="vjs-button">Button</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
The actual default component structure of the Video.js player looks something like this:
|
||||
Adding the new button to the player
|
||||
|
||||
```js
|
||||
// adding a button to the player
|
||||
var player = videojs('some-video-id');
|
||||
var button = player.addChild('button');
|
||||
|
||||
console.log(button.el());
|
||||
// will have the same html result as the previous example
|
||||
```
|
||||
|
||||
## Component Children
|
||||
|
||||
Again, refer to [the component API docs](http://docs.videojs.com/docs/api/component.html) for complete details on methods available for managing component structures.
|
||||
|
||||
### Basic Example
|
||||
|
||||
When child component is added to a parent component, Video.js inserts the element of the child into the element of the parent. For example, adding a component like this:
|
||||
|
||||
```js
|
||||
// Add a "BigPlayButton" component to the player. Its element will be appended to the player's element.
|
||||
player.addChild('BigPlayButton');
|
||||
```
|
||||
|
||||
Results in a DOM that looks like this:
|
||||
|
||||
```html
|
||||
<!-- Player Element -->
|
||||
<div class="video-js">
|
||||
<!-- BigPlayButton Element -->
|
||||
<div class="vjs-big-play-button"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Conversely, removing child components will remove the child component's element from the DOM:
|
||||
|
||||
```js
|
||||
player.removeChild('BigPlayButton');
|
||||
```
|
||||
|
||||
Results in a DOM that looks like this:
|
||||
|
||||
```html
|
||||
<!-- Player Element -->
|
||||
<div class="video-js">
|
||||
</div>
|
||||
```
|
||||
|
||||
### Using Options
|
||||
|
||||
Pass in options for child constructors and options for children of the child.
|
||||
|
||||
```js
|
||||
var player = videojs('some-vid-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var myComponent = new Component(player);
|
||||
var myButton = myComponent.addChild('MyButton', {
|
||||
text: 'Press Me',
|
||||
buttonChildExample: {
|
||||
buttonChildOption: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Children can also be added via options when a component is initialized.
|
||||
|
||||
> Note: Include a 'name' key which will be used if two child components of the same
|
||||
> type that need different options.
|
||||
|
||||
```js
|
||||
// MyComponent is from the above example
|
||||
var myComp = new MyComponent(player, {
|
||||
children: ['button', {
|
||||
name: 'button',
|
||||
someOtherOption: true
|
||||
}, {
|
||||
name: 'button',
|
||||
someOtherOption: false
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
## Event Listening
|
||||
|
||||
### Using `on`
|
||||
|
||||
```js
|
||||
var player = videojs('some-player-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var myComponent = new Component(player);
|
||||
var myFunc = function() {
|
||||
var myComponent = this;
|
||||
console.log('myFunc called');
|
||||
};
|
||||
|
||||
myComponent.on('eventType', myFunc);
|
||||
myComponent.trigger('eventType');
|
||||
// logs 'myFunc called'
|
||||
```
|
||||
|
||||
The context of `myFunc` will be `myComponent` unless it is bound. You can add
|
||||
a listener to another element or component.
|
||||
|
||||
```js
|
||||
var otherComponent = new Component(player);
|
||||
|
||||
// myComponent/myFunc is from the above example
|
||||
myComponent.on(otherComponent.el(), 'eventName', myFunc);
|
||||
myComponent.on(otherComponent, 'eventName', myFunc);
|
||||
|
||||
otherComponent.trigger('eventName');
|
||||
// logs 'myFunc called' twice
|
||||
```
|
||||
|
||||
### Using `off`
|
||||
|
||||
```js
|
||||
var player = videojs('some-player-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var myComponent = new Component(player);
|
||||
var myFunc = function() {
|
||||
var myComponent = this;
|
||||
console.log('myFunc called');
|
||||
};
|
||||
myComponent.on('eventType', myFunc);
|
||||
myComponent.trigger('eventType');
|
||||
// logs 'myFunc called'
|
||||
|
||||
myComponent.off('eventType', myFunc);
|
||||
myComponent.trigger('eventType');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
If myFunc gets excluded, _all_ listeners for the event type will get removed. If
|
||||
eventType gets excluded, _all_ listeners will get removed from the component.
|
||||
You can use `off` to remove listeners that get added to other elements or
|
||||
components using:
|
||||
|
||||
`myComponent.on(otherComponent...`
|
||||
|
||||
In this case both the event type and listener function are **REQUIRED**.
|
||||
|
||||
```js
|
||||
var otherComponent = new Component(player);
|
||||
|
||||
// myComponent/myFunc is from the above example
|
||||
myComponent.on(otherComponent.el(), 'eventName', myFunc);
|
||||
myComponent.on(otherComponent, 'eventName', myFunc);
|
||||
|
||||
otherComponent.trigger('eventName');
|
||||
// logs 'myFunc called' twice
|
||||
myComponent.off(ootherComponent.el(), 'eventName', myFunc);
|
||||
myComponent.off(otherComponent, 'eventName', myFunc);
|
||||
otherComponent.trigger('eventName');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
### Using `one`
|
||||
|
||||
```js
|
||||
var player = videojs('some-player-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var myComponent = new Component(player);
|
||||
var myFunc = function() {
|
||||
var myComponent = this;
|
||||
console.log('myFunc called');
|
||||
};
|
||||
myComponent.one('eventName', myFunc);
|
||||
myComponent.trigger('eventName');
|
||||
// logs 'myFunc called'
|
||||
|
||||
myComponent.trigger('eventName');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
You can also add a listener to another element or component that will get
|
||||
triggered only once.
|
||||
|
||||
```js
|
||||
var otherComponent = new Component(player);
|
||||
|
||||
// myComponent/myFunc is from the above example
|
||||
myComponent.one(otherComponent.el(), 'eventName', myFunc);
|
||||
myComponent.one(otherComponent, 'eventName', myFunc);
|
||||
|
||||
otherComponent.trigger('eventName');
|
||||
// logs 'myFunc called' twice
|
||||
|
||||
otherComponent.trigger('eventName');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
### Using `trigger`
|
||||
|
||||
```js
|
||||
var player = videojs('some-player-id');
|
||||
var Component = videojs.getComponent('Component');
|
||||
var myComponent = new Component(player);
|
||||
var myFunc = function(data) {
|
||||
var myComponent = this;
|
||||
console.log('myFunc called');
|
||||
console.log(data);
|
||||
};
|
||||
myComponent.one('eventName', myFunc);
|
||||
myComponent.trigger('eventName');
|
||||
// logs 'myFunc called' and 'undefined'
|
||||
|
||||
myComponent.trigger({'type':'eventName'});
|
||||
// logs 'myFunc called' and 'undefined'
|
||||
|
||||
myComponent.trigger('eventName', {data: 'some data'});
|
||||
// logs 'myFunc called' and "{data: 'some data'}"
|
||||
|
||||
myComponent.trigger({'type':'eventName'}, {data: 'some data'});
|
||||
// logs 'myFunc called' and "{data: 'some data'}"
|
||||
```
|
||||
|
||||
## Default Component Tree
|
||||
|
||||
The default component structure of the Video.js player looks something like this:
|
||||
|
||||
```tree
|
||||
Player
|
||||
PosterImage
|
||||
TextTrackDisplay
|
||||
LoadingSpinner
|
||||
BigPlayButton
|
||||
ControlBar
|
||||
PlayToggle
|
||||
VolumeMenuButton
|
||||
CurrentTimeDisplay (Hidden by default)
|
||||
TimeDivider (Hidden by default)
|
||||
DurationDisplay (Hidden by default)
|
||||
ProgressControl
|
||||
SeekBar
|
||||
LoadProgressBar
|
||||
MouseTimeDisplay
|
||||
PlayProgressBar
|
||||
LiveDisplay (Hidden by default)
|
||||
RemainingTimeDisplay
|
||||
CustomControlsSpacer (No UI)
|
||||
ChaptersButton (Hidden by default)
|
||||
SubtitlesButton (Hidden by default)
|
||||
CaptionsButton (Hidden by default)
|
||||
FullscreenToggle
|
||||
ErrorDisplay
|
||||
TextTrackSettings
|
||||
├── PosterImage
|
||||
├── TextTrackDisplay
|
||||
├── LoadingSpinner
|
||||
├── BigPlayButton
|
||||
├─┬ ControlBar
|
||||
│ ├── PlayToggle
|
||||
│ ├── VolumeMenuButton
|
||||
│ ├── CurrentTimeDisplay (hidden by default)
|
||||
│ ├── TimeDivider (hidden by default)
|
||||
│ ├── DurationDisplay (hidden by default)
|
||||
│ ├─┬ ProgressControl (hidden during live playback)
|
||||
│ │ └─┬ SeekBar
|
||||
│ │ ├── LoadProgressBar
|
||||
│ │ ├── MouseTimeDisplay
|
||||
│ │ └── PlayProgressBar
|
||||
│ ├── LiveDisplay (hidden during VOD playback)
|
||||
│ ├── RemainingTimeDisplay
|
||||
│ ├── CustomControlSpacer (has no UI)
|
||||
│ ├── PlaybackRateMenuButton (hidden, unless playback tech supports rate changes)
|
||||
│ ├── ChaptersButton (hidden, unless there are relevant tracks)
|
||||
│ ├── DescriptionsButton (hidden, unless there are relevant tracks)
|
||||
│ ├── SubtitlesButton (hidden, unless there are relevant tracks)
|
||||
│ ├── CaptionsButton (hidden, unless there are relevant tracks)
|
||||
│ ├── AudioTrackButton (hidden, unless there are relevant tracks)
|
||||
│ └── FullscreenToggle
|
||||
├── ErrorDisplay (hidden, until there is an error)
|
||||
└── TextTrackSettings
|
||||
```
|
||||
|
||||
## Progress Control
|
||||
The progress control is made up of the SeekBar. The seekbar contains the load progress bar
|
||||
and the play progress bar. In addition, it contains the Mouse Time Display which
|
||||
is used to display the time tooltip that follows the mouse cursor.
|
||||
The play progress bar also has a time tooltip that show the current time.
|
||||
## Specific Component Details
|
||||
|
||||
By default, the progress control is sandwiched between the volume menu button and
|
||||
the remaining time display inside the control bar, but in some cases, a skin would
|
||||
want to move the progress control above the control bar and have it span the full
|
||||
width of the player, in those cases, it is less than ideal to have the tooltips
|
||||
get cut off or leave the bounds of the player. This can be prevented by setting the
|
||||
`keepTooltipsInside` option on the progress control. This also makes the tooltips use
|
||||
a real element instead of pseudo elements so targetting them with css will be different.
|
||||
### Volume Panel
|
||||
|
||||
The `VolumePanel` includes the `MuteToggle` and the `VolumeControl` Components, which will be hidden if volume changes are not supported. There is one important option for the `VolumePanel` which can make your `VolumeControl` appear vertically over the `MuteToggle`. This can be set by passing `VolumePanel` `{inline: false}` as the default behavior is a horizontal `VolumeControl` with `{inline: true}`.
|
||||
|
||||
Example of a vertical `VolumeControl`
|
||||
|
||||
```js
|
||||
let player = videojs('myplayer', {
|
||||
controlBar: {
|
||||
progressControl: {
|
||||
keepTooltipsInside: true
|
||||
volumePanel: {
|
||||
inline: false
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Text Track Settings
|
||||
|
||||
The text track settings component is only available when using emulated text tracks.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# Debugging
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Logging](#logging)
|
||||
* [API Overview](#api-overview)
|
||||
* [Log Safely](#log-safely)
|
||||
* [Log Objects Usefully](#log-objects-usefully)
|
||||
* [Log Levels](#log-levels)
|
||||
* [Available Log Levels](#available-log-levels)
|
||||
* [History](#history)
|
||||
|
||||
## Logging
|
||||
|
||||
Video.js includes a lightweight wrapper - `videojs.log` - around a subset of [the `console` API][console]. The available methods are `videojs.log`, `videojs.log.warn`, and `videojs.log.error`.
|
||||
|
||||
### API Overview
|
||||
|
||||
Most of these methods should be fairly self-explanatory, but for complete details, see [the API docs][api].
|
||||
|
||||
| Method | Alias Of | Matching Level(s) |
|
||||
| ------------------------------- | --------------- | ----------------- |
|
||||
| `videojs.log()` | `console.log` | all |
|
||||
| `videojs.log.warn()` | `console.warn` | all, warn |
|
||||
| `videojs.log.error()` | `console.error` | all, warn, error |
|
||||
| `videojs.log.level()` | n/a | n/a |
|
||||
| `videojs.log.history()` | n/a | n/a |
|
||||
| `videojs.log.history.clear()` | n/a | n/a |
|
||||
| `videojs.log.history.disable()` | n/a | n/a |
|
||||
| `videojs.log.history.enable()` | n/a | n/a |
|
||||
|
||||
For descriptions of these features, please refer to the sections below.
|
||||
|
||||
### Log Safely
|
||||
|
||||
Unlike the `console`, it's safe to leave `videojs.log` calls in your code. They won't throw errors when the `console` doesn't exist.
|
||||
|
||||
### Log Objects Usefully
|
||||
|
||||
Similar to the `console`, any number of mixed-type values can be passed to `videojs.log` methods:
|
||||
|
||||
```js
|
||||
videojs.log('this is a string', {butThis: 'is an object'});
|
||||
```
|
||||
|
||||
However, certain browser consoles (namely, IE10 and lower) do not support non-string values. Video.js improves on this situation by passing objects through `JSON.stringify` before logging them in IE10 and below. In other words, instead of the above producing this:
|
||||
|
||||
```txt
|
||||
VIDEOJS: this is a string [object Object]
|
||||
```
|
||||
|
||||
it will produce this:
|
||||
|
||||
```txt
|
||||
VIDEOJS: this is a string {"butThis": "is an object"}
|
||||
```
|
||||
|
||||
### Log Levels
|
||||
|
||||
Unlike the `console`, `videojs.log` includes the concept of logging levels. These levels toggle logging methods on or off.
|
||||
|
||||
Levels are exposed through the `videojs.log.level` method. This method acts as both a getter and setter for the current logging level. With no arguments, it returns the current logging level:
|
||||
|
||||
```js
|
||||
videojs.log.level(); // "all"
|
||||
```
|
||||
|
||||
By passing a string, the logging level can be changed to one of the available logging levels:
|
||||
|
||||
```js
|
||||
videojs.log.level('error'); // show only error messages and suppress others
|
||||
videojs.log('foo'); // does nothing
|
||||
videojs.log.warn('foo'); // does nothing
|
||||
videojs.log.error('foo'); // logs "foo" as an error
|
||||
```
|
||||
|
||||
### Available Log Levels
|
||||
|
||||
* **all** (default): enables all logging methods
|
||||
* **error**: only show `log.error` messages
|
||||
* **off**: disable all logging methods
|
||||
* **warn**: only show `log.warn` _and_ `log.error` messages
|
||||
|
||||
### History
|
||||
|
||||
> **Note:** In Video.js 5, `videojs.log.history` was an array. As of Video.js 6, it is a function which returns an array. This change was made to provide a richer, safer logging history API.
|
||||
|
||||
By default, the `videojs.log` module tracks a history of _everything_ passed to it regardless of logging level:
|
||||
|
||||
```js
|
||||
videojs.log.history(); // an array of everything that's been logged up to now
|
||||
```
|
||||
|
||||
This will work even when logging is set to **off**.
|
||||
|
||||
This can be useful, but it can also be a source of memory leaks. For example, logged objects will be retained in history even if references are removed everywhere else!
|
||||
|
||||
To avoid this problem, history can be disabled or enabled via method calls (using the `disable` and `enable` methods respectively). Disabling history is as easy as:
|
||||
|
||||
```js
|
||||
videojs.log.history.disable();
|
||||
```
|
||||
|
||||
Finally, the history (if enabled) can be cleared at any time via:
|
||||
|
||||
```js
|
||||
videojs.log.history.clear();
|
||||
```
|
||||
|
||||
[api]: http://docs.videojs.com/docs/api/index.html
|
||||
|
||||
[console]: https://developer.mozilla.org/en-US/docs/Web/API/Console
|
||||
@@ -0,0 +1,118 @@
|
||||
# Event Target
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Overview](#overview)
|
||||
* [on() and addEventListener()](#on-and-addeventlistener)
|
||||
* [off() and removeEventListener()](#off-and-removeeventlistener)
|
||||
* [one()](#one)
|
||||
* [trigger() and dispatchEvent()](#trigger-and-dispatchevent)
|
||||
|
||||
## Overview
|
||||
|
||||
Events in video.js are setup so that they mimic the DOM API that is used on object, but also have helpful shorthand functions with the same functionality.
|
||||
|
||||
## `on()` and `addEventListener()`
|
||||
|
||||
This function is used to add an event listener to an EventTarget.
|
||||
|
||||
```js
|
||||
var foo = new EventTarget();
|
||||
var handleBar = function() {
|
||||
console.log('bar was triggered');
|
||||
};
|
||||
|
||||
foo.on('bar', handleBar);
|
||||
|
||||
// This causes any `event listeners` for the `bar` event to get called
|
||||
// see {@link EventTarget#trigger} for more information
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
```
|
||||
|
||||
## `off()` and `removeEventListener()`
|
||||
|
||||
This function is used to remove an listener function from an EventTarget.
|
||||
|
||||
```js
|
||||
var foo = new EventTarget();
|
||||
var handleBar = function() {
|
||||
console.log('bar was triggered');
|
||||
};
|
||||
|
||||
// adds an `event listener` for the `bar` event
|
||||
// see {@link EventTarget#on} for more info
|
||||
foo.on('bar', handleBar);
|
||||
|
||||
// runs all `event listeners` for the `bar` event
|
||||
// see {@link EventTarget#trigger} for more info
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
|
||||
foo.off('bar', handleBar);
|
||||
foo.trigger('bar');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
## `one()`
|
||||
|
||||
This function is used to only have an event listener called once and never again.
|
||||
|
||||
Using `on()` and `off()` to mimic `one()` (not recommended)
|
||||
|
||||
```js
|
||||
var foo = new EventTarget();
|
||||
var handleBar = function() {
|
||||
console.log('bar was triggered');
|
||||
// after the first trigger remove this handler
|
||||
foo.off('bar', handleBar);
|
||||
};
|
||||
|
||||
foo.on('bar', handleBar);
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
|
||||
foo.trigger('bar');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
Using `one()`
|
||||
|
||||
```js
|
||||
var foo = new EventTarget();
|
||||
var handleBar = function() {
|
||||
console.log('bar was triggered');
|
||||
};
|
||||
|
||||
// removed after the first trigger
|
||||
foo.one('bar', handleBar);
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
|
||||
foo.trigger('bar');
|
||||
// does nothing
|
||||
```
|
||||
|
||||
## `trigger()` and `dispatchEvent()`
|
||||
|
||||
This function is used to trigger an event on an EventTarget which will cause all listeners to run.
|
||||
|
||||
> Note: if 'click' is in `EventTarget.allowedEvents_`, trigger will attempt to call the
|
||||
> `onClick` function if it exists.
|
||||
|
||||
```js
|
||||
var foo = new EventTarget();
|
||||
var handleBar = function() {
|
||||
console.log('bar was triggered');
|
||||
};
|
||||
|
||||
foo.on('bar', handleBar);
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
|
||||
foo.trigger('bar');
|
||||
// logs 'bar was triggered'
|
||||
|
||||
foo.trigger('foo');
|
||||
// does nothing
|
||||
```
|
||||
@@ -0,0 +1,329 @@
|
||||
# FAQ
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Q: What is video.js?](#q-what-is-videojs)
|
||||
* [Q: How do I install video.js?](#q-how-do-i-install-videojs)
|
||||
* [Q: Is video.js on bower?](#q-is-videojs-on-bower)
|
||||
* [Q: What do video.js version numbers mean?](#q-what-do-videojs-version-numbers-mean)
|
||||
* [Q: How can I troubleshoot playback issues?](#q-how-can-i-troubleshoot-playback-issues)
|
||||
* [Q: A video does not play in a specific browser. Why?](#q-a-video-does-not-play-in-a-specific-browser-why)
|
||||
* [Q: Why does the entire video download before playback? Why does the video load for a long time?](#q-why-does-the-entire-video-download-before-playback-why-does-the-video-load-for-a-long-time)
|
||||
* [Q: I see an error thrown that mentions vdata12345. What is that?](#q-i-see-an-error-thrown-that-mentions-vdata12345-what-is-that)
|
||||
* [Q: I think I found a bug with video.js or I want to add a feature. What should I do?](#q-i-think-i-found-a-bug-with-videojs-or-i-want-to-add-a-feature-what-should-i-do)
|
||||
* [if you think that you can fix the issue or add the feature](#if-you-think-that-you-can-fix-the-issue-or-add-the-feature)
|
||||
* [If you don't think you can fix the issue or add the feature](#if-you-dont-think-you-can-fix-the-issue-or-add-the-feature)
|
||||
* [Q: What is a reduced test case?](#q-what-is-a-reduced-test-case)
|
||||
* [Q: What media formats does video.js support?](#q-what-media-formats-does-videojs-support)
|
||||
* [Q: How to I autoplay the video?](#q-how-to-i-autoplay-the-video)
|
||||
* [Q: How can I autoplay a video on a mobile device?](#q-how-can-i-autoplay-a-video-on-a-mobile-device)
|
||||
* [Q: How can I play RTMP video in video.js?](#q-how-can-i-play-rtmp-video-in-videojs)
|
||||
* [Q: How can I hide the links to my video/subtitles/audio/tracks?](#q-how-can-i-hide-the-links-to-my-videosubtitlesaudiotracks)
|
||||
* [Q: Can I turn off video.js logging?](#q-can-i-turn-off-videojs-logging)
|
||||
* [Q: What is a plugin?](#q-what-is-a-plugin)
|
||||
* [Q: How do I make a plugin for video.js?](#q-how-do-i-make-a-plugin-for-videojs)
|
||||
* [Q: Where can I find a list of video.js plugins?](#q-where-can-i-find-a-list-of-videojs-plugins)
|
||||
* [Q: How can I get my plugin listed on the website?](#q-how-can-i-get-my-plugin-listed-on-the-website)
|
||||
* [Q: Where can I find a list of video.js skins?](#q-where-can-i-find-a-list-of-videojs-skins)
|
||||
* [Q: Does video.js work as an audio only player?](#q-does-videojs-work-as-an-audio-only-player)
|
||||
* [Q: Does video.js support audio tracks?](#q-does-videojs-support-audio-tracks)
|
||||
* [Q: Does video.js support video tracks?](#q-does-videojs-support-video-tracks)
|
||||
* [Q: Does video.js support text tracks (captions, subtitles, etc)?](#q-does-videojs-support-text-tracks-captions-subtitles-etc)
|
||||
* [Q: Does video.js support HLS (HTTP Live streaming) video?](#q-does-videojs-support-hls-http-live-streaming-video)
|
||||
* [Q: Does video.js support MPEG Dash video?](#q-does-videojs-support-mpeg-dash-video)
|
||||
* [Q: Does video.js support live video?](#q-does-videojs-support-live-video)
|
||||
* [Q: Can video.js wrap around YouTube videos?](#q-can-videojs-wrap-around-youtube-videos)
|
||||
* [Q: Can video.js wrap around Vimeo videos?](#q-can-videojs-wrap-around-vimeo-videos)
|
||||
* [Q: Does video.js support DRM video?](#q-does-videojs-support-drm-video)
|
||||
* [Q: Does video.js have any support for advertisement integrations?](#q-does-videojs-have-any-support-for-advertisement-integrations)
|
||||
* [Q: Can video.js be required in node.js?](#q-can-videojs-be-required-in-nodejs)
|
||||
* [Q: Does video.js work with webpack?](#q-does-videojs-work-with-webpack)
|
||||
* [Q: Does video.js work with react?](#q-does-videojs-work-with-react)
|
||||
|
||||
## Q: What is video.js?
|
||||
|
||||
video.js is an extendable framework/library around the native video element. It does the following:
|
||||
|
||||
* Offers a plugin API so that different types of video can be handed to the native
|
||||
video element (e.g. HLS, Flash, HTML5 video, etc).
|
||||
* Unifies the native video api across browsers (polyfilling support for features
|
||||
if necessary)
|
||||
* Offers an extendable and themable UI
|
||||
* Takes care of accessibility for the user (in-progress)
|
||||
* Has a set of core plugins that offer support for tons of additional video formats.
|
||||
* [videojs-contrib-hls][hls]
|
||||
* [videojs-contrib-dash][dash]
|
||||
* Support for DRM video via a core plugin
|
||||
* [videojs-contrib-eme][eme]
|
||||
* Lots of plugins which offer support for all kinds of features. See the [plugin list on videojs.com][plugin-list]
|
||||
|
||||
## Q: How do I install video.js?
|
||||
|
||||
Currently video.js can be installed using bower, 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.
|
||||
|
||||
## Q: What do video.js version numbers mean?
|
||||
|
||||
video.js follows [semver][semver] which means that the API should not change
|
||||
out from under a user unless there is a major version increase.
|
||||
|
||||
## Q: How can I troubleshoot playback issues?
|
||||
|
||||
See the [troubleshooting guide][troubleshooting]. If troubleshooting does not
|
||||
solve your issue, please submit a [pull request or an issue][pr-issue-question].
|
||||
|
||||
## Q: A video does not play in a specific browser. Why?
|
||||
|
||||
See the [troubleshooting guide][troubleshooting]. If troubleshooting does not
|
||||
solve your issue, please submit a [pull request or an issue][pr-issue-question].
|
||||
|
||||
## Q: Why does the entire video download before playback? Why does the video load for a long time?
|
||||
|
||||
See the [troubleshooting guide][troubleshooting]. If troubleshooting does not
|
||||
solve your issue, please submit a [pull request or an issue][pr-issue-question].
|
||||
|
||||
## Q: I see an error thrown that mentions `vdata12345`. What is that?
|
||||
|
||||
See the [troubleshooting guide][troubleshooting]. If troubleshooting does not
|
||||
solve your issue, please submit a [pull request or an issue][pr-issue-question].
|
||||
|
||||
## Q: I think I found a bug with video.js or I want to add a feature. What should I do?
|
||||
|
||||
### if you think that you can fix the issue or add the feature
|
||||
|
||||
Submit a pull request to the [video.js repo][vjs-prs].
|
||||
Make sure to follow the [contributing guide][contributing-prs] and
|
||||
the [pull request template][pr-template].
|
||||
|
||||
### If you don't think you can fix the issue or add the feature
|
||||
|
||||
Open an [issue on the video.js repo][vjs-issues]. Make
|
||||
sure that you follow the [issue template][issue-template] and the
|
||||
[contributing guide][contributing-issues] so that we can better assist you
|
||||
with your issue.
|
||||
|
||||
## Q: What is a reduced test case?
|
||||
|
||||
A reduced test case is an example of the problem that you are facing in isolation.
|
||||
Think of it as example page that reproduces the issue in the least amount of possible code.
|
||||
We have a [starter example][starter-example] for reduced test cases. To learn more
|
||||
about reduced test cases visit [css-tricks](https://css-tricks.com/reduced-test-cases/)
|
||||
|
||||
## Q: What media formats does video.js support?
|
||||
|
||||
This depends on the formats supported by the browser's HTML5 video element, and the playback
|
||||
techs made available to video.js. For example, video.js 5 includes the Flash tech by default which
|
||||
enables the playback of FLV video where the Flash plugin is available. For more information
|
||||
on media formats see the [troubleshooting guide][troubleshooting].
|
||||
|
||||
## Q: How to I autoplay the video?
|
||||
|
||||
Video.js supports the standard html5 `autoplay` attribute on the video element.
|
||||
It also supports it as an option to video.js or as a method invocation on the player.
|
||||
|
||||
```html
|
||||
<video autoplay controls class="video-js">
|
||||
```
|
||||
|
||||
```js
|
||||
var player = videojs('my-video', {
|
||||
autoplay: true
|
||||
});
|
||||
|
||||
// or
|
||||
|
||||
player.autoplay(true);
|
||||
```
|
||||
|
||||
### Q: How can I autoplay a video on a mobile device?
|
||||
|
||||
Most mobile devices have blocked autoplaying videos until recently.
|
||||
For mobile devices that don't support autoplaying, autoplay isn't supported by video.js.
|
||||
For those devices that support autoplaying, like iOS10 and Chrome for Android 53+,
|
||||
you must mute the video or have a video without audio tracks to be able to play it.
|
||||
For example:
|
||||
|
||||
```html
|
||||
<video muted autoplay playsinline>
|
||||
```
|
||||
|
||||
Will make an inline, muted, autoplaying video on an iPhone with iOS10.
|
||||
|
||||
## Q: How can I play RTMP video in video.js?
|
||||
|
||||
Make sure that the Flash tech is available -- RTMP is not playable on browsers without Flash including mobile. Then, just set the rtmp source with
|
||||
an appropriate type -- `rtmp/mp4` or `rtmp/flv`.
|
||||
The main thing to be aware of is that video.js splits the connection url and stream name with the `&` character.
|
||||
So, you'd want to update the url to follow that format. For example: `rtmp://example.com/live&foo` or `rtmp://example.com/fms&mp4:path/to/file.mp4`.
|
||||
|
||||
If the server requires query parameters for authentication, these should be added to the connection part url, for example `rtmp://example.com/live?token=1234&foo`.
|
||||
|
||||
## Q: How can I hide the links to my video/subtitles/audio/tracks?
|
||||
|
||||
It's impossible to hide the network requests a browser makes and difficult to
|
||||
sufficiently obfuscate URLs in the source. Techniques such as token authentication may
|
||||
help but are outside of the scope of video.js.
|
||||
|
||||
For content that must be highly secure [videojs-contrib-eme][eme] adds DRM support.
|
||||
|
||||
## Q: Can I turn off video.js logging?
|
||||
|
||||
Yes! This can be achieved by adding the following code _after_ including Video.js, but _before_ creating any player(s):
|
||||
|
||||
```js
|
||||
videojs.log.level('off');
|
||||
```
|
||||
|
||||
For more information, including which logging levels are available, check out the [debugging guide][debug-guide].
|
||||
|
||||
## Q: What is a plugin?
|
||||
|
||||
A plugin is a group of reusable functionality that can be re-used by others. For instance a plugin could add
|
||||
a button to video.js that makes the video replay 10 times in a row before it stops playback for good. If such
|
||||
a plugin existed and was published users could include it on their page to share that functionality.
|
||||
|
||||
## Q: How do I make a plugin for video.js?
|
||||
|
||||
See the [plugin guide][plugin-guide] for information on making a plugin for video.js.
|
||||
|
||||
<!-- TODO: Once these is a button guide, add this back in
|
||||
## Q: How do I add a button to video.js?
|
||||
See the [button guide][button-guide] for information on adding a button to video.js.
|
||||
-->
|
||||
|
||||
## Q: Where can I find a list of video.js plugins?
|
||||
|
||||
The official [list of plugins on videojs.com][plugin-list].
|
||||
|
||||
## Q: How can I get my plugin listed on the website?
|
||||
|
||||
Add the 'videojs-plugin' [keyword to your array in package.json][npm-keywords]
|
||||
and publish your package to npm. If you use the [plugin generator][generator] this will be done automatically for you. See
|
||||
the [plugins guide][plugin-guide] for more information.
|
||||
|
||||
## Q: Where can I find a list of video.js skins?
|
||||
|
||||
See the [video.js github wiki][skins-list].
|
||||
|
||||
## Q: Does video.js work as an audio only player?
|
||||
|
||||
Yes! It can be used to play audio only files in a `<video>` or `<audio>` tag. The
|
||||
difference being that the `<audio>` tag will not have a blank display area and the `<video>`
|
||||
tag will. Note that audio only will not work with the Flash playback tech.
|
||||
|
||||
## Q: Does video.js support audio tracks?
|
||||
|
||||
Yes! See the [audio tracks guide][audio-tracks] for information on using audio tracks.
|
||||
|
||||
## Q: Does video.js support video tracks?
|
||||
|
||||
The code for video tracks exists but it has not been tested. See the [video tracks guide][video-tracks]
|
||||
for more information on using video tracks.
|
||||
|
||||
## Q: Does video.js support text tracks (captions, subtitles, etc)?
|
||||
|
||||
Yes! See the [text tracks guide][text-tracks] for information on using text tracks.
|
||||
|
||||
## Q: Does video.js support HLS (HTTP Live streaming) video?
|
||||
|
||||
video.js supports HLS video if the native HTML5 element supports HLS (e.g. Safari, Edge,
|
||||
Chrome for Android, and iOS). For browsers without native support see the [videojs-contrib-hls][hls]
|
||||
project which adds support.
|
||||
|
||||
## Q: Does video.js support MPEG Dash video?
|
||||
|
||||
video.js itself does not support MPEG DASH, however an offical project called [videojs-contrib-dash][dash]
|
||||
adds support for MPEG DASH video.
|
||||
|
||||
## Q: Does video.js support live video?
|
||||
|
||||
Yes! Video.js adds support for live videos via the Flash tech which supports RTMP streams.
|
||||
The official HLS tech, [videojs-contrib-hls][hls], will add support for live HLS video
|
||||
if you add it to your page with video.js.
|
||||
|
||||
## Q: Can video.js wrap around YouTube videos?
|
||||
|
||||
No. There is an official plugin that adds support. It is called [videojs-youtube][youtube].
|
||||
|
||||
## Q: Can video.js wrap around Vimeo videos?
|
||||
|
||||
No. There is an official plugin that adds support. It is called [videojs-vimeo][vimeo].
|
||||
|
||||
## Q: Does video.js support DRM video?
|
||||
|
||||
No. There is an official plugin that adds support. It is called [videojs-contrib-eme][eme].
|
||||
|
||||
## Q: Does video.js have any support for advertisement integrations?
|
||||
|
||||
No. There is an official plugin that adds support. It is called [videojs-contrib-ads][ads].
|
||||
|
||||
## Q: Can video.js be required in node.js?
|
||||
|
||||
Yes! Please [submit an issue or open a pull request][pr-issue-question] if this does not work.
|
||||
|
||||
## Q: Does video.js work with webpack?
|
||||
|
||||
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.
|
||||
|
||||
## Q: Does video.js work with react?
|
||||
|
||||
Yes! See [ReactJS integration example](./guides/react.md).
|
||||
|
||||
[plugin-guide]: plugins.md
|
||||
|
||||
[install-guide]: http://videojs.com/getting-started/
|
||||
|
||||
[troubleshooting]: troubleshooting.md
|
||||
|
||||
[video-tracks]: video-tracks.md
|
||||
|
||||
[audio-tracks]: audio-tracks.md
|
||||
|
||||
[text-tracks]: text-tracks.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
|
||||
|
||||
[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
|
||||
|
||||
[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
|
||||
|
||||
[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
|
||||
|
||||
[debug-guide]: ./guides/debug.md
|
||||
@@ -1,39 +0,0 @@
|
||||
Glossary
|
||||
========
|
||||
Terms related to web video.
|
||||
|
||||
### DOM (Document Object Model)
|
||||
The container of elements on the page that must be loaded before you can interact with the elements with through Javascript.
|
||||
http://en.wikipedia.org/wiki/Document_Object_Model
|
||||
|
||||
|
||||
### Flash Fallback
|
||||
The Flash video player (SWF) used to play a video when HTML5 isn't supported.
|
||||
|
||||
|
||||
### TimeRange
|
||||
|
||||
|
||||
### HTML5 Video
|
||||
HTML is the markup language that makes up every page on the web. The newest version, HTML5, includes specifications for a video tag, that is meant to allow website developers to add a video to a page the same way they would add an image. In order for this to work, web browser developers (Mozilla, Apple, Microsoft, Google, Opera, etc.) have to build the video playback functionality into their browsers. The W3C has created directions on how video should work in browsers, and it’s up to browser developers to follow those directions, so that video works the same across all browsers. This doesn’t always happen thanks to technology, legal, and financial choices made by browser developers, but so far no one’s varying too far from the specifications. However the specifications are still being changed and refined, so browsers developers have to keep up with that as well.
|
||||
|
||||
Playing video in a web page may not seem so special since you can already view video on a web page through plugins like Flash Player, Quicktime, Silverlight, and RealPlayer, however this is a big step forward for standardizing video playback across web browsers and devices. The goal is that in the future, developers will only need to use one method for embedding a video, that’s based on open standards (not controlled by one company), and it will work everywhere.
|
||||
|
||||
A prime example of this is the iPhone and iPad. Apple has decided not to support Flash on their mobile devices, but they do support HTML5 video. Since Flash is currently the most common way video is added to web pages, most web video (aside from YouTube who has a special relationship with Apple) can’t be viewed on the iPhone or iPad. These devices are very popular, so many web sites are switching to hybrid HTML5/Flash player setups (like VideoJS).
|
||||
|
||||
|
||||
### Video Tag
|
||||
There are a number of great resources that will give you an introduction to the video tag an how it is used including:
|
||||
|
||||
- [Dive into HTML5](http://diveintohtml5.org/video.html)
|
||||
- Lynda.com's ['HTML5 Video and Audio in Depth'](http://www.lynda.com/HTML-5-tutorials/HTML5-Video-and-Audio-in-Depth/80781-2.html) video tutorials created by yours truly.
|
||||
|
||||
An if you really want to dig in, you can read the (W3C Spec)[http://www.w3.org/TR/html5/video.html]. (Warning - not for the faint of heart)
|
||||
|
||||
|
||||
### Skin
|
||||
"Skin" refers to the design of the player's controls, also sometimes called the chrome. With VideoJS, new skins can be built simply by creating a new stylesheet.
|
||||
|
||||
|
||||
### Content Delivery Network (CDN)
|
||||
A network of servers around the world that host copies of a file. When your browser requests one of these files, the CDN automatically determines which server is closest to your location and delivers the file from there. This drastically increases delivery time, especially internationally.
|
||||
@@ -0,0 +1,146 @@
|
||||
# Hooks
|
||||
|
||||
Hooks exist so that users can "hook" on to certain video.js player lifecycle
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Current Hooks](#current-hooks)
|
||||
* [beforesetup](#beforesetup)
|
||||
* [setup](#setup)
|
||||
* [Usage](#usage)
|
||||
* [Adding](#adding)
|
||||
* [Getting](#getting)
|
||||
* [Removing](#removing)
|
||||
|
||||
## Current Hooks
|
||||
|
||||
Currently, the following hooks are avialable:
|
||||
|
||||
### beforesetup
|
||||
|
||||
`beforesetup` is called just before the player is created. This allows:
|
||||
|
||||
* modification of the options passed to the video.js function (`videojs('some-id, options)`)
|
||||
* modification of the dom video element that will be used for the player
|
||||
|
||||
`beforesetup` hook functions should:
|
||||
|
||||
* take two arguments
|
||||
1. videoEl: dom video element that video.js is going to use to create a player
|
||||
1. options: options that video.js was intialized with and will later pass to the player during creation
|
||||
* return options that will merge and override options that video.js with intialized with
|
||||
|
||||
Example: adding beforesetup hook
|
||||
|
||||
```js
|
||||
var beforeSetup = function(videoEl, options) {
|
||||
// videoEl.id will be some-id here, since that is what video.js
|
||||
// was created with
|
||||
|
||||
videoEl.className += ' some-super-class';
|
||||
|
||||
// autoplay will be true here, since we passed in as such
|
||||
(options.autoplay) {
|
||||
options.autoplay = false
|
||||
}
|
||||
|
||||
// options that are returned here will be merged with old options
|
||||
// in this example options will now be
|
||||
// {autoplay: false, controls: true}
|
||||
return options;
|
||||
};
|
||||
|
||||
videojs.hook('beforesetup', beforeSetup);
|
||||
videojs('some-id', {autoplay: true, controls: true});
|
||||
```
|
||||
|
||||
### setup
|
||||
|
||||
`setup` is called just after the player is created. This allows:
|
||||
|
||||
* plugin or custom functionalify to intialize on the player
|
||||
* changes to the player object itself
|
||||
|
||||
`setup` hook functions:
|
||||
|
||||
* Take one argument
|
||||
* player: the player that video.js created
|
||||
* Don't have to return anything
|
||||
|
||||
Example: adding setup hook
|
||||
|
||||
```js
|
||||
var setup = function(player) {
|
||||
// initialize the foo plugin
|
||||
player.foo();
|
||||
};
|
||||
var foo = function() {};
|
||||
|
||||
videojs.plugin('foo', foo);
|
||||
videojs.hook('setup', setup);
|
||||
var player = videojs('some-id', {autoplay: true, controls: true});
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Adding
|
||||
|
||||
In order to use hooks you must first include video.js in the page or script that you are using. Then you add hooks using `videojs.hook(<name>, function)` before running the `videojs()` function.
|
||||
|
||||
Example: adding hooks
|
||||
|
||||
```js
|
||||
videojs.hook('beforesetup', function(videoEl, options) {
|
||||
// videoEl will be the element with id=vid1
|
||||
// options will contain {autoplay: false}
|
||||
});
|
||||
videojs.hook('setup', function(player) {
|
||||
// player will be the same player that is defined below
|
||||
// as `var player`
|
||||
});
|
||||
|
||||
var player = videojs('vid1', {autoplay: false});
|
||||
```
|
||||
|
||||
After adding your hooks they will automatically be run at the correct time in the video.js lifecycle.
|
||||
|
||||
### Getting
|
||||
|
||||
To access the array of hooks that currently exists and will be run on the video.js object you can use the `videojs.hooks` function.
|
||||
|
||||
Example: getting all hooks attached to video.js
|
||||
|
||||
```js
|
||||
var beforeSetupHooks = videojs.hooks('beforesetup');
|
||||
var setupHooks = videojs.hooks('setup');
|
||||
```
|
||||
|
||||
### Removing
|
||||
|
||||
To stop hooks from being executed during the video.js lifecycle you will remove them using `videojs.removeHook`.
|
||||
|
||||
Example: remove a hook that was defined by you
|
||||
|
||||
```js
|
||||
var beforeSetup = function(videoEl, options) {};
|
||||
|
||||
// add the hook
|
||||
videojs.hook('beforesetup', beforeSetup);
|
||||
|
||||
// remove that same hook
|
||||
videojs.removeHook('beforesetup', beforeSetup);
|
||||
```
|
||||
|
||||
You can also use `videojs.hooks` in conjunction with `videojs.removeHook` but it may have unexpected results if used during an asynchronous callbacks as other plugins/functionality may have added hooks.
|
||||
|
||||
Example: using `videojs.hooks` and `videojs.removeHook` to remove a hook
|
||||
|
||||
```js
|
||||
// add the hook
|
||||
videojs.hook('setup', function(videoEl, options) {});
|
||||
|
||||
var setupHooks = videojs.hooks('setup');
|
||||
|
||||
// remove the hook you just added
|
||||
videojs.removeHook('setup', setupHooks[setupHooks.length - 1]);
|
||||
```
|
||||
+111
-270
@@ -1,15 +1,41 @@
|
||||
Languages
|
||||
=========
|
||||
# Languages
|
||||
|
||||
Multiple language support allows for users of non-English locales to natively interact with the displayed player. Video.js will compile multiple language files (see below) and instantiate with a global dictionary of language key/value support. Video.js player instances can be created with per-player language support that amends/overrides these default values. Player instances can also hard-set default languages to values other than English as of version 4.7.
|
||||
Multiple language support allows for users of non-English locales to natively interact with the Video.js player.
|
||||
|
||||
Creating the Language File
|
||||
--------------------------
|
||||
Video.js uses key/value object dictionaries in JSON form.
|
||||
For an up-to-date list of the languages Video.js supports, see the [languages folder (`lang`)][lang-supported]. These JSON files are converted to JavaScript during the Video.js build process.
|
||||
|
||||
An English lang file is at [/lang/en.json](https://github.com/videojs/video.js/tree/master/lang/en.json) which should be used as a template for new files. This will be kept up to date with strings in the core player that need localizations.
|
||||
## Table of Contents
|
||||
|
||||
A sample dictionary for Spanish `['es']` would look as follows:
|
||||
* [Using Video.js Languages](#using-videojs-languages)
|
||||
* [Contributing to Video.js Translations](#contributing-to-videojs-translations)
|
||||
* [JSON Format](#json-format)
|
||||
* [File Naming](#file-naming)
|
||||
* [Updating an Existing Translation](#updating-an-existing-translation)
|
||||
* [Writing a New Translation](#writing-a-new-translation)
|
||||
* [Advanced Language Usage](#advanced-language-usage)
|
||||
* [Adding Languages via the API](#adding-languages-via-the-api)
|
||||
* [Per-Player Languages](#per-player-languages)
|
||||
* [Setting Default Player Language](#setting-default-player-language)
|
||||
* [Determining Player Language](#determining-player-language)
|
||||
* [Internal Language Selection](#internal-language-selection)
|
||||
* [References](#references)
|
||||
|
||||
## Using Video.js Languages
|
||||
|
||||
Video.js ships with multiple translations (in `dist/lang/`) in JavaScript files. Each of these files can be included in a web page to provide support for that language in _all_ Video.js players:
|
||||
|
||||
```html
|
||||
<script src="//example.com/path/to/video.min.js"></script>
|
||||
<script src="//example.com/path/to/lang/es.js"></script>
|
||||
```
|
||||
|
||||
## Contributing to Video.js Translations
|
||||
|
||||
We welcome new translations and improvements to existing ones! Please see the [contributing document](../../CONTRIBUTING.md) to get started contributing to Video.js and continue reading for specifics on how to contribute to translations of Video.js...
|
||||
|
||||
### JSON Format
|
||||
|
||||
Video.js uses a JSON object to describe a language, where the keys are English and the values are the target language. For example, a Spanish translation might look like this:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -18,298 +44,113 @@ A sample dictionary for Spanish `['es']` would look as follows:
|
||||
"Current Time": "Tiempo reproducido",
|
||||
"Duration Time": "Duración total",
|
||||
"Remaining Time": "Tiempo restante",
|
||||
"Stream Type": "Tipo de secuencia",
|
||||
"LIVE": "DIRECTO",
|
||||
"Loaded": "Cargado",
|
||||
"Progress": "Progreso",
|
||||
"Fullscreen": "Pantalla completa",
|
||||
"Non-Fullscreen": "Pantalla no completa",
|
||||
"Mute": "Silenciar",
|
||||
"Unmute": "No silenciado",
|
||||
"Playback Rate": "Velocidad de reproducción",
|
||||
"Subtitles": "Subtítulos",
|
||||
"subtitles off": "Subtítulos desactivados",
|
||||
"Captions": "Subtítulos especiales",
|
||||
"captions off": "Subtítulos especiales desactivados",
|
||||
"Chapters": "Capítulos",
|
||||
"Close Modal Dialog": "Cerca de diálogo modal",
|
||||
"You aborted the video playback": "Ha interrumpido la reproducción del vídeo.",
|
||||
"A network error caused the video download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.",
|
||||
"The video could not be loaded, either because the server or network failed or because the format is not supported.": "No se ha podido cargar el vídeo debido a un fallo de red o del servidor o porque el formato es incompatible.",
|
||||
"The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.",
|
||||
"No compatible source was found for this video.": "No se ha encontrado ninguna fuente compatible con este vídeo."
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
### File Naming
|
||||
|
||||
- The file name should always be in the format `XX.json`, where `XX` is the language code. This should be a two letter code (for options see the bottom of this document) except for cases where a more specific code with sub-code is appropriate, e.g. `zh-CN.lang`.
|
||||
- For automatic inclusion at build time, add your language file to the `/lang` directory (see 'Adding Languages to Video.js below').
|
||||
Translations are always found in the `lang/` directory.
|
||||
|
||||
Adding Languages to Video.js
|
||||
----------------------------
|
||||
Additional language support can be added to Video.js in multiple ways.
|
||||
Each file's name should be the [standard language code][lang-codes] that is most appropriate. For example, "es" for Spanish or "zh-CN" for Chinese.
|
||||
|
||||
1. Create language scripts out of your JSON objects by using our custom grunt task `vjslanguages`. This task is automatically run as part of the default grunt task in Video.JS, but can be configured to match your `src`/`dist` directories if different. Once these scripts are created, just add them to your DOM like any other script.
|
||||
Finally, each file's extension is always `.json`.
|
||||
|
||||
NOTE: These need to be added after the core Video.js script.
|
||||
### Updating an Existing Translation
|
||||
|
||||
If there is a [missing translation](../translations-needed.md), mistake, or room for improvement in an existing translation, don't hesitate to open a pull request!
|
||||
|
||||
2. Add your JSON objects via the videojs.addLanguage API. Preferably in the HEAD element of your DOM or otherwise prior to player instantiation.
|
||||
1. Edit the relevant JSON file and make the necessary changes.
|
||||
1. Verify the language compiles by running `grunt dist`.
|
||||
1. Verify the translation appears properly in the player UI.
|
||||
1. Run `grunt check-translations` to update the [missing translation document](../translations-needed.md).
|
||||
1. Commit and open a pull request on GitHub.
|
||||
|
||||
```html
|
||||
<head>
|
||||
<script>
|
||||
videojs.options.flash.swf = '../node_modules/videojs-swf/dist/video-js.swf';
|
||||
videojs.addLanguage('es', {
|
||||
"Play": "Reproducción",
|
||||
"Pause": "Pausa",
|
||||
"Current Time": "Tiempo reproducido",
|
||||
"Duration Time": "Duración total",
|
||||
"Remaining Time": "Tiempo restante",
|
||||
"Stream Type": "Tipo de secuencia",
|
||||
"LIVE": "DIRECTO",
|
||||
"Loaded": "Cargado",
|
||||
"Progress": "Progreso",
|
||||
"Fullscreen": "Pantalla completa",
|
||||
"Non-Fullscreen": "Pantalla no completa",
|
||||
"Mute": "Silenciar",
|
||||
"Unmute": "No silenciado",
|
||||
"Playback Rate": "Velocidad de reproducción",
|
||||
"Subtitles": "Subtítulos",
|
||||
"subtitles off": "Subtítulos desactivados",
|
||||
"Captions": "Subtítulos especiales",
|
||||
"captions off": "Subtítulos especiales desactivados",
|
||||
"Chapters": "Capítulos",
|
||||
"Close Modal Dialog": "Cerca de diálogo modal",
|
||||
"You aborted the video playback": "Ha interrumpido la reproducción del vídeo.",
|
||||
"A network error caused the video download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.",
|
||||
"The video could not be loaded, either because the server or network failed or because the format is not supported.": "No se ha podido cargar el vídeo debido a un fallo de red o del servidor o porque el formato es incompatible.",
|
||||
"The video playback was aborted due to a corruption problem or because the video used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.",
|
||||
"No compatible source was found for this video.": "No se ha encontrado ninguna fuente compatible con este vídeo."
|
||||
### Writing a New Translation
|
||||
|
||||
The process for writing an entirely new translation is virtually identical to the process for [updating an existing translation](#updating-an-existing-translation) except that the new translation JSON file needs to be created.
|
||||
|
||||
The template for new language files is the English file ([lang/en.json][lang-en]). This file is always up-to-date with strings that need translations.
|
||||
|
||||
The first step to writing a new translation is to copy the English file:
|
||||
|
||||
```sh
|
||||
cp lang/en.json lang/${NEW_LANG_CODE}.json
|
||||
```
|
||||
|
||||
Otherwise, the process is the same as [updating an existing translation](#updating-an-existing-translation).
|
||||
|
||||
## Advanced Language Usage
|
||||
|
||||
The instructions above for [using Video.js languages](#using-videojs-languages) should be sufficient for the majority of use-cases. However, languages can be provided at runtime.
|
||||
|
||||
In each case, these custom language definitions _take precedence over any Video.js-provided languages!_
|
||||
|
||||
### Adding Languages via the API
|
||||
|
||||
In addition to the stand-alone scripts provided by Video.js, the API supports manual definition of new languages via the `addLanguage` method. It takes two arguments: the [standard language code][lang-codes] and a [language definition object](#json-format).
|
||||
|
||||
```js
|
||||
videojs.addLanguage('es', {
|
||||
'Play': 'Reproducción',
|
||||
'Pause': 'Pausa',
|
||||
'Current Time': 'Tiempo reproducido',
|
||||
'Duration Time': 'Duración total',
|
||||
'Remaining Time': 'Tiempo restante',
|
||||
...
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
```
|
||||
|
||||
3. During a Video.js player instantiation. Adding the languages to the configuration object provided in the `data-setup` attribute.
|
||||
### Per-Player Languages
|
||||
|
||||
```html
|
||||
<video id="example_video_1" class="video-js vjs-default-skin"
|
||||
controls preload="auto" width="640" height="264"
|
||||
data-setup='{"languages":{"es":{"Play":"Juego"}}}'>
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
In addition to providing languages to Video.js itself, individual `Player` instances can be provided custom language support via [the `languages` option](options.md#languages):
|
||||
|
||||
<track kind="captions" src="http://example.com/path/to/captions.vtt" srclang="en" label="English" default>
|
||||
|
||||
</video>
|
||||
```js
|
||||
// Provide a custom definition of Spanish to this player.
|
||||
videojs('my-player', {
|
||||
languages: {
|
||||
es: {...}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
- This will add your language key/values to the Video.js player instances individually. If these values already exist in the global dictionary via the process above, those will be overridden for the player instance in question.
|
||||
### Setting Default Player Language
|
||||
|
||||
Player instances can also have a default language via [the `language` option](options.md#language):
|
||||
|
||||
Setting Default Language in a Video.js Player
|
||||
---------------------------------------------
|
||||
During a Video.js player instantiation you can force it to localize to a specific language by including the locale value into the configuration object via the `data-setup` attribute. Valid options listed at the bottom of the page for reference.
|
||||
|
||||
```html
|
||||
<video id="example_video_1" class="video-js vjs-default-skin"
|
||||
controls preload="auto" width="640" height="264"
|
||||
data-setup='{"language":"es"}'>
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
|
||||
<track kind="captions" src="http://example.com/path/to/captions.vtt" srclang="en" label="English" default>
|
||||
|
||||
</video>
|
||||
```js
|
||||
// Set the default language to Spanish for this player.
|
||||
videojs('my-player', {
|
||||
language: 'es'
|
||||
});
|
||||
```
|
||||
|
||||
Determining Player Language
|
||||
---------------------------
|
||||
Additionally, the `language` method of the player can be used to set the language after instantiation (e.g., `language('es')`). However, this is not recommended as it does not update the UI in place. _Setting the language via options is always preferred._
|
||||
|
||||
### Determining Player Language
|
||||
|
||||
The player language is set to one of the following in descending priority:
|
||||
|
||||
* The language specified in setup options as above
|
||||
* The language specified by the closet element with a `lang` attribute. This could be the player itself or a parent element. Usually the document language is specified on the `html` tag.
|
||||
* Browser language preference (the first language if more than one is configured)
|
||||
* 'en'
|
||||
* The language [specified in options](#setting-default-player-language)
|
||||
* The language specified by the closest element with a `lang` attribute. This could be the player itself or a parent element. Usually, the document language is specified on the `<html>` tag.
|
||||
* Browser language preference; the first language if more than one is configured
|
||||
* English
|
||||
|
||||
The player language can be change after instantiation with `language('fr')`. However localizable text will not be modified by doing this, for best results set the language beforehand.
|
||||
#### Internal Language Selection
|
||||
|
||||
Language selection
|
||||
------------------
|
||||
* Language codes are considered case-insensitively (e.g. `en-US` == `en-us`).
|
||||
* If there is no match for a language code with a subcode (e.g. `en-us`), a match for the primary code (e.g. `en`) is used if available.
|
||||
|
||||
* Language codes are considered case-insensitively (`en-US` == `en-us`).
|
||||
* If there is no match for a language code with a subcode (`en-us`), a match for the primary code (`en`) is used if available.
|
||||
## References
|
||||
|
||||
Localization in Plugins
|
||||
-----------------------
|
||||
For information on translation/localization in plugins, see [the plugins guide](plugins.md).
|
||||
|
||||
When you're developing a plugin, you can also introduce new localized strings. Simply wrap the string with the player's `localize` function:
|
||||
Standard languages codes [are defined by the IANA][lang-codes].
|
||||
|
||||
```js
|
||||
var details = '<div class="vjs-errors-details">' + player.localize('Technical details') + '</div>';
|
||||
```
|
||||
For all existing/supported languages, please see the [languages lolder (`lang/`)][lang-supported] folder located in the project root.
|
||||
|
||||
Language Codes
|
||||
--------------
|
||||
The following is a list of official language codes.
|
||||
[lang-en]: https://github.com/videojs/video.js/tree/master/lang/en.json
|
||||
|
||||
**NOTE:** For supported language translations, please see the [Languages Folder (/lang)](https://github.com/videojs/video.js/tree/master/lang) folder located in the project root.
|
||||
[lang-supported]: https://github.com/videojs/video.js/tree/master/lang
|
||||
|
||||
<table border="0" cellspacing="5" cellpadding="5">
|
||||
<tr>
|
||||
<table>
|
||||
<tr><th>ab<th><td>Abkhazian</td></tr>
|
||||
<tr><th>aa<th><td>Afar</td></tr>
|
||||
<tr><th>af<th><td>Afrikaans</td></tr>
|
||||
<tr><th>sq<th><td>Albanian</td></tr>
|
||||
<tr><th>am<th><td>Amharic</td></tr>
|
||||
<tr><th>ar<th><td>Arabic</td></tr>
|
||||
<tr><th>an<th><td>Aragonese</td></tr>
|
||||
<tr><th>hy<th><td>Armenian</td></tr>
|
||||
<tr><th>as<th><td>Assamese</td></tr>
|
||||
<tr><th>ay<th><td>Aymara</td></tr>
|
||||
<tr><th>az<th><td>Azerbaijani</td></tr>
|
||||
<tr><th>ba<th><td>Bashkir</td></tr>
|
||||
<tr><th>eu<th><td>Basque</td></tr>
|
||||
<tr><th>bn<th><td>Bengali (Bangla)</td></tr>
|
||||
<tr><th>dz<th><td>Bhutani</td></tr>
|
||||
<tr><th>bh<th><td>Bihari</td></tr>
|
||||
<tr><th>bi<th><td>Bislama</td></tr>
|
||||
<tr><th>br<th><td>Breton</td></tr>
|
||||
<tr><th>bg<th><td>Bulgarian</td></tr>
|
||||
<tr><th>my<th><td>Burmese</td></tr>
|
||||
<tr><th>be<th><td>Byelorussian (Belarusian)</td></tr>
|
||||
<tr><th>km<th><td>Cambodian</td></tr>
|
||||
<tr><th>ca<th><td>Catalan</td></tr>
|
||||
<tr><th>zh<th><td>Chinese (Simplified)</td></tr>
|
||||
<tr><th>zh<th><td>Chinese (Traditional)</td></tr>
|
||||
<tr><th>co<th><td>Corsican</td></tr>
|
||||
<tr><th>hr<th><td>Croatian</td></tr>
|
||||
<tr><th>cs<th><td>Czech</td></tr>
|
||||
<tr><th>da<th><td>Danish</td></tr>
|
||||
<tr><th>nl<th><td>Dutch</td></tr>
|
||||
<tr><th>en<th><td>English</td></tr>
|
||||
<tr><th>eo<th><td>Esperanto</td></tr>
|
||||
<tr><th>et<th><td>Estonian</td></tr>
|
||||
<tr><th>fo<th><td>Faeroese</td></tr>
|
||||
<tr><th>fa<th><td>Farsi</td></tr>
|
||||
<tr><th>fj<th><td>Fiji</td></tr>
|
||||
<tr><th>fi<th><td>Finnish</td></tr>
|
||||
<tr><th>fr<th><td>French</td></tr>
|
||||
<tr><th>fy<th><td>Frisian</td></tr>
|
||||
<tr><th>gl<th><td>Galician</td></tr>
|
||||
<tr><th>gd<th><td>Gaelic (Scottish)</td></tr>
|
||||
<tr><th>gv<th><td>Gaelic (Manx)</td></tr>
|
||||
<tr><th>ka<th><td>Georgian</td></tr>
|
||||
<tr><th>de<th><td>German</td></tr>
|
||||
<tr><th>el<th><td>Greek</td></tr>
|
||||
<tr><th>kl<th><td>Greenlandic</td></tr>
|
||||
<tr><th>gn<th><td>Guarani</td></tr>
|
||||
<tr><th>gu<th><td>Gujarati</td></tr>
|
||||
<tr><th>ht<th><td>Haitian Creole</td></tr>
|
||||
<tr><th>ha<th><td>Hausa</td></tr>
|
||||
<tr><th>he<th><td>Hebrew</td></tr>
|
||||
<tr><th>hi<th><td>Hindi</td></tr>
|
||||
<tr><th>hu<th><td>Hungarian</td></tr>
|
||||
<tr><th>is<th><td>Icelandic</td></tr>
|
||||
<tr><th>io<th><td>Ido</td></tr>
|
||||
<tr><th>id<th><td>Indonesian</td></tr>
|
||||
<tr><th>ia<th><td>Interlingua</td></tr>
|
||||
<tr><th>ie<th><td>Interlingue</td></tr>
|
||||
<tr><th>iu<th><td>Inuktitut</td></tr>
|
||||
<tr><th>ik<th><td>Inupiak</td></tr>
|
||||
<tr><th>ga<th><td>Irish</td></tr>
|
||||
<tr><th>it<th><td>Italian</td></tr>
|
||||
<tr><th>ja<th><td>Japanese</td></tr>
|
||||
<tr><th>jv<th><td>Javanese</td></tr>
|
||||
<tr><th>kn<th><td>Kannada</td></tr>
|
||||
<tr><th>ks<th><td>Kashmiri</td></tr>
|
||||
<tr><th>kk<th><td>Kazakh</td></tr>
|
||||
<tr><th>rw<th><td>Kinyarwanda (Ruanda)</td></tr>
|
||||
<tr><th>ky<th><td>Kirghiz</td></tr>
|
||||
<tr><th>rn<th><td>Kirundi (Rundi)</td></tr>
|
||||
<tr><th>ko<th><td>Korean</td></tr>
|
||||
<tr><th>ku<th><td>Kurdish</td></tr>
|
||||
<tr><th>lo<th><td>Laothian</td></tr>
|
||||
<tr><th>la<th><td>Latin</td></tr>
|
||||
<tr><th>lv<th><td>Latvian (Lettish)</td></tr>
|
||||
<tr><th>li<th><td>Limburgish ( Limburger)</td></tr>
|
||||
<tr><th>ln<th><td>Lingala</td></tr>
|
||||
<tr><th>lt<th><td>Lithuanian</td></tr>
|
||||
<tr><th>mk<th><td>Macedonian</td></tr>
|
||||
<tr><th>mg<th><td>Malagasy</td></tr>
|
||||
<tr><th>ms<th><td>Malay</td></tr>
|
||||
<tr><th>ml<th><td>Malayalam</td></tr>
|
||||
<tr><th>mt<th><td>Maltese</td></tr>
|
||||
<tr><th>mi<th><td>Maori</td></tr>
|
||||
<tr><th>mr<th><td>Marathi</td></tr>
|
||||
<tr><th>mo<th><td>Moldavian</td></tr>
|
||||
<tr><th>mn<th><td>Mongolian</td></tr>
|
||||
<tr><th>na<th><td>Nauru</td></tr>
|
||||
<tr><th>ne<th><td>Nepali</td></tr>
|
||||
<tr><th>no<th><td>Norwegian</td></tr>
|
||||
<tr><th>oc<th><td>Occitan</td></tr>
|
||||
<tr><th>or<th><td>Oriya</td></tr>
|
||||
<tr><th>om<th><td>Oromo (Afan, Galla)</td></tr>
|
||||
<tr><th>ps<th><td>Pashto (Pushto)</td></tr>
|
||||
<tr><th>pl<th><td>Polish</td></tr>
|
||||
<tr><th>pt<th><td>Portuguese</td></tr>
|
||||
<tr><th>pa<th><td>Punjabi</td></tr>
|
||||
<tr><th>qu<th><td>Quechua</td></tr>
|
||||
<tr><th>rm<th><td>Rhaeto-Romance</td></tr>
|
||||
<tr><th>ro<th><td>Romanian</td></tr>
|
||||
<tr><th>ru<th><td>Russian</td></tr>
|
||||
<tr><th>sm<th><td>Samoan</td></tr>
|
||||
<tr><th>sg<th><td>Sangro</td></tr>
|
||||
<tr><th>sa<th><td>Sanskrit</td></tr>
|
||||
<tr><th>sr<th><td>Serbian</td></tr>
|
||||
<tr><th>sh<th><td>Serbo-Croatian</td></tr>
|
||||
<tr><th>st<th><td>Sesotho</td></tr>
|
||||
<tr><th>tn<th><td>Setswana</td></tr>
|
||||
<tr><th>sn<th><td>Shona</td></tr>
|
||||
<tr><th>ii<th><td>Sichuan Yi</td></tr>
|
||||
<tr><th>sd<th><td>Sindhi</td></tr>
|
||||
<tr><th>si<th><td>Sinhalese</td></tr>
|
||||
<tr><th>ss<th><td>Siswati</td></tr>
|
||||
<tr><th>sk<th><td>Slovak</td></tr>
|
||||
<tr><th>sl<th><td>Slovenian</td></tr>
|
||||
<tr><th>so<th><td>Somali</td></tr>
|
||||
<tr><th>es<th><td>Spanish</td></tr>
|
||||
<tr><th>su<th><td>Sundanese</td></tr>
|
||||
<tr><th>sw<th><td>Swahili (Kiswahili)</td></tr>
|
||||
<tr><th>sv<th><td>Swedish</td></tr>
|
||||
<tr><th>tl<th><td>Tagalog</td></tr>
|
||||
<tr><th>tg<th><td>Tajik</td></tr>
|
||||
<tr><th>ta<th><td>Tamil</td></tr>
|
||||
<tr><th>tt<th><td>Tatar</td></tr>
|
||||
<tr><th>te<th><td>Telugu</td></tr>
|
||||
<tr><th>th<th><td>Thai</td></tr>
|
||||
<tr><th>bo<th><td>Tibetan</td></tr>
|
||||
<tr><th>ti<th><td>Tigrinya</td></tr>
|
||||
<tr><th>to<th><td>Tonga</td></tr>
|
||||
<tr><th>ts<th><td>Tsonga</td></tr>
|
||||
<tr><th>tr<th><td>Turkish</td></tr>
|
||||
<tr><th>tk<th><td>Turkmen</td></tr>
|
||||
<tr><th>tw<th><td>Twi</td></tr>
|
||||
<tr><th>ug<th><td>Uighur</td></tr>
|
||||
<tr><th>uk<th><td>Ukrainian</td></tr>
|
||||
<tr><th>ur<th><td>Urdu</td></tr>
|
||||
<tr><th>uz<th><td>Uzbek</td></tr>
|
||||
<tr><th>vi<th><td>Vietnamese</td></tr>
|
||||
<tr><th>vo<th><td>Volapük</td></tr>
|
||||
<tr><th>wa<th><td>Wallon</td></tr>
|
||||
<tr><th>cy<th><td>Welsh</td></tr>
|
||||
<tr><th>wo<th><td>Wolof</td></tr>
|
||||
<tr><th>xh<th><td>Xhosa</td></tr>
|
||||
<tr><th>yi<th><td>Yiddish</td></tr>
|
||||
<tr><th>yo<th><td>Yoruba</td></tr>
|
||||
<tr><th>zu<th><td>Zulu</td></tr>
|
||||
</table>
|
||||
</tr>
|
||||
</table>
|
||||
[lang-codes]: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Using the Modal Dialog Component
|
||||
|
||||
The `ModalDialog` component is part of Video.js core and provides a baked-in UI for full-player overlays.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Creating the ModalDialog](#creating-the-modaldialog)
|
||||
* [Example Using createModal()](#example-using-createmodal)
|
||||
* [Example Using the ModalDialog Constructor](#example-using-the-modaldialog-constructor)
|
||||
* [Styling Modals Independently](#styling-modals-independently)
|
||||
|
||||
## Creating a ModalDialog
|
||||
|
||||
Aside from the [built-in Video.js component-creation methods][creating-component], the player includes a `createModal()` helper method.
|
||||
|
||||
We'll demonstrate both approaches in this document by creating a modal that opens when the player becomes paused and resumes playback when it is closed.
|
||||
|
||||
### Example Using `createModal()`
|
||||
|
||||
The `createModal()` method is intended for creating one-off modals that need to open for some temporary purpose. Therefore, they open themselves immediately upon creation and, by default, dispose themselves immediately upon closing.
|
||||
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.on('pause', function() {
|
||||
|
||||
// Modals are temporary by default. They dispose themselves when they are
|
||||
// closed; so, we can create a new one each time the player is paused and
|
||||
// not worry about leaving extra nodes hanging around.
|
||||
var modal = player.createModal('This is a modal!');
|
||||
|
||||
// When the modal closes, resume playback.
|
||||
modal.on('modalclose', function() {
|
||||
player.play();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The `createModal()` method also takes a second argument - an object containing options for the modal. Refer to [the API documentation][api-doc] for a full set of options.
|
||||
|
||||
### Example Using the `ModalDialog` Constructor
|
||||
|
||||
Unlike when using `createModal()`, a modal created with any of the [common component creation methods][creating-component] _does not_ open by default. This makes this approach better suited to modals that are expected to live in the DOM indefinitely.
|
||||
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
var ModalDialog = videojs.getComponent('ModalDialog');
|
||||
|
||||
var modal = new ModalDialog(player, {
|
||||
|
||||
// We don't want this modal to go away when it closes.
|
||||
temporary: false
|
||||
});
|
||||
|
||||
player.addChild(modal);
|
||||
|
||||
player.on('pause', function() {
|
||||
modal.open();
|
||||
});
|
||||
|
||||
player.on('play', function() {
|
||||
modal.close();
|
||||
});
|
||||
```
|
||||
|
||||
Both of these examples are equivalent when it comes to the user's experience. Implementors should use whichever better suits their use-case.
|
||||
|
||||
## Styling Modals Independently
|
||||
|
||||
A common need for modals is to style them independently from one another. The recommended approach for this is to add a custom class to your modal and target that using CSS:
|
||||
|
||||
```js
|
||||
modal.addClass('vjs-my-fancy-modal');
|
||||
```
|
||||
|
||||
[api-doc]: http://docs.videojs.com/docs/api/modal-dialog.html
|
||||
|
||||
[creating-component]: ./components.md#creating-a-component
|
||||
+366
-123
@@ -1,138 +1,381 @@
|
||||
Options
|
||||
=======
|
||||
# Video.js Options Reference
|
||||
|
||||
Setting Options
|
||||
---------------
|
||||
> **Note:** This document is only a reference for available options. To learn about passing options to Video.js, see [the setup guide](setup.md#options).
|
||||
|
||||
The Video.js embed code is simply an HTML5 video tag, so for many of the options you can use the standard tag attributes to set the options.
|
||||
## Table of Contents
|
||||
|
||||
```html
|
||||
<video controls autoplay preload="auto" ...>
|
||||
```
|
||||
* [Standard <video> Element Options](#standard-video-element-options)
|
||||
* [autoplay](#autoplay)
|
||||
* [controls](#controls)
|
||||
* [height](#height)
|
||||
* [loop](#loop)
|
||||
* [muted](#muted)
|
||||
* [poster](#poster)
|
||||
* [preload](#preload)
|
||||
* ['auto'](#auto)
|
||||
* ['metadata'](#metadata)
|
||||
* ['none'](#none)
|
||||
* [src](#src)
|
||||
* [width](#width)
|
||||
* [Video.js-specific Options](#videojs-specific-options)
|
||||
* [aspectRatio](#aspectratio)
|
||||
* [children](#children)
|
||||
* [fluid](#fluid)
|
||||
* [inactivityTimeout](#inactivitytimeout)
|
||||
* [language](#language)
|
||||
* [languages](#languages)
|
||||
* [nativeControlsForTouch](#nativecontrolsfortouch)
|
||||
* [notSupportedMessage](#notsupportedmessage)
|
||||
* [plugins](#plugins)
|
||||
* [sourceOrder](#sourceorder)
|
||||
* [sources](#sources)
|
||||
* [techOrder](#techorder)
|
||||
* [vtt.js](#vttjs)
|
||||
* [Component Options](#component-options)
|
||||
* [children](#children-1)
|
||||
* [${componentName}](#componentname)
|
||||
* [Tech Options](#tech-options)
|
||||
* [${techName}](#techname)
|
||||
* [nativeControlsForTouch](#nativecontrolsfortouch-1)
|
||||
* [nativeTextTracks](#nativetexttracks)
|
||||
|
||||
Alternatively, you can use the data-setup attribute to provide options in the [JSON](http://json.org/example.html) format. This is also how you would set options that aren't standard to the video tag.
|
||||
## Standard `<video>` Element Options
|
||||
|
||||
```html
|
||||
<video data-setup='{ "controls": true, "autoplay": false, "preload": "auto" }'...>
|
||||
```
|
||||
Each of these options is also available as a [standard `<video>` element attribute][video-attrs]; so, they can be defined in all three manners [outlined in the setup guide](setup.md#options). Typically, defaults are not listed as this is left to browser vendors.
|
||||
|
||||
Finally, if you're not using the data-setup attribute to trigger the player setup, you can pass in an object with the player options as the second argument in the javascript setup function.
|
||||
### `autoplay`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
If `true`/present as an attribute, begins playback when the player is ready.
|
||||
|
||||
> **Note:** As of iOS 10, Apple offers `autoplay` support in Safari. For details, refer to ["New <video> Policies for iOS"][ios-10-updates].
|
||||
|
||||
### `controls`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Determines whether or not the player has controls that the user can interact with. Without controls the only way to start the video playing is with the `autoplay` attribute or through the Player API.
|
||||
|
||||
### `height`
|
||||
|
||||
> Type: `string|number`
|
||||
|
||||
Sets the display height of the video player in pixels.
|
||||
|
||||
### `loop`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Causes the video to start over as soon as it ends.
|
||||
|
||||
### `muted`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Will silence any audio by default.
|
||||
|
||||
### `poster`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
A URL to an image that displays before the video begins playing. This is often a frame of the video or a custom title screen. As soon as the user hits "play" the image will go away.
|
||||
|
||||
### `preload`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
Suggests to the browser whether or not the video data should begin downloading as soon as the `<video>` element is loaded. Supported values are:
|
||||
|
||||
#### `'auto'`
|
||||
|
||||
Start loading the video immediately (if the browser supports it). Some mobile devices will not preload the video in order to protect their users' bandwidth/data usage. This is why the value is called 'auto' and not something more conclusive like `'true'`.
|
||||
|
||||
_This tends to be the most common and recommended value as it allows the browser to choose the best behavior._
|
||||
|
||||
#### `'metadata'`
|
||||
|
||||
Load only the meta data of the video, which includes information like the duration and dimensions of the video. Sometimes, the meta data will be loaded by downloading a few frames of video.
|
||||
|
||||
#### `'none'`
|
||||
|
||||
Don't preload any data. The browser will wait until the user hits "play" to begin downloading.
|
||||
|
||||
### `src`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
The source URL to a video source to embed.
|
||||
|
||||
### `width`
|
||||
|
||||
> Type: `string|number`
|
||||
|
||||
Sets the display height of the video player in pixels.
|
||||
|
||||
## Video.js-specific Options
|
||||
|
||||
Each option is `undefined` by default unless otherwise specified.
|
||||
|
||||
### `aspectRatio`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
Puts the player in [fluid](#fluid) mode and the value is used when calculating the dynamic size of the player. The value should represent a ratio - two numbers separated by a colon (e.g. `"16:9"` or `"4:3"`).
|
||||
|
||||
### `children`
|
||||
|
||||
> Type: `Array|Object`
|
||||
|
||||
This option is inherited from the [`Component` base class](#component-options).
|
||||
|
||||
### `fluid`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
When `true`, the Video.js player will have a fluid size. In other words, it will scale to fit its container.
|
||||
|
||||
Also, if the `<video>` element has the `"vjs-fluid"`, this option is automatically set to `true`.
|
||||
|
||||
### `inactivityTimeout`
|
||||
|
||||
> Type: `number`
|
||||
|
||||
Video.js indicates that the user is interacting with the player by way of the `"vjs-user-active"` and `"vjs-user-inactive"` classes and the `"useractive"` event.
|
||||
|
||||
The `inactivityTimeout` determines how many milliseconds of inactivity is required before declaring the user inactive. A value of `0` indicates that there is no `inactivityTimeout` and the user will never be considered inactive.
|
||||
|
||||
### `language`
|
||||
|
||||
> Type: `string`, Default: browser default or `'en'`
|
||||
|
||||
A [language code][lang-codes] matching one of the available languages in the player. This sets the initial language for a player, but it can always be changed.
|
||||
|
||||
Learn more about [languages in Video.js](languages.md).
|
||||
|
||||
### `languages`
|
||||
|
||||
> Type: `Object`
|
||||
|
||||
Customize which languages are available in a player. The keys of this object will be [language codes][lang-codes] and the values will be objects with English keys and translated values.
|
||||
|
||||
Learn more about [languages in Video.js](languages.md).
|
||||
|
||||
> **Note**: Generally, this option is not needed and it would be better to pass your custom languages to `videojs.addLanguage()`, so they are available in all players!
|
||||
|
||||
### `nativeControlsForTouch`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Explicitly set a default value for [the associated tech option](#nativecontrolsfortouch).
|
||||
|
||||
### `notSupportedMessage`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
Allows overriding the default message that is displayed when Video.js cannot play back a media source.
|
||||
|
||||
### `plugins`
|
||||
|
||||
> Type: `Object`
|
||||
|
||||
This supports having plugins be initialized automatically with custom options when the player is initialized - rather than requiring you to initialize them manually.
|
||||
|
||||
```js
|
||||
videojs("example_video_1", { "controls": true, "autoplay": false, "preload": "auto" });
|
||||
```
|
||||
|
||||
|
||||
Individual Options
|
||||
------------------
|
||||
|
||||
> ### Note on Video Tag Attributes ###
|
||||
> With HTML5 video tag attributes that can only be true or false (boolean), you simply include the attribute (no equals sign) to turn it on, or exclude it to turn it off. For example, to turn controls on:
|
||||
|
||||
WRONG
|
||||
```html
|
||||
<video controls="true" ...>
|
||||
```
|
||||
|
||||
RIGHT
|
||||
```html
|
||||
<video controls ...>
|
||||
```
|
||||
|
||||
> The biggest issue people run into is trying to set these values to false using false as the value (e.g. controls="false") which actually does the opposite and sets the value to true because the attribute is still included. If you need the attribute to include an equals sign for XHTML validation, you can set the attribute's value to the same as its name (e.g. controls="controls").
|
||||
|
||||
|
||||
### controls ###
|
||||
The controls option sets whether or not the player has controls that the user can interact with. Without controls the only way to start the video playing is with the autoplay attribute or through the API.
|
||||
|
||||
```html
|
||||
<video controls ...>
|
||||
or
|
||||
{ "controls": true }
|
||||
```
|
||||
|
||||
|
||||
### autoplay ###
|
||||
If autoplay is true, the video will start playing as soon as page is loaded (without any interaction from the user).
|
||||
NOT SUPPORTED BY APPLE iOS DEVICES. Apple blocks the autoplay functionality in an effort to protect it's customers from unwillingly using a lot of their (often expensive) monthly data plans. A user touch/click is required to start the video in this case.
|
||||
```html
|
||||
<video autoplay ...>
|
||||
or
|
||||
{ "autoplay": true }
|
||||
```
|
||||
|
||||
|
||||
### preload ###
|
||||
The preload attribute informs the browser whether or not the video data should begin downloading as soon as the video tag is loaded. The options are auto, metadata, and none.
|
||||
|
||||
'auto': Start loading the video immediately (if the browser agrees). Some mobile devices like iPhones and iPads will not preload the video in order to protect their users' bandwidth. This is why the value is called 'auto' and not something more final like 'true'.
|
||||
|
||||
'metadata': Load only the meta data of the video, which includes information like the duration and dimensions of the video.
|
||||
|
||||
'none': Don't preload any of the video data. This will wait until the user clicks play to begin downloading.
|
||||
|
||||
```html
|
||||
<video preload ...>
|
||||
or
|
||||
{ "preload": "auto" }
|
||||
```
|
||||
|
||||
|
||||
### poster ###
|
||||
The poster attribute sets the image that displays before the video begins playing. This is often a frame of the video or a custom title screen. As soon as the user clicks play the image will go away.
|
||||
```html
|
||||
<video poster="myPoster.jpg" ...>
|
||||
or
|
||||
{ "poster": "myPoster.jpg" }
|
||||
```
|
||||
|
||||
|
||||
### loop ###
|
||||
The loop attribute causes the video to start over as soon as it ends. This could be used for a visual effect like clouds in the background.
|
||||
```html
|
||||
<video loop ...>
|
||||
or
|
||||
{ "loop": true }
|
||||
```
|
||||
|
||||
|
||||
### width ###
|
||||
The width attribute sets the display width of the video.
|
||||
```html
|
||||
<video width="640" ...>
|
||||
or
|
||||
{ "width": 640 }
|
||||
```
|
||||
|
||||
|
||||
### height ###
|
||||
The height attribute sets the display height of the video.
|
||||
```html
|
||||
<video height="480" ...>
|
||||
or
|
||||
{ "height": 480 }
|
||||
```
|
||||
|
||||
Component Options
|
||||
-----------------
|
||||
|
||||
You can set the options for any single player component. For instance, if you wanted to remove the `muteToggle` button, which
|
||||
is a child of `controlBar`, you can just set that component to false:
|
||||
|
||||
```js
|
||||
var player = videojs('video-id', {
|
||||
controlBar: {
|
||||
muteToggle: false
|
||||
videojs('my-player', {
|
||||
plugins: {
|
||||
foo: {bar: true},
|
||||
boo: {baz: false}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This also works using the `data-setup` attribute on the video element, just remember the options need to use proper JSON
|
||||
notation.
|
||||
The above is roughly equivalent to:
|
||||
|
||||
```html
|
||||
<video ... data-setup='{ "controlBar": { "muteToggle": false } }'></video>
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.foo({bar: true});
|
||||
player.boo({baz: false});
|
||||
```
|
||||
|
||||
The [components guide](./components.md) has an excellent breakdown of the structure of a player, you
|
||||
just need to remember to nest child components in a `children` array for each level.
|
||||
Although, since the `plugins` option is an object, the order of initialization is not guaranteed!
|
||||
|
||||
See [the plugins guide](plugins.md) for more information on Video.js plugins.
|
||||
|
||||
### `sourceOrder`
|
||||
|
||||
> Type: `boolean`, Default: `false`
|
||||
>
|
||||
> **Note:** In video.js 6.0, this option will default to `true`.
|
||||
|
||||
Tells Video.js to prefer the order of [`sources`](#sources) over [`techOrder`](#techorder) in selecting a source and playback tech.
|
||||
|
||||
Given the following example:
|
||||
|
||||
```js
|
||||
videojs('my-player', {
|
||||
sourceOrder: true,
|
||||
sources: [{
|
||||
src: '//path/to/video.flv',
|
||||
type: 'video/x-flv'
|
||||
}, {
|
||||
src: '//path/to/video.mp4',
|
||||
type: 'video/mp4'
|
||||
}, {
|
||||
src: '//path/to/video.webm',
|
||||
type: 'video/webm'
|
||||
}],
|
||||
techOrder: ['html5', 'flash']
|
||||
});
|
||||
```
|
||||
|
||||
Normally, the fact that HTML5 comes before Flash in the `techOrder` would mean Video.js would look for a compatible _source_ for HTML5 and would pick either the MP4 or WebM video (depending on browser support) only falling back to Flash if no compatible source for HTML5 was found.
|
||||
|
||||
However, because the `sourceOrder` is `true`, Video.js flips that process around. It will look for a compatible _tech_ for each source in order. Presumably, it would first find a match between the FLV (since it's first in the source order) and the Flash tech.
|
||||
|
||||
In summary, the default algorithm is:
|
||||
|
||||
* for each tech:
|
||||
* for each source:
|
||||
* if tech can play source, use this tech/source combo
|
||||
|
||||
With `sourceOrder: true`, the algorithm becomes:
|
||||
|
||||
* for each source:
|
||||
* for each tech:
|
||||
* if tech can play source, use this tech/source combo
|
||||
|
||||
### `sources`
|
||||
|
||||
> Type: `Array`
|
||||
|
||||
An array of objects that mirror the native `<video>` element's capability to have a series of child `<source>` elements. This should be an array of objects with the `src` and `type` properties. For example:
|
||||
|
||||
```js
|
||||
videojs('my-player', {
|
||||
sources: [{
|
||||
src: '//path/to/video.mp4',
|
||||
type: 'video/mp4'
|
||||
}, {
|
||||
src: '//path/to/video.webm',
|
||||
type: 'video/webm'
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
Using `<source>` elements will have the same effect:
|
||||
|
||||
```html
|
||||
<video ...>
|
||||
<source src="//path/to/video.mp4" type="video/mp4">
|
||||
<source src="//path/to/video.webm" type="video/webm">
|
||||
</video>
|
||||
```
|
||||
|
||||
### `techOrder`
|
||||
|
||||
> Type: `Array`, Default: `['html5', 'flash']`
|
||||
|
||||
Defines the order in which Video.js techs are preferred. By default, this means that the `Html5` tech is preferred, but Video.js will fall back to `Flash` if no `Html5`-compatible source can be found.
|
||||
|
||||
### `vtt.js`
|
||||
|
||||
> Type: `string`
|
||||
|
||||
Allows overriding the default URL to vtt.js, which may be loaded asynchronously to polyfill support for `WebVTT`.
|
||||
|
||||
This option will be used in the "novtt" build of video.js (i.e. `video.novtt.js`). Otherwise, vtt.js is bundled with video.js.
|
||||
|
||||
## Component Options
|
||||
|
||||
The Video.js player is a component. Like all components, you can define what children it includes, what order they appear in, and what options are passed to them.
|
||||
|
||||
This is meant to be a quick reference; so, for more detailed information on components in Video.js, check out the [components guide](components.md).
|
||||
|
||||
### `children`
|
||||
|
||||
> Type: `Array|Object`
|
||||
|
||||
If an `Array` - which is the default - this is used to determine which children (by component name) and in which order they are created on a player (or other component):
|
||||
|
||||
```js
|
||||
// The following code creates a player with ONLY bigPlayButton and
|
||||
// controlBar child components.
|
||||
videojs('my-player', {
|
||||
children: [
|
||||
'bigPlayButton',
|
||||
'controlBar'
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
The `children` options can also be passed as an `Object`. In this case, it is used to provide `options` for any/all children, including disabling them with `false`:
|
||||
|
||||
```js
|
||||
// This player's ONLY child will be the controlBar. Clearly, this is not the
|
||||
// ideal method for disabling a grandchild!
|
||||
videojs('my-player', {
|
||||
children: {
|
||||
controlBar: {
|
||||
fullscreenControl: false
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### `${componentName}`
|
||||
|
||||
> Type: `Object`
|
||||
|
||||
Components can be given custom options via the _lower-camel-case variant of the component name_ (e.g. `controlBar` for `ControlBar`). These can be nested in a representation of grandchild relationships. For example, to disable the fullscreen control:
|
||||
|
||||
```js
|
||||
videojs('my-player', {
|
||||
controlBar: {
|
||||
fullscreenControl: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Tech Options
|
||||
|
||||
### `${techName}`
|
||||
|
||||
> Type: `Object`
|
||||
|
||||
Video.js playback technologies (i.e. "techs") can be given custom options as part of the options passed to the `videojs` function. They should be passed under the _lower-case variant of the tech name_ (e.g. `"flash"` or `"html5"`).
|
||||
|
||||
This is not used in most implementations, but one case where it may be is dictating where the Video.js SWF file is located for the `Flash` tech:
|
||||
|
||||
```js
|
||||
videojs('my-player', {
|
||||
flash: {
|
||||
swf: '//path/to/videojs.swf'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
However, this is a case where changing the global defaults is more useful:
|
||||
|
||||
```js
|
||||
videojs.options.flash.swf = '//path/to/videojs.swf'
|
||||
```
|
||||
|
||||
#### `nativeControlsForTouch`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Only supported by the `Html5` tech, this option can be set to `true` to force native controls for touch devices.
|
||||
|
||||
#### `nativeTextTracks`
|
||||
|
||||
> Type: `boolean`
|
||||
|
||||
Can be set to `false` to force emulation of text tracks instead of native support. The `nativeCaptions` option also exists, but is simply an alias to `nativeTextTracks`.
|
||||
|
||||
[ios-10-updates]: https://webkit.org/blog/6784/new-video-policies-for-ios/
|
||||
|
||||
[lang-codes]: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
|
||||
[video-attrs]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
# Player Workflows
|
||||
|
||||
This document outlines many considerations for using Video.js for advanced player workflows. Be sure to read [the setup guide](setup.md) first!
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Accessing a player that has already been created on a page](#accessing-a-player-that-has-already-been-created-on-a-page)
|
||||
* [Removing Players](#removing-players)
|
||||
* [dispose()](#dispose)
|
||||
* [Signs of an Undisposed Player](#signs-of-an-undisposed-player)
|
||||
* [Showing and Hiding a Player](#showing-and-hiding-a-player)
|
||||
* [Changing the volume of a player](#changing-the-volume-of-a-player)
|
||||
* [Making the player fullscreen](#making-the-player-fullscreen)
|
||||
* [Using Playback information functions](#using-playback-information-functions)
|
||||
* [Dealing with the source or the poster on the player](#dealing-with-the-source-or-the-poster-on-the-player)
|
||||
* [Accesing the Tech on the player](#accesing-the-tech-on-the-player)
|
||||
* [Using Video.js with...](#using-videojs-with)
|
||||
* [jQuery](#jquery)
|
||||
* [React](#react)
|
||||
* [Ember](#ember)
|
||||
* [Angular](#angular)
|
||||
|
||||
## Accessing a player that has already been created on a page
|
||||
|
||||
After an instance has been created it can be accessed globally in two ways:
|
||||
|
||||
1. By calling `videojs('example_video_id');`
|
||||
1. By using it directly via `videojs.players.example_video_id;`
|
||||
|
||||
## Removing Players
|
||||
|
||||
No matter the term used for it, web applications are becoming common. Not everything is a static, load-once-and-done web page anymore! This means that developers need to be able to manage the full lifecycle of a video player - from creation to destruction. Video.js supports player removal through the `dispose()` method.
|
||||
|
||||
### [`dispose()`](http://docs.videojs.com/docs/api/player.html#Methodsdispose)
|
||||
|
||||
This method is available on all Video.js players and [components](http://docs.videojs.com/docs/api/component.html#Methodsdispose). It is _the only_ supported method of removing a Video.js player from both the DOM and memory. For example, the following code sets up a player and then disposes it when media playback is complete:
|
||||
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.on('ended', function() {
|
||||
this.dispose();
|
||||
});
|
||||
```
|
||||
|
||||
Calling `dispose()` will have a few effects:
|
||||
|
||||
1. Trigger a `"dispose"` event on the player, allowing for any custom cleanup tasks that need to be run by your integration.
|
||||
1. Remove all event listeners from the player.
|
||||
1. Remove the player's DOM element(s).
|
||||
|
||||
Additionally, these actions are recursively applied to _all_ the player's child components.
|
||||
|
||||
> **Note**: Do _not_ remove players via standard DOM removal methods: this will leave listeners and other objects in memory that you might not be able to clean up!
|
||||
|
||||
### Signs of an Undisposed Player
|
||||
|
||||
Seeing an error such as:
|
||||
|
||||
```console
|
||||
TypeError: this.el_.vjs_getProperty is not a function
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```console
|
||||
TypeError: Cannot read property 'vdata1234567890' of null
|
||||
```
|
||||
|
||||
Suggests that a player or component was removed from the DOM without using `dispose()`. It usually means something tried to trigger an event on it or call a method on it.
|
||||
|
||||
## Showing and Hiding a Player
|
||||
|
||||
It is not recommended that you attempt to toggle the visibility or display of a Video.js player. Doing so can be particularly problematic when it comes to the Flash tech. Instead, players should be created and [disposed](#removing-players) as needed.
|
||||
|
||||
This is relevant to use cases such as displaying a player in a modal/overlay. Rather than keeping a hidden Video.js player in a DOM element, it's recommended that you create the player when the modal opens and dispose it when the modal closes.
|
||||
|
||||
This is particularly relevant where memory/resource usage is concerned (e.g. mobile devices).
|
||||
|
||||
Depending on the libraries/frameworks in use, an implementation might look something like this:
|
||||
|
||||
```js
|
||||
modal.on('show', function() {
|
||||
var videoEl = modal.findEl('video');
|
||||
modal.player = videojs(videoEl);
|
||||
});
|
||||
|
||||
modal.on('hide', function() {
|
||||
modal.player.dispose();
|
||||
});
|
||||
```
|
||||
|
||||
## Changing the volume of a player
|
||||
|
||||
Volume for a player can be changed through the `volume` function on a player. The volume function accepts a number from 0-1. Calling it without an argument will return the current volume.
|
||||
|
||||
Example
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// get
|
||||
var howLoudIsIt = myPlayer.volume();
|
||||
// set
|
||||
myPlayer.volume(0.5); // Set volume to half
|
||||
});
|
||||
```
|
||||
|
||||
Volume can also be muted (without actually changing the volume value) using the `muted` function. Calling it without an argument will return the current status of muted on the player.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// get, should be false
|
||||
console.log(myPlayer.muted());
|
||||
// set to true
|
||||
myPlayer.muted(true);
|
||||
// get should be true
|
||||
console.log(myPlayer.muted());
|
||||
});
|
||||
```
|
||||
|
||||
## Making the player fullscreen
|
||||
|
||||
To check if the player is currently fullscreen call the `isFullscreen` function on a player like so.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// get, should be false
|
||||
console.log(myPlayer.isFullscreen());
|
||||
|
||||
// set, tell the player it's in fullscreen
|
||||
myPlayer.isFullscreen(true);
|
||||
|
||||
// get, should be true
|
||||
console.log(myPlayer.isFullscreen());
|
||||
});
|
||||
```
|
||||
|
||||
To request that the player enter fullscreen call `requestFullscreen`.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
myPlayer.requestFullscreen();
|
||||
});
|
||||
```
|
||||
|
||||
To exit fullscreen call `exitFullscreen`
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
myPlayer.requestFullscreen();
|
||||
myPlayer.exitFullscreen();
|
||||
});
|
||||
```
|
||||
|
||||
## Using Playback information functions
|
||||
|
||||
`play` can be used to start playback on a player that has a source.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
myPlayer.play();
|
||||
});
|
||||
```
|
||||
|
||||
`pause` can be used to pause playback on a player that is playing.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
myPlayer.play();
|
||||
myPlayer.pause();
|
||||
});
|
||||
```
|
||||
|
||||
`paused` can be used to determine if a player is currently paused.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
|
||||
myPlayer.ready(function() {
|
||||
// true
|
||||
console.log(myPlayer.paused());
|
||||
// false
|
||||
console.log(!myPlayer.paused());
|
||||
|
||||
myPlayer.play();
|
||||
// false
|
||||
console.log(myPlayer.paused());
|
||||
// true
|
||||
console.log(!myPlayer.paused());
|
||||
|
||||
myPlayer.pause();
|
||||
// true
|
||||
console.log(myPlayer.paused());
|
||||
// false
|
||||
console.log(!myPlayer.paused());
|
||||
});
|
||||
```
|
||||
|
||||
`currentTime` will give you the currentTime (in seconds) that playback is currently occuring at.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// set current time to 2 minutes into the video
|
||||
myPlayer.currentTime(120);
|
||||
|
||||
// get the current time, should be 120 seconds
|
||||
var whereYouAt = myPlayer.currentTime();
|
||||
});
|
||||
```
|
||||
|
||||
`duration` will give you the total duration of the video that is playing
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
var lengthOfVideo = myPlayer.duration();
|
||||
});
|
||||
```
|
||||
|
||||
`remainingTime` will give you the seconds that are remaing in the video.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
myPlayer.currentTime(10);
|
||||
|
||||
// should be 10 seconds less than duration
|
||||
console.log(myPlayer.remainingTime());
|
||||
});
|
||||
```
|
||||
|
||||
`buffered` will give you a timeRange object representing the current ranges of time that are ready to be played at a future time.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
var bufferedTimeRange = myPlayer.buffered();
|
||||
|
||||
// number of different ranges of time have been buffered.
|
||||
// Usually 1
|
||||
var numberOfRanges = bufferedTimeRange.length,
|
||||
|
||||
// Time in seconds when the first range starts.
|
||||
// Usually 0
|
||||
var firstRangeStart = bufferedTimeRange.start(0),
|
||||
|
||||
// Time in seconds when the first range ends
|
||||
var firstRangeEnd = bufferedTimeRange.end(0),
|
||||
|
||||
// Length in seconds of the first time range
|
||||
var firstRangeLength = firstRangeEnd - firstRangeStart;
|
||||
});
|
||||
```
|
||||
|
||||
`bufferedPercent` will give you the the current percentage of the video that is buffered.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// example 0.11 aka 11%
|
||||
var howMuchIsDownloaded = myPlayer.bufferedPercent();
|
||||
});
|
||||
```
|
||||
|
||||
## Dealing with the source or the poster on the player
|
||||
|
||||
Passing a source to the player via the API. (this can also be done using options)
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
```
|
||||
|
||||
**Source Object (or element):** A javascript object containing information
|
||||
about the source file. Use this method if you want the player to determine if
|
||||
it can support the file using the type information.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src({type: "video/mp4", src: "http://www.example.com/path/to/video.mp4"});
|
||||
```
|
||||
|
||||
**Array of Source Objects:** To provide multiple versions of the source so
|
||||
that it can be played using HTML5 across browsers you can use an array of
|
||||
source objects. Video.js will detect which version is supported and load that
|
||||
file.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src([
|
||||
{type: "video/mp4", src: "http://www.example.com/path/to/video.mp4"},
|
||||
{type: "video/webm", src: "http://www.example.com/path/to/video.webm"},
|
||||
{type: "video/ogg", src: "http://www.example.com/path/to/video.ogv"}
|
||||
]);
|
||||
```
|
||||
|
||||
Changing or setting the poster via the API. (this can also be done with options)
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('example_video_1');
|
||||
|
||||
// set
|
||||
myPlayer.poster('http://example.com/myImage.jpg');
|
||||
|
||||
// get
|
||||
console.log(myPlayer.poster());
|
||||
// 'http://example.com/myImage.jpg'
|
||||
```
|
||||
|
||||
## Accesing the Tech on the player
|
||||
|
||||
The tech on the player can only be accesed by pasing `{IWillNotUseThisInPlugins: true}` into the `tech()`
|
||||
function on the player.
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('some-player-id');
|
||||
|
||||
myPlayer.src("http://www.example.com/path/to/video.mp4");
|
||||
myPlayer.ready(function() {
|
||||
// function call throws an error if we
|
||||
// dont add {IWillNotUseThisInPlugins: true}
|
||||
var tech = myPlayer.tech({IWillNotUseThisInPlugins: true});
|
||||
});
|
||||
```
|
||||
|
||||
## Using Video.js with...
|
||||
|
||||
Coming soon...
|
||||
|
||||
### jQuery
|
||||
|
||||
### React
|
||||
|
||||
See [ReactJS integration example](./react.md)
|
||||
|
||||
### Ember
|
||||
|
||||
### Angular
|
||||
+303
-35
@@ -1,59 +1,327 @@
|
||||
Plugins
|
||||
=======
|
||||
If you've built something cool with Video.js, you can easily share it with the rest of the world by creating a plugin. Although, you can roll your own, you can also use [generator-videojs-plugin](https://github.com/dmlap/generator-videojs-plugin), a [Yeoman](http://yeoman.io) generator that provides scaffolding for video.js plugins including:
|
||||
* [Grunt](http://gruntjs.com) for build management
|
||||
* [npm](https://www.npmjs.org) for dependency management
|
||||
* [QUnit](http://qunitjs.com) for testing
|
||||
# Video.js Plugins
|
||||
|
||||
One of the great strengths of Video.js is its ecosystem of plugins that allow authors from all over the world to share their video player customizations. This includes everything from the simplest UI tweaks to new [playback technologies and source handlers](tech.md)!
|
||||
|
||||
Because we view plugins as such an important part of Video.js, the organization is committed to maintaining a robust set of tools for plugin authorship:
|
||||
|
||||
Step 1: Write Some Javascript
|
||||
-----------------------------
|
||||
You may have already done this step. Code up something interesting and then wrap it in a function. At the most basic level, that's all a video.js plugin is. By convention, plugins take a hash of options as their first argument:
|
||||
* [generator-videojs-plugin][generator]
|
||||
|
||||
A [Yeoman][yeoman] generator for scaffolding a Video.js plugin project. Additionally, it offers a set of [conventions for plugin authorship][standards] that, if followed, make authorship, contribution, and usage consistent and predictable.
|
||||
|
||||
In short, the generator sets up plugin authors to focus on writing their plugin - not messing with tools.
|
||||
|
||||
* [videojs-spellbook][spellbook]
|
||||
|
||||
As of version 3, the plugin generator includes a new dependency: [videojs-spellbook][spellbook]. Spellbook is a kitchen sink plugin development tool: it builds plugins, creates tags, runs a development server, and more.
|
||||
|
||||
The benefit of Spellbook is that you can run the generator _once_ and receive updates and bugfixes in Spellbook without having to run the generator again and deal with Yeoman conflicts and other headaches.
|
||||
|
||||
As long as your plugin project follows the [conventions][standards], Spellbook should work on it!
|
||||
|
||||
## Writing a Basic Plugin
|
||||
|
||||
If you've written a Video.js plugin before, the basic plugin concept should be familiar. It's similar to a jQuery plugin in that the core idea is that you're adding a method to the player.
|
||||
|
||||
### Write a JavaScript Function
|
||||
|
||||
A basic plugin is a plain JavaScript function:
|
||||
|
||||
```js
|
||||
function examplePlugin(options) {
|
||||
this.on('play', function(e) {
|
||||
console.log('playback has started!');
|
||||
});
|
||||
};
|
||||
function examplePlugin(options) {
|
||||
|
||||
if (options.customClass) {
|
||||
this.addClass(options.customClass);
|
||||
}
|
||||
|
||||
this.on('playing', function() {
|
||||
videojs.log('playback began!');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
When it's activated, `this` will be the Video.js player your plugin is attached to. You can use anything you'd like in the [Video.js API](./api.md) when you're writing a plugin: change the `src`, mess up the DOM, or listen for and emit your own events.
|
||||
By convention, plugins are passed an `options` object; however, you can realistically accept whatever arguments you want. This example plugin will add a custom class (whatever is passed in as `options.customClass`) and, whenever playback begins, it will log a message to the browser console.
|
||||
|
||||
Step 2: Registering A Plugin
|
||||
-------------------------------
|
||||
It's time to give the rest of the world the opportunity to be awed by your genius. When your plugin is loaded, it needs to let Video.js know this amazing new functionality is now available:
|
||||
> **Note:** The value of `this` in the plugin function is the player instance; so, you have access to [its complete API][api-player].
|
||||
|
||||
### Register a Basic Plugin
|
||||
|
||||
Now that we have a function that does something with a player, all that's left is to register the plugin with Video.js:
|
||||
|
||||
```js
|
||||
videojs.plugin('examplePlugin', examplePlugin);
|
||||
videojs.registerPlugin('examplePlugin', examplePlugin);
|
||||
```
|
||||
|
||||
From this point on, your plugin will be added to the Video.js prototype and will show up as a property on every instance created. Make sure you choose a unique name that doesn't clash with any of the properties already in Video.js. Which leads us to...
|
||||
After that, any player will automatically have an `examplePlugin` method on its prototype!
|
||||
|
||||
Step 3: Using A Plugin
|
||||
----------------------
|
||||
There are two ways to initialize a plugin. If you're creating your video tag dynamically, you can specify the plugins you'd like to initialize with it and any options you want to pass to them:
|
||||
> **Note:** The only stipulation with the name of the plugin is that it cannot conflict with any existing plugin or player method.
|
||||
|
||||
## Writing an Advanced Plugin
|
||||
|
||||
Video.js 6 introduces advanced plugins: these are plugins that share a similar API with basic plugins, but are class-based and offer a range of extra features out of the box.
|
||||
|
||||
While reading the following sections, you may want to refer to the [Plugin API docs][api-plugin] for more detail.
|
||||
|
||||
### Write a JavaScript Class/Constructor
|
||||
|
||||
If you're familiar with creating [components](components.md), this process is similar. An advanced plugin starts with a JavaScript class (a.k.a. a constructor function).
|
||||
|
||||
If you're using ES6 already, you can use that syntax with your transpiler/language of choice (Babel, TypeScript, etc):
|
||||
|
||||
```js
|
||||
videojs('vidId', {
|
||||
plugins: {
|
||||
examplePlugin: {
|
||||
exampleOption: true
|
||||
}
|
||||
}
|
||||
const Plugin = videojs.getPlugin('plugin');
|
||||
|
||||
class ExamplePlugin extends Plugin {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
if (options.customClass) {
|
||||
player.addClass(options.customClass);
|
||||
}
|
||||
|
||||
player.on('playing', function() {
|
||||
videojs.log('playback began!');
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you've already initialized your video tag, you can activate a plugin at any time by calling its setup function directly:
|
||||
Or with ES5:
|
||||
|
||||
```js
|
||||
var video = videojs('cool-vid');
|
||||
video.examplePlugin({ exampleOption: true });
|
||||
var Plugin = videojs.getPlugin('plugin');
|
||||
|
||||
var ExamplePlugin = videojs.extend(Plugin, {
|
||||
|
||||
constructor: function(player, options) {
|
||||
Plugin.call(this, player, options);
|
||||
|
||||
if (options.customClass) {
|
||||
player.addClass(options.customClass);
|
||||
}
|
||||
|
||||
player.on('playing', function() {
|
||||
videojs.log('playback began!');
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
That's it. Head on over to the [Video.js wiki](https://github.com/videojs/video.js/wiki/Plugins) and add your plugin to the list so everyone else can check it out.
|
||||
For now, this example advanced plugin does the exact same thing as the basic plugin described above - not to worry, we will make it more interesting as we continue!
|
||||
|
||||
## How should I use the Video.js icons in my plugin?
|
||||
### Register a Advanced Plugin
|
||||
|
||||
If you'd like to use any of the icons available in the [Video.js icon set](http://videojs.github.io/font/), please target them via the CSS class names instead of codepoints. The codepoints *may* change between versions of the font, so using the class names ensures that your plugin will stay up to date with any font changes.
|
||||
The registration process for advanced plugins is identical to [the process for basic plugins](#register-a-basic-plugin).
|
||||
|
||||
```js
|
||||
videojs.registerPlugin('examplePlugin', ExamplePlugin);
|
||||
```
|
||||
|
||||
> **Note:** Because ES6 classes are syntactic sugar on top of existing constructor function and prototype architecture in JavaScript, in all cases `registerPlugin`'s second argument is a function.
|
||||
|
||||
### Key Differences from Basic Plugins
|
||||
|
||||
Advanced plugins have two key differences from basic plugins that are important to understand before describing their advanced features.
|
||||
|
||||
#### The Value of `this`
|
||||
|
||||
With basic plugins, the value of `this` in the plugin function will be the _player_.
|
||||
|
||||
With advanced plugins, the value of `this` is the _instance of the plugin class_. The player is passed to the plugin constructor as its first argument (and is automatically applied to the plugin instance as the `player` property) and any further arguments are passed after that.
|
||||
|
||||
#### The Player Plugin Name Property
|
||||
|
||||
Both basic plugins and advanced plugins are set up by calling a method on a player with a name matching the plugin (e.g., `player.examplePlugin()`).
|
||||
|
||||
However, with advanced plugins, this method acts like a factory function and it is _replaced_ for the current player by a new function which returns the plugin instance:
|
||||
|
||||
```js
|
||||
// `examplePlugin` has not been called, so it is a factory function.
|
||||
player.examplePlugin();
|
||||
|
||||
// `examplePlugin` is now a function that returns the same instance of
|
||||
// `ExamplePlugin` that was generated by the previous call.
|
||||
player.examplePlugin().someMethodName();
|
||||
```
|
||||
|
||||
With basic plugins, the method does not change - it is always the same function. It is up to the authors of basic plugins to deal with multiple calls to their plugin function.
|
||||
|
||||
### Features of Advanced Plugins
|
||||
|
||||
Up to this point, our example advanced plugin is functionally identical to our example basic plugin. However, advanced plugins bring with them a great deal of benefit that is not built into basic plugins.
|
||||
|
||||
#### Events
|
||||
|
||||
Like components, advanced plugins offer an implementation of events. This includes:
|
||||
|
||||
* The ability to listen for events on the plugin instance using `on` or `one`:
|
||||
|
||||
```js
|
||||
player.examplePlugin().on('example-event', function() {
|
||||
videojs.log('example plugin received an example-event');
|
||||
});
|
||||
```
|
||||
|
||||
* The ability to `trigger` custom events on a plugin instance:
|
||||
|
||||
```js
|
||||
player.examplePlugin().trigger('example-event');
|
||||
```
|
||||
|
||||
* The ability to stop listening to custom events on a plugin instance using `off`:
|
||||
|
||||
```js
|
||||
player.examplePlugin().off('example-event');
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
#### Statefulness
|
||||
|
||||
A new concept introduced for advanced plugins is _statefulness_. This is similar to React components' `state` property and `setState` method.
|
||||
|
||||
Advanced plugin instances each have a `state` property, which is a plain JavaScript object - it can contain any keys and values the plugin author wants.
|
||||
|
||||
A default `state` can be provided by adding a static property to a plugin constructor:
|
||||
|
||||
```js
|
||||
ExamplePlugin.defaultState = {
|
||||
customClass: 'default-custom-class'
|
||||
};
|
||||
```
|
||||
|
||||
When the `state` is updated via the `setState` method, the plugin instance fires a `"statechanged"` event, but _only if something changed!_ This event can be used as a signal to update the DOM or perform some other action. The event object passed to listeners for this event includes, an object describing the changes that occurred on the `state` property:
|
||||
|
||||
```js
|
||||
player.examplePlugin().on('statechanged', function(e) {
|
||||
if (e.changes && e.changes.customClass) {
|
||||
this.player
|
||||
.removeClass(e.changes.customClass.from)
|
||||
.addClass(e.changes.customClass.to);
|
||||
}
|
||||
});
|
||||
|
||||
player.examplePlugin().setState({customClass: 'another-custom-class'});
|
||||
```
|
||||
|
||||
#### Lifecycle
|
||||
|
||||
Like components, advanced plugins have a lifecycle. They can be created with their factory function and they can be destroyed using their `dispose` method:
|
||||
|
||||
```js
|
||||
// set up a example plugin instance
|
||||
player.examplePlugin();
|
||||
|
||||
// dispose of it anytime thereafter
|
||||
player.examplePlugin().dispose();
|
||||
```
|
||||
|
||||
The `dispose` method has several effects:
|
||||
|
||||
* Triggers a `"dispose"` event on the plugin instance.
|
||||
* Cleans up all event listeners on the plugin instance, which helps avoid errors caused by events being triggered after an object is cleaned up.
|
||||
* Removes plugin state and references to the player to avoid memory leaks.
|
||||
* Reverts the player's named property (e.g. `player.examplePlugin`) _back_ to the original factory function, so the plugin can be set up again.
|
||||
|
||||
In addition, if the player is disposed, the disposal of all its advanced plugin instances will be triggered as well.
|
||||
|
||||
### Advanced Example Advanced Plugin
|
||||
|
||||
What follows is a complete ES6 advanced plugin that logs a custom message when the player's state changes between playing and paused. It uses all the described advanced features:
|
||||
|
||||
```js
|
||||
import videojs from 'video.js';
|
||||
|
||||
const Plugin = videojs.getPlugin('plugin');
|
||||
|
||||
class Advanced extends Plugin {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
// Whenever the player emits a playing or paused event, we update the
|
||||
// state if necessary.
|
||||
this.on(player, ['playing', 'paused'], this.updateState);
|
||||
this.on('statechanged', this.logState);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
videojs.log('the advanced plugin is being disposed');
|
||||
}
|
||||
|
||||
updateState() {
|
||||
this.setState({playing: !player.paused()});
|
||||
}
|
||||
|
||||
logState(changed) {
|
||||
videojs.log(`the player is now ${this.state.playing ? 'playing' : 'paused'}`);
|
||||
}
|
||||
}
|
||||
|
||||
videojs.registerPlugin('advanced', Advanced);
|
||||
|
||||
const player = videojs('example-player');
|
||||
|
||||
player.advanced();
|
||||
|
||||
// This will begin playback, which will trigger a "playing" event, which will
|
||||
// update the state of the plugin, which will cause a message to be logged.
|
||||
player.play();
|
||||
|
||||
// This will pause playback, which will trigger a "paused" event, which will
|
||||
// update the state of the plugin, which will cause a message to be logged.
|
||||
player.pause();
|
||||
|
||||
player.advanced().dispose();
|
||||
|
||||
// This will begin playback, but the plugin has been disposed, so it will not
|
||||
// log any messages.
|
||||
player.play();
|
||||
```
|
||||
|
||||
This example may be a bit pointless in reality, but it demonstrates the sort of flexibility offered by advanced plugins over basic plugins.
|
||||
|
||||
## Setting up a Plugin
|
||||
|
||||
There are two ways to set up (or initialize) a plugin on a player. Both ways work identically for both basic and advanced plugins.
|
||||
|
||||
The first way is during creation of the player. Using the `plugins` option, a plugin can be automatically set up on a player:
|
||||
|
||||
```js
|
||||
videojs('example-player', {
|
||||
plugins: {
|
||||
examplePlugin: {
|
||||
customClass: 'example-class'
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Otherwise, a plugin can be manually set up:
|
||||
|
||||
```js
|
||||
var player = videojs('example-player');
|
||||
player.examplePlugin({customClass: 'example-class'});
|
||||
```
|
||||
|
||||
These two methods are functionally identical - use whichever you prefer!
|
||||
|
||||
## References
|
||||
|
||||
* [Player API][api-player]
|
||||
* [Plugin API][api-plugin]
|
||||
* [Plugin Generator][generator]
|
||||
* [Plugin Conventions][standards]
|
||||
|
||||
[api-player]: http://docs.videojs.com/docs/api/player.html
|
||||
|
||||
[api-plugin]: http://docs.videojs.com/docs/api/plugin.html
|
||||
|
||||
[generator]: https://github.com/videojs/generator-videojs-plugin
|
||||
|
||||
[spellbook]: https://github.com/videojs/spellbook
|
||||
|
||||
[standards]: https://github.com/videojs/generator-videojs-plugin/blob/master/docs/standards.md
|
||||
|
||||
[yeoman]: http://yeoman.io
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# video.js and ReactJS integration
|
||||
|
||||
Here's a basic ReactJS player implementation.
|
||||
|
||||
It just instantiates the video.js player on `componentDidMount` and destroys it on `componentWillUnmount`.
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import videojs from 'video.js'
|
||||
|
||||
export default class VideoPlayer extends React.Component {
|
||||
componentDidMount() {
|
||||
// instantiate video.js
|
||||
this.player = videojs(this.videoNode, this.props, function onPlayerReady() {
|
||||
console.log('onPlayerReady', this)
|
||||
});
|
||||
}
|
||||
|
||||
// destroy player on unmount
|
||||
componentWillUnmount() {
|
||||
if (this.player) {
|
||||
this.player.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
// wrap the player in a div with a `data-vjs-player` attribute
|
||||
// so videojs won't create additional wrapper in the DOM
|
||||
// see https://github.com/videojs/video.js/pull/3856
|
||||
render() {
|
||||
return (
|
||||
<div data-vjs-player>
|
||||
<video ref={ node => this.videoNode = node } className="video-js"></video>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can then use it like this:
|
||||
|
||||
```jsx
|
||||
// see https://github.com/videojs/video.js/blob/master/docs/guides/options.md
|
||||
const videoJsOptions = {
|
||||
autoPlay: true,
|
||||
controls: true,
|
||||
sources: [{
|
||||
src: '/path/to/video.mp4',
|
||||
type: 'video/mp4'
|
||||
}]
|
||||
}
|
||||
|
||||
return <VideoPlayer { ...videoJsOptions } />
|
||||
```
|
||||
|
||||
Dont forget to include the video.js CSS, located at `video.js/dist/video-js.css`.
|
||||
@@ -1,41 +0,0 @@
|
||||
Removing Players
|
||||
================
|
||||
|
||||
Sometimes, you want to remove players after page load (in single page apps or modals, for instance). It's easy to manage, but there are some simple rules you need to follow.
|
||||
|
||||
Call `.dispose()`
|
||||
-----------------
|
||||
|
||||
To remove the html associated with your videojs player from the page always call the player's [`dispose()`](http://docs.videojs.com/docs/api/player.html#Methodsdispose) method:
|
||||
|
||||
```js
|
||||
var oldPlayer = document.getElementById('my-player');
|
||||
videojs(oldPlayer).dispose();
|
||||
```
|
||||
|
||||
This method will:
|
||||
|
||||
1. reset the internal state of videojs
|
||||
2. remove the player's dom from the page
|
||||
|
||||
Showing / Hiding a Player
|
||||
-------------------------
|
||||
|
||||
For instance, if you have a modal that a player appears in, you should create the player when the modal pops up. When the modal hides, dispose the player. If you try to hide the Flash tech, things will go poorly. Even with other tech, calling `dispose()` on a player that's not needed will free up resources for the browser.
|
||||
|
||||
Why Is This Needed?
|
||||
-------------------
|
||||
|
||||
VideoJS internally tracks all players and their associated data by html id attribute. If you plan to create new players with the same id as previously created players, you'll need to call the player's dispose() method to clear VideoJS's internal state before creating the new player.
|
||||
|
||||
Signs You Did It Wrong
|
||||
-------------------------
|
||||
|
||||
```
|
||||
TypeError: this.el_.vjs_getProperty is not a function
|
||||
"VIDEOJS:" "Video.js: buffered unavailable on Hls playback technology element." TypeError: this.el_.vjs_getProperty is not a function
|
||||
Stack trace:
|
||||
...
|
||||
```
|
||||
|
||||
If you encounter a console error in the browser similar to the above, you've probably forgotten to `dispose()` a player before removing it from the dom. This would happen when using the [contrib-hls](https://github.com/videojs/videojs-contrib-hls) plugin.
|
||||
+169
-101
@@ -1,124 +1,192 @@
|
||||
Setup
|
||||
=====
|
||||
# Video.js Setup
|
||||
|
||||
Video.js is pretty easy to set up. It can take a matter of seconds to get the player up and working on your web page.
|
||||
## Table of Contents
|
||||
|
||||
Step 1: Include the Video.js Javascript and CSS files in the head of your page.
|
||||
------------------------------------------------------------------------------
|
||||
* [Getting Video.js](#getting-videojs)
|
||||
* [Creating a Player](#creating-a-player)
|
||||
* [Automatic Setup](#automatic-setup)
|
||||
* [Manual Setup](#manual-setup)
|
||||
* [Options](#options)
|
||||
* [Global Defaults](#global-defaults)
|
||||
* [A Note on <video> Tag Attributes](#a-note-on-video-tag-attributes)
|
||||
* [Player Readiness](#player-readiness)
|
||||
* [Advanced Player Workflows](#advanced-player-workflows)
|
||||
|
||||
You can download the Video.js source and host it on your own servers, or use the free CDN hosted version. As of Video.js 5.0, the source is [transpiled from ES2015](http://babeljs.io/) (formerly known as ES6) to [ES5](https://es5.github.io/), but IE8 only supports ES3. In order to continue to support IE8, we've bundled an [ES5 shim and sham](https://github.com/es-shims/es5-shim) together and hosted it on the CDN.
|
||||
## Getting Video.js
|
||||
|
||||
Video.js is officially available via CDN, npm, and Bower.
|
||||
|
||||
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.
|
||||
|
||||
Please refer to the [Getting Started][getting-started] document for details.
|
||||
|
||||
## Creating a Player
|
||||
|
||||
> **Note:** Video.js works with `<video>` _and_ `<audio>` elements, but for simplicity we'll refer only to `<video>` elements going forward.
|
||||
|
||||
Once you have Video.js [loaded on your page][getting-started], you're ready to create a player!
|
||||
|
||||
The core strength of Video.js is that it decorates a [standard `<video>` element][w3c-video] and emulates its associated [events and APIs][w3c-media-events], while providing a customizable DOM-based UI.
|
||||
|
||||
Video.js supports all attributes of the `<video>` element (such as `controls`, `preload`, etc), but it also supports [its own options](#options). There are two ways to create a Video.js player and pass it options, but they both start with a standard `<video>` element with the attribute `class="video-js"`:
|
||||
|
||||
```html
|
||||
<script src="//vjs.zencdn.net/ie8/1.1.1/videojs-ie8.min.js"></script>
|
||||
```
|
||||
|
||||
### CDN Version ###
|
||||
```html
|
||||
<link href="//vjs.zencdn.net/5.4.6/video-js.min.css" rel="stylesheet">
|
||||
<script src="//vjs.zencdn.net/5.4.6/video.min.js"></script>
|
||||
```
|
||||
|
||||
Alternatively you can always [go here](http://videojs.com/getting-started/) to get the latest URL for videojs CDN.
|
||||
|
||||
We include a stripped down Google Analytics pixel that tracks a random percentage (currently 1%) of players loaded from the CDN. This allows us to see (roughly) what browsers are in use in the wild, along with other useful metrics such as OS and device. If you'd like to disable analytics, you can simply include the following global **before** including Video.js:
|
||||
|
||||
```js
|
||||
window.HELP_IMPROVE_VIDEOJS = false;
|
||||
```
|
||||
|
||||
## Install via package manager
|
||||
|
||||
### NPM
|
||||
```
|
||||
$ npm install --save video.js
|
||||
```
|
||||
|
||||
### Bower
|
||||
```
|
||||
$ bower install --save video.js
|
||||
```
|
||||
|
||||
|
||||
### Self Hosted. ###
|
||||
To entirely self-host, you'll need to pull in the font files and let Video.js know where the swf is located. If you simply copy the dist folder or zip file contents into your project everything
|
||||
should Just Work™, but the paths can easily be changed by editing the LESS file and re-building, or by modifying the generated CSS file. Additionally include the [videojs-vtt.js](https://www.npmjs.com/package/videojs-vtt.js) source, which adds the `WebVTT` object to the global scope.
|
||||
|
||||
```html
|
||||
<link href="//example.com/path/to/video-js.min.css" rel="stylesheet">
|
||||
<script src="//example.com/path/to/videojs-vtt.js"></script>
|
||||
<script src="//example.com/path/to/video.min.js"></script>
|
||||
<script>
|
||||
videojs.options.flash.swf = "http://example.com/path/to/video-js.swf"
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
Step 2: Add an HTML5 video tag to your page.
|
||||
--------------------------------------------
|
||||
With Video.js you just use an HTML5 video tag to embed a video. Video.js will then read the tag and make it work in all browsers, not just ones that support HTML5 video. Beyond the basic markup, Video.js needs a few extra pieces.
|
||||
|
||||
> Note: The `data-setup` attribute described here should not be used if you use the alternative setup described in the next section.
|
||||
|
||||
1. The 'data-setup' Attribute tells Video.js to automatically set up the video when the page is ready, and read any options (in JSON format) from the attribute (see [options](./options.md)). There are other methods for initializing the player, but this is the easiest.
|
||||
|
||||
2. The 'id' Attribute: Should be used and unique for every video on the same page.
|
||||
|
||||
3. The 'class' attribute contains two classes:
|
||||
- `video-js` applies styles that are required for Video.js functionality, like fullscreen and subtitles.
|
||||
- `vjs-default-skin` applies the default skin to the HTML controls, and can be removed or overridden to create your own controls design.
|
||||
|
||||
Otherwise include/exclude attributes, settings, sources, and tracks exactly as you would for HTML5 video.*
|
||||
```html
|
||||
<video id="example_video_1" class="video-js vjs-default-skin"
|
||||
controls preload="auto" width="640" height="264"
|
||||
poster="http://video-js.zencoder.com/oceans-clip.png"
|
||||
data-setup='{"example_option":true}'>
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type="video/mp4" />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type="video/webm" />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type="video/ogg" />
|
||||
<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 class="video-js">
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
|
||||
</video>
|
||||
```
|
||||
|
||||
By default, the big play button is located in the upper left hand corner so it doesn't cover up the interesting parts of the poster. If you'd prefer to center the big play button, you can add an additional `vjs-big-play-centered` class to your video element. For example:
|
||||
### Automatic Setup
|
||||
|
||||
By default, when your web page finishes loading, Video.js will scan for media elements that have the `data-setup` attribute. The `data-setup` attribute is used to pass options to Video.js. A minimal example looks like this:
|
||||
|
||||
```html
|
||||
<video id="example_video_1" class="video-js vjs-default-skin vjs-big-play-centered"
|
||||
controls preload="auto" width="640" height="264"
|
||||
poster="http://video-js.zencoder.com/oceans-clip.png"
|
||||
data-setup='{"example_option":true}'>
|
||||
...
|
||||
<video class="video-js" data-setup='{}'>
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
|
||||
</video>
|
||||
```
|
||||
|
||||
Alternative Setup for Dynamically Loaded HTML
|
||||
---------------------------------------------
|
||||
If your web page or application loads the video tag dynamically (ajax, appendChild, etc.), so that it may not exist when the page loads, you'll want to manually set up the player instead of relying on the data-setup attribute. To do this, first remove the data-setup attribute from the tag so there's no confusion around when the player is initialized. Next, run the following javascript some time after the Video.js javascript library has loaded, and after the video tag has been loaded into the DOM.
|
||||
```js
|
||||
videojs("example_video_1", {}, function(){
|
||||
// Player (this) is initialized and ready.
|
||||
});
|
||||
```
|
||||
> **Note:** You _must_ use single-quotes with `data-setup` as it is expected to contain JSON.
|
||||
|
||||
The first argument in the `videojs` function is the ID of your video tag. Replace it with your own.
|
||||
### Manual Setup
|
||||
|
||||
The second argument is an options object. It allows you to set additional options like you can with the data-setup attribute.
|
||||
On the modern web, a `<video>` element often does not exist when the page finishes loading. In these cases, automatic setup is not possible, but manual setup is available via [the `videojs` function][videojs].
|
||||
|
||||
The third argument is a 'ready' callback. Once Video.js has initialized it will call this function.
|
||||
One way to call this function is by providing it a string matching a `<video>` element's `id` attribute:
|
||||
|
||||
Instead of using an element ID, you can also pass a reference to the element itself.
|
||||
|
||||
```js
|
||||
videojs(document.getElementById('example_video_1'), {}, function() {
|
||||
// This is functionally the same as the previous example.
|
||||
});
|
||||
```html
|
||||
<video id="my-player" class="video-js">
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
|
||||
</video>
|
||||
```
|
||||
|
||||
```js
|
||||
videojs(document.getElementsByClassName('awesome_video_class')[0], {}, function() {
|
||||
// You can grab an element by class if you'd like, just make sure
|
||||
// if it's an array that you pick one (here we chose the first).
|
||||
videojs('my-player');
|
||||
```
|
||||
|
||||
However, using an `id` attribute isn't always practical; so, the `videojs` function accepts a DOM element instead:
|
||||
|
||||
```html
|
||||
<video class="video-js">
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
|
||||
</video>
|
||||
```
|
||||
|
||||
```js
|
||||
videojs(document.querySelector('.video-js'));
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
> **Note:** This guide only covers how to pass options during player setup. For a complete reference on _all_ available options, see the [options guide](options.md).
|
||||
|
||||
There are three ways to pass options to Video.js. Because Video.js decorates an HTML5 `<video>` element, many of the options available are also available as [standard `<video>` tag attributes][video-attrs]:
|
||||
|
||||
```html
|
||||
<video controls autoplay preload="auto" ...>
|
||||
```
|
||||
|
||||
Alternatively, you can use the `data-setup` attribute to pass options as [JSON][json]. This is also how you would set options that aren't standard to the `<video>` element:
|
||||
|
||||
```html
|
||||
<video data-setup='{"controls": true, "autoplay": false, "preload": "auto"}'...>
|
||||
```
|
||||
|
||||
Finally, if you're not using the `data-setup` attribute to trigger the player setup, you can pass in an object of player options as the second argument to the `videojs` function:
|
||||
|
||||
```js
|
||||
videojs('my-player', {
|
||||
controls: true,
|
||||
autoplay: false,
|
||||
preload: 'auto'
|
||||
});
|
||||
```
|
||||
|
||||
\* If you have trouble playing back content you know is in the [correct format](http://blog.zencoder.com/2013/09/13/what-formats-do-i-need-for-html5-video/), your HTTP server might not be delivering the content with the correct [MIME type](http://en.wikipedia.org/wiki/Internet_media_type#Type_video). Please double check your content's headers before opening an [issue](https://github.com/videojs/video.js/blob/master/CONTRIBUTING.md).
|
||||
### Global Defaults
|
||||
|
||||
Default options for all players can be found at `videojs.options` and can be changed directly. For example, to set `{autoplay: true}` for all future players:
|
||||
|
||||
```js
|
||||
videojs.options.autoplay = true;
|
||||
```
|
||||
|
||||
### A Note on `<video>` Tag Attributes
|
||||
|
||||
Many attributes are so-called [boolean attributes][boolean-attrs]. This means they are either on or off. In these cases, the attribute _should have no value_ (or should have its name as its value) - its presence implies a true value and its absence implies a false value.
|
||||
|
||||
_These are incorrect:_
|
||||
|
||||
```html
|
||||
<video controls="true" ...>
|
||||
<video loop="true" ...>
|
||||
<video controls="false" ...>
|
||||
```
|
||||
|
||||
> **Note:** The example with `controls="false"` can be a point of confusion for new developers - it will actually turn controls _on_!
|
||||
|
||||
These are correct:
|
||||
|
||||
```html
|
||||
<video controls ...>
|
||||
<video loop="loop" ...>
|
||||
<video ...>
|
||||
```
|
||||
|
||||
## Player Readiness
|
||||
|
||||
Because Video.js techs have the potential to be loaded asynchronously, it isn't always safe to interact with a player immediately upon setup. For this reason, Video.js players have a concept of "readiness" which will be familiar to anyone who has used jQuery before.
|
||||
|
||||
Essentially, any number of ready callbacks can be defined for a Video.js player. There are three ways to pass these callbacks. In each example, we'll add an identical class to the player:
|
||||
|
||||
Pass a callback to the `videojs()` function as a third argument:
|
||||
|
||||
```js
|
||||
// Passing `null` for the options argument.
|
||||
videojs('my-player', null, function() {
|
||||
this.addClass('my-example');
|
||||
});
|
||||
```
|
||||
|
||||
Pass a callback to a player's `ready()` method:
|
||||
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.ready(function() {
|
||||
this.addClass('my-example');
|
||||
});
|
||||
```
|
||||
|
||||
Listen for the player's `"ready"` event:
|
||||
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.on('ready', function() {
|
||||
this.addClass('my-example');
|
||||
});
|
||||
```
|
||||
|
||||
In each case, the callback is called asynchronously - _even if the player is already ready!_
|
||||
|
||||
## Advanced Player Workflows
|
||||
|
||||
For a discussion of more advanced player workflows, see the [player workflows guide](player-workflows.md).
|
||||
|
||||
[boolean-attrs]: https://www.w3.org/TR/2011/WD-html5-20110525/common-microsyntaxes.html#boolean-attributes
|
||||
|
||||
[getting-started]: http://videojs.com/getting-started/
|
||||
|
||||
[json]: http://json.org/example.html
|
||||
|
||||
[video-attrs]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes
|
||||
|
||||
[videojs]: http://docs.videojs.com/docs/api/video.html
|
||||
|
||||
[w3c-media-events]: https://www.w3.org/2010/05/video/mediaevents.html
|
||||
|
||||
[w3c-video]: http://www.w3.org/TR/html5/embedded-content-0.html#the-video-element
|
||||
|
||||
+82
-39
@@ -1,54 +1,97 @@
|
||||
Skins
|
||||
=====
|
||||
# Skins
|
||||
|
||||
## Base Skin
|
||||
The base Video.js skin is made using HTML and CSS (although we use the [Sass preprocessor](http://sass-lang.com)),
|
||||
and by default these styles are added to the DOM for you!
|
||||
That means you can build a custom skin by simply taking advantage of the cascading aspect of CSS and overriding
|
||||
the styles you'd like to change.
|
||||
## Table of Contents
|
||||
|
||||
If you don't want Video.js to inject the base styles for you, you can disable it by setting `window.VIDEOJS_NO_BASE_THEME = true` before Video.js is loaded.
|
||||
Keep in mind that without these base styles enabled, you'll need to manually include them.
|
||||
* [Default Skin](#default-skin)
|
||||
* [Additional <style> Elements](#additional-style-elements)
|
||||
* [Disabling Additional <style> Elements](#disabling-additional-style-elements)
|
||||
* [Effect on Player#width() and Player#height()](#effect-on-playerwidth-and-playerheight)
|
||||
* [Icons](#icons)
|
||||
* [Creating a Skin](#creating-a-skin)
|
||||
* [Add a Custom Class to the Player](#add-a-custom-class-to-the-player)
|
||||
* [Customize Styles](#customize-styles)
|
||||
|
||||
Video.js does not currently include the base skin automatically yet, so, this option isn't necessary.
|
||||
## Default Skin
|
||||
|
||||
## Default style elements
|
||||
Video.js uses a couple of style elements dynamically, specifically, there's a default styles element as well as a player dimensions style element.
|
||||
They are used to provide extra default flexiblity with styling the player. However, in some cases, like if a user has the HEAD tag managed by React, users do not want this.
|
||||
When `window.VIDEOJS_NO_DYNAMIC_STYLE` is set to `true`, video.js will *not* include these element in the page.
|
||||
This means that default dimensions and configured player dimensions will *not* be applied.
|
||||
For example, the following player will end up having a width and height of 0 when initialized if `window.VIDEOJS_NO_DYNAMIC_STYLE === true`:
|
||||
```html
|
||||
<video width="600" height="300"></video>
|
||||
```
|
||||
When you include the Video.js CSS file (`video-js.min.css`), the default Video.js skin is applied. That means that customizing the look of a Video.js player is a matter of taking advantage of the cascading aspect of CSS to override styles.
|
||||
|
||||
### `Player#width` and `Player#height`
|
||||
When `VIDEOJS_NO_DYNAMIC_STYLE` is set, `Player#width` and `Player#height` will apply any width and height
|
||||
that is set directly to the video element (or whatever element the current tech uses).
|
||||
## Additional `<style>` Elements
|
||||
|
||||
In addition to the Video.js CSS file, there are some styles generated automatically by JavaScript and included in the `<head>` as `<style>` elements.
|
||||
|
||||
* The `"vjs-styles-defaults"` element sets default dimensions for all Video.js players on the page.
|
||||
* A `"vjs-styles-dimensions"` element is created for _each_ player on the page and is used to adjust its size. This styling is handled in this manner to allow you to override it with custom CSS without relying on scripting or `!important` to overcome inline styles.
|
||||
|
||||
### Disabling Additional `<style>` Elements
|
||||
|
||||
In some cases, particularly with web applications using frameworks that may manage the `<head>` element (e.g. React, Ember, Angular, etc), these `<style>` elements are not desirable. They can be suppressed by setting `window.VIDEOJS_NO_DYNAMIC_STYLE = true` before including Video.js.
|
||||
|
||||
_This disables all CSS-based player sizing. Players will have no `height` or `width` by default!_ Even dimensional attributes, such as `width="600" height="300"` will be _ignored_. When using this global, you will need to set their own dimensions in a way that makes sense for their website or web app.
|
||||
|
||||
#### Effect on `Player#width()` and `Player#height()`
|
||||
|
||||
When `VIDEOJS_NO_DYNAMIC_STYLE` is set, `Player#width()` and `Player#height()` will apply any width and height that is set directly to the `<video>` element (or whichever element the current tech uses).
|
||||
|
||||
## Icons
|
||||
|
||||
You can view all of the icons available in the base theme by renaming and viewing
|
||||
[`icons.html.example`](https://github.com/videojs/video.js/blob/master/sandbox/icons.html.example) in the sandbox directory.
|
||||
Video.js ships with a number of icons built into the skin via an icon font.
|
||||
|
||||
## Customization
|
||||
You can view all of the icons available in the default skin by renaming [`sandbox/icons.html.example`](https://github.com/videojs/video.js/blob/master/sandbox/icons.html.example) to `sandbox/icons.html`, building Video.js with `npm run build`, and opening `sandbox/icons.html` in your browser of choice.
|
||||
|
||||
When you create a new skin, the easiest way to get started is to simply override the base Video.js theme.
|
||||
You should include a new class matching the name of your theme, then just start overriding!
|
||||
## Creating a Skin
|
||||
|
||||
```css
|
||||
.vjs-skin-hotdog-stand { color: #FF0000; }
|
||||
.vjs-skin-hotdog-stand .vjs-control-bar { background: #FFFF00; }
|
||||
.vjs-skin-hotdog-stand .vjs-play-progress { background: #FF0000; }
|
||||
The recommended process for creating a skin is to override the styles provided by the default skin. In this way, you don't need to start from scratch.
|
||||
|
||||
### Add a Custom Class to the Player
|
||||
|
||||
The most convenient way to create a hook in the player for your skin is to add a class to it. You can do this by adding a class to the initial `<video>` element:
|
||||
|
||||
```html
|
||||
<video class="vjs-matrix video-js">...</video>
|
||||
```
|
||||
|
||||
This would take care of the major areas of the skin (play progress, the control bar background, and icon colors),
|
||||
but you can skin any other aspect.
|
||||
Our suggestion is to use a browser such as Firefox and Chrome,
|
||||
and use the developer tools to inspect the different elements and see what you'd like to change and what classes
|
||||
to target when you do so.
|
||||
Or via JavaScript:
|
||||
|
||||
More custom skins will be available for download soon.
|
||||
If you have one you like you can share it by forking [this example on CodePen.io](http://codepen.io/heff/pen/EarCt),
|
||||
and adding a link on the [Skins wiki page](https://github.com/videojs/video.js/wiki/Skins).
|
||||
```js
|
||||
var player = videojs('my-player');
|
||||
|
||||
player.addClass('vjs-matrix');
|
||||
```
|
||||
|
||||
> **Note:** The `vjs-` prefix is a convention for all classes that are contained in a Video.js player.
|
||||
|
||||
### Customize Styles
|
||||
|
||||
The first step in overriding default styles with custom ones is to determine which selectors and properties need overriding. As an example, let's say we don't like the default color of controls (white) and we want to change them to a bright green (say, `#00ff00`).
|
||||
|
||||
To do this, we'll use your browser's developer tools to inspect the player and figure out which selectors we need to use to adjust those styles - and we'll add our custom `.vjs-matrix` selector to ensure our final selectors are specific enough to override the default skin.
|
||||
|
||||
In this case, we'll need the following:
|
||||
|
||||
```css
|
||||
/* Change all text and icon colors in the player. */
|
||||
.vjs-matrix.video-js {
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
/* Change the border of the big play button. */
|
||||
.vjs-matrix .vjs-big-play-button {
|
||||
border-color: #00ff00;
|
||||
}
|
||||
|
||||
/* Change the color of various "bars". */
|
||||
.vjs-matrix .vjs-volume-level,
|
||||
.vjs-matrix .vjs-play-progress,
|
||||
.vjs-matrix .vjs-slider-bar {
|
||||
background: #00ff00;
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we can save that as a `videojs-matrix.css` file and include it _after_ the Video.js CSS:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" type="text/css" href="path/to/video-js.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="path/to/videojs-matrix.css">
|
||||
```
|
||||
|
||||
If you create a skin you're particularly proud of, you can share it by adding a link on the [Skins wiki page](https://github.com/videojs/video.js/wiki/Skins). One way to create shareable skins is by forking [this example on CodePen](http://codepen.io/heff/pen/EarCt).
|
||||
|
||||
+63
-77
@@ -1,102 +1,88 @@
|
||||
Playback Technology ("Tech")
|
||||
============================
|
||||
Playback Technology refers to the specific browser or plugin technology used to play the video or audio. When using HTML5, the playback technology is the video or audio element. When using Flash, the playback technology is the specific Flash player used, e.g. Flowplayer, YouTube Player, video-js.swf, etc. (not just "Flash"). This could also include Silverlight, Quicktime, or any other plugin that will play back video in the browser, as long as there is an API wrapper written for it.
|
||||
# Playback Technology ("Tech")
|
||||
|
||||
Essentially we're using HTML5 and plugins only as video decoders, and using HTML and JavaScript to create a consistent API and skinning experience across all of them.
|
||||
Playback Technology refers to the specific browser or plugin technology used to play the video or audio.
|
||||
When using HTML5, the playback technology is the video or audio element. When using Flash and the
|
||||
[videojs-flash project](http://github.com/videojs/videojs-flash) (which was builtin to Video.js prior to Video.js 6), the playback technology is the specific Flash player used, and in most cases that will be [video-js-swf](https://github.com/videojs/video-js-swf). This could also include Silverlight,
|
||||
Quicktime, or any other plugin that will play back video in the browser, as long as there is an API wrapper written for it.
|
||||
|
||||
Building an API Wrapper
|
||||
-----------------------
|
||||
We'll write a more complete guide on writing a wrapper soon, but for now the best resource is the [Video.js](https://github.com/zencoder/video-js/tree/master/src) source where you can see how both the HTML5 and video-js.swf API wrappers were created.
|
||||
Essentially we're using HTML5 and plugins only as video decoders, and using HTML and JavaScript to create a
|
||||
consistent API and skinning experience across all of them.
|
||||
|
||||
Required Methods
|
||||
----------------
|
||||
canPlayType
|
||||
play
|
||||
pause
|
||||
currentTime
|
||||
volume
|
||||
duration
|
||||
buffered
|
||||
supportsFullScreen
|
||||
## Table of Contents
|
||||
|
||||
Required Events
|
||||
---------------
|
||||
loadstart
|
||||
play
|
||||
pause
|
||||
playing
|
||||
ended
|
||||
volumechange
|
||||
durationchange
|
||||
error
|
||||
* [Writing a new Tech](#writing-a-new-tech)
|
||||
* [Proritizing Techs using techOrder](#proritizing-techs-using-techorder)
|
||||
* [Resources](#resources)
|
||||
|
||||
Optional Events (include if supported)
|
||||
--------------------------------------
|
||||
timeupdate
|
||||
progress
|
||||
enterFullScreen
|
||||
exitFullScreen
|
||||
## Writing a new Tech
|
||||
|
||||
Adding Playback Technology
|
||||
==================
|
||||
When adding additional Tech to a video player, make sure to add the supported tech to the video object.
|
||||
When writing a Tech you will have to integrate your technology's API with the existing API. In the following examples
|
||||
we are going to make a clone of the base `Tech` class which would not actually play back any media.
|
||||
|
||||
### Tag Method: ###
|
||||
<video data-setup='{"techOrder": ["html5", "flash", "other supported tech"]}'
|
||||
ES6 Example of a new Tech
|
||||
|
||||
### Object Method: ###
|
||||
videojs("videoID", {
|
||||
techOrder: ["html5", "flash", "other supported tech"]
|
||||
});
|
||||
```js
|
||||
const Tech = videojs.getTech('Tech');
|
||||
class MyTech extnds Tech {
|
||||
constructor(options, ready) {
|
||||
super(options, ready);
|
||||
console.log('My new Tech!');
|
||||
}
|
||||
}
|
||||
|
||||
Technology Ordering
|
||||
==================
|
||||
By default Video.js performs "Tech-first" ordering when it searches for a source/tech combination to play videos. This means that if you have two sources and two techs, video.js will try to play each video with the first tech in the `techOrder` option property before moving on to try the next playback technology.
|
||||
// Add our new Tech to the internal Tech list and to the end of the default tech order list
|
||||
// the techOrder will now look like this by default: `['html5', 'mytech']`
|
||||
videojs.registerTech('MyTech', MyTech);
|
||||
```
|
||||
|
||||
Tech-first ordering can present a problem if you have a `sourceHandler` that supports both `Html5` and `Flash` techs such as videojs-contrib-hls.
|
||||
ES5 Example of a new Tech
|
||||
|
||||
For example, given the following video element:
|
||||
```js
|
||||
var Tech = videojs.getTech('Tech');
|
||||
var MyTech = videojs.extend(Tech, {
|
||||
constructor: function(options, ready) {
|
||||
Tech.call(options, ready);
|
||||
console.log('My new Tech!');
|
||||
}
|
||||
});
|
||||
|
||||
<video data-setup='{"techOrder": ["html5", "flash"]}'>
|
||||
<source src="http://your.static.provider.net/path/to/video.m3u8" type="application/x-mpegURL">
|
||||
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
// Add our new Tech to the internal Tech list and to the end of the default `techOrder` list
|
||||
videojs.registerTech('MyTech', MyTech);
|
||||
```
|
||||
|
||||
There is a good chance that the mp4 source will be selected on platforms that do not have media source extensions. Video.js will try all sources against the first playback technology, in this case `Html5`, and select the first source that can play - in this case MP4.
|
||||
For more information on what methods and events a `Tech` is required to implement. See the [Html5 tech API](https://github.com/videojs/video.js/blob/master/src/js/tech/html5.js)
|
||||
|
||||
In "Tech-first" mode, the tests run something like this:
|
||||
Can video.m3u8 play with Html5? No...
|
||||
Can video.mp4 play with Html5? Yes! Use the second source.
|
||||
## Proritizing Techs using `techOrder`
|
||||
|
||||
Video.js now provides another method of selecting the source - "Source-first" ordering. In this mode, Video.js tries the first source against every tech in `techOrder` before moving onto the next source.
|
||||
By default Video.js performs "Tech-first" ordering when it searches for a source/tech combination to play videos.
|
||||
This means that if you have two sources and two techs, video.js will try to play each video with the first tech in
|
||||
the `techOrder` option property before moving on to try the next playback technology. By default Techs will be added
|
||||
to the `techOrder` in the order in which they were registered to video.js. So in our `MyTech` example above it would
|
||||
look something like this:
|
||||
|
||||
With a player setup as follows:
|
||||
```json
|
||||
['Html5', 'MyTech']
|
||||
```
|
||||
|
||||
<video data-setup='{"techOrder": ["html5", "flash"], "sourceOrder": true}'>
|
||||
<source src="http://your.static.provider.net/path/to/video.m3u8" type="application/x-mpegURL">
|
||||
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
Tech-first is only a problem when you want to prioritize techs differently than the order of registration. To demonstrate how you would change that lets give some examples:
|
||||
|
||||
The Flash-based HLS support will be tried before falling back to the MP4 source.
|
||||
Example 1: Only use `MyTech`:
|
||||
|
||||
In "Source-first" mode, the tests run something like this:
|
||||
Can video.m3u8 play with Html5? No...
|
||||
Can video.m3u8 play with Flash? Yes! Use the first source.
|
||||
```js
|
||||
var player = videojs('some-player-id', {techOrder: ['MyTech']});
|
||||
```
|
||||
|
||||
Flash Technology
|
||||
==================
|
||||
The Flash playback tech is a part of the default `techOrder`. You may notice undesirable playback behavior in browsers that are subject to using this playback tech, in particular when scrubbing and seeking within a video. This behavior is a result of Flash's progressive video playback.
|
||||
Example 2: Use `MyTech` and fallback to `Html5` when `MyTech` cannot playback a video:
|
||||
|
||||
Enabling Streaming Playback
|
||||
--------------------------------
|
||||
In order to force the Flash tech to choose streaming playback, you need to provide a valid streaming source **before other valid Flash video sources**. This is necessary because of the source selection algorithm, where playback tech chooses the first possible source object with a valid type. Valid streaming `type` values include `rtmp/mp4` and `rtmp/flv`. The streaming `src` value requires valid connection and stream strings, separated by an `&`. An example of supplying a streaming source through your HTML markup might look like:
|
||||
```html
|
||||
var player = videojs('some-player-id', {techOrder: ['MyTech', 'Html5']});
|
||||
```
|
||||
|
||||
<source src="rtmp://your.streaming.provider.net/cfx/st/&mp4:path/to/video.mp4" type="rtmp/mp4">
|
||||
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
|
||||
<source src="http://your.static.provider.net/path/to/video.webm" type="video/webm">
|
||||
> Note: each of these examples use JavaScript only, but it is possible to pass the `techOrder` using HTML by using the [`data-setup` attribute on a video](./setup.md#automatic-setup).
|
||||
|
||||
You may optionally use the last `/` as the separator between connection and stream strings, for example:
|
||||
## Resources
|
||||
|
||||
<source src="rtmp://your.streaming.provider.net/cfx/st/mp4:video.mp4" type="rtmp/mp4">
|
||||
Here are some examples of a `Tech` which you can use as resources when designing a new `Tech`:
|
||||
|
||||
All four RTMP protocols are valid in the `src` (RTMP, RTMPT, RTMPE, and RTMPS).
|
||||
* [The Html5 Tech](https://github.com/videojs/video.js/blob/master/src/js/tech/html5.js)
|
||||
* [The videojs-flash project](http://github.com/videojs/videojs-flash/blob/master/src/index.js)
|
||||
|
||||
+168
-109
@@ -1,119 +1,177 @@
|
||||
# Text Tracks
|
||||
|
||||
Text Tracks are a function of HTML5 video for providing time triggered text to the viewer. Video.js makes tracks work across all browsers. There are currently five types of tracks:
|
||||
Text tracks are a feature of HTML5 video for displaying time-triggered text to the viewer. Video.js offers a cross-browser implementation of text tracks.
|
||||
|
||||
- **Subtitles**: Translations of the dialogue in the video for when audio is available but not understood. Subtitles are shown over the video.
|
||||
- **Captions**: Transcription of the dialogue, sound effects, musical cues, and other audio information for when the viewer is deaf/hard of hearing, or the video is muted. Captions are also shown over the video.
|
||||
- **Chapters**: Chapter titles that are used to create navigation within the video. Typically they're in the form of a list of chapters that the viewer can click on to go to a specific chapter.
|
||||
- **Descriptions**: Text descriptions of what's happening in the video for when the video portion isn't available, because the viewer is blind, not using a screen, or driving and about to crash because they're trying to enjoy a video while driving. Descriptions are read by a screen reader or turned into a separate audio track.
|
||||
- **Metadata**: Tracks that have data meant for javascript to parse and do something with. These aren't shown to the user.
|
||||
## Table of Contents
|
||||
|
||||
* [A Note on "Remote" Text Tracks](#a-note-on-remote-text-tracks)
|
||||
* [Creating the Text File](#creating-the-text-file)
|
||||
* [Adding Text Tracks to Video.js](#adding-text-tracks-to-videojs)
|
||||
* [track Attributes](#track-attributes)
|
||||
* [kind](#kind)
|
||||
* [label](#label)
|
||||
* [default](#default)
|
||||
* [srclang](#srclang)
|
||||
* [Text Tracks from Another Domain](#text-tracks-from-another-domain)
|
||||
* [Working with Text Tracks](#working-with-text-tracks)
|
||||
* [Showing Tracks Programmatically](#showing-tracks-programmatically)
|
||||
* [Listen for a Cue Becoming Active](#listen-for-a-cue-becoming-active)
|
||||
* [Emulated Text Tracks](#emulated-text-tracks)
|
||||
* [Text Track Settings](#text-track-settings)
|
||||
* [Text Track Precedence](#text-track-precedence)
|
||||
* [API](#api)
|
||||
* [Remote Text Tracks](#remote-text-tracks)
|
||||
* [Text Tracks](#text-tracks-1)
|
||||
|
||||
## A Note on "Remote" Text Tracks
|
||||
|
||||
Video.js refers to so-called "remote" text tracks. This is a convenient term for tracks that have an associated `<track>` element rather than those that do not.
|
||||
|
||||
Either can be created programmatically, but _only remote text tracks can be removed from a player._ For that reason, we recommend _only_ using remote text tracks.
|
||||
|
||||
## Creating the Text File
|
||||
Timed text requires a text file in [WebVTT](http://dev.w3.org/html5/webvtt/) format. This format defines a list of "cues" that have a start time, and end time, and text to display. [Microsoft has a builder](https://dev.modern.ie/testdrive/demos/captionmaker/) that can help you get started on the file.
|
||||
|
||||
When creating captions, there's also additional [caption formatting techniques] (http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_FORMAT.HTML#style) that would be good to use, like brackets around sound effects: [ sound effect ]. If you'd like a more in depth style guide for captioning, you can reference the [Captioning Key](http://www.dcmp.org/captioningkey/), but keep in mind not all features are supported by WebVTT or (more likely) the Video.js WebVTT implementation.
|
||||
Timed text requires a text file in [WebVTT](http://dev.w3.org/html5/webvtt/) format. This format defines a list of "cues" that have a start time, an end time, and text to display. [Microsoft has a builder](https://dev.modern.ie/testdrive/demos/captionmaker/) that can help you get started on the file.
|
||||
|
||||
## Adding to Video.js
|
||||
Once you have your WebVTT file created, you can add it to Video.js using the track tag. Put your track tag after all the source elements, and before any fallback content.
|
||||
> **Note:** When creating captions, there are additional [caption formatting techniques](http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_FORMAT.HTML#style) to make captions more meaningful, like brackets around sound effects (e.g. `[ birds chirping ]`).
|
||||
>
|
||||
> For a more in depth style guide for captioning, see the [Captioning Key](http://www.dcmp.org/captioningkey/), but keep in mind not all features are supported by WebVTT or (more likely) the Video.js WebVTT implementation.
|
||||
|
||||
## Adding Text Tracks to Video.js
|
||||
|
||||
Once you have your WebVTT files created, you can add them to your `video` element using the `track` tag. Similar to `source` elements, `track` elements should be added as children of the `video` element:
|
||||
|
||||
```html
|
||||
<video id="example_video_1" class="video-js"
|
||||
controls preload="auto" width="640" height="264"
|
||||
data-setup='{"example_option":true}'>
|
||||
<source src="http://vjs.zencdn.net/v/oceans.mp4" type="video/mp4" />
|
||||
<source src="http://vjs.zencdn.net/v/oceans.webm" type="video/webm" />
|
||||
<source src="http://vjs.zencdn.net/v/oceans.ogv" type="video/ogg" />
|
||||
|
||||
<track kind="captions" src="http://example.com/path/to/captions.vtt" srclang="en" label="English" default>
|
||||
|
||||
<video
|
||||
class="video-js"
|
||||
controls
|
||||
preload="auto"
|
||||
width="640"
|
||||
height="264"
|
||||
data-setup='{}'>
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
|
||||
<track kind="captions" src="//example.com/path/to/captions.vtt" srclang="en" label="English" default>
|
||||
</video>
|
||||
```
|
||||
|
||||
You can also add tracks [programatically](#api).
|
||||
Video.js will automatically read `track` elements from the `video` element. Tracks (remote and non-remote) can also be added programmatically.
|
||||
|
||||
### `track` Attributes
|
||||
|
||||
#### `kind`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#text-track-kind)
|
||||
|
||||
One of the track types supported by Video.js:
|
||||
|
||||
* `"subtitles"` (default): Translations of the dialogue in the video for when audio is available but not understood. Subtitles are shown over the video.
|
||||
* `"captions"`: Transcription of the dialogue, sound effects, musical cues, and other audio information for viewer who are deaf/hard of hearing, or the video is muted. Captions are also shown over the video.
|
||||
* `"chapters"`: Chapter titles that are used to create navigation within the video. Typically, these are in the form of a list of chapters that the viewer can use to navigate the video.
|
||||
* `"descriptions"`: Text descriptions of the action in the content for when the video portion isn't available or because the viewer is blind or not using a screen. Descriptions are read by a screen reader or turned into a separate audio track.
|
||||
* `"metadata"`: Tracks that have data meant for JavaScript to parse and do something with. These aren't shown to the user.
|
||||
|
||||
#### `label`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#text-track-label)
|
||||
|
||||
Short descriptive text for the track that will used in the user interface. For example, in a menu for selecting a captions language.
|
||||
|
||||
#### `default`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-track-default)
|
||||
|
||||
The boolean `default` attribute can be used to indicate that a track's mode should start as `"showing"`. Otherwise, the viewer would need to select their language from a captions or subtitles menu.
|
||||
|
||||
> **Note:** For chapters, `default` is required if you want the chapters menu to show.
|
||||
|
||||
#### `srclang`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-track-srclang)
|
||||
|
||||
The valid [BCP 47](https://tools.ietf.org/html/bcp47) code for the language of the text track, e.g. `"en"` for English or `"es"` for Spanish.
|
||||
|
||||
For supported language translations, please see the [languages folder (/lang)](https://github.com/videojs/video.js/tree/master/lang) folder located in the Video.js root and refer to the [languages guide](./languages.md) for more information on languages in Video.js.
|
||||
|
||||
### Text Tracks from Another Domain
|
||||
|
||||
Because Video.js loads the text track file via JavaScript, the [same-origin policy](http://en.wikipedia.org/wiki/Same_origin_policy) applies. If you'd like to have a player served from one domain, but the text track served from another, you'll need to [enable CORS](http://enable-cors.org/) on the server that is serving your text tracks.
|
||||
|
||||
In addition to enabling CORS, you will need to add the [`crossorigin` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) to the video element itself. This attribute has two possible values `"anonymous"` and `"use-credentials"`. Most users will want to use `"anonymous"` with cross-origin tracks:
|
||||
|
||||
## Subtitles from Another Domain
|
||||
Because we're pulling in the text track file via Javascript, the [same-origin policy](http://en.wikipedia.org/wiki/Same_origin_policy) applies. If you'd like to have a player served from one domain,
|
||||
but the text track served from another, you'll need to [enable CORS](http://enable-cors.org/) in order to do so.
|
||||
In addition to enabling CORS on the server serving the text tracks, you will need to add the [`crossorigin` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) to the video element itself. This attribute has two values `anonymous` and `use-credentials`. Most users will want to use `anonymous` with cross-origin tracks.
|
||||
It can be added to the video element like so:
|
||||
```html
|
||||
<video class="video-js" crossorigin="anonymous">
|
||||
<source src="http://vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<track src="http://example.com/oceans.vtt" kind="captions" srclang="en" label="English">
|
||||
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
|
||||
<track src="//example.com/oceans.vtt" kind="captions" srclang="en" label="English">
|
||||
</video>
|
||||
```
|
||||
One thing to be aware of is that in this case the video files themselves will *also* needs CORS headers applied to it. This is because some browsers apply the crossorigin attribute to the video source itself and not just the tracks and is considered a [security concern by the spec](https://html.spec.whatwg.org/multipage/embedded-content.html#security-and-privacy-considerations).
|
||||
|
||||
## Track Attributes
|
||||
Additional settings for track tags.
|
||||
One thing to be aware of is that the video files themselves will _also_ need CORS headers. This is because some browsers apply the `crossorigin` attribute to the video source itself and not just the tracks. This is considered a [security concern by the spec](https://html.spec.whatwg.org/multipage/embedded-content.html#security-and-privacy-considerations).
|
||||
|
||||
### kind
|
||||
One of the five track types listed above. Kind defaults to subtitles if no kind is included.
|
||||
## Working with Text Tracks
|
||||
|
||||
### label
|
||||
The label for the track that will be show to the user, for example in a menu that list the different languages available for subtitles.
|
||||
### Showing Tracks Programmatically
|
||||
|
||||
### default
|
||||
The default attribute can be used to have a track default to showing. Otherwise the viewer would need to select their language from the captions or subtitles menu.
|
||||
NOTE: For chapters, default is required if you want the chapters menu to show.
|
||||
Certain use cases call for turning captions on and off programmatically rather than forcing the user to do so themselves. This can be easily achieved by modifying the `mode` property of the text tracks.
|
||||
|
||||
### srclang
|
||||
The two-letter code (valid BCP 47 language tag) for the language of the text track, for example "en" for English. A list of language codes is [available here](languages.md#language-codes).
|
||||
The `mode` can be one of three values `"disabled"`, `"hidden"`, and `"showing"`. When a text track's `mode` is `"disabled"`, the track does not show on screen as the video is playing.
|
||||
|
||||
When the `mode` is set to `"showing"`, the track is visible to the viewer and updates while the video is playing.
|
||||
|
||||
## Interacting with Text Tracks
|
||||
### Showing tracks programmatically
|
||||
Some of you would want to turn captions on and off programmatically rather than just forcing the user to do so themselves. This can be easily achieved by modifying the `mode` of the text tracks.
|
||||
The `mode` can be one of three values `disabled`, `hidden`, and `showing`.
|
||||
When a text track's `mode` is `disabled`, the track does not show on screen as the video is playing.
|
||||
When the `mode` is set to `showing`, the track is visible to the viewer and updates while the video is playing.
|
||||
You can change of a particular track like so:
|
||||
```js
|
||||
let tracks = player.textTracks();
|
||||
// Get all text tracks for the current player.
|
||||
var tracks = player.textTracks();
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
|
||||
// find the captions track that's in english
|
||||
// Find the English captions track and mark it as "showing".
|
||||
if (track.kind === 'captions' && track.language === 'en') {
|
||||
track.mode = 'showing';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Doing something when a cue becomes active
|
||||
Above, we mentioned that `mode` can also be `hidden`, what this means is that the track will update
|
||||
as the video is playing but it won't be visible to the viewer. This is most useful for `metadata` text tracks.
|
||||
One usecase for metadata text tracks is to have something happen when their cues become active, to do so, you listen to the `cuechange` event on the track. These events fire when the mode is `showing` as well.
|
||||
Here's an example:
|
||||
### Listen for a Cue Becoming Active
|
||||
|
||||
One of the supported values for `mode` is `"hidden"`. This `mode` means that the track will update as the video is playing, but it won't be visible to the viewer. This is most useful for tracks where `kind="metadata"`.
|
||||
|
||||
A common use case for metadata text tracks is to use them to trigger behaviors when their cues become active. For this purpose, tracks emit a `"cuechange"` event.
|
||||
|
||||
```js
|
||||
let tracks = player.textTracks();
|
||||
let metadataTrack;
|
||||
// Get all text tracks for the current player.
|
||||
var tracks = player.textTracks();
|
||||
var metadataTrack;
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
|
||||
// find the metadata track that's labeled ads
|
||||
if (track.kind === 'captions' && track.label === 'ads') {
|
||||
// Find the metadata track that's labeled "ads".
|
||||
if (track.kind === 'metadata' && track.label === 'ads') {
|
||||
track.mode = 'hidden';
|
||||
// store it for usage outside of the loop
|
||||
|
||||
// Store it for usage outside of the loop.
|
||||
metadataTrack = track;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a listener for the "cuechange" event and start ad playback.
|
||||
metadataTrack.addEventListener('cuechange', function() {
|
||||
player.ads.startLinearAdMode();
|
||||
});
|
||||
```
|
||||
|
||||
## Emulated Text Tracks
|
||||
By default, video.js will try and use native text tracks if possible and fall back to emulated text tracks if the native functionality is broken or incomplete or non-existent.
|
||||
The Flash tech will always use the emulated text track functionality.
|
||||
The video.js API and TextTrack objects were modeled after the w3c's specification.
|
||||
video.js uses [Mozilla's vtt.js](https://github.com/mozilla/vtt.js) library to parse and display its emulated text tracks.
|
||||
|
||||
If you wanted to disable native text track functionality and force video.js to use emulated text tracks always, you can supply the `nativeTextTracks` option to the tech like so:
|
||||
By default, Video.js will use native text tracks and fall back to emulated text tracks if the native functionality is broken, incomplete, or non-existent. The Flash tech will always use the emulated text track functionality.
|
||||
|
||||
The Video.js API and TextTrack objects were modeled after the W3C specification. Video.js uses [Mozilla's vtt.js](https://github.com/mozilla/vtt.js) library to parse and display emulated text tracks.
|
||||
|
||||
To disable native text track functionality and force Video.js to use emulated text tracks always, the `nativeTextTracks` option can be passed to a tech:
|
||||
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
// Create a player, passing `nativeTextTracks: false` to the HTML5 tech.
|
||||
var player = videojs('myvideo', {
|
||||
html5: {
|
||||
nativeTextTracks: false
|
||||
}
|
||||
@@ -121,64 +179,65 @@ let player = videojs('myvideo', {
|
||||
```
|
||||
|
||||
### Text Track Settings
|
||||
When using emulated Text Tracks, captions will have an additional item in the menu called "caption settings".
|
||||
This allows the viewer of the player to change some styles of how the captions are displayed on screen.
|
||||
|
||||
If you don't want that, you can disable it by turning off the text track settings component and hiding the menu item like so:
|
||||
When using emulated text tracks, captions will have an additional item in the menu called "Caption Settings". This allows the user to alter how captions are styled on screen.
|
||||
|
||||
This feature can be disabled by turning off the `TextTrackSettings` component and hiding the menu item.
|
||||
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
// make the text track settings dialog not initialize
|
||||
var player = videojs('myvideo', {
|
||||
// Make the text track settings dialog not initialize.
|
||||
textTrackSettings: false
|
||||
});
|
||||
```
|
||||
|
||||
```css
|
||||
/* hide the captions settings item from the captions menu */
|
||||
/* Hide the captions settings item from the captions menu. */
|
||||
.vjs-texttrack-settings {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
## Text Track Precedence
|
||||
In general, the Descriptions tracks is of lower precedence than captions and subtitles.
|
||||
What this means for you?
|
||||
* If you are using the `default` attribute, videojs will choose the first track that is marked as `default` and turn it on. If There are multiple tracks marked `default`, it will try and turn on the first `captions` or `subtitles` track *before* any `descriptions` tracks.
|
||||
* This only applied to the emulated captions support, native text tracks behavior will change depending on the browser
|
||||
* If you select a given track from the menu, videojs will turn off all the other tracks of the same kind. This may seem like you can have both subtitles and captions turned on at the same time but unfortuantely, at this time we only support one track being displayed at a time.
|
||||
* This means that for emulated text tracks, we'll choose the first captions or subtitles track that is enabled to display.
|
||||
* When native text tracks are supported, we will still disable the other tracks of the same kind but it is possible that multiple text tracks are shown.
|
||||
* If a `descriptions` track is selected and subsequently a `subtitles` or `captions` track is selected, the `descriptions` track is disabled and its menu button is also disabled.
|
||||
* When enabling a track programmatically, there's not much checking that videojs does.
|
||||
* For emulated text tracks, when it's time to display the captions, video.js would choose the first track that's showing, again choosing `subtitles` or `captions` over `descriptions`, if necessary.
|
||||
* For native text tracks, this behavior depends on the browser. Some browsers will let you have multiple text tracks but others will disable all other tracks when a new one is selected.
|
||||
|
||||
In general, `"descriptions"` tracks are of lower precedence than `"captions"` and `"subtitles"`. What this mean for developers using Video.js?
|
||||
|
||||
* If you are using the `default` attribute, Video.js will choose the first track that is marked as `default` and turn it on. If there are multiple tracks marked `default`, it will turn on the first `"captions"` or `"subtitles"` track _before_ any `"descriptions"` tracks.
|
||||
* This only applied to the emulated text track support, native text tracks behavior will change depending on the browser.
|
||||
* If a track is selected from the menu, Video.js will turn off all the other tracks of the same kind. While this suggests Video.js supports both `"subtitles"` and `"captions"` being turned on simultaneously, this is currently not the case; Video.js only supports one track being displayed at a time.
|
||||
* This means that for emulated text tracks, Video.js will display the first enabled `"subtitles"` or `"captions"` track.
|
||||
* When native text tracks are supported, other tracks of the same kind will still be disabled, but it is possible that multiple text tracks are shown.
|
||||
* If a `"descriptions"` track is selected and subsequently a `"subtitles"` or `"captions"` track is selected, the `"descriptions"` track is disabled and its menu button is also disabled.
|
||||
* When enabling a track programmatically, Video.js performs minimal enforcement.
|
||||
* For emulated text tracks, Video.js chooses the first track that's `"showing"` - again choosing `"subtitles"` or `"captions"` over `"descriptions"`.
|
||||
* For native text tracks, this behavior depends on the browser. Some browsers will allow multiple text tracks, but others will disable all other tracks when a new one is selected.
|
||||
|
||||
## API
|
||||
|
||||
### `player.textTracks() -> TextTrackList`
|
||||
This is the main interface into the text tracks of the player.
|
||||
It return a TextTrackList which lists all the tracks on the player.
|
||||
For more complete information, refer to the [Video.js API docs](http://docs.videojs.com/docs/api/index.html).
|
||||
|
||||
### `player.remoteTextTracks() -> TextTrackList`
|
||||
This is a helper method to get a list of all the tracks that were created from `track` elements or that were added to the player by the `addRemoteTextTrack` method. All these tracks are removeable from the player, where-as not all tracks from `player.textTracks()` are necessarily removeable.
|
||||
### Remote Text Tracks
|
||||
|
||||
### `player.remoteTextTrackEls() -> HTMLTrackElementList`
|
||||
Another helper method, this is a list of all the `track` elements associated with the player. Both emulated or otherwise.
|
||||
As mentioned [above](#a-note-on-remote-text-tracks), remote text tracks represent the recommended API offered by Video.js as they can be removed.
|
||||
|
||||
### `player.addTextTrack(String kind, [String label [, String language]]) -> TextTrack`
|
||||
This is based on the [w3c spec API](http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack) and when given a kind and an optional label and language, will create a new text track for you to use.
|
||||
This method is intended for purely programmatic usage of tracks and has one important limitation:
|
||||
tracks created using this method *cannot* be removed. The native `addTextTrack` does not have a corresponding `removeTextTrack`, so, we actually discourage the usage of this method.
|
||||
* `Player#remoteTextTracks()`
|
||||
* `Player#remoteTextTrackEls()`
|
||||
* `Player#addRemoteTextTrack(Object options)`
|
||||
|
||||
### `player.addRemoteTextTrack(Object options) -> HTMLTrackElement`
|
||||
This function takes an options object that looks pretty similar to the track element and returns a HTMLTrackElement.
|
||||
This object has a `track` property on it which is the actual TextTrack object.
|
||||
This `TextTrack` object is equivalent to the one that can be returned from `player.addTextTrack` with the added bonus that it can be removed from the player.
|
||||
Internally, video.js will either add a `<track>` element for you, or emulate that depending on whether native text tracks are supported or not.
|
||||
The options available are:
|
||||
* `kind`
|
||||
* `label`
|
||||
* `language` (also `srclang`)
|
||||
* `id`
|
||||
* `src`
|
||||
Available options are the same as the [available `track` attributes](#track-attributes). And `language` is a supported option as an alias for the `srclang` attribute - either works here.
|
||||
|
||||
### `player.removeRemoteTextTrack(HTMLTrackElement|TextTrack)`
|
||||
This function takes either an HTMLTrackElement or a TextTrack object and removes it from the player.
|
||||
* `Player#removeRemoteTextTrack(HTMLTrackElement|TextTrack)`
|
||||
|
||||
### Text Tracks
|
||||
|
||||
It is generally recommended that you use _remote_ text tracks rather than these purely programmatic text tracks for the majority of use-cases.
|
||||
|
||||
* `Player#textTracks()`
|
||||
* `Player#addTextTrack(String kind, [String label [, String language]])`
|
||||
|
||||
> **Note:** Non-remote text tracks are intended for _purely programmatic usage_ of tracks and have the important limitation that they _cannot be removed once created_.
|
||||
>
|
||||
> The standard `addTextTrack` does **not** have a corresponding `removeTextTrack` method; so, we actually discourage the use of this method!
|
||||
|
||||
* `TextTrackList()`
|
||||
* `TextTrack()`
|
||||
|
||||
+19
-4
@@ -1,6 +1,21 @@
|
||||
# Tracks
|
||||
There are currently three types of tracks
|
||||
|
||||
* [AudioTracks](./audio-tracks.md) - allows the selection of alternative AudioTracks for a video
|
||||
* [VideoTracks](./video-tracks.md) - allows the selection of an alternative VideoTrack for a video
|
||||
* [TextTracks](./text-tracks.md) - Text Tracks are used to display subtitles and captions, and add a menu for navigating between chapters in a video.
|
||||
There are currently three types of tracks:
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Audio Tracks](#audio-tracks)
|
||||
* [Video Tracks](#video-tracks)
|
||||
* [Text Tracks](#text-tracks)
|
||||
|
||||
## [Audio Tracks](./audio-tracks.md)
|
||||
|
||||
Audio tracks allow the selection of alternate audio for a video.
|
||||
|
||||
## [Video Tracks](./video-tracks.md)
|
||||
|
||||
Video tracks allow the selection of alternate video content.
|
||||
|
||||
## [Text Tracks](./text-tracks.md)
|
||||
|
||||
Text tracks are used to display subtitles and captions and add a menu for navigating between chapters in a video.
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Problems with media formats](#problems-with-media-formats)
|
||||
* [Choosing a video format](#choosing-a-video-format)
|
||||
* [I want to have a single source and don't care about live/adaptive streaming:](#i-want-to-have-a-single-source-and-dont-care-about-liveadaptive-streaming)
|
||||
* [I need adaptive streaming or live streaming](#i-need-adaptive-streaming-or-live-streaming)
|
||||
* [Make sure you are using formats that Video.js can play:](#make-sure-you-are-using-formats-that-videojs-can-play)
|
||||
* [Make sure that the codec used in the file container is supported:](#make-sure-that-the-codec-used-in-the-file-container-is-supported)
|
||||
* [If you are using Flash videos:](#if-you-are-using-flash-videos)
|
||||
* [Problems when hosting media](#problems-when-hosting-media)
|
||||
* [Problems with fullscreen](#problems-with-fullscreen)
|
||||
* [Problems with playback](#problems-with-playback)
|
||||
* [Video.js Errors](#videojs-errors)
|
||||
* [vdata123456 errors](#vdata123456-errors)
|
||||
|
||||
## Problems with media formats
|
||||
|
||||
### Choosing a video format
|
||||
|
||||
#### I want to have a single source and don't care about live/adaptive streaming:
|
||||
|
||||
Most browsers now play MP4 with h264 video. If you want to have a single source, and neither live streaming
|
||||
nor adaptive streaming is a consideration, MP4 with h264 video and acc audio is a good choice.
|
||||
|
||||
#### I need adaptive streaming or live streaming
|
||||
|
||||
Use HLS with [videojs-contrib-hls][hls] or
|
||||
Use Dash with [videojs-contrib-dash][dash].
|
||||
HLS is more convenient as mobile browsers have native support.
|
||||
|
||||
### Make sure you are using formats that Video.js can play:
|
||||
|
||||
* Does your browser/OS support the type of media that you are trying to play?
|
||||
* Do you have a Video.js plugin that will add support for a media format to Video.js? For example:
|
||||
* [videojs-youtube][youtube]
|
||||
* [videojs-contrib-hls][hls]
|
||||
* [videojs-contrib-dash][dash]
|
||||
* Verify that you are using the correct [mime-type/content-type][media-types] for your videos.
|
||||
This is used to determine if Video.js can play a certain type of media.
|
||||
|
||||
### Make sure that the codec used in the file container is supported:
|
||||
|
||||
* MP4 in browsers typically only supports h264 video and MP3 or AAC audio
|
||||
* Some low end phones save video in 3GP format but give it an MP4 extension. These files will not play.
|
||||
|
||||
### If you are using Flash videos:
|
||||
|
||||
* Make sure that Flash is installed
|
||||
* Make sure the Flash tech is included with Video.js (in `video.js >= v6.0.0` it won't be by default, see [videojs-flash][flash])
|
||||
* Flash media include RTMP streams and FLV format media.
|
||||
* SWF is not a media format
|
||||
|
||||
## Problems when hosting media
|
||||
|
||||
* Your server must support byte-range requests as Chrome and Safari rely on them:
|
||||
* Most servers support this by default.
|
||||
* If you are proxying the media files via a server side script (PHP), this script must implement ranges. PHP does not do this by default.
|
||||
* The impact of not doing this ranges from seeking being broken to no playback at all (on iOS).
|
||||
* Your server must return the correct [mime-type/content-type][media-types] for the media being sent.
|
||||
* Your server must implement [CORS (cross-origin resource)][cors] headers if:
|
||||
* You are using [videojs-contrib-hls][hls], [videojs-contrib-dash][dash] and your media is served from a different domain than your page.
|
||||
* You are using [text tracks][text-tracks] (captions, subtitles, etc.) and they are being served from a different domain than your page.
|
||||
|
||||
## Problems with fullscreen
|
||||
|
||||
* If your player is in an iframe, the parent iframes must have the following attributes for fullscreen to be allowed:
|
||||
* `allowfullscreen`
|
||||
* `webkitallowfullscreen`
|
||||
* `mozallowfullscreen`
|
||||
|
||||
## Problems with playback
|
||||
|
||||
* Make sure that the media host supports byte-range requests, this could be breaking playback. See [Problems when hosting media][hosting-media] for more info.
|
||||
* If your media is taking a long time to start playback or the entire mediadownloads before playback:
|
||||
* It is likely that metadata for the media has not been included at the start of the media. In MP4 terms this is called
|
||||
the "moov atom". Many encoders are configured to do this by default, others may require you to choose
|
||||
a "fast start" or "optimize for streaming" option.
|
||||
|
||||
## Video.js Errors
|
||||
|
||||
### vdata123456 errors
|
||||
|
||||
This error is thrown when an element that is associated with a component is removed
|
||||
from the DOM but the event handlers associated with the element are not removed. This
|
||||
is almost always due to event listeners not being disposed when dispose is called on
|
||||
a component.
|
||||
|
||||
To fix this issue please make sure that all event listeners are cleaned up on dispose.
|
||||
|
||||
[hosting-media]: #problems-when-hosting-media
|
||||
|
||||
[text-tracks]: text-tracks.md
|
||||
|
||||
[hls]: https://github.com/videojs/videojs-contrib-hls
|
||||
|
||||
[dash]: https://github.com/videojs/videojs-contrib-dash
|
||||
|
||||
[youtube]: https://github.com/videojs/videojs-youtube
|
||||
|
||||
[flash]: https://github.com/videojs/videojs-flash
|
||||
|
||||
[media-types]: http://www.iana.org/assignments/media-types/media-types.xhtml#video
|
||||
|
||||
[cors]: http://enable-cors.org/
|
||||
+118
-50
@@ -1,70 +1,138 @@
|
||||
# Video Tracks
|
||||
|
||||
Video Tracks are a function of HTML5 video for providing a selection of alternative video tracks to the user, so that they can change type of video they want to watch. Video.js makes video tracks work across all browsers. There are currently six types of tracks:
|
||||
> **Note:** While video tracks [are a standard][spec-videotrack], there are no compatible implementations at this time. This is an experimental technology!
|
||||
|
||||
- **Alternative**: an alternative video representation of the main video track
|
||||
- **Captions**: The main video track with burned in captions
|
||||
- **Main**: the main video track
|
||||
- **Sign**: the main video track with added sign language overlay
|
||||
- **Subtitles**: the main video track with burned in subtitles
|
||||
- **Commentary**: the main video track with burned in commentary
|
||||
Video tracks are a feature of HTML5 video for providing alternate video tracks to the user, so they can change the type of video they want to watch. Video.js offers a cross-browser implementation of video tracks.
|
||||
|
||||
## Missing Funtionality
|
||||
- It is currently impossible to add VideoTracks in a non-programtic way
|
||||
- Literal switching of VideoTracks for playback is not handled by video.js and must be handled by something else. video.js only stores the track representation
|
||||
- There is currently no UI implementation of VideoTracks
|
||||
## Table of Contents
|
||||
|
||||
## Adding to Video.js
|
||||
* [Caveats](#caveats)
|
||||
* [Working with Video Tracks](#working-with-video-tracks)
|
||||
* [Add a Video Track to the Player](#add-a-video-track-to-the-player)
|
||||
* [Listen for a Video Track Becoming Enabled](#listen-for-a-video-track-becoming-enabled)
|
||||
* [Removing an Video Track from the Player](#removing-an-video-track-from-the-player)
|
||||
* [API](#api)
|
||||
* [videojs.VideoTrack](#videojsvideotrack)
|
||||
* [id](#id)
|
||||
* [kind](#kind)
|
||||
* [label](#label)
|
||||
* [language](#language)
|
||||
* [selected](#selected)
|
||||
|
||||
> Right now adding video tracks in the HTML is unsupported. Video Tracks must be added programatically.
|
||||
## Caveats
|
||||
|
||||
You must add video tracks [programatically](#api) for the time being.
|
||||
* It is not possible to add video tracks through HTML like you can with text tracks. They must be added programmatically.
|
||||
* Video.js only stores track representations. Switching video tracks for playback is _not handled by Video.js_ and must be handled elsewhere.
|
||||
|
||||
## Attributes
|
||||
Video Track propertites and settings
|
||||
## Working with Video Tracks
|
||||
|
||||
### kind
|
||||
One of the five track types listed above. Kind defaults to empty string if no kind is included, or an invalid kind is used.
|
||||
### Add a Video Track to the Player
|
||||
|
||||
### label
|
||||
The label for the track that will be show to the user, for example in a menu that list the different languages available for video tracks.
|
||||
|
||||
### language
|
||||
The two-letter code (valid BCP 47 language tag) for the language of the video track, for example "en" for English. A list of language codes is [available here](languages.md#language-codes).
|
||||
|
||||
### selected
|
||||
If this track should be playing or not. Trying to select more than one track will cause other tracks to be deselected.
|
||||
|
||||
## Interacting with Video Tracks
|
||||
### Doing something when a track becomes enabled
|
||||
When a new track is enabled (other than the main track) an event is fired on the `VideoTrackList` called `change` you can listen to that event and do something with it.
|
||||
Here's an example:
|
||||
```js
|
||||
// get the current players VideoTrackList object
|
||||
let tracks = player.videoTracks();
|
||||
// Create a player.
|
||||
var player = videojs('my-player');
|
||||
|
||||
// listen to the change event
|
||||
tracks.addEventListener('change', function() {
|
||||
// get the currently selected track
|
||||
let index = tracks.selectedIndex;
|
||||
let track = tracks[index];
|
||||
|
||||
// print the currently selected track
|
||||
console.log(track.label);
|
||||
// Create a track object.
|
||||
var track = new videojs.VideoTrack({
|
||||
id: 'my-alternate-video-track',
|
||||
kind: 'commentary',
|
||||
label: 'Director\'s Commentary',
|
||||
language: 'en'
|
||||
});
|
||||
|
||||
// Add the track to the player's video track list.
|
||||
player.videoTracks().addTrack(track);
|
||||
```
|
||||
|
||||
### Listen for a Video Track Becoming Enabled
|
||||
|
||||
When a track is enabled or disabled on an `VideoTrackList`, a `change` event will be fired. You can listen for that event and do something with it.
|
||||
|
||||
> NOTE: The initial `VideoTrack` selection (usually the main track that is selected) should not fire a `change` event.
|
||||
|
||||
```js
|
||||
// Get the current player's VideoTrackList object.
|
||||
var videoTrackList = player.videoTracks();
|
||||
|
||||
// Listen to the "change" event.
|
||||
videoTrackList.addEventListener('change', function() {
|
||||
|
||||
// Log the currently enabled VideoTrack label.
|
||||
for (var i = 0; i < videoTrackList.length; i++) {
|
||||
var track = videoTrackList[i];
|
||||
|
||||
if (track.enabled) {
|
||||
videojs.log(track.label);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Removing an Video Track from the Player
|
||||
|
||||
Assuming a player already exists and has an video track that you want to remove, you might do something like the following:
|
||||
|
||||
```js
|
||||
// Get the track we created in an earlier example.
|
||||
var track = player.videoTracks().getTrackById('my-alternate-video-track');
|
||||
|
||||
// Remove it from the video track list.
|
||||
player.videoTracks().removeTrack(track);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `player.videoTracks() -> VideoTrackList`
|
||||
This is the main interface into the video tracks of the player.
|
||||
It returns an VideoTrackList which is an array like object that contains all the `VideoTrack` on the player.
|
||||
For more complete information, refer to the [Video.js API docs](http://docs.videojs.com/docs/api/index.html), specifically:
|
||||
|
||||
### `player.videoTracks().addTrack(VideoTrack)`
|
||||
Add an existing VideoTrack to the players internal list of VideoTracks.
|
||||
* `Player#videoTracks`
|
||||
* `VideoTrackList`
|
||||
* `VideoTrack`
|
||||
|
||||
### `player.videoTracks().removeTrack(VideoTrack)`
|
||||
Remove a track from the VideoTrackList currently on the player. if no track exists this will do nothing.
|
||||
### `videojs.VideoTrack`
|
||||
|
||||
### `player.videoTracks().selectedIndex`
|
||||
The current index for the selected track
|
||||
This class is based on [the `VideoTrack` standard][spec-videotrack] and can be used to create new video track objects.
|
||||
|
||||
Each property below is available as an option to the `VideoTrack` constructor.
|
||||
|
||||
#### `id`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-id)
|
||||
|
||||
A unique identifier for this track. Video.js will generate one if not given.
|
||||
|
||||
#### `kind`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind)
|
||||
|
||||
Video.js supports standard `kind` values for `VideoTracks`:
|
||||
|
||||
* `"alternative"`: A possible alternative to the main track.
|
||||
* `"captions"`: The main video track with burned in captions
|
||||
* `"main"`: The main video track.
|
||||
* `"sign"`: The main video track with added sign language overlay.
|
||||
* `"subtitles"`: The main video track with burned in subtitles.
|
||||
* `"commentary"`: The main video track with burned in commentary.
|
||||
* `""` (default): No explicit kind, or the kind given by the track's metadata is not recognized by the user agent.
|
||||
|
||||
#### `label`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-label)
|
||||
|
||||
The label for the track that will be shown to the user. For example, in a menu that lists the different captions available as alternate video tracks.
|
||||
|
||||
#### `language`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-language)
|
||||
|
||||
The valid [BCP 47](https://tools.ietf.org/html/bcp47) code for the language of the video track, e.g. `"en"` for English or `"es"` for Spanish.
|
||||
|
||||
For supported language translations, please see the [languages folder (/lang)](https://github.com/videojs/video.js/tree/master/lang) folder located in the Video.js root and refer to the [languages guide](./languages.md) for more information on languages in Video.js.
|
||||
|
||||
#### `selected`
|
||||
|
||||
> [standard definition](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-selected)
|
||||
|
||||
Whether or not this track should be playing. Only one video track may be selected at a time.
|
||||
|
||||
[spec-videotrack]: https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# Usage examples for the functions on videojs
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [videojs()](#videojs)
|
||||
* [options](#options)
|
||||
* [getComponent()](#getcomponent)
|
||||
* [registerComponent()](#registercomponent)
|
||||
* [getTech()](#gettech)
|
||||
* [registerTech()](#registertech)
|
||||
* [extend()](#extend)
|
||||
* [mergeOptions()](#mergeoptions)
|
||||
* [bind()](#bind)
|
||||
* [plugin()](#plugin)
|
||||
* [xhr()](#xhr)
|
||||
|
||||
## `videojs()`
|
||||
|
||||
```js
|
||||
var myPlayer = videojs('my_video_id');
|
||||
```
|
||||
|
||||
## `options`
|
||||
|
||||
```js
|
||||
videojs.options.autoplay = true
|
||||
// -> all players will autoplay by default
|
||||
```
|
||||
|
||||
## `getComponent()`
|
||||
|
||||
```js
|
||||
var VjsButton = videojs.getComponent('Button');
|
||||
// Create a new instance of the component
|
||||
var myButton = new VjsButton(myPlayer);
|
||||
```
|
||||
|
||||
## `registerComponent()`
|
||||
|
||||
```js
|
||||
// Get a component to subclass
|
||||
var VjsButton = videojs.getComponent('Button');
|
||||
// Subclass the component (see 'extend' doc for more info)
|
||||
var MySpecialButton = videojs.extend(VjsButton, {});
|
||||
// Register the new component
|
||||
VjsButton.registerComponent('MySepcialButton', MySepcialButton);
|
||||
// (optionally) add the new component as a default player child
|
||||
myPlayer.addChild('MySepcialButton');
|
||||
```
|
||||
|
||||
## `getTech()`
|
||||
|
||||
```js
|
||||
var Html5 = videojs.getTech('Html5');
|
||||
// Create a new instance of the component
|
||||
var html5 = new Html5(options);
|
||||
```
|
||||
|
||||
## `registerTech()`
|
||||
|
||||
```js
|
||||
// get the Html5 Tech
|
||||
var Html5 = videojs.getTech('Html5');
|
||||
var MyTech = videojs.extend(Html5, {});
|
||||
// Register the new Tech
|
||||
VjsButton.registerTech('Tech', MyTech);
|
||||
var player = videojs('myplayer', {
|
||||
techOrder: ['myTech', 'html5']
|
||||
});
|
||||
```
|
||||
|
||||
## `extend()`
|
||||
|
||||
```js
|
||||
// Create a basic javascript 'class'
|
||||
function MyClass(name) {
|
||||
// Set a property at initialization
|
||||
this.myName = name;
|
||||
}
|
||||
// Create an instance method
|
||||
MyClass.prototype.sayMyName = function() {
|
||||
alert(this.myName);
|
||||
};
|
||||
// Subclass the exisitng class and change the name
|
||||
// when initializing
|
||||
var MySubClass = videojs.extend(MyClass, {
|
||||
constructor: function(name) {
|
||||
// Call the super class constructor for the subclass
|
||||
MyClass.call(this, name)
|
||||
}
|
||||
});
|
||||
// Create an instance of the new sub class
|
||||
var myInstance = new MySubClass('John');
|
||||
myInstance.sayMyName(); // -> should alert "John"
|
||||
```
|
||||
|
||||
## `mergeOptions()`
|
||||
|
||||
```js
|
||||
var defaultOptions = {
|
||||
foo: true,
|
||||
bar: {
|
||||
a: true,
|
||||
b: [1,2,3]
|
||||
}
|
||||
};
|
||||
var newOptions = {
|
||||
foo: false,
|
||||
bar: {
|
||||
b: [4,5,6]
|
||||
}
|
||||
};
|
||||
var result = videojs.mergeOptions(defaultOptions, newOptions);
|
||||
// result.foo = false;
|
||||
// result.bar.a = true;
|
||||
// result.bar.b = [4,5,6];
|
||||
```
|
||||
|
||||
## `bind()`
|
||||
|
||||
```js
|
||||
var someClass = function() {};
|
||||
var someObj = new someClass();
|
||||
videojs.bind(someObj, function() {
|
||||
// this will be the context of someObj here
|
||||
});
|
||||
```
|
||||
|
||||
## `plugin()`
|
||||
|
||||
**See the [plugin guide](plugins.md) in the docs for a more detailed example**
|
||||
|
||||
```js
|
||||
// Make a plugin that alerts when the player plays
|
||||
videojs.plugin('myPlugin', function(myPluginOptions) {
|
||||
myPluginOptions = myPluginOptions || {};
|
||||
|
||||
var player = this;
|
||||
var alertText = myPluginOptions.text || 'Player is playing!'
|
||||
|
||||
player.on('play', function() {
|
||||
alert(alertText);
|
||||
});
|
||||
});
|
||||
// USAGE EXAMPLES
|
||||
// EXAMPLE 1: New player with plugin options, call plugin immediately
|
||||
var player1 = videojs('idOne', {
|
||||
myPlugin: {
|
||||
text: 'Custom text!'
|
||||
}
|
||||
});
|
||||
// Click play
|
||||
// --> Should alert 'Custom text!'
|
||||
// EXAMPLE 3: New player, initialize plugin later
|
||||
var player3 = videojs('idThree');
|
||||
// Click play
|
||||
// --> NO ALERT
|
||||
// Click pause
|
||||
// Initialize plugin using the plugin function on the player instance
|
||||
player3.myPlugin({
|
||||
text: 'Plugin added later!'
|
||||
});
|
||||
// Click play
|
||||
// --> Should alert 'Plugin added later!'
|
||||
```
|
||||
|
||||
## `xhr()`
|
||||
|
||||
```js
|
||||
videojs.xhr({
|
||||
body: someJSONString,
|
||||
uri: "/foo",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}, function (err, resp, body) {
|
||||
// check resp.statusCode
|
||||
});
|
||||
```
|
||||
+76
-18
@@ -1,37 +1,95 @@
|
||||
[Video.js homepage](http://videojs.com)
|
||||
# [Video.js][vjs-website] Documentation
|
||||
|
||||
<h1>Video.js Documentation</h1>
|
||||
There are two categories of docs: [Guides](guides) and [API docs][api].
|
||||
|
||||
There are two categories of docs: [Guides](./guides/) and [API docs](./api/). Guides explain general topics and use cases (e.g. setup). API docs are automatically generated from the codebase and give specific details about functions, properties, and events.
|
||||
Guides explain general topics and use cases (e.g. setup). API docs are automatically generated from the codebase and give specific details about functions, properties, and events.
|
||||
|
||||
(Corrections and additions welcome)
|
||||
## Table of Contents
|
||||
|
||||
## Guides
|
||||
* [Resolving Issues](#resolving-issues)
|
||||
* [FAQ](#faq)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
* [Guides](#guides)
|
||||
* [Getting Started](#getting-started)
|
||||
* [Setup](#setup)
|
||||
* [Player Workflows](#player-workflows)
|
||||
* [Options](#options)
|
||||
* [Tracks](#tracks)
|
||||
* [Customizing](#customizing)
|
||||
* [Skins](#skins)
|
||||
* [Plugins](#plugins)
|
||||
* [Components](#components)
|
||||
* [Tech](#tech)
|
||||
* [Languages](#languages)
|
||||
* [Hooks](#hooks)
|
||||
* [API Docs](#api-docs)
|
||||
|
||||
## Resolving Issues
|
||||
|
||||
### [FAQ](guides/faq.md)
|
||||
|
||||
The frequently asked questions for video.js.
|
||||
|
||||
### [Troubleshooting](guides/troubleshooting.md)
|
||||
|
||||
Troubleshooting help for video.js.
|
||||
|
||||
## [Guides](guides)
|
||||
|
||||
### Getting Started
|
||||
|
||||
* [Setup](./guides/setup.md) - The setup documentation gives a deeper view of the additional methods you can use to trigger the player setup.
|
||||
#### [Setup](guides/setup.md)
|
||||
|
||||
* [Options](./guides/options.md) - There are a number of options that can be used to change how the player behaves, starting with the HTML5 media options like autoplay and preload, and expanding to Video.js specific options.
|
||||
The setup guide covers all methods of setting up Video.js players.
|
||||
|
||||
* [Tracks](./guides/tracks.md) - Tracks are used for displaying text information over a video, selecting different audio tracks for a video, or selected different video tracks for a video.
|
||||
#### [Player Workflows](guides/player-workflows.md)
|
||||
|
||||
After mastering the basics of setup move over to this guide for some more advanced player workflows.
|
||||
|
||||
#### [Options](guides/options.md)
|
||||
|
||||
There are a number of options that can be used to change how the player behaves, starting with the HTML5 media options like autoplay and preload, and expanding to Video.js specific options.
|
||||
|
||||
#### [Tracks](guides/tracks.md)
|
||||
|
||||
Tracks are used for displaying text information over a video, selecting different audio tracks for a video, or selecting different video tracks.
|
||||
|
||||
### Customizing
|
||||
|
||||
* [API](./guides/api.md) - The Video.js API allows you to control the video through javascript or trigger event listeners, whether the video is playing through HTML5, flash, or another playback technology.
|
||||
#### [Skins](guides/skins.md)
|
||||
|
||||
* [Skins](./guides/skins.md) - You can change the look of the player across playback technologies just by editing a CSS file. The skins documentation gives you a intro to how the HTML and CSS of the default skin is put together.
|
||||
You can change the look of the player across playback technologies just by editing a CSS file. The skins documentation gives you a intro to how the HTML and CSS of the default skin is put together. For a list of skins you can check the [video.js wiki][skins-list].
|
||||
|
||||
* [Tech](./guides/tech.md) - A 'playback technology' is the term we're using to represent HTML5 video, Flash, and other video plugins, as well as other players like the YouTube player. Basically anything that has a unique API to audio or video. Additional playback technologies can be added relatively easily.
|
||||
#### [Plugins](guides/plugins.md)
|
||||
|
||||
* [Plugins](./guides/plugins.md) - You can package up interesting Video.js customizations and reuse them elsewhere. Find out how to build your own plugin or use one created by someone else.
|
||||
You can package up interesting Video.js customizations and reuse them elsewhere. Find out how to build your own plugin or [use one created by someone else][plugins-list].
|
||||
|
||||
### Resources
|
||||
#### [Components](guides/components.md)
|
||||
|
||||
* [Glossary](./guides/glossary.md) - Some helpful definitions.
|
||||
Video.js is built around a collection of components. These are the building blocks of the player UI.
|
||||
|
||||
* [Removing Players](./guides/removing-players.md) - Helpful for using VideoJS in single page apps.
|
||||
#### [Tech](guides/tech.md)
|
||||
|
||||
## API Docs
|
||||
- The most relevant API doc is the [player API doc](./api/vjs.Player.md).
|
||||
- [Full list of API Docs](./api/)
|
||||
A "tech" is the shorthand we're using to describe any video playback technology - be it HTML5 video, Flash, . Basically anything that has a unique API to audio or video. Additional playback technologies can be added relatively easily.
|
||||
|
||||
#### [Languages](guides/languages.md)
|
||||
|
||||
Video.js has multi-language support! Follow this guide to see how you can contribute to and use languages.
|
||||
|
||||
#### [Hooks](guides/hooks.md)
|
||||
|
||||
A "hook" is functionality that wants to do when videojs creates a player. Right now only `beforesetup` and `setup` are supported. See the guide for more information on that.
|
||||
|
||||
## [API Docs][api]
|
||||
|
||||
You can refer to the [full list of API docs][api], but the most relevant API doc is for the [Player][api-player].
|
||||
|
||||
[plugins-list]: http://videojs.com/plugins
|
||||
|
||||
[skins-list]: https://github.com/videojs/video.js/wiki/Skins
|
||||
|
||||
[api]: http://docs.videojs.com/docs/api/index.html
|
||||
|
||||
[api-player]: http://docs.videojs.com/docs/api/player.html
|
||||
|
||||
[vjs-website]: http://videojs.com
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+44
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Play": "Wiedergabe",
|
||||
"Pause": "Pause",
|
||||
"Replay": "Erneut abspielen",
|
||||
"Current Time": "Aktueller Zeitpunkt",
|
||||
"Duration Time": "Dauer",
|
||||
"Remaining Time": "Verbleibende Zeit",
|
||||
@@ -30,5 +31,47 @@
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Durch Drücken der Esc-Taste bzw. Betätigung der Schaltfläche \"Schließen\" wird dieses modale Fenster geschlossen.",
|
||||
", opens captions settings dialog": ", öffnet Einstellungen für Untertitel",
|
||||
", opens subtitles settings dialog": ", öffnet Einstellungen für Untertitel",
|
||||
", selected": " (ausgewählt)"
|
||||
", selected": ", ausgewählt",
|
||||
"captions settings": "Untertiteleinstellungen",
|
||||
"subititles settings": "Untertiteleinstellungen",
|
||||
"descriptions settings": "Einstellungen für Beschreibungen",
|
||||
"Close Modal Dialog": "Modales Fenster schließen",
|
||||
"Descriptions": "Beschreibungen",
|
||||
"descriptions off": "Beschreibungen aus",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Die Entschlüsselungsschlüssel für den verschlüsselten Medieninhalt sind nicht verfügbar.",
|
||||
", opens descriptions settings dialog": ", öffnet Einstellungen für Beschreibungen",
|
||||
"Audio Track": "Tonspur",
|
||||
"Text": "Schrift",
|
||||
"White": "Weiß",
|
||||
"Black": "Schwarz",
|
||||
"Red": "Rot",
|
||||
"Green": "Grün",
|
||||
"Blue": "Blau",
|
||||
"Yellow": "Gelb",
|
||||
"Magenta": "Magenta",
|
||||
"Cyan": "Türkis",
|
||||
"Background": "Hintergrund",
|
||||
"Window": "Fenster",
|
||||
"Transparent": "Durchsichtig",
|
||||
"Semi-Transparent": "Halbdurchsichtig",
|
||||
"Opaque": "Undurchsictig",
|
||||
"Font Size": "Schriftgröße",
|
||||
"Text Edge Style": "Textkantenstil",
|
||||
"None": "Kein",
|
||||
"Raised": "Erhoben",
|
||||
"Depressed": "Gedrückt",
|
||||
"Uniform": "Uniform",
|
||||
"Dropshadow": "Schlagschatten",
|
||||
"Font Family": "Schristfamilie",
|
||||
"Proportional Sans-Serif": "Proportionale Sans-Serif",
|
||||
"Monospace Sans-Serif": "Monospace Sans-Serif",
|
||||
"Proportional Serif": "Proportionale Serif",
|
||||
"Monospace Serif": "Monospace Serif",
|
||||
"Casual": "Zwanglos",
|
||||
"Script": "Schreibeschrift",
|
||||
"Small Caps": "Small-Caps",
|
||||
"Defaults": "Standardeinstellungen",
|
||||
"Done": "Fertig",
|
||||
"Caption Settings Dialog": "Einstellungsdialog für Untertitel",
|
||||
"Beginning of dialog window. Escape will cancel and close the window.": "Anfang des Dialogfensters. Esc bricht ab und schließt das Fenster."
|
||||
}
|
||||
|
||||
+7
-1
@@ -18,17 +18,23 @@
|
||||
"Captions": "Λεζάντες",
|
||||
"captions off": "απόκρυψη λεζάντων",
|
||||
"Chapters": "Κεφάλαια",
|
||||
"Close Modal Dialog": "Κλείσιμο παραθύρου",
|
||||
"Descriptions": "Περιγραφές",
|
||||
"descriptions off": "απόκρυψη περιγραφών",
|
||||
"Audio Track": "Ροή ήχου",
|
||||
"You aborted the media playback": "Ακυρώσατε την αναπαραγωγή",
|
||||
"A network error caused the media download to fail part-way.": "Ένα σφάλμα δικτύου προκάλεσε την αποτυχία μεταφόρτωσης του αρχείου προς αναπαραγωγή.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Το αρχείο προς αναπαραγωγή δεν ήταν δυνατό να φορτωθεί είτε γιατί υπήρξε σφάλμα στον διακομιστή ή το δίκτυο, είτε γιατί ο τύπος του αρχείου δεν υποστηρίζεται.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Η αναπαραγωγή ακυρώθηκε είτε λόγω κατεστραμμένου αρχείου, είτε γιατί το αρχείο απαιτεί λειτουργίες που δεν υποστηρίζονται από το πρόγραμμα περιήγησης που χρησιμοποιείτε.",
|
||||
"No compatible source was found for this media.": "Δεν βρέθηκε συμβατή πηγή αναπαραγωγής για το συγκεκριμένο αρχείο.",
|
||||
"Play video": "Αναπαραγωγή βίντεο",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Το αρχείο προς αναπαραγωγή είναι κρυπτογραφημένo και δεν υπάρχουν τα απαραίτητα κλειδιά αποκρυπτογράφησης.",
|
||||
"Play Video": "Αναπαραγωγή βίντεο",
|
||||
"Close": "Κλείσιμο",
|
||||
"Modal Window": "Aναδυόμενο παράθυρο",
|
||||
"This is a modal window": "Το παρών είναι ένα αναδυόμενο παράθυρο",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Αυτό το παράθυρο μπορεί να εξαφανιστεί πατώντας το πλήκτρο Escape ή πατώντας το κουμπί κλεισίματος.",
|
||||
", opens captions settings dialog": ", εμφανίζει τις ρυθμίσεις για τις λεζάντες",
|
||||
", opens subtitles settings dialog": ", εμφανίζει τις ρυθμίσεις για τους υπότιτλους",
|
||||
", opens descriptions settings dialog": ", εμφανίζει τις ρυθμίσεις για τις περιγραφές",
|
||||
", selected": ", επιλεγμένο"
|
||||
}
|
||||
|
||||
+39
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Play": "Play",
|
||||
"Pause": "Pause",
|
||||
"Replay": "Replay",
|
||||
"Current Time": "Current Time",
|
||||
"Duration Time": "Duration Time",
|
||||
"Remaining Time": "Remaining Time",
|
||||
@@ -27,6 +28,7 @@
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
|
||||
"No compatible source was found for this media.": "No compatible source was found for this media.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
|
||||
"Play Video": "Play Video",
|
||||
"Close": "Close",
|
||||
"Modal Window": "Modal Window",
|
||||
@@ -35,5 +37,41 @@
|
||||
", opens captions settings dialog": ", opens captions settings dialog",
|
||||
", opens subtitles settings dialog": ", opens subtitles settings dialog",
|
||||
", opens descriptions settings dialog": ", opens descriptions settings dialog",
|
||||
", selected": ", selected"
|
||||
", selected": ", selected",
|
||||
"captions settings": "captions settings",
|
||||
"subititles settings": "subititles settings",
|
||||
"descriptions settings": "descriptions settings",
|
||||
"Text": "Text",
|
||||
"White": "White",
|
||||
"Black": "Black",
|
||||
"Red": "Red",
|
||||
"Green": "Green",
|
||||
"Blue": "Blue",
|
||||
"Yellow": "Yellow",
|
||||
"Magenta": "Magenta",
|
||||
"Cyan": "Cyan",
|
||||
"Background": "Background",
|
||||
"Window": "Window",
|
||||
"Transparent": "Transparent",
|
||||
"Semi-Transparent": "Semi-Transparent",
|
||||
"Opaque": "Opaque",
|
||||
"Font Size": "Font Size",
|
||||
"Text Edge Style": "Text Edge Style",
|
||||
"None": "None",
|
||||
"Raised": "Raised",
|
||||
"Depressed": "Depressed",
|
||||
"Uniform": "Uniform",
|
||||
"Dropshadow": "Dropshadow",
|
||||
"Font Family": "Font Family",
|
||||
"Proportional Sans-Serif": "Proportional Sans-Serif",
|
||||
"Monospace Sans-Serif": "Monospace Sans-Serif",
|
||||
"Proportional Serif": "Proportional Serif",
|
||||
"Monospace Serif": "Monospace Serif",
|
||||
"Casual": "Casual",
|
||||
"Script": "Script",
|
||||
"Small Caps": "Small Caps",
|
||||
"Defaults": "Defaults",
|
||||
"Done": "Done",
|
||||
"Caption Settings Dialog": "Caption Settings Dialog",
|
||||
"Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window."
|
||||
}
|
||||
|
||||
+18
-3
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Play": "Lecture",
|
||||
"Pause": "Pause",
|
||||
"Replay": "Revoir",
|
||||
"Current Time": "Temps actuel",
|
||||
"Duration Time": "Durée",
|
||||
"Remaining Time": "Temps restant",
|
||||
@@ -15,12 +16,26 @@
|
||||
"Playback Rate": "Vitesse de lecture",
|
||||
"Subtitles": "Sous-titres",
|
||||
"subtitles off": "Sous-titres désactivés",
|
||||
"Captions": "Sous-titres",
|
||||
"captions off": "Sous-titres désactivés",
|
||||
"Captions": "Sous-titres transcrits",
|
||||
"captions off": "Sous-titres transcrits désactivés",
|
||||
"Chapters": "Chapitres",
|
||||
"Close Modal Dialog": "Fermer la boîte de dialogue modale",
|
||||
"Descriptions": "Descriptions",
|
||||
"descriptions off": "descriptions désactivées",
|
||||
"Audio Track": "Piste audio",
|
||||
"You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.",
|
||||
"A network error caused the media download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.",
|
||||
"No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo."
|
||||
"No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.",
|
||||
"Play Video": "Lire la vidéo",
|
||||
"Close": "Fermer",
|
||||
"Modal Window": "Fenêtre modale",
|
||||
"This is a modal window": "Ceci est une fenêtre modale",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.",
|
||||
", opens captions settings dialog": ", ouvrir les paramètres des sous-titres transcrits",
|
||||
", opens subtitles settings dialog": ", ouvrir les paramètres des sous-titres",
|
||||
", opens descriptions settings dialog": ", ouvrir les paramètres des descriptions",
|
||||
", selected": ", sélectionné"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"Play": "Reproduzir",
|
||||
"Pause": "Parar",
|
||||
"Replay": "Reiniciar",
|
||||
"Current Time": "Tempo Atual",
|
||||
"Duration Time": "Duração",
|
||||
"Remaining Time": "Tempo Restante",
|
||||
"Stream Type": "Tipo de Stream",
|
||||
"LIVE": "EM DIRETO",
|
||||
"Loaded": "Carregado",
|
||||
"Progress": "Progresso",
|
||||
"Fullscreen": "Ecrã inteiro",
|
||||
"Non-Fullscreen": "Ecrã normal",
|
||||
"Mute": "Desativar som",
|
||||
"Unmute": "Ativar som",
|
||||
"Playback Rate": "Velocidade de reprodução",
|
||||
"Subtitles": "Legendas",
|
||||
"subtitles off": "desativar legendas",
|
||||
"Captions": "Anotações",
|
||||
"captions off": "desativar anotações",
|
||||
"Chapters": "Capítulos",
|
||||
"Close Modal Dialog": "Fechar Janela Modal",
|
||||
"Descriptions": "Descrições",
|
||||
"descriptions off": "desativar descrições",
|
||||
"Audio Track": "Faixa Áudio",
|
||||
"You aborted the media playback": "Parou a reprodução do vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo falhar parcialmente.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "O vídeo não pode ser carregado, ou porque houve um problema na rede ou no servidor, ou porque formato do vídeo não é compatível.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A reprodução foi interrompida por um problema com o vídeo ou porque o formato não é compatível com o seu navegador.",
|
||||
"No compatible source was found for this media.": "Não foi encontrada uma fonte de vídeo compatível.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "O vídeo está encriptado e não há uma chave para o desencriptar.",
|
||||
"Play Video": "Reproduzir Vídeo",
|
||||
"Close": "Fechar",
|
||||
"Modal Window": "Janela Modal",
|
||||
"This is a modal window": "Isto é uma janela modal",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Esta modal pode ser fechada pressionando a tecla ESC ou ativando o botão de fechar.",
|
||||
", opens captions settings dialog": ", abre janela com definições de legendas",
|
||||
", opens subtitles settings dialog": ", abre janela com definições de legendas",
|
||||
", opens descriptions settings dialog": ", abre janela com definições de descrições",
|
||||
", selected": ", seleccionado"
|
||||
}
|
||||
+15
-1
@@ -18,9 +18,23 @@
|
||||
"Captions": "Подписи",
|
||||
"captions off": "Подписи выкл.",
|
||||
"Chapters": "Главы",
|
||||
"Close Modal Dialog": "Закрыть модальное окно",
|
||||
"Descriptions": "Описания",
|
||||
"descriptions off": "описания выкл.",
|
||||
"Audio Track": "Звуковая дорожка",
|
||||
"You aborted the media playback": "Вы прервали воспроизведение видео",
|
||||
"A network error caused the media download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.",
|
||||
"No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют."
|
||||
"No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Видео в зашифрованном виде, и у нас нет ключей для расшифровки.",
|
||||
"Play Video": "Воспроизвести видео",
|
||||
"Close": "Закрыть",
|
||||
"Modal Window": "Модальное окно",
|
||||
"This is a modal window": "Это модальное окно.",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Модальное окно можно закрыть нажав Esc или кнопку закрытия окна.",
|
||||
", opens captions settings dialog": ", откроется диалог настройки подписей",
|
||||
", opens subtitles settings dialog": ", откроется диалог настройки субтитров",
|
||||
", opens descriptions settings dialog": ", откроется диалог настройки описаний",
|
||||
", selected": ", выбрано"
|
||||
}
|
||||
|
||||
+47
-4
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Play": "Oynat",
|
||||
"Pause": "Duraklat",
|
||||
"Replay": "Yeniden Oynat",
|
||||
"Current Time": "Süre",
|
||||
"Duration Time": "Toplam Süre",
|
||||
"Remaining Time": "Kalan Süre",
|
||||
@@ -15,20 +16,62 @@
|
||||
"Playback Rate": "Oynatma Hızı",
|
||||
"Subtitles": "Altyazı",
|
||||
"subtitles off": "Altyazı Kapalı",
|
||||
"Captions": "Ek Açıklamalar",
|
||||
"captions off": "Ek Açıklamalar Kapalı",
|
||||
"Captions": "Altyazı",
|
||||
"captions off": "Altyazı Kapalı",
|
||||
"Chapters": "Bölümler",
|
||||
"Close Modal Dialog": "Dialogu Kapat",
|
||||
"Descriptions": "Açıklamalar",
|
||||
"descriptions off": "Açıklamalar kapalı",
|
||||
"Audio Track": "Ses Dosyası",
|
||||
"You aborted the media playback": "Video oynatmayı iptal ettiniz",
|
||||
"A network error caused the media download to fail part-way.": "Video indirilirken bağlantı sorunu oluştu.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video oynatılamadı, ağ ya da sunucu hatası veya belirtilen format desteklenmiyor.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Tarayıcınız desteklemediği için videoda hata oluştu.",
|
||||
"No compatible source was found for this media.": "Video için kaynak bulunamadı.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Video, şifrelenmiş bir kaynaktan geliyor ve oynatmak için gerekli anahtar bulunamadı.",
|
||||
"Play Video": "Videoyu Oynat",
|
||||
"Close": "Kapat",
|
||||
"Modal Window": "Modal Penceresi",
|
||||
"This is a modal window": "Bu bir modal penceresidir",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Bu modal ESC tuşuna basarak ya da kapata tıklanarak kapatılabilir.",
|
||||
", opens captions settings dialog": ", ek açıklama ayarları menüsünü açar",
|
||||
", opens captions settings dialog": ", altyazı ayarları menüsünü açar",
|
||||
", opens subtitles settings dialog": ", altyazı ayarları menüsünü açar",
|
||||
", selected": ", seçildi"
|
||||
", opens descriptions settings dialog": ", açıklama ayarları menüsünü açar",
|
||||
", selected": ", seçildi",
|
||||
"captions settings": "altyazı ayarları",
|
||||
"subititles settings": "altyazı ayarları",
|
||||
"descriptions settings": "açıklama ayarları",
|
||||
"Text": "Yazı",
|
||||
"White": "Beyaz",
|
||||
"Black": "Siyah",
|
||||
"Red": "Kırmızı",
|
||||
"Green": "Yeşil",
|
||||
"Blue": "Mavi",
|
||||
"Yellow": "Sarı",
|
||||
"Magenta": "Macenta",
|
||||
"Cyan": "Açık Mavi (Camgöbeği)",
|
||||
"Background": "Arka plan",
|
||||
"Window": "Pencere",
|
||||
"Transparent": "Saydam",
|
||||
"Semi-Transparent": "Yarı-Saydam",
|
||||
"Opaque": "Mat",
|
||||
"Font Size": "Yazı Boyutu",
|
||||
"Text Edge Style": "Yazı Kenarlıkları",
|
||||
"None": "Hiçbiri",
|
||||
"Raised": "Kabartılmış",
|
||||
"Depressed": "Yassı",
|
||||
"Uniform": "Düz",
|
||||
"Dropshadow": "Gölgeli",
|
||||
"Font Family": "Yazı Tipi",
|
||||
"Proportional Sans-Serif": "Orantılı Sans-Serif",
|
||||
"Monospace Sans-Serif": "Eşaralıklı Sans-Serif",
|
||||
"Proportional Serif": "Orantılı Serif",
|
||||
"Monospace Serif": "Eşaralıklı Serif",
|
||||
"Casual": "Gündelik",
|
||||
"Script": "El Yazısı",
|
||||
"Small Caps": "Küçük Boyutlu Büyük Harfli",
|
||||
"Defaults": "Varsayılan Ayarlar",
|
||||
"Done": "Tamam",
|
||||
"Caption Settings Dialog": "Altyazı Ayarları Menüsü",
|
||||
"Beginning of dialog window. Escape will cancel and close the window.": "Diyalog penceresinin başlangıcı. ESC tuşu işlemi iptal edip pencereyi kapatacaktır."
|
||||
}
|
||||
|
||||
+15
-1
@@ -18,9 +18,23 @@
|
||||
"Captions": "Підписи",
|
||||
"captions off": "Без підписів",
|
||||
"Chapters": "Розділи",
|
||||
"Close Modal Dialog": "Закрити модальний діалог",
|
||||
"Descriptions": "Описи",
|
||||
"descriptions off": "Без описів",
|
||||
"Audio Track": "Аудіодоріжка",
|
||||
"You aborted the media playback": "Ви припинили відтворення відео",
|
||||
"A network error caused the media download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.",
|
||||
"No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні."
|
||||
"No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Відео в зашифрованому вигляді, і ми не маємо ключі для розшифровки.",
|
||||
"Play Video": "Відтворити відео",
|
||||
"Close": "Закрити",
|
||||
"Modal Window": "Модальне вікно",
|
||||
"This is a modal window": "Це модальне вікно.",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Модальне вікно можна закрити, натиснувши клавішу Esc або кнопку закриття вікна.",
|
||||
", opens captions settings dialog": ", відкриється діалогове вікно налаштування підписів",
|
||||
", opens subtitles settings dialog": ", відкриється діалогове вікно налаштування субтитрів",
|
||||
", opens descriptions settings dialog": ", відкриється діалогове вікно налаштування описів",
|
||||
", selected": ", обраний"
|
||||
}
|
||||
|
||||
+21
-6
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Play": "Phát",
|
||||
"Pause": "Tạm dừng",
|
||||
"Replay": "Phát lại",
|
||||
"Current Time": "Thời gian hiện tại",
|
||||
"Duration Time": "Độ dài",
|
||||
"Remaining Time": "Thời gian còn lại",
|
||||
@@ -12,15 +13,29 @@
|
||||
"Non-Fullscreen": "Thoát toàn màn hình",
|
||||
"Mute": "Tắt tiếng",
|
||||
"Unmute": "Bật âm thanh",
|
||||
"Playback Rate": "Tốc độ phát",
|
||||
"Playback Rate": "Tỉ lệ phát lại",
|
||||
"Subtitles": "Phụ đề",
|
||||
"subtitles off": "Tắt phụ đề",
|
||||
"subtitles off": "tắt phụ đề",
|
||||
"Captions": "Chú thích",
|
||||
"captions off": "Tắt chú thích",
|
||||
"captions off": "tắt chú thích",
|
||||
"Chapters": "Chương",
|
||||
"You aborted the media playback": "Bạn đã hủy việc phát media.",
|
||||
"Close Modal Dialog": "Đóng hộp thoại",
|
||||
"Descriptions": "Mô tả",
|
||||
"descriptions off": "tắt mô tả",
|
||||
"Audio Track": "Track âm thanh",
|
||||
"You aborted the media playback": "Bạn đã hủy việc phát lại media.",
|
||||
"A network error caused the media download to fail part-way.": "Một lỗi mạng dẫn đến việc tải media bị lỗi.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video không tải được, mạng hay server có lỗi hoặc định dạng không được hỗ trợ.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Phát media đã bị hủy do một sai lỗi hoặc media sử dụng những tính năng trình duyệt không hỗ trợ.",
|
||||
"No compatible source was found for this media.": "Không có nguồn tương thích cho media này."
|
||||
}
|
||||
"No compatible source was found for this media.": "Không có nguồn tương thích cho media này.",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "Media đã được mã hóa và chúng tôi không thể giải mã nó.",
|
||||
"Play Video": "Phát Video",
|
||||
"Close": "Đóng",
|
||||
"Modal Window": "Cửa sổ",
|
||||
"This is a modal window": "Đây là một cửa sổ",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Cửa sổ này có thể đóng lại bằng việc nhấn phím Esc hoặc kích hoạt nút đóng.",
|
||||
", opens captions settings dialog": ", mở hộp thoại thiết lập chú thích",
|
||||
", opens subtitles settings dialog": ", mở hộp thoại thiết lập phụ đề",
|
||||
", opens descriptions settings dialog": ", mở hộp thoại thiết lập mô tả",
|
||||
", selected": ", đã chọn"
|
||||
}
|
||||
+14
-2
@@ -18,11 +18,23 @@
|
||||
"Captions": "內嵌字幕",
|
||||
"captions off": "關閉內嵌字幕",
|
||||
"Chapters": "章節",
|
||||
"Close Modal Dialog": "關閉對話框",
|
||||
"Descriptions": "描述",
|
||||
"descriptions off": "關閉描述",
|
||||
"Audio Track": "音軌",
|
||||
"You aborted the media playback": "影片播放已終止",
|
||||
"A network error caused the media download to fail part-way.": "網路錯誤導致影片下載失敗。",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "影片因格式不支援或者伺服器或網路的問題無法載入。",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由於影片檔案損毀或是該影片使用了您的瀏覽器不支援的功能,播放終止。",
|
||||
"No compatible source was found for this media.": "無法找到相容此影片的來源。",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。"
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。",
|
||||
"Play Video": "播放影片",
|
||||
"Close": "關閉",
|
||||
"Modal Window": "對話框",
|
||||
"This is a modal window": "這是一個對話框",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "可以按ESC按鍵或啟用關閉按鈕來關閉此對話框。",
|
||||
", opens captions settings dialog": ", 開啟標題設定對話框",
|
||||
", opens subtitles settings dialog": ", 開啟字幕設定對話框",
|
||||
", opens descriptions settings dialog": ", 開啟描述設定對話框",
|
||||
", selected": ", 選擇"
|
||||
}
|
||||
|
||||
|
||||
+91
-49
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.11.7",
|
||||
"version": "6.0.0-RC.1",
|
||||
"main": "./es5/video.js",
|
||||
"style": "./dist/video-js.css",
|
||||
"copyright": "Copyright Brightcove, Inc. <https://www.brightcove.com/>",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
@@ -14,90 +16,130 @@
|
||||
"homepage": "http://videojs.com",
|
||||
"author": "Steve Heffernan",
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
"changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s",
|
||||
"build": "grunt dist",
|
||||
"change": "grunt chg-add",
|
||||
"clean": "grunt clean",
|
||||
"grunt": "grunt",
|
||||
"lint": "vjsstandard",
|
||||
"start": "grunt dev",
|
||||
"test": "grunt test",
|
||||
"docs": "npm run docs:lint && npm run docs:api",
|
||||
"jsdoc": "jsdoc",
|
||||
"docs:api": "jsdoc -c .jsdoc.json",
|
||||
"docs:lint": "remark -- './**/*.md'",
|
||||
"docs:fix": "remark --output -- './**/*.md'",
|
||||
"babel": "babel src/js -d es5",
|
||||
"prepublish": "not-in-install && npm run docs:api || in-install"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/videojs/video.js.git"
|
||||
},
|
||||
"main": "./dist/video.js",
|
||||
"style": "./dist/video-js.css",
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.9.2",
|
||||
"global": "4.3.0",
|
||||
"lodash-compat": "3.10.2",
|
||||
"object.assign": "4.0.3",
|
||||
"safe-json-parse": "4.0.0",
|
||||
"tsml": "1.0.1",
|
||||
"videojs-font": "2.0.0",
|
||||
"videojs-ie8": "1.1.2",
|
||||
"videojs-swf": "5.1.0",
|
||||
"videojs-vtt.js": "0.12.1",
|
||||
"xhr": "2.2.0"
|
||||
"xhr": "2.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "^5.2.2",
|
||||
"babelify": "^6.0.1",
|
||||
"aliasify": "^2.1.0",
|
||||
"babel-cli": "^6.11.4",
|
||||
"babel-plugin-inline-json": "^1.1.1",
|
||||
"babel-plugin-transform-runtime": "^6.9.0",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"babel-preset-es3": "^1.0.1",
|
||||
"babel-register": "^6.9.0",
|
||||
"babelify": "^7.3.0",
|
||||
"blanket": "^1.1.6",
|
||||
"browserify-derequire": "^0.9.4",
|
||||
"browserify-istanbul": "^0.2.1",
|
||||
"browserify-istanbul": "^2.0.0",
|
||||
"browserify-versionify": "^1.0.4",
|
||||
"bundle-collapser": "^1.2.1",
|
||||
"chg": "^0.3.2",
|
||||
"css": "^2.2.0",
|
||||
"conventional-changelog-cli": "^1.2.0",
|
||||
"conventional-changelog-videojs": "^3.0.0",
|
||||
"es5-shim": "^4.1.3",
|
||||
"es6-shim": "^0.35.1",
|
||||
"gkatsev-grunt-sass": "^1.1.1",
|
||||
"grunt": "^0.4.4",
|
||||
"grunt-aws-s3": "^0.12.1",
|
||||
"grunt-banner": "^0.4.0",
|
||||
"grunt-browserify": "3.5.1",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"grunt-concurrent": "^1.0.0",
|
||||
"grunt-contrib-clean": "~0.4.0a",
|
||||
"grunt-contrib-concat": "^0.5.1",
|
||||
"grunt-contrib-connect": "~0.7.1",
|
||||
"grunt-contrib-copy": "^0.8.0",
|
||||
"grunt-contrib-cssmin": "~0.6.0",
|
||||
"grunt-contrib-jshint": "~0.11.3",
|
||||
"grunt-contrib-less": "~0.6.4",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-contrib-watch": "~0.1.4",
|
||||
"ghooks": "^1.3.2",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-accessibility": "^5.0.0",
|
||||
"grunt-babel": "^6.0.0",
|
||||
"grunt-banner": "^0.6.0",
|
||||
"grunt-browserify": "5.0.0",
|
||||
"grunt-cli": "~1.2.0",
|
||||
"grunt-concurrent": "^2.3.1",
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-connect": "~1.0.2",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.2",
|
||||
"grunt-contrib-uglify": "^2.0.0",
|
||||
"grunt-contrib-watch": "~1.0.0",
|
||||
"grunt-coveralls": "^1.0.0",
|
||||
"grunt-exorcise": "^1.0.1",
|
||||
"grunt-fastly": "^0.1.3",
|
||||
"grunt-github-releaser": "^0.1.17",
|
||||
"grunt-karma": "^0.8.3",
|
||||
"grunt-version": "~0.3.0",
|
||||
"grunt-karma": "^2.0.0",
|
||||
"grunt-sass": "^2.0.0",
|
||||
"grunt-shell": "^2.0.0",
|
||||
"grunt-version": "~1.1.1",
|
||||
"grunt-videojs-languages": "0.0.4",
|
||||
"grunt-zip": "0.10.2",
|
||||
"karma": "^0.12.36",
|
||||
"karma-browserify": "^4.0.0",
|
||||
"karma-browserstack-launcher": "^0.1.4",
|
||||
"karma-chrome-launcher": "^0.1.3",
|
||||
"karma-coverage": "^0.4.0",
|
||||
"karma-detect-browsers": "^2.0.2",
|
||||
"karma-firefox-launcher": "^0.1.3",
|
||||
"karma-ie-launcher": "^0.1.5",
|
||||
"karma-opera-launcher": "~0.1.0",
|
||||
"karma-qunit": "^0.1.2",
|
||||
"karma-safari-launcher": "^0.1.1",
|
||||
"karma-sinon": "^1.0.3",
|
||||
"grunt-zip": "0.17.1",
|
||||
"in-publish": "^2.0.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"jsdoc": "^3.4.2",
|
||||
"karma": "1.3.0",
|
||||
"karma-browserify": "^5.1.0",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-detect-browsers": "~2.2.3",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-ie-launcher": "^1.0.0",
|
||||
"karma-opera-launcher": "^1.0.0",
|
||||
"karma-qunit": "^1.2.0",
|
||||
"karma-safari-launcher": "^1.0.0",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"load-grunt-tasks": "^3.1.0",
|
||||
"lodash": "^4.16.6",
|
||||
"markdown-table": "^1.0.0",
|
||||
"npm-run": "^4.1.0",
|
||||
"proxyquireify": "^3.0.0",
|
||||
"qunitjs": "^1.18.0",
|
||||
"qunitjs": "1.23.1",
|
||||
"remark-cli": "^2.1.0",
|
||||
"remark-lint": "^5.2.0",
|
||||
"remark-toc": "^3.1.0",
|
||||
"remark-validate-links": "^5.0.0",
|
||||
"shelljs": "^0.7.5",
|
||||
"sinon": "^1.16.1",
|
||||
"time-grunt": "^1.1.1",
|
||||
"uglify-js": "~2.3.6",
|
||||
"videojs-doc-generator": "0.0.1"
|
||||
"tui-jsdoc-template": "^1.1.0",
|
||||
"uglify-js": "~2.7.3",
|
||||
"videojs-doc-generator": "0.0.1",
|
||||
"videojs-standard": "^6.0.1",
|
||||
"webpack": "^1.13.2"
|
||||
},
|
||||
"standard": {
|
||||
"vjsstandard": {
|
||||
"ignore": [
|
||||
"**/Gruntfile.js",
|
||||
"**/es5/**",
|
||||
"**/build/**",
|
||||
"**/dist/**",
|
||||
"**/docs/**",
|
||||
"**/lang/**",
|
||||
"**/sandbox/**",
|
||||
"**/test/**"
|
||||
"**/test/api/**",
|
||||
"**/test/coverage/**",
|
||||
"**/test/karma.conf.js"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"ghooks": {
|
||||
"pre-push": "npm run lint -- --errors"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,38 @@
|
||||
<li><span class="vjs-icon-circle"></span> <code>.vjs-icon-circle</code></li>
|
||||
<li><span class="vjs-icon-circle-outline"></span> <code>.vjs-icon-circle-outline</code></li>
|
||||
<li><span class="vjs-icon-circle-inner-circle"></span> <code>.vjs-icon-circle-inner-circle</code></li>
|
||||
|
||||
<li><span class="play"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="play-circle"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="pause"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="volume-mute"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="volume-low"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="volume-mid"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="volume-high"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="fullscreen-enter"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="fullscreen-exit"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="square"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="spinner"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="subtitles"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="captions"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="chapters"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="share"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="cog"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="circle"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="circle-outline"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="circle-inner-circle"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="hd"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="cancel"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="replay"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="facebook"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="gplus"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="linkedin"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="twitter"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="tumblr"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="pinterest"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="audio-description"></span> <code>.vjs-icon-play</code></li>
|
||||
<li><span class="audio"></span> <code>.vjs-icon-play</code></li>
|
||||
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
@mixin transition($string: $transition--default) {
|
||||
-webkit-transition: $string;
|
||||
-moz-transition: $string;
|
||||
-ms-transition: $string;
|
||||
-o-transition: $string;
|
||||
transition: $string;
|
||||
}
|
||||
@@ -24,7 +25,6 @@
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
@@ -86,7 +86,7 @@
|
||||
-webkit-user-select: $string;
|
||||
-moz-user-select: $string;
|
||||
-ms-user-select: $string;
|
||||
user-select: $string
|
||||
user-select: $string;
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
.vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
|
||||
.vjs-playback-rate, .vjs-progress-control,
|
||||
.vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
|
||||
.vjs-mute-control, .vjs-volume-control,
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) {
|
||||
.vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
|
||||
.vjs-playback-rate,
|
||||
.vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
|
||||
.vjs-mute-control, .vjs-volume-control,
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
|
||||
.video-js:hover .vjs-big-play-button,
|
||||
.video-js .vjs-big-play-button:focus {
|
||||
outline: 0;
|
||||
border-color: $primary-foreground-color;
|
||||
|
||||
@include background-color-with-alpha($secondary-background-color, $secondary-background-transparency);
|
||||
@@ -50,3 +49,8 @@
|
||||
.vjs-error .vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Show big play button if video is paused and .vjs-show-big-play-button-on-pause is set on video element
|
||||
.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause .vjs-big-play-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
@include transition($trans);
|
||||
}
|
||||
|
||||
// IE 8 hack for media queries
|
||||
$ie8screen: "\\0screen";
|
||||
|
||||
// Video has started playing AND user is inactive
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
// Remain visible for screen reader and keyboard users
|
||||
@@ -32,10 +29,6 @@ $ie8screen: "\\0screen";
|
||||
$trans: visibility 1.0s, opacity 1.0s;
|
||||
@include transition($trans);
|
||||
|
||||
// Make controls hidden in IE8 for now
|
||||
@media #{$ie8screen} {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-controls-disabled .vjs-control-bar,
|
||||
@@ -51,15 +44,6 @@ $ie8screen: "\\0screen";
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
// IE8 is flakey with fonts, and you have to change the actual content to force
|
||||
// fonts to show/hide properly.
|
||||
// - "\9" IE8 hack didn't work for this
|
||||
// Found in XP IE8 from http://modern.ie. Does not show up in "IE8 mode" in IE9
|
||||
.vjs-user-inactive.vjs-playing .vjs-control-bar :before {
|
||||
@media #{$ie8screen} { content: ""; }
|
||||
}
|
||||
|
||||
// IE 8 + 9 Support
|
||||
.vjs-has-started.vjs-no-flex .vjs-control-bar {
|
||||
display: table;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// It's used on both real buttons (play button)
|
||||
// and div buttons (menu buttons)
|
||||
.video-js .vjs-control {
|
||||
outline: none;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
.video-js .vjs-fullscreen-control {
|
||||
cursor: pointer;
|
||||
@include flex(none);
|
||||
}
|
||||
.video-js .vjs-fullscreen-control {
|
||||
@extend .vjs-icon-fullscreen-enter;
|
||||
}
|
||||
// Switch to the exit icon when the player is in fullscreen
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
.video-js .vjs-play-control {
|
||||
cursor: pointer;
|
||||
@include flex(none);
|
||||
}
|
||||
.video-js .vjs-play-control {
|
||||
@extend .vjs-icon-play;
|
||||
}
|
||||
.video-js .vjs-play-control.vjs-playing {
|
||||
@extend .vjs-icon-pause;
|
||||
}
|
||||
.video-js .vjs-play-control.vjs-ended {
|
||||
@extend .vjs-icon-replay;
|
||||
}
|
||||
|
||||
@@ -36,12 +36,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Hide the poster when controls are disabled because it's clickable
|
||||
// and the native poster can take over
|
||||
.vjs-controls-disabled .vjs-poster {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Hide the poster when native controls are used otherwise it covers them
|
||||
.vjs-using-native-controls .vjs-poster {
|
||||
display: none;
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
// .vjs-progress-control / ProgressControl
|
||||
//
|
||||
// Let's talk pixel math!
|
||||
// Start with a base font size of 10px (assuming that hasn't changed)
|
||||
// No Hover:
|
||||
// - Progress holder is 3px
|
||||
// - Progress handle is 9px
|
||||
// - Progress handle is pulled up 3px to center it.
|
||||
//
|
||||
// Hover:
|
||||
// - Progress holder becomes 5px
|
||||
// - Progress handle becomes 15px
|
||||
// - Progress handle is pulled up 5px to center it
|
||||
//
|
||||
|
||||
// This is the container for all progress bar-related components/elements.
|
||||
.video-js .vjs-progress-control {
|
||||
@include flex(auto);
|
||||
@include display-flex(center);
|
||||
@@ -22,35 +11,32 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Box containing play and load progresses. Also acts as seek scrubber.
|
||||
.vjs-no-flex .vjs-progress-control {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// .vjs-progress-holder / SeekBar
|
||||
//
|
||||
// Box containing play and load progress bars. It also acts as seek scrubber.
|
||||
.video-js .vjs-progress-holder {
|
||||
@include flex(auto);
|
||||
@include transition(all 0.2s);
|
||||
height: 0.3em;
|
||||
}
|
||||
|
||||
// We need an increased hit area on hover
|
||||
// This increases the size of the progress holder so there is an increased
|
||||
// hit area for clicks/touches.
|
||||
.video-js .vjs-progress-control:hover .vjs-progress-holder {
|
||||
font-size: 1.666666666666666666em
|
||||
font-size: 1.666666666666666666em;
|
||||
}
|
||||
|
||||
/* If we let the font size grow as much as everything else, the current time tooltip ends up
|
||||
ginormous. If you'd like to enable the current time tooltip all the time, this should be disabled
|
||||
to avoid a weird hitch when you roll off the hover. */
|
||||
|
||||
// Also show the current time tooltip
|
||||
.video-js .vjs-progress-control:hover .vjs-time-tooltip,
|
||||
.video-js .vjs-progress-control:hover .vjs-mouse-display:after,
|
||||
.video-js .vjs-progress-control:hover .vjs-play-progress:after {
|
||||
font-family: $text-font-family;
|
||||
visibility: visible;
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
// Progress Bars
|
||||
// .vjs-play-progress / PlayProgressBar and .vjs-load-progress / LoadProgressBar
|
||||
//
|
||||
// These are bars that appear within the progress control to communicate the
|
||||
// amount of media that has played back and the amount of media that has
|
||||
// loaded, respectively.
|
||||
.video-js .vjs-progress-holder .vjs-play-progress,
|
||||
.video-js .vjs-progress-holder .vjs-load-progress,
|
||||
.video-js .vjs-progress-holder .vjs-tooltip-progress-bar,
|
||||
.video-js .vjs-progress-holder .vjs-load-progress div {
|
||||
position: absolute;
|
||||
display: block;
|
||||
@@ -64,86 +50,80 @@
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.video-js .vjs-mouse-display {
|
||||
@extend .vjs-icon-circle;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.video-js .vjs-play-progress {
|
||||
background-color: $primary-foreground-color;
|
||||
@extend .vjs-icon-circle;
|
||||
|
||||
// Progress handle
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: -0.333333333333333em;
|
||||
right: -0.5em;
|
||||
font-size: 0.9em;
|
||||
position: absolute;
|
||||
right: -0.5em;
|
||||
top: -0.333333333333333em;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Current Time "tooltip"
|
||||
// By default this is hidden and only shown when hovering over the progress control
|
||||
.video-js .vjs-time-tooltip,
|
||||
.video-js .vjs-mouse-display:after,
|
||||
.video-js .vjs-play-progress:after {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: -3.4em;
|
||||
right: -1.9em;
|
||||
font-size: 0.9em;
|
||||
color: #000;
|
||||
content: attr(data-current-time);
|
||||
padding: 6px 8px 8px 8px;
|
||||
@include background-color-with-alpha(#fff, 0.8);
|
||||
@include border-radius(0.3em);
|
||||
}
|
||||
|
||||
.video-js .vjs-time-tooltip,
|
||||
.video-js .vjs-play-progress:before,
|
||||
.video-js .vjs-play-progress:after {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.video-js .vjs-progress-control .vjs-keep-tooltips-inside:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js .vjs-load-progress {
|
||||
// For IE8 we'll lighten the color
|
||||
// For IE8, we'll lighten the color
|
||||
background: lighten($secondary-background-color, 25%);
|
||||
// Otherwise we'll rely on stacked opacities
|
||||
// Otherwise, we'll rely on stacked opacities
|
||||
background: rgba($secondary-background-color, $secondary-background-transparency);
|
||||
}
|
||||
|
||||
// there are child elements of the load progress bar that represent the
|
||||
// specific time ranges that have been buffered
|
||||
// There are child elements of the load progress bar that represent the
|
||||
// specific time ranges that have been buffered.
|
||||
.video-js .vjs-load-progress div {
|
||||
// For IE8 we'll lighten the color
|
||||
// For IE8, we'll lighten the color
|
||||
background: lighten($secondary-background-color, 50%);
|
||||
// Otherwise we'll rely on stacked opacities
|
||||
// Otherwise, we'll rely on stacked opacities
|
||||
background: rgba($secondary-background-color, 0.75);
|
||||
}
|
||||
|
||||
.video-js.vjs-no-flex .vjs-progress-control {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// .vjs-time-tooltip
|
||||
//
|
||||
// These elements are displayed above the progress bar. They are not components
|
||||
// themselves, but they are managed by the MouseTimeDisplay and PlayProgressBar
|
||||
// components individually.
|
||||
//
|
||||
// By default, they are hidden and only shown when hovering over the progress
|
||||
// control.
|
||||
.video-js .vjs-time-tooltip {
|
||||
@include background-color-with-alpha(#fff, 0.8);
|
||||
@include border-radius(0.3em);
|
||||
color: #000;
|
||||
display: inline-block;
|
||||
height: 2.4em;
|
||||
position: relative;
|
||||
|
||||
// By floating the tooltips to the right, their right edge becomes aligned
|
||||
// with the right edge of their parent element. However, in order to have them
|
||||
// centered, they must be pulled further to the right via positioning (e.g.
|
||||
// `right: -10px;`. This part is left to JavaScript.
|
||||
float: right;
|
||||
right: -1.9em;
|
||||
}
|
||||
font-family: $text-font-family;
|
||||
|
||||
.vjs-tooltip-progress-bar {
|
||||
// The font-size should translate to a consistent 10px for time tooltips in
|
||||
// all states. This is tricky because the .vjs-progress-holder element
|
||||
// changes its font-size when the .vjs-progress-control is hovered.
|
||||
font-size: 1em;
|
||||
padding: 6px 8px 8px 8px;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
top: -3.4em;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.video-js .vjs-progress-control:hover .vjs-time-tooltip {
|
||||
|
||||
// Ensure that we maintain a font-size of ~10px.
|
||||
font-size: 0.6em;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
// .vjs-mouse-display / MouseTimeDisplay
|
||||
//
|
||||
// This element tracks the mouse position along the progress control and
|
||||
// includes a tooltip, which displays the time at that point in the media.
|
||||
.video-js .vjs-progress-control .vjs-mouse-display {
|
||||
display: none;
|
||||
position: absolute;
|
||||
@@ -152,25 +132,27 @@
|
||||
background-color: #000;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.vjs-no-flex .vjs-progress-control .vjs-mouse-display {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.video-js .vjs-progress-control:hover .vjs-mouse-display {
|
||||
display: block;
|
||||
}
|
||||
.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display,
|
||||
.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display:after {
|
||||
|
||||
.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
$trans: visibility 1.0s, opacity 1.0s;
|
||||
@include transition($trans);
|
||||
}
|
||||
.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display,
|
||||
.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display:after {
|
||||
|
||||
.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display {
|
||||
display: none;
|
||||
}
|
||||
.vjs-mouse-display .vjs-time-tooltip,
|
||||
.video-js .vjs-progress-control .vjs-mouse-display:after {
|
||||
|
||||
.vjs-mouse-display .vjs-time-tooltip {
|
||||
color: #fff;
|
||||
@include background-color-with-alpha(#000, 0.8);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
.video-js .vjs-slider {
|
||||
outline: 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
// IE 8 + IE 9 width: auto container fix
|
||||
.vjs-no-flex .vjs-remaining-time.vjs-time-control.vjs-control {
|
||||
width: 0px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.video-js .vjs-duration,
|
||||
.vjs-no-flex .vjs-duration {
|
||||
display: none;
|
||||
|
||||
@@ -1,27 +1,128 @@
|
||||
.video-js .vjs-mute-control,
|
||||
.video-js .vjs-volume-menu-button {
|
||||
.video-js .vjs-mute-control {
|
||||
cursor: pointer;
|
||||
@include flex(none);
|
||||
@extend .vjs-icon-volume-high;
|
||||
// padding here is for IE < 9, it doesn't do width: auto from
|
||||
// another style correctly
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
.video-js .vjs-mute-control.vjs-vol-0,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-0 {
|
||||
.video-js .vjs-mute-control.vjs-vol-0 {
|
||||
@extend .vjs-icon-volume-mute;
|
||||
}
|
||||
.video-js .vjs-mute-control.vjs-vol-1,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-1 {
|
||||
.video-js .vjs-mute-control.vjs-vol-1 {
|
||||
@extend .vjs-icon-volume-low;
|
||||
}
|
||||
.video-js .vjs-mute-control.vjs-vol-2,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-2 {
|
||||
.video-js .vjs-mute-control.vjs-vol-2 {
|
||||
@extend .vjs-icon-volume-mid;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-control {
|
||||
cursor: pointer;
|
||||
margin-right: 1em;
|
||||
@include display-flex;
|
||||
}
|
||||
.video-js .vjs-volume-control.vjs-volume-horizontal {
|
||||
width: 5em;
|
||||
@include flex(none);
|
||||
@include display-flex(center);
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel .vjs-volume-control {
|
||||
visibility: visible;
|
||||
opacity: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin-left: -1px;
|
||||
|
||||
}
|
||||
.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
& .vjs-volume-bar,
|
||||
& .vjs-volume-level {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
}
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel {
|
||||
&:hover .vjs-volume-control,
|
||||
&:active .vjs-volume-control,
|
||||
&:focus .vjs-volume-control,
|
||||
& .vjs-volume-control:hover ,
|
||||
& .vjs-volume-control:active ,
|
||||
& .vjs-volume-control:focus ,
|
||||
& .vjs-mute-control:hover ~ .vjs-volume-control,
|
||||
& .vjs-mute-control:active ~ .vjs-volume-control,
|
||||
& .vjs-mute-control:focus ~ .vjs-volume-control,
|
||||
& .vjs-volume-control.vjs-slider-active {
|
||||
&.vjs-volume-horizontal {
|
||||
width: 5em;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
|
||||
&.vjs-volume-vertical {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
& .vjs-volume-bar,
|
||||
& .vjs-volume-level {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
}
|
||||
}
|
||||
|
||||
$transition-property: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
|
||||
@include transition($transition-property);
|
||||
}
|
||||
|
||||
&.vjs-volume-panel-horizontal {
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.vjs-slider-active {
|
||||
width: 9em;
|
||||
|
||||
@include transition(width 0.1s);
|
||||
}
|
||||
}
|
||||
|
||||
@include transition(width 1s);
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
|
||||
height: 8em;
|
||||
width: 3em;
|
||||
left: -3.5em;
|
||||
|
||||
$transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
|
||||
@include transition($transition-property)
|
||||
}
|
||||
.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal {
|
||||
$transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
|
||||
@include transition($transition-property)
|
||||
}
|
||||
|
||||
.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal {
|
||||
width: 5em;
|
||||
height: 3em;
|
||||
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
|
||||
@include transition(none);
|
||||
}
|
||||
|
||||
.video-js.vjs-no-flex .vjs-volume-control.vjs-volume-vertical,
|
||||
.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical {
|
||||
position: absolute;
|
||||
bottom: 3em;
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel {
|
||||
@include display-flex;
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-bar {
|
||||
@@ -74,6 +175,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel.vjs-volume-panel-vertical {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
// Assumes volume starts at 1.0.
|
||||
.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level {
|
||||
height: 100%;
|
||||
@@ -83,53 +188,14 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// The volume menu button is like menu buttons (captions/subtitles) but works
|
||||
// a little differently. It needs to be possible to tab to the volume slider
|
||||
// without hitting space bar on the menu button. To do this we're not using
|
||||
// display:none to hide the slider menu by default, and instead setting the
|
||||
// width and height to zero.
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top-color: transparent;
|
||||
.video-js .vjs-volume-vertical {
|
||||
width: 3em;
|
||||
height: 8em;
|
||||
bottom: 8em;
|
||||
|
||||
@include background-color-with-alpha($primary-background-color, $primary-background-transparency);
|
||||
}
|
||||
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button-vertical .vjs-menu {
|
||||
left: 0.5em;
|
||||
height: 8em;
|
||||
}
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button-horizontal .vjs-menu {
|
||||
.video-js .vjs-volume-horizontal .vjs-menu {
|
||||
left: -2em;
|
||||
}
|
||||
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu-content {
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
// Avoids unnecessary scrollbars in the menu content. Primarily noticed in Chrome on Linux.
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button-vertical:hover .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical:focus .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical.vjs-slider-active .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical .vjs-lock-showing .vjs-menu-content {
|
||||
height: 8em;
|
||||
width: 2.9em;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button-horizontal:hover .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal:focus .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal .vjs-slider-active .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal .vjs-lock-showing .vjs-menu-content {
|
||||
height: 2.9em;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button.vjs-menu-button-inline .vjs-menu-content {
|
||||
// An inline volume should never have a menu background color.
|
||||
// This protects it from external changes to background colors.
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,6 @@
|
||||
// This width is currently specific to the inline volume bar.
|
||||
width: 12em;
|
||||
}
|
||||
// Don't transition when tabbing in reverse to the volume menu
|
||||
// because it looks weird
|
||||
.video-js .vjs-menu-button-inline.vjs-slider-active {
|
||||
@include transition(none);
|
||||
}
|
||||
|
||||
.vjs-menu-button-inline .vjs-menu {
|
||||
opacity: 0;
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
.vjs-menu .vjs-menu-content {
|
||||
display: block;
|
||||
padding: 0; margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
font-family: $text-font-family;
|
||||
}
|
||||
@@ -34,9 +35,8 @@
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.vjs-menu li:focus,
|
||||
.vjs-menu li:hover {
|
||||
outline: 0;
|
||||
.vjs-menu li.vjs-menu-item:focus,
|
||||
.vjs-menu li.vjs-menu-item:hover {
|
||||
@include background-color-with-alpha($secondary-background-color, $secondary-background-transparency);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@media \0screen {
|
||||
.vjs-user-inactive.vjs-playing .vjs-control-bar :before {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
@media \0screen {
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
@import "private-variables";
|
||||
@import "utilities";
|
||||
|
||||
@import "node_modules/videojs-font/scss/icons";
|
||||
@import "../../node_modules/videojs-font/scss/icons";
|
||||
|
||||
@import "components/layout";
|
||||
@import "components/big-play";
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @file base-styles.js
|
||||
*
|
||||
* This code injects the required base styles in the head of the document.
|
||||
*/
|
||||
import window from 'global/window';
|
||||
import document from 'global/document';
|
||||
|
||||
if (window.VIDEOJS_NO_BASE_THEME) return;
|
||||
|
||||
const styles = '{{GENERATED_STYLES}}';
|
||||
|
||||
if (styles === '{{GENERATED'+'_STYLES}}');
|
||||
|
||||
const styleNode = document.createElement('style');
|
||||
styleNode.innerHTML = styles;
|
||||
|
||||
document.head.insertBefore(styleNode, document.head.firstChild);
|
||||
+20
-16
@@ -5,41 +5,45 @@ import Button from './button.js';
|
||||
import Component from './component.js';
|
||||
|
||||
/**
|
||||
* Initial play button. Shows before the video has played. The hiding of the
|
||||
* big play button is done via CSS and player states.
|
||||
* The initial play button that shows before the video has played. The hiding of the
|
||||
* `BigPlayButton` get done via CSS and `Player` states.
|
||||
*
|
||||
* @param {Object} player Main Player
|
||||
* @param {Object=} options Object of option names and values
|
||||
* @extends Button
|
||||
* @class BigPlayButton
|
||||
*/
|
||||
class BigPlayButton extends Button {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object. Always returns 'vjs-big-play-button'.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return 'vjs-big-play-button';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles click for play
|
||||
* This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
|
||||
* for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} event
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
handleClick(event) {
|
||||
this.player_.play();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `BigPlayButton`s controls. Added to for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
BigPlayButton.prototype.controlText_ = 'Play Video';
|
||||
|
||||
Component.registerComponent('BigPlayButton', BigPlayButton);
|
||||
|
||||
+73
-52
@@ -3,61 +3,50 @@
|
||||
*/
|
||||
import ClickableComponent from './clickable-component.js';
|
||||
import Component from './component';
|
||||
import * as Events from './utils/events.js';
|
||||
import * as Fn from './utils/fn.js';
|
||||
import log from './utils/log.js';
|
||||
import document from 'global/document';
|
||||
import assign from 'object.assign';
|
||||
import {assign} from './utils/obj';
|
||||
|
||||
/**
|
||||
* Base class for all buttons
|
||||
* Base class for all buttons.
|
||||
*
|
||||
* @param {Object} player Main Player
|
||||
* @param {Object=} options Object of option names and values
|
||||
* @extends ClickableComponent
|
||||
* @class Button
|
||||
*/
|
||||
class Button extends ClickableComponent {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Button`s DOM element.
|
||||
*
|
||||
* @param {string} [tag="button"]
|
||||
* The element's node type. This argument is IGNORED: no matter what
|
||||
* is passed, it will always create a `button` element.
|
||||
*
|
||||
* @param {Object} [props={}]
|
||||
* An object of properties that should be set on the element.
|
||||
*
|
||||
* @param {Object} [attributes={}]
|
||||
* An object of attributes that should be set on the element.
|
||||
*
|
||||
* @param {String=} type Element's node type. e.g. 'div'
|
||||
* @param {Object=} props An object of properties that should be set on the element
|
||||
* @param {Object=} attributes An object of attributes that should be set on the element
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that gets created.
|
||||
*/
|
||||
createEl(tag='button', props={}, attributes={}) {
|
||||
createEl(tag, props = {}, attributes = {}) {
|
||||
tag = 'button';
|
||||
|
||||
props = assign({
|
||||
className: this.buildCSSClass()
|
||||
}, props);
|
||||
|
||||
if (tag !== 'button') {
|
||||
log.warn(`Creating a Button with an HTML element of ${tag} is deprecated; use ClickableComponent instead.`);
|
||||
|
||||
// Add properties for clickable element which is not a native HTML button
|
||||
props = assign({
|
||||
tabIndex: 0
|
||||
}, props);
|
||||
|
||||
// Add ARIA attributes for clickable element which is not a native HTML button
|
||||
attributes = assign({
|
||||
role: 'button'
|
||||
}, attributes);
|
||||
}
|
||||
|
||||
// Add attributes for button element
|
||||
attributes = assign({
|
||||
type: 'button', // Necessary since the default button type is "submit"
|
||||
'aria-live': 'polite' // let the screen reader user know that the text of the button may change
|
||||
|
||||
// Necessary since the default button type is "submit"
|
||||
'type': 'button',
|
||||
|
||||
// let the screen reader user know that the text of the button may change
|
||||
'aria-live': 'polite'
|
||||
}, attributes);
|
||||
|
||||
let el = Component.prototype.createEl.call(this, tag, props, attributes);
|
||||
const el = Component.prototype.createEl.call(this, tag, props, attributes);
|
||||
|
||||
this.createControlTextEl(el);
|
||||
|
||||
@@ -65,16 +54,24 @@ class Button extends ClickableComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child component inside this button
|
||||
* Add a child `Component` inside of this `Button`.
|
||||
*
|
||||
* @param {String|Component} child The class name or instance of a child to add
|
||||
* @param {Object=} options Options, including options to be passed to children of the child.
|
||||
* @return {Component} The child component (created by this process if a string was used)
|
||||
* @deprecated
|
||||
* @method addChild
|
||||
* @param {string|Component} child
|
||||
* The name or instance of a child to add.
|
||||
*
|
||||
* @param {Object} [options={}]
|
||||
* The key/value store of options that will get passed to children of
|
||||
* the child.
|
||||
*
|
||||
* @return {Component}
|
||||
* The `Component` that gets added as a child. When using a string the
|
||||
* `Component` will get created by this process.
|
||||
*
|
||||
* @deprecated since version 5
|
||||
*/
|
||||
addChild(child, options={}) {
|
||||
let className = this.constructor.name;
|
||||
addChild(child, options = {}) {
|
||||
const className = this.constructor.name;
|
||||
|
||||
log.warn(`Adding an actionable (user controllable) child to a Button (${className}) is not supported; use a ClickableComponent instead.`);
|
||||
|
||||
// Avoid the error message generated by ClickableComponent's addChild method
|
||||
@@ -82,18 +79,42 @@ class Button extends ClickableComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle KeyPress (document level) - Extend with specific functionality for button
|
||||
*
|
||||
* @method handleKeyPress
|
||||
* Enable the `Button` element so that it can be activated or clicked. Use this with
|
||||
* {@link Button#disable}.
|
||||
*/
|
||||
handleKeyPress(event) {
|
||||
// Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
|
||||
if (event.which === 32 || event.which === 13) {
|
||||
} else {
|
||||
super.handleKeyPress(event); // Pass keypress handling up for unsupported keys
|
||||
}
|
||||
enable() {
|
||||
super.enable();
|
||||
this.el_.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the `Button` element so that it cannot be activated or clicked. Use this with
|
||||
* {@link Button#enable}.
|
||||
*/
|
||||
disable() {
|
||||
super.disable();
|
||||
this.el_.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called when a `Button` has focus and `keydown` is triggered via a key
|
||||
* press.
|
||||
*
|
||||
* @param {EventTarget~Event} event
|
||||
* The event that caused this function to get called.
|
||||
*
|
||||
* @listens keydown
|
||||
*/
|
||||
handleKeyPress(event) {
|
||||
|
||||
// Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
|
||||
if (event.which === 32 || event.which === 13) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass keypress handling up for unsupported keys
|
||||
super.handleKeyPress(event);
|
||||
}
|
||||
}
|
||||
|
||||
Component.registerComponent('Button', Button);
|
||||
|
||||
+115
-76
@@ -7,39 +7,49 @@ import * as Events from './utils/events.js';
|
||||
import * as Fn from './utils/fn.js';
|
||||
import log from './utils/log.js';
|
||||
import document from 'global/document';
|
||||
import assign from 'object.assign';
|
||||
import {assign} from './utils/obj';
|
||||
|
||||
/**
|
||||
* Clickable Component which is clickable or keyboard actionable, but is not a native HTML button
|
||||
* Clickable Component which is clickable or keyboard actionable,
|
||||
* but is not a native HTML button.
|
||||
*
|
||||
* @param {Object} player Main Player
|
||||
* @param {Object=} options Object of option names and values
|
||||
* @extends Component
|
||||
* @class ClickableComponent
|
||||
*/
|
||||
class ClickableComponent extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.emitTapEvents();
|
||||
|
||||
this.on('tap', this.handleClick);
|
||||
this.on('click', this.handleClick);
|
||||
this.on('focus', this.handleFocus);
|
||||
this.on('blur', this.handleBlur);
|
||||
this.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`s DOM element.
|
||||
*
|
||||
* @param {string} [tag=div]
|
||||
* The element's node type.
|
||||
*
|
||||
* @param {Object} [props={}]
|
||||
* An object of properties that should be set on the element.
|
||||
*
|
||||
* @param {Object} [attributes={}]
|
||||
* An object of attributes that should be set on the element.
|
||||
*
|
||||
* @param {String=} type Element's node type. e.g. 'div'
|
||||
* @param {Object=} props An object of properties that should be set on the element
|
||||
* @param {Object=} attributes An object of attributes that should be set on the element
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that gets created.
|
||||
*/
|
||||
createEl(tag='div', props={}, attributes={}) {
|
||||
createEl(tag = 'div', props = {}, attributes = {}) {
|
||||
props = assign({
|
||||
className: this.buildCSSClass(),
|
||||
tabIndex: 0
|
||||
@@ -51,11 +61,15 @@ class ClickableComponent extends Component {
|
||||
|
||||
// Add ARIA attributes for clickable element which is not a native HTML button
|
||||
attributes = assign({
|
||||
role: 'button',
|
||||
'aria-live': 'polite' // let the screen reader user know that the text of the element may change
|
||||
'role': 'button',
|
||||
|
||||
// let the screen reader user know that the text of the element may change
|
||||
'aria-live': 'polite'
|
||||
}, attributes);
|
||||
|
||||
let el = super.createEl(tag, props, attributes);
|
||||
this.tabIndex_ = props.tabIndex;
|
||||
|
||||
const el = super.createEl(tag, props, attributes);
|
||||
|
||||
this.createControlTextEl(el);
|
||||
|
||||
@@ -63,11 +77,13 @@ class ClickableComponent extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* create control text
|
||||
* Create a control text element on this `Component`
|
||||
*
|
||||
* @param {Element} [el]
|
||||
* Parent element for the control text.
|
||||
*
|
||||
* @param {Element} el Parent element for the control text
|
||||
* @return {Element}
|
||||
* @method controlText
|
||||
* The control text element that gets created.
|
||||
*/
|
||||
createControlTextEl(el) {
|
||||
this.controlTextEl_ = Dom.createEl('span', {
|
||||
@@ -84,116 +100,139 @@ class ClickableComponent extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls text - both request and localize
|
||||
* Get or set the localize text to use for the controls on the `Component`.
|
||||
*
|
||||
* @param {String} text Text for element
|
||||
* @param {Element=} el Element to set the title on
|
||||
* @return {String}
|
||||
* @method controlText
|
||||
* @param {string} [text]
|
||||
* Control text for element.
|
||||
*
|
||||
* @param {Element} [el=this.el()]
|
||||
* Element to set the title on.
|
||||
*
|
||||
* @return {string}
|
||||
* - The control text when getting
|
||||
*/
|
||||
controlText(text, el=this.el()) {
|
||||
if (!text) return this.controlText_ || 'Need Text';
|
||||
|
||||
controlText(text, el = this.el()) {
|
||||
if (!text) {
|
||||
return this.controlText_ || 'Need Text';
|
||||
}
|
||||
|
||||
const localizedText = this.localize(text);
|
||||
|
||||
this.controlText_ = text;
|
||||
this.controlTextEl_.innerHTML = localizedText;
|
||||
el.setAttribute('title', localizedText);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String}
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-control vjs-button ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child component inside this clickable-component
|
||||
*
|
||||
* @param {String|Component} child The class name or instance of a child to add
|
||||
* @param {Object=} options Options, including options to be passed to children of the child.
|
||||
* @return {Component} The child component (created by this process if a string was used)
|
||||
* @method addChild
|
||||
*/
|
||||
addChild(child, options={}) {
|
||||
// TODO: Fix adding an actionable child to a ClickableComponent; currently
|
||||
// it will cause issues with assistive technology (e.g. screen readers)
|
||||
// which support ARIA, since an element with role="button" cannot have
|
||||
// actionable child elements.
|
||||
|
||||
//let className = this.constructor.name;
|
||||
//log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);
|
||||
|
||||
return super.addChild(child, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the component element
|
||||
*
|
||||
* @return {Component}
|
||||
* @method enable
|
||||
* Enable this `Component`s element.
|
||||
*/
|
||||
enable() {
|
||||
this.removeClass('vjs-disabled');
|
||||
this.el_.setAttribute('aria-disabled', 'false');
|
||||
return this;
|
||||
if (typeof this.tabIndex_ !== 'undefined') {
|
||||
this.el_.setAttribute('tabIndex', this.tabIndex_);
|
||||
}
|
||||
this.on('tap', this.handleClick);
|
||||
this.on('click', this.handleClick);
|
||||
this.on('focus', this.handleFocus);
|
||||
this.on('blur', this.handleBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the component element
|
||||
*
|
||||
* @return {Component}
|
||||
* @method disable
|
||||
* Disable this `Component`s element.
|
||||
*/
|
||||
disable() {
|
||||
this.addClass('vjs-disabled');
|
||||
this.el_.setAttribute('aria-disabled', 'true');
|
||||
return this;
|
||||
if (typeof this.tabIndex_ !== 'undefined') {
|
||||
this.el_.removeAttribute('tabIndex');
|
||||
}
|
||||
this.off('tap', this.handleClick);
|
||||
this.off('click', this.handleClick);
|
||||
this.off('focus', this.handleFocus);
|
||||
this.off('blur', this.handleBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Click - Override with specific functionality for component
|
||||
* This gets called when a `ClickableComponent` gets:
|
||||
* - Clicked (via the `click` event, listening starts in the constructor)
|
||||
* - Tapped (via the `tap` event, listening starts in the constructor)
|
||||
* - The following things happen in order:
|
||||
* 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
|
||||
* `ClickableComponent`.
|
||||
* 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
|
||||
* {@link ClickableComponent#handleKeyPress}.
|
||||
* 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
|
||||
* the space or enter key.
|
||||
* 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
|
||||
* event as a parameter.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} event
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
* @abstract
|
||||
*/
|
||||
handleClick() {}
|
||||
handleClick(event) {}
|
||||
|
||||
/**
|
||||
* Handle Focus - Add keyboard functionality to element
|
||||
* This gets called when a `ClickableComponent` gains focus via a `focus` event.
|
||||
* Turns on listening for `keydown` events. When they happen it
|
||||
* calls `this.handleKeyPress`.
|
||||
*
|
||||
* @method handleFocus
|
||||
* @param {EventTarget~Event} event
|
||||
* The `focus` event that caused this function to be called.
|
||||
*
|
||||
* @listens focus
|
||||
*/
|
||||
handleFocus() {
|
||||
handleFocus(event) {
|
||||
Events.on(document, 'keydown', Fn.bind(this, this.handleKeyPress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle KeyPress (document level) - Trigger click when Space or Enter key is pressed
|
||||
* Called when this ClickableComponent has focus and a key gets pressed down. By
|
||||
* default it will call `this.handleClick` when the key is space or enter.
|
||||
*
|
||||
* @method handleKeyPress
|
||||
* @param {EventTarget~Event} event
|
||||
* The `keydown` event that caused this function to be called.
|
||||
*
|
||||
* @listens keydown
|
||||
*/
|
||||
handleKeyPress(event) {
|
||||
|
||||
// Support Space (32) or Enter (13) key operation to fire a click event
|
||||
if (event.which === 32 || event.which === 13) {
|
||||
event.preventDefault();
|
||||
this.handleClick(event);
|
||||
} else if (super.handleKeyPress) {
|
||||
super.handleKeyPress(event); // Pass keypress handling up for unsupported keys
|
||||
|
||||
// Pass keypress handling up for unsupported keys
|
||||
super.handleKeyPress(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Blur - Remove keyboard triggers
|
||||
* Called when a `ClickableComponent` loses focus. Turns off the listener for
|
||||
* `keydown` events. Which Stops `this.handleKeyPress` from getting called.
|
||||
*
|
||||
* @method handleBlur
|
||||
* @param {EventTarget~Event} event
|
||||
* The `blur` event that caused this function to be called.
|
||||
*
|
||||
* @listens blur
|
||||
*/
|
||||
handleBlur() {
|
||||
handleBlur(event) {
|
||||
Events.off(document, 'keydown', Fn.bind(this, this.handleKeyPress));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,66 @@
|
||||
/**
|
||||
* @file close-button.js
|
||||
*/
|
||||
import Button from './button';
|
||||
import Component from './component';
|
||||
|
||||
/**
|
||||
* The `CloseButton` component is a button which fires a "close" event
|
||||
* when it is activated.
|
||||
* The `CloseButton` is a `{@link Button}` that fires a `close` event when
|
||||
* it gets clicked.
|
||||
*
|
||||
* @extends Button
|
||||
* @class CloseButton
|
||||
*/
|
||||
class CloseButton extends Button {
|
||||
|
||||
/**
|
||||
* Creates an instance of the this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.controlText(options && options.controlText || this.localize('Close'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-close-button ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
/**
|
||||
* This gets called when a `CloseButton` gets clicked. See
|
||||
* {@link ClickableComponent#handleClick} for more information on when this will be
|
||||
* triggered
|
||||
*
|
||||
* @param {EventTarget~Event} event
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
* @fires CloseButton#close
|
||||
*/
|
||||
handleClick(event) {
|
||||
|
||||
/**
|
||||
* Triggered when the a `CloseButton` is clicked.
|
||||
*
|
||||
* @event CloseButton#close
|
||||
* @type {EventTarget~Event}
|
||||
*
|
||||
* @property {boolean} [bubbles=false]
|
||||
* set to false so that the close event does not
|
||||
* bubble up to parents if there is no listener
|
||||
*/
|
||||
this.trigger({type: 'close', bubbles: false});
|
||||
}
|
||||
}
|
||||
|
||||
+676
-664
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -3,20 +3,26 @@
|
||||
*/
|
||||
import TrackButton from '../track-button.js';
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import AudioTrackMenuItem from './audio-track-menu-item.js';
|
||||
|
||||
/**
|
||||
* The base class for buttons that toggle specific text track types (e.g. subtitles)
|
||||
* The base class for buttons that toggle specific {@link AudioTrack} types.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends TrackButton
|
||||
* @class AudioTrackButton
|
||||
*/
|
||||
class AudioTrackButton extends TrackButton {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options={}]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options = {}) {
|
||||
options.tracks = player.audioTracks && player.audioTracks();
|
||||
options.tracks = player.audioTracks();
|
||||
|
||||
super(player, options);
|
||||
|
||||
@@ -24,10 +30,10 @@ class AudioTrackButton extends TrackButton {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-audio-button ${super.buildCSSClass()}`;
|
||||
@@ -36,29 +42,35 @@ class AudioTrackButton extends TrackButton {
|
||||
/**
|
||||
* Create a menu item for each audio track
|
||||
*
|
||||
* @return {Array} Array of menu items
|
||||
* @method createItems
|
||||
* @param {AudioTrackMenuItem[]} [items=[]]
|
||||
* An array of existing menu items to use.
|
||||
*
|
||||
* @return {AudioTrackMenuItem[]}
|
||||
* An array of menu items
|
||||
*/
|
||||
createItems(items = []) {
|
||||
let tracks = this.player_.audioTracks && this.player_.audioTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
const tracks = this.player_.audioTracks();
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
const track = tracks[i];
|
||||
|
||||
items.push(new AudioTrackMenuItem(this.player_, {
|
||||
track,
|
||||
// MenuItem is selectable
|
||||
'selectable': true,
|
||||
'track': track
|
||||
selectable: true
|
||||
}));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `AudioTrackButton`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
AudioTrackButton.prototype.controlText_ = 'Audio Track';
|
||||
Component.registerComponent('AudioTrackButton', AudioTrackButton);
|
||||
export default AudioTrackButton;
|
||||
|
||||
@@ -6,17 +6,24 @@ import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
|
||||
/**
|
||||
* The audio track menu item
|
||||
* An {@link AudioTrack} {@link MenuItem}
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends MenuItem
|
||||
* @class AudioTrackMenuItem
|
||||
*/
|
||||
class AudioTrackMenuItem extends MenuItem {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
let track = options.track;
|
||||
let tracks = player.audioTracks();
|
||||
const track = options.track;
|
||||
const tracks = player.audioTracks();
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options.label = track.label || track.language || 'Unknown';
|
||||
@@ -26,39 +33,44 @@ class AudioTrackMenuItem extends MenuItem {
|
||||
|
||||
this.track = track;
|
||||
|
||||
if (tracks) {
|
||||
let changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', () => {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', () => {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on audio track
|
||||
* This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
|
||||
* for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick(event) {
|
||||
let tracks = this.player_.audioTracks();
|
||||
const tracks = this.player_.audioTracks();
|
||||
|
||||
super.handleClick(event);
|
||||
|
||||
if (!tracks) return;
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
const track = tracks[i];
|
||||
|
||||
track.enabled = track === this.track;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle audio track change
|
||||
* Handle any {@link AudioTrack} change.
|
||||
*
|
||||
* @method handleTracksChange
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The {@link AudioTrackList#change} event that caused this to run.
|
||||
*
|
||||
* @listens AudioTrackList#change
|
||||
*/
|
||||
handleTracksChange(event) {
|
||||
this.selected(this.track.enabled);
|
||||
|
||||
@@ -4,53 +4,57 @@
|
||||
import Component from '../component.js';
|
||||
|
||||
// Required children
|
||||
import PlayToggle from './play-toggle.js';
|
||||
import CurrentTimeDisplay from './time-controls/current-time-display.js';
|
||||
import DurationDisplay from './time-controls/duration-display.js';
|
||||
import TimeDivider from './time-controls/time-divider.js';
|
||||
import RemainingTimeDisplay from './time-controls/remaining-time-display.js';
|
||||
import LiveDisplay from './live-display.js';
|
||||
import ProgressControl from './progress-control/progress-control.js';
|
||||
import FullscreenToggle from './fullscreen-toggle.js';
|
||||
import VolumeControl from './volume-control/volume-control.js';
|
||||
import VolumeMenuButton from './volume-menu-button.js';
|
||||
import MuteToggle from './mute-toggle.js';
|
||||
import ChaptersButton from './text-track-controls/chapters-button.js';
|
||||
import DescriptionsButton from './text-track-controls/descriptions-button.js';
|
||||
import SubtitlesButton from './text-track-controls/subtitles-button.js';
|
||||
import CaptionsButton from './text-track-controls/captions-button.js';
|
||||
import AudioTrackButton from './audio-track-controls/audio-track-button.js';
|
||||
import PlaybackRateMenuButton from './playback-rate-menu/playback-rate-menu-button.js';
|
||||
import CustomControlSpacer from './spacer-controls/custom-control-spacer.js';
|
||||
import './play-toggle.js';
|
||||
import './time-controls/current-time-display.js';
|
||||
import './time-controls/duration-display.js';
|
||||
import './time-controls/time-divider.js';
|
||||
import './time-controls/remaining-time-display.js';
|
||||
import './live-display.js';
|
||||
import './progress-control/progress-control.js';
|
||||
import './fullscreen-toggle.js';
|
||||
import './volume-panel.js';
|
||||
import './text-track-controls/chapters-button.js';
|
||||
import './text-track-controls/descriptions-button.js';
|
||||
import './text-track-controls/subtitles-button.js';
|
||||
import './text-track-controls/captions-button.js';
|
||||
import './audio-track-controls/audio-track-button.js';
|
||||
import './playback-rate-menu/playback-rate-menu-button.js';
|
||||
import './spacer-controls/custom-control-spacer.js';
|
||||
|
||||
/**
|
||||
* Container of main controls
|
||||
* Container of main controls.
|
||||
*
|
||||
* @extends Component
|
||||
* @class ControlBar
|
||||
*/
|
||||
class ControlBar extends Component {
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-control-bar',
|
||||
dir: 'ltr'
|
||||
}, {
|
||||
'role': 'group' // The control bar is a group, so it can contain menuitems
|
||||
// The control bar is a group, so it can contain menuitems
|
||||
role: 'group'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for `ControlBar`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
ControlBar.prototype.options_ = {
|
||||
children: [
|
||||
'playToggle',
|
||||
'volumeMenuButton',
|
||||
'volumePanel',
|
||||
'currentTimeDisplay',
|
||||
'timeDivider',
|
||||
'durationDisplay',
|
||||
|
||||
@@ -8,42 +8,62 @@ import Component from '../component.js';
|
||||
* Toggle fullscreen video
|
||||
*
|
||||
* @extends Button
|
||||
* @class FullscreenToggle
|
||||
*/
|
||||
class FullscreenToggle extends Button {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-fullscreen-control ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Fullscreenchange on the component and change control text accordingly
|
||||
* Handles fullscreenchange on the player and change control text accordingly.
|
||||
*
|
||||
* @method handleFullscreenChange
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The {@link Player#fullscreenchange} event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens Player#fullscreenchange
|
||||
*/
|
||||
handleFullscreenChange() {
|
||||
handleFullscreenChange(event) {
|
||||
if (this.player_.isFullscreen()) {
|
||||
this.controlText('Non-Fullscreen');
|
||||
} else {
|
||||
this.controlText('Fullscreen');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles click for full screen
|
||||
* This gets called when an `FullscreenToggle` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
handleClick(event) {
|
||||
if (!this.player_.isFullscreen()) {
|
||||
this.player_.requestFullscreen();
|
||||
} else {
|
||||
@@ -53,6 +73,12 @@ class FullscreenToggle extends Button {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `FullscreenToggle`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
FullscreenToggle.prototype.controlText_ = 'Fullscreen';
|
||||
|
||||
Component.registerComponent('FullscreenToggle', FullscreenToggle);
|
||||
|
||||
@@ -4,15 +4,24 @@
|
||||
import Component from '../component';
|
||||
import * as Dom from '../utils/dom.js';
|
||||
|
||||
// TODO - Future make it click to snap to live
|
||||
|
||||
/**
|
||||
* Displays the live indicator
|
||||
* TODO - Future make it click to snap to live
|
||||
* Displays the live indicator when duration is Infinity.
|
||||
*
|
||||
* @extends Component
|
||||
* @class LiveDisplay
|
||||
*/
|
||||
class LiveDisplay extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
@@ -21,13 +30,13 @@ class LiveDisplay extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
var el = super.createEl('div', {
|
||||
const el = super.createEl('div', {
|
||||
className: 'vjs-live-control vjs-control'
|
||||
});
|
||||
|
||||
@@ -42,7 +51,16 @@ class LiveDisplay extends Component {
|
||||
return el;
|
||||
}
|
||||
|
||||
updateShowing() {
|
||||
/**
|
||||
* Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
|
||||
* it accordingly
|
||||
*
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The {@link Player#durationchange} event that caused this function to run.
|
||||
*
|
||||
* @listens Player#durationchange
|
||||
*/
|
||||
updateShowing(event) {
|
||||
if (this.player().duration() === Infinity) {
|
||||
this.show();
|
||||
} else {
|
||||
|
||||
@@ -4,65 +4,78 @@
|
||||
import Button from '../button';
|
||||
import Component from '../component';
|
||||
import * as Dom from '../utils/dom.js';
|
||||
import checkVolumeSupport from './volume-control/check-volume-support';
|
||||
|
||||
/**
|
||||
* A button component for muting the audio
|
||||
* A button component for muting the audio.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Button
|
||||
* @class MuteToggle
|
||||
*/
|
||||
class MuteToggle extends Button {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'volumechange', this.update);
|
||||
// hide this control if volume support is missing
|
||||
checkVolumeSupport(this, player);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
|
||||
this.on(player, 'loadstart', function() {
|
||||
this.update(); // We need to update the button to account for a default muted state.
|
||||
|
||||
if (player.tech_['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
});
|
||||
this.on(player, ['loadstart', 'volumechange'], this.update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-mute-control ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on mute
|
||||
* This gets called when an `MuteToggle` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
this.player_.muted( this.player_.muted() ? false : true );
|
||||
handleClick(event) {
|
||||
const vol = this.player_.volume();
|
||||
const lastVolume = this.player_.lastVolume_();
|
||||
|
||||
if (vol === 0) {
|
||||
this.player_.volume(lastVolume);
|
||||
this.player_.muted(false);
|
||||
} else {
|
||||
this.player_.muted(this.player_.muted() ? false : true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update volume
|
||||
* Update the state of volume.
|
||||
*
|
||||
* @method update
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The {@link Player#loadstart} event if this function was called through an
|
||||
* event.
|
||||
*
|
||||
* @listens Player#loadstart
|
||||
*/
|
||||
update() {
|
||||
var vol = this.player_.volume(),
|
||||
level = 3;
|
||||
update(event) {
|
||||
const vol = this.player_.volume();
|
||||
let level = 3;
|
||||
|
||||
if (vol === 0 || this.player_.muted()) {
|
||||
level = 0;
|
||||
@@ -75,20 +88,27 @@ class MuteToggle extends Button {
|
||||
// Don't rewrite the button text if the actual text doesn't change.
|
||||
// This causes unnecessary and confusing information for screen reader users.
|
||||
// This check is needed because this function gets called every time the volume level is changed.
|
||||
let toMute = this.player_.muted() ? 'Unmute' : 'Mute';
|
||||
const toMute = this.player_.muted() ? 'Unmute' : 'Mute';
|
||||
|
||||
if (this.controlText() !== toMute) {
|
||||
this.controlText(toMute);
|
||||
}
|
||||
|
||||
/* TODO improve muted icon classes */
|
||||
for (var i = 0; i < 4; i++) {
|
||||
Dom.removeElClass(this.el_, `vjs-vol-${i}`);
|
||||
// TODO improve muted icon classes
|
||||
for (let i = 0; i < 4; i++) {
|
||||
Dom.removeClass(this.el_, `vjs-vol-${i}`);
|
||||
}
|
||||
Dom.addElClass(this.el_, `vjs-vol-${level}`);
|
||||
Dom.addClass(this.el_, `vjs-vol-${level}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `MuteToggle`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
MuteToggle.prototype.controlText_ = 'Mute';
|
||||
|
||||
Component.registerComponent('MuteToggle', MuteToggle);
|
||||
|
||||
@@ -5,38 +5,51 @@ import Button from '../button.js';
|
||||
import Component from '../component.js';
|
||||
|
||||
/**
|
||||
* Button to toggle between play and pause
|
||||
* Button to toggle between play and pause.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Button
|
||||
* @class PlayToggle
|
||||
*/
|
||||
class PlayToggle extends Button {
|
||||
|
||||
constructor(player, options){
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'play', this.handlePlay);
|
||||
this.on(player, 'pause', this.handlePause);
|
||||
this.on(player, 'ended', this.handleEnded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-play-control ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click to toggle between play and pause
|
||||
* This gets called when an `PlayToggle` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
handleClick(event) {
|
||||
if (this.player_.paused()) {
|
||||
this.player_.play();
|
||||
} else {
|
||||
@@ -45,29 +58,55 @@ class PlayToggle extends Button {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vjs-playing class to the element so it can change appearance
|
||||
* Add the vjs-playing class to the element so it can change appearance.
|
||||
*
|
||||
* @method handlePlay
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The event that caused this function to run.
|
||||
*
|
||||
* @listens Player#play
|
||||
*/
|
||||
handlePlay() {
|
||||
handlePlay(event) {
|
||||
this.removeClass('vjs-ended');
|
||||
this.removeClass('vjs-paused');
|
||||
this.addClass('vjs-playing');
|
||||
this.controlText('Pause'); // change the button text to "Pause"
|
||||
// change the button text to "Pause"
|
||||
this.controlText('Pause');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vjs-paused class to the element so it can change appearance
|
||||
* Add the vjs-paused class to the element so it can change appearance.
|
||||
*
|
||||
* @method handlePause
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The event that caused this function to run.
|
||||
*
|
||||
* @listens Player#pause
|
||||
*/
|
||||
handlePause() {
|
||||
handlePause(event) {
|
||||
this.removeClass('vjs-playing');
|
||||
this.addClass('vjs-paused');
|
||||
this.controlText('Play'); // change the button text to "Play"
|
||||
// change the button text to "Play"
|
||||
this.controlText('Play');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vjs-ended class to the element so it can change appearance
|
||||
*
|
||||
* @method handleEnded
|
||||
*/
|
||||
handleEnded(event) {
|
||||
this.removeClass('vjs-playing');
|
||||
this.addClass('vjs-ended');
|
||||
// change the button text to "Replay"
|
||||
this.controlText('Replay');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `PlayToggle`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
PlayToggle.prototype.controlText_ = 'Play';
|
||||
|
||||
Component.registerComponent('PlayToggle', PlayToggle);
|
||||
|
||||
@@ -8,16 +8,22 @@ import Component from '../../component.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
|
||||
/**
|
||||
* The component for controlling the playback rate
|
||||
* The component for controlling the playback rate.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends MenuButton
|
||||
* @class PlaybackRateMenuButton
|
||||
*/
|
||||
class PlaybackRateMenuButton extends MenuButton {
|
||||
|
||||
constructor(player, options){
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.updateVisibility();
|
||||
@@ -28,13 +34,13 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl();
|
||||
const el = super.createEl();
|
||||
|
||||
this.labelEl_ = Dom.createEl('div', {
|
||||
className: 'vjs-playback-rate-value',
|
||||
@@ -47,10 +53,10 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-playback-rate ${super.buildCSSClass()}`;
|
||||
@@ -59,17 +65,17 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
/**
|
||||
* Create the playback rate menu
|
||||
*
|
||||
* @return {Menu} Menu object populated with items
|
||||
* @method createMenu
|
||||
* @return {Menu}
|
||||
* Menu object populated with {@link PlaybackRateMenuItem}s
|
||||
*/
|
||||
createMenu() {
|
||||
let menu = new Menu(this.player());
|
||||
let rates = this.playbackRates();
|
||||
const menu = new Menu(this.player());
|
||||
const rates = this.playbackRates();
|
||||
|
||||
if (rates) {
|
||||
for (let i = rates.length - 1; i >= 0; i--) {
|
||||
menu.addChild(
|
||||
new PlaybackRateMenuItem(this.player(), { 'rate': rates[i] + 'x'})
|
||||
new PlaybackRateMenuItem(this.player(), {rate: rates[i] + 'x'})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -79,8 +85,6 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
|
||||
/**
|
||||
* Updates ARIA accessibility attributes
|
||||
*
|
||||
* @method updateARIAAttributes
|
||||
*/
|
||||
updateARIAAttributes() {
|
||||
// Current playback rate
|
||||
@@ -88,18 +92,25 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle menu item click
|
||||
* This gets called when an `PlaybackRateMenuButton` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
handleClick(event) {
|
||||
// select next rate option
|
||||
let currentRate = this.player().playbackRate();
|
||||
let rates = this.playbackRates();
|
||||
const currentRate = this.player().playbackRate();
|
||||
const rates = this.playbackRates();
|
||||
|
||||
// this will select first one if the last one currently selected
|
||||
let newRate = rates[0];
|
||||
for (let i = 0; i < rates.length ; i++) {
|
||||
|
||||
for (let i = 0; i < rates.length; i++) {
|
||||
if (rates[i] > currentRate) {
|
||||
newRate = rates[i];
|
||||
break;
|
||||
@@ -111,34 +122,37 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
/**
|
||||
* Get possible playback rates
|
||||
*
|
||||
* @return {Array} Possible playback rates
|
||||
* @method playbackRates
|
||||
* @return {Array}
|
||||
* All possible playback rates
|
||||
*/
|
||||
playbackRates() {
|
||||
return this.options_['playbackRates'] || (this.options_.playerOptions && this.options_.playerOptions['playbackRates']);
|
||||
return this.options_.playbackRates || (this.options_.playerOptions && this.options_.playerOptions.playbackRates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether playback rates is supported by the tech
|
||||
* and an array of playback rates exists
|
||||
*
|
||||
* @return {Boolean} Whether changing playback rate is supported
|
||||
* @method playbackRateSupported
|
||||
* @return {boolean}
|
||||
* Whether changing playback rate is supported
|
||||
*/
|
||||
playbackRateSupported() {
|
||||
return this.player().tech_
|
||||
&& this.player().tech_['featuresPlaybackRate']
|
||||
&& this.playbackRates()
|
||||
&& this.playbackRates().length > 0
|
||||
return this.player().tech_ &&
|
||||
this.player().tech_.featuresPlaybackRate &&
|
||||
this.playbackRates() &&
|
||||
this.playbackRates().length > 0
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide playback rate controls when they're no playback rate options to select
|
||||
*
|
||||
* @method updateVisibility
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The event that caused this function to run.
|
||||
*
|
||||
* @listens Player#loadstart
|
||||
*/
|
||||
updateVisibility() {
|
||||
updateVisibility(event) {
|
||||
if (this.playbackRateSupported()) {
|
||||
this.removeClass('vjs-hidden');
|
||||
} else {
|
||||
@@ -149,9 +163,12 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
/**
|
||||
* Update button label when rate changed
|
||||
*
|
||||
* @method updateLabel
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The event that caused this function to run.
|
||||
*
|
||||
* @listens Player#ratechange
|
||||
*/
|
||||
updateLabel() {
|
||||
updateLabel(event) {
|
||||
if (this.playbackRateSupported()) {
|
||||
this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
|
||||
}
|
||||
@@ -159,6 +176,12 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `FullscreenToggle`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
|
||||
|
||||
Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
|
||||
|
||||
@@ -5,22 +5,28 @@ import MenuItem from '../../menu/menu-item.js';
|
||||
import Component from '../../component.js';
|
||||
|
||||
/**
|
||||
* The specific menu item type for selecting a playback rate
|
||||
* The specific menu item type for selecting a playback rate.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends MenuItem
|
||||
* @class PlaybackRateMenuItem
|
||||
*/
|
||||
class PlaybackRateMenuItem extends MenuItem {
|
||||
|
||||
constructor(player, options){
|
||||
let label = options['rate'];
|
||||
let rate = parseFloat(label, 10);
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
const label = options.rate;
|
||||
const rate = parseFloat(label, 10);
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = label;
|
||||
options['selected'] = rate === 1;
|
||||
options.label = label;
|
||||
options.selected = rate === 1;
|
||||
super(player, options);
|
||||
|
||||
this.label = label;
|
||||
@@ -30,26 +36,41 @@ class PlaybackRateMenuItem extends MenuItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on menu item
|
||||
* This gets called when an `PlaybackRateMenuItem` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick() {
|
||||
handleClick(event) {
|
||||
super.handleClick();
|
||||
this.player().playbackRate(this.rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update playback rate with selected rate
|
||||
* Update the PlaybackRateMenuItem when the playbackrate changes.
|
||||
*
|
||||
* @method update
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `ratechange` event that caused this function to run.
|
||||
*
|
||||
* @listens Player#ratechange
|
||||
*/
|
||||
update() {
|
||||
update(event) {
|
||||
this.selected(this.player().playbackRate() === this.rate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
PlaybackRateMenuItem.prototype.contentElType = 'button';
|
||||
|
||||
Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
|
||||
|
||||
@@ -5,25 +5,32 @@ import Component from '../../component.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Shows load progress
|
||||
* Shows loading progress
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Component
|
||||
* @class LoadProgressBar
|
||||
*/
|
||||
class LoadProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.partEls_ = [];
|
||||
this.on(player, 'progress', this.update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
@@ -35,17 +42,22 @@ class LoadProgressBar extends Component {
|
||||
/**
|
||||
* Update progress bar
|
||||
*
|
||||
* @method update
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `progress` event that caused this function to run.
|
||||
*
|
||||
* @listens Player#progress
|
||||
*/
|
||||
update() {
|
||||
let buffered = this.player_.buffered();
|
||||
let duration = this.player_.duration();
|
||||
let bufferedEnd = this.player_.bufferedEnd();
|
||||
let children = this.el_.children;
|
||||
update(event) {
|
||||
const buffered = this.player_.buffered();
|
||||
const duration = this.player_.duration();
|
||||
const bufferedEnd = this.player_.bufferedEnd();
|
||||
const children = this.partEls_;
|
||||
|
||||
// get the percent width of a time compared to the total end
|
||||
let percentify = function (time, end){
|
||||
let percent = (time / end) || 0; // no NaN
|
||||
const percentify = function(time, end) {
|
||||
// no NaN
|
||||
const percent = (time / end) || 0;
|
||||
|
||||
return ((percent >= 1 ? 1 : percent) * 100) + '%';
|
||||
};
|
||||
|
||||
@@ -54,12 +66,13 @@ class LoadProgressBar extends Component {
|
||||
|
||||
// add child elements to represent the individual buffered time ranges
|
||||
for (let i = 0; i < buffered.length; i++) {
|
||||
let start = buffered.start(i);
|
||||
let end = buffered.end(i);
|
||||
const start = buffered.start(i);
|
||||
const end = buffered.end(i);
|
||||
let part = children[i];
|
||||
|
||||
if (!part) {
|
||||
part = this.el_.appendChild(Dom.createEl());
|
||||
children[i] = part;
|
||||
}
|
||||
|
||||
// set the percent based on the width of the progress bar (bufferedEnd)
|
||||
@@ -69,8 +82,9 @@ class LoadProgressBar extends Component {
|
||||
|
||||
// remove unused buffered range elements
|
||||
for (let i = children.length; i > buffered.length; i--) {
|
||||
this.el_.removeChild(children[i-1]);
|
||||
this.el_.removeChild(children[i - 1]);
|
||||
}
|
||||
children.length = buffered.length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,52 +1,43 @@
|
||||
/**
|
||||
* @file mouse-time-display.js
|
||||
*/
|
||||
import window from 'global/window';
|
||||
import Component from '../../component.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
// import * as Dom from '../../utils/dom.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import formatTime from '../../utils/format-time.js';
|
||||
import throttle from 'lodash-compat/function/throttle';
|
||||
// import computedStyle from '../../utils/computed-style.js';
|
||||
|
||||
import './time-tooltip';
|
||||
|
||||
/**
|
||||
* The Mouse Time Display component shows the time you will seek to
|
||||
* when hovering over the progress bar
|
||||
* The {@link MouseTimeDisplay} component tracks mouse movement over the
|
||||
* {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
|
||||
* indicating the time which is represented by a given point in the
|
||||
* {@link ProgressControl}.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Component
|
||||
* @class MouseTimeDisplay
|
||||
*/
|
||||
class MouseTimeDisplay extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The {@link Player} that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
if (options.playerOptions &&
|
||||
options.playerOptions.controlBar &&
|
||||
options.playerOptions.controlBar.progressControl &&
|
||||
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
|
||||
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
|
||||
}
|
||||
|
||||
if (this.keepTooltipsInside) {
|
||||
this.tooltip = Dom.createEl('div', {className: 'vjs-time-tooltip'});
|
||||
this.el().appendChild(this.tooltip);
|
||||
this.addClass('vjs-keep-tooltips-inside');
|
||||
}
|
||||
|
||||
this.update(0, 0);
|
||||
|
||||
player.on('ready', () => {
|
||||
this.on(player.controlBar.progressControl.el(), 'mousemove', throttle(Fn.bind(this, this.handleMouseMove), 25));
|
||||
});
|
||||
this.update = Fn.throttle(Fn.bind(this, this.update), 25);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the the DOM element for this class.
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
@@ -54,64 +45,45 @@ class MouseTimeDisplay extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleMouseMove(event) {
|
||||
let duration = this.player_.duration();
|
||||
let newTime = this.calculateDistance(event) * duration;
|
||||
let position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
|
||||
|
||||
this.update(newTime, position);
|
||||
}
|
||||
|
||||
update(newTime, position) {
|
||||
let time = formatTime(newTime, this.player_.duration());
|
||||
|
||||
this.el().style.left = position + 'px';
|
||||
this.el().setAttribute('data-current-time', time);
|
||||
|
||||
if (this.keepTooltipsInside) {
|
||||
let clampedPosition = this.clampPosition_(position);
|
||||
let difference = position - clampedPosition + 1;
|
||||
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
|
||||
let tooltipWidthHalf = tooltipWidth / 2;
|
||||
|
||||
this.tooltip.innerHTML = time;
|
||||
this.tooltip.style.right = `-${tooltipWidthHalf - difference}px`;
|
||||
}
|
||||
}
|
||||
|
||||
calculateDistance(event) {
|
||||
return Dom.getPointerPosition(this.el().parentNode, event).x;
|
||||
}
|
||||
|
||||
/**
|
||||
* This takes in a horizontal position for the bar and returns a clamped position.
|
||||
* Clamped position means that it will keep the position greater than half the width
|
||||
* of the tooltip and smaller than the player width minus half the width o the tooltip.
|
||||
* It will only clamp the position if `keepTooltipsInside` option is set.
|
||||
* Enqueues updates to its own DOM as well as the DOM of its
|
||||
* {@link TimeTooltip} child.
|
||||
*
|
||||
* @param {Number} position the position the bar wants to be
|
||||
* @return {Number} newPosition the (potentially) clamped position
|
||||
* @method clampPosition_
|
||||
* @param {Object} seekBarRect
|
||||
* The `ClientRect` for the {@link SeekBar} element.
|
||||
*
|
||||
* @param {number} seekBarPoint
|
||||
* A number from 0 to 1, representing a horizontal reference point
|
||||
* from the left edge of the {@link SeekBar}
|
||||
*/
|
||||
clampPosition_(position) {
|
||||
if (!this.keepTooltipsInside) {
|
||||
return position;
|
||||
update(seekBarRect, seekBarPoint) {
|
||||
|
||||
// If there is an existing rAF ID, cancel it so we don't over-queue.
|
||||
if (this.rafId_) {
|
||||
this.cancelAnimationFrame(this.rafId_);
|
||||
}
|
||||
|
||||
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
|
||||
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
|
||||
let tooltipWidthHalf = tooltipWidth / 2;
|
||||
let actualPosition = position;
|
||||
this.rafId_ = this.requestAnimationFrame(() => {
|
||||
const duration = this.player_.duration();
|
||||
const content = formatTime(seekBarPoint * duration, duration);
|
||||
|
||||
if (position < tooltipWidthHalf) {
|
||||
actualPosition = Math.ceil(tooltipWidthHalf);
|
||||
} else if (position > (playerWidth - tooltipWidthHalf)) {
|
||||
actualPosition = Math.floor(playerWidth - tooltipWidthHalf);
|
||||
}
|
||||
|
||||
return actualPosition;
|
||||
this.el_.style.left = `${seekBarRect.width * seekBarPoint}px`;
|
||||
this.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for `MouseTimeDisplay`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
MouseTimeDisplay.prototype.options_ = {
|
||||
children: [
|
||||
'timeTooltip'
|
||||
]
|
||||
};
|
||||
|
||||
Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
|
||||
export default MouseTimeDisplay;
|
||||
|
||||
@@ -2,43 +2,36 @@
|
||||
* @file play-progress-bar.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import formatTime from '../../utils/format-time.js';
|
||||
|
||||
import './time-tooltip';
|
||||
|
||||
/**
|
||||
* Shows play progress
|
||||
* Used by {@link SeekBar} to display media playback progress as part of the
|
||||
* {@link ProgressControl}.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Component
|
||||
* @class PlayProgressBar
|
||||
*/
|
||||
class PlayProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The {@link Player} that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.updateDataAttr();
|
||||
this.on(player, 'timeupdate', this.updateDataAttr);
|
||||
player.ready(Fn.bind(this, this.updateDataAttr));
|
||||
|
||||
if (options.playerOptions &&
|
||||
options.playerOptions.controlBar &&
|
||||
options.playerOptions.controlBar.progressControl &&
|
||||
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
|
||||
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
|
||||
}
|
||||
|
||||
if (this.keepTooltipsInside) {
|
||||
this.addClass('vjs-keep-tooltips-inside');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the the DOM element for this class.
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
@@ -47,12 +40,47 @@ class PlayProgressBar extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
updateDataAttr() {
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
this.el_.setAttribute('data-current-time', formatTime(time, this.player_.duration()));
|
||||
}
|
||||
/**
|
||||
* Enqueues updates to its own DOM as well as the DOM of its
|
||||
* {@link TimeTooltip} child.
|
||||
*
|
||||
* @param {Object} seekBarRect
|
||||
* The `ClientRect` for the {@link SeekBar} element.
|
||||
*
|
||||
* @param {number} seekBarPoint
|
||||
* A number from 0 to 1, representing a horizontal reference point
|
||||
* from the left edge of the {@link SeekBar}
|
||||
*/
|
||||
update(seekBarRect, seekBarPoint) {
|
||||
|
||||
// If there is an existing rAF ID, cancel it so we don't over-queue.
|
||||
if (this.rafId_) {
|
||||
this.cancelAnimationFrame(this.rafId_);
|
||||
}
|
||||
|
||||
this.rafId_ = this.requestAnimationFrame(() => {
|
||||
const time = (this.player_.scrubbing()) ?
|
||||
this.player_.getCache().currentTime :
|
||||
this.player_.currentTime();
|
||||
|
||||
const content = formatTime(time, this.player_.duration());
|
||||
|
||||
this.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for {@link PlayProgressBar}.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
PlayProgressBar.prototype.options_ = {
|
||||
children: [
|
||||
'timeTooltip'
|
||||
]
|
||||
};
|
||||
|
||||
Component.registerComponent('PlayProgressBar', PlayProgressBar);
|
||||
export default PlayProgressBar;
|
||||
|
||||
@@ -2,33 +2,80 @@
|
||||
* @file progress-control.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import SeekBar from './seek-bar.js';
|
||||
import MouseTimeDisplay from './mouse-time-display.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
|
||||
import './seek-bar.js';
|
||||
|
||||
/**
|
||||
* The Progress Control component contains the seek bar, load progress,
|
||||
* and play progress
|
||||
* and play progress.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Component
|
||||
* @class ProgressControl
|
||||
*/
|
||||
class ProgressControl extends Component {
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.handleMouseMove = Fn.throttle(Fn.bind(this, this.handleMouseMove), 25);
|
||||
this.on(this.el_, 'mousemove', this.handleMouseMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-progress-control vjs-control'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When the mouse moves over the `ProgressControl`, the pointer position
|
||||
* gets passed down to the `MouseTimeDisplay` component.
|
||||
*
|
||||
* @param {EventTarget~Event} event
|
||||
* The `mousemove` event that caused this function to run.
|
||||
*
|
||||
* @listen mousemove
|
||||
*/
|
||||
handleMouseMove(event) {
|
||||
const seekBar = this.getChild('seekBar');
|
||||
const seekBarEl = seekBar.el();
|
||||
const seekBarRect = Dom.getBoundingClientRect(seekBarEl);
|
||||
let seekBarPoint = Dom.getPointerPosition(seekBarEl, event).x;
|
||||
|
||||
// The default skin has a gap on either side of the `SeekBar`. This means
|
||||
// that it's possible to trigger this behavior outside the boundaries of
|
||||
// the `SeekBar`. This ensures we stay within it at all times.
|
||||
if (seekBarPoint > 1) {
|
||||
seekBarPoint = 1;
|
||||
} else if (seekBarPoint < 0) {
|
||||
seekBarPoint = 0;
|
||||
}
|
||||
|
||||
seekBar.getChild('mouseTimeDisplay').update(seekBarRect, seekBarPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for `ProgressControl`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
ProgressControl.prototype.options_ = {
|
||||
children: [
|
||||
'seekBar'
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
/**
|
||||
* @file seek-bar.js
|
||||
*/
|
||||
import window from 'global/window';
|
||||
import Slider from '../../slider/slider.js';
|
||||
import Component from '../../component.js';
|
||||
import LoadProgressBar from './load-progress-bar.js';
|
||||
import PlayProgressBar from './play-progress-bar.js';
|
||||
import TooltipProgressBar from './tooltip-progress-bar.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import formatTime from '../../utils/format-time.js';
|
||||
import assign from 'object.assign';
|
||||
|
||||
import './load-progress-bar.js';
|
||||
import './play-progress-bar.js';
|
||||
import './mouse-time-display.js';
|
||||
|
||||
// The number of seconds the `step*` functions move the timeline.
|
||||
const STEP_SECONDS = 5;
|
||||
|
||||
/**
|
||||
* Seek Bar and holder for the progress bars
|
||||
* Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
|
||||
* as its `bar`.
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Slider
|
||||
* @class SeekBar
|
||||
*/
|
||||
class SeekBar extends Slider {
|
||||
|
||||
constructor(player, options){
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'timeupdate', this.updateProgress);
|
||||
this.on(player, 'ended', this.updateProgress);
|
||||
player.ready(Fn.bind(this, this.updateProgress));
|
||||
|
||||
if (options.playerOptions &&
|
||||
options.playerOptions.controlBar &&
|
||||
options.playerOptions.controlBar.progressControl &&
|
||||
options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
|
||||
this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
|
||||
}
|
||||
|
||||
if (this.keepTooltipsInside) {
|
||||
this.tooltipProgressBar = this.addChild('TooltipProgressBar');
|
||||
}
|
||||
this.update = Fn.throttle(Fn.bind(this, this.update), 50);
|
||||
this.on(player, ['timeupdate', 'ended'], this.update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
@@ -54,68 +52,85 @@ class SeekBar extends Slider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update ARIA accessibility attributes
|
||||
* Update the seek bar's UI.
|
||||
*
|
||||
* @method updateARIAAttributes
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `timeupdate` or `ended` event that caused this to run.
|
||||
*
|
||||
* @listens Player#timeupdate
|
||||
* @listens Player#ended
|
||||
*/
|
||||
updateProgress() {
|
||||
this.updateAriaAttributes(this.el_);
|
||||
update() {
|
||||
const percent = super.update();
|
||||
const duration = this.player_.duration();
|
||||
|
||||
if (this.keepTooltipsInside) {
|
||||
this.updateAriaAttributes(this.tooltipProgressBar.el_);
|
||||
this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width;
|
||||
|
||||
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
|
||||
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width);
|
||||
let tooltipStyle = this.tooltipProgressBar.el().style;
|
||||
tooltipStyle.maxWidth = Math.floor(playerWidth - (tooltipWidth / 2)) + 'px';
|
||||
tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px';
|
||||
tooltipStyle.right = `-${tooltipWidth / 2}px`;
|
||||
}
|
||||
}
|
||||
|
||||
updateAriaAttributes(el) {
|
||||
// Allows for smooth scrubbing, when player can't keep up.
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete)
|
||||
el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete)
|
||||
const time = (this.player_.scrubbing()) ?
|
||||
this.player_.getCache().currentTime :
|
||||
this.player_.currentTime();
|
||||
|
||||
// machine readable value of progress bar (percentage complete)
|
||||
this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
|
||||
|
||||
// human readable value of progress bar (time complete)
|
||||
this.el_.setAttribute('aria-valuetext', formatTime(time, duration));
|
||||
|
||||
// Update the `PlayProgressBar`.
|
||||
this.bar.update(Dom.getBoundingClientRect(this.el_), percent);
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get percentage of video played
|
||||
* Get the percentage of media played so far.
|
||||
*
|
||||
* @return {Number} Percentage played
|
||||
* @method getPercent
|
||||
* @return {number}
|
||||
* The percentage of media played so far (0 to 1).
|
||||
*/
|
||||
getPercent() {
|
||||
let percent = this.player_.currentTime() / this.player_.duration();
|
||||
|
||||
// Allows for smooth scrubbing, when player can't keep up.
|
||||
const time = (this.player_.scrubbing()) ?
|
||||
this.player_.getCache().currentTime :
|
||||
this.player_.currentTime();
|
||||
|
||||
const percent = time / this.player_.duration();
|
||||
|
||||
return percent >= 1 ? 1 : percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse down on seek bar
|
||||
*
|
||||
* @method handleMouseDown
|
||||
* @param {EventTarget~Event} event
|
||||
* The `mousedown` event that caused this to run.
|
||||
*
|
||||
* @listens mousedown
|
||||
*/
|
||||
handleMouseDown(event) {
|
||||
super.handleMouseDown(event);
|
||||
|
||||
this.player_.scrubbing(true);
|
||||
|
||||
this.videoWasPlaying = !this.player_.paused();
|
||||
this.player_.pause();
|
||||
|
||||
super.handleMouseDown(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse move on seek bar
|
||||
*
|
||||
* @method handleMouseMove
|
||||
* @param {EventTarget~Event} event
|
||||
* The `mousemove` event that caused this to run.
|
||||
*
|
||||
* @listens mousemove
|
||||
*/
|
||||
handleMouseMove(event) {
|
||||
let newTime = this.calculateDistance(event) * this.player_.duration();
|
||||
|
||||
// Don't let video end while scrubbing.
|
||||
if (newTime === this.player_.duration()) { newTime = newTime - 0.1; }
|
||||
if (newTime === this.player_.duration()) {
|
||||
newTime = newTime - 0.1;
|
||||
}
|
||||
|
||||
// Set new time (tell player to seek to new time)
|
||||
this.player_.currentTime(newTime);
|
||||
@@ -124,7 +139,10 @@ class SeekBar extends Slider {
|
||||
/**
|
||||
* Handle mouse up on seek bar
|
||||
*
|
||||
* @method handleMouseUp
|
||||
* @param {EventTarget~Event} event
|
||||
* The `mouseup` event that caused this to run.
|
||||
*
|
||||
* @listens mouseup
|
||||
*/
|
||||
handleMouseUp(event) {
|
||||
super.handleMouseUp(event);
|
||||
@@ -137,33 +155,39 @@ class SeekBar extends Slider {
|
||||
|
||||
/**
|
||||
* Move more quickly fast forward for keyboard-only users
|
||||
*
|
||||
* @method stepForward
|
||||
*/
|
||||
stepForward() {
|
||||
this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
|
||||
this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move more quickly rewind for keyboard-only users
|
||||
*
|
||||
* @method stepBack
|
||||
*/
|
||||
stepBack() {
|
||||
this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
|
||||
this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for the `SeekBar`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
SeekBar.prototype.options_ = {
|
||||
children: [
|
||||
'loadProgressBar',
|
||||
'mouseTimeDisplay',
|
||||
'playProgressBar'
|
||||
],
|
||||
'barName': 'playProgressBar'
|
||||
barName: 'playProgressBar'
|
||||
};
|
||||
|
||||
/**
|
||||
* Call the update event for this Slider when this event happens on the player.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
SeekBar.prototype.playerEvent = 'timeupdate';
|
||||
|
||||
Component.registerComponent('SeekBar', SeekBar);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file time-tooltip.js
|
||||
*/
|
||||
import Component from '../../component';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
|
||||
/**
|
||||
* Time tooltips display a time above the progress bar.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class TimeTooltip extends Component {
|
||||
|
||||
/**
|
||||
* Create the time tooltip DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-time-tooltip'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the position of the time tooltip relative to the `SeekBar`.
|
||||
*
|
||||
* @param {Object} seekBarRect
|
||||
* The `ClientRect` for the {@link SeekBar} element.
|
||||
*
|
||||
* @param {number} seekBarPoint
|
||||
* A number from 0 to 1, representing a horizontal reference point
|
||||
* from the left edge of the {@link SeekBar}
|
||||
*/
|
||||
update(seekBarRect, seekBarPoint, content) {
|
||||
const tooltipRect = Dom.getBoundingClientRect(this.el_);
|
||||
const playerRect = Dom.getBoundingClientRect(this.player_.el());
|
||||
const seekBarPointPx = seekBarRect.width * seekBarPoint;
|
||||
|
||||
// do nothing if either rect isn't available
|
||||
// for example, if the player isn't in the DOM for testing
|
||||
if (!playerRect || !tooltipRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the space left of the `seekBarPoint` available within the bounds
|
||||
// of the player. We calculate any gap between the left edge of the player
|
||||
// and the left edge of the `SeekBar` and add the number of pixels in the
|
||||
// `SeekBar` before hitting the `seekBarPoint`
|
||||
const spaceLeftOfPoint = (seekBarRect.left - playerRect.left) + seekBarPointPx;
|
||||
|
||||
// This is the space right of the `seekBarPoint` available within the bounds
|
||||
// of the player. We calculate the number of pixels from the `seekBarPoint`
|
||||
// to the right edge of the `SeekBar` and add to that any gap between the
|
||||
// right edge of the `SeekBar` and the player.
|
||||
const spaceRightOfPoint = (seekBarRect.width - seekBarPointPx) +
|
||||
(playerRect.right - seekBarRect.right);
|
||||
|
||||
// This is the number of pixels by which the tooltip will need to be pulled
|
||||
// further to the right to center it over the `seekBarPoint`.
|
||||
let pullTooltipBy = tooltipRect.width / 2;
|
||||
|
||||
// Adjust the `pullTooltipBy` distance to the left or right depending on
|
||||
// the results of the space calculations above.
|
||||
if (spaceLeftOfPoint < pullTooltipBy) {
|
||||
pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
|
||||
} else if (spaceRightOfPoint < pullTooltipBy) {
|
||||
pullTooltipBy = spaceRightOfPoint;
|
||||
}
|
||||
|
||||
// Due to the imprecision of decimal/ratio based calculations and varying
|
||||
// rounding behaviors, there are cases where the spacing adjustment is off
|
||||
// by a pixel or two. This adds insurance to these calculations.
|
||||
if (pullTooltipBy < 0) {
|
||||
pullTooltipBy = 0;
|
||||
} else if (pullTooltipBy > tooltipRect.width) {
|
||||
pullTooltipBy = tooltipRect.width;
|
||||
}
|
||||
|
||||
this.el_.style.right = `-${pullTooltipBy}px`;
|
||||
Dom.textContent(this.el_, content);
|
||||
}
|
||||
}
|
||||
|
||||
Component.registerComponent('TimeTooltip', TimeTooltip);
|
||||
export default TimeTooltip;
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* @file play-progress-bar.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import formatTime from '../../utils/format-time.js';
|
||||
|
||||
/**
|
||||
* Shows play progress
|
||||
*
|
||||
* @param {Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @extends Component
|
||||
* @class PlayProgressBar
|
||||
*/
|
||||
class TooltipProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
super(player, options);
|
||||
this.updateDataAttr();
|
||||
this.on(player, 'timeupdate', this.updateDataAttr);
|
||||
player.ready(Fn.bind(this, this.updateDataAttr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl('div', {
|
||||
className: 'vjs-tooltip-progress-bar vjs-slider-bar',
|
||||
innerHTML: `<div class="vjs-time-tooltip"></div>
|
||||
<span class="vjs-control-text"><span>${this.localize('Progress')}</span>: 0%</span>`
|
||||
});
|
||||
|
||||
this.tooltip = el.querySelector('.vjs-time-tooltip');
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
updateDataAttr() {
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
let formattedTime = formatTime(time, this.player_.duration());
|
||||
this.el_.setAttribute('data-current-time', formattedTime);
|
||||
this.tooltip.innerHTML = formattedTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component.registerComponent('TooltipProgressBar', TooltipProgressBar);
|
||||
export default TooltipProgressBar;
|
||||
@@ -8,29 +8,28 @@ import Component from '../../component.js';
|
||||
* Spacer specifically meant to be used as an insertion point for new plugins, etc.
|
||||
*
|
||||
* @extends Spacer
|
||||
* @class CustomControlSpacer
|
||||
*/
|
||||
class CustomControlSpacer extends Spacer {
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {String} The constructed class name
|
||||
* @method buildCSSClass
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `vjs-custom-control-spacer ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl({
|
||||
className: this.buildCSSClass(),
|
||||
const el = super.createEl({
|
||||
className: this.buildCSSClass()
|
||||
});
|
||||
|
||||
// No-flex/table-cell mode requires there be some content
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário