Comparar commits
131 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 4944f85888 | |||
| 5f95e413e3 | |||
| 057588f6c7 | |||
| 7cf2de30ac | |||
| d520d29fd6 | |||
| 3a927523c3 | |||
| ecc95331a7 | |||
| 282bda7e34 | |||
| 3a859f97d0 | |||
| 0d0ff90ca1 | |||
| b4265a8468 | |||
| fac52046f3 | |||
| a17de688f3 | |||
| fdd8550307 | |||
| 83b372c0d4 | |||
| daab267736 | |||
| 8beab2791a | |||
| 2d9d6b9e46 | |||
| 94f22bb995 | |||
| 0b10b69775 | |||
| f37383784d | |||
| eff1cf34db | |||
| a2167a6b95 | |||
| af6beb21c3 | |||
| ebf7718f1b | |||
| 80851ea7ca | |||
| c154518df4 | |||
| 1d2d08584e | |||
| 46d713589b | |||
| 23d1fcd882 | |||
| b56ed61523 | |||
| b90f04fc3b | |||
| 141ecf221b | |||
| c859d8e104 | |||
| f787cbad18 | |||
| 46058ddb0f | |||
| 955185379f | |||
| d58be409a6 | |||
| 13d16c1d76 | |||
| 773b5ab1c1 | |||
| 5178c122a5 | |||
| db0e821f38 | |||
| fa1c6430f4 | |||
| bf2eabf82d | |||
| 2f5a0ca48b | |||
| 2c84f45ff3 | |||
| 6385d1d429 | |||
| 4ae370c1a2 | |||
| 72c44daaf3 | |||
| b3e4e95f9c | |||
| ab82bf0008 | |||
| 1bb40b8d14 | |||
| f2a21d4aa2 | |||
| c15d64adf5 | |||
| b9d811c2e2 | |||
| a0e9318862 | |||
| 876d1128a6 | |||
| 14d36a47ee | |||
| ae788be15f | |||
| 56fc3356dc | |||
| 1e1636e1f0 | |||
| 2e46c516f6 | |||
| c306e4f7f3 | |||
| f3f979ae49 | |||
| f947ed7791 | |||
| 1ff9f381a2 | |||
| d86d4b2222 | |||
| 052c2ce1a9 | |||
| 4f6cb03add | |||
| 485524882e | |||
| 945711855a | |||
| 272d5eed83 | |||
| 86068a5b45 | |||
| e85c1c0391 | |||
| c89b75699e | |||
| fc72fb90e7 | |||
| 34a78b8989 | |||
| 8dd01467f3 | |||
| 3e9c53f84f | |||
| b270f58bc8 | |||
| 5b15feb5af | |||
| 4b4954ef4d | |||
| b557695e5a | |||
| 238c752aee | |||
| 58604795f8 | |||
| 20b53206a5 | |||
| ec42a1cfd7 | |||
| 5883c9236e | |||
| 7ae2c885a7 | |||
| c51c19ae88 | |||
| 13d349b0da | |||
| e2bfe09c7b | |||
| e7ca49e668 | |||
| f36da276e3 | |||
| 9f37a64146 | |||
| c51c180b3c | |||
| 68c7ab4748 | |||
| a84623dc9c | |||
| e541af30dc | |||
| a63eb1241d | |||
| 382d5aadbe | |||
| 6620f2d7be | |||
| 2a76453cdd | |||
| 43551797c3 | |||
| b6da0a7f70 | |||
| 7f6ce63ad9 | |||
| 9de215fa4c | |||
| 0808f843eb | |||
| 819b787f5f | |||
| 165a499271 | |||
| 7da89f51a6 | |||
| b7fdf21a05 | |||
| 546138e433 | |||
| 4e0325013e | |||
| 8d5a1b1193 | |||
| 40cf2730b9 | |||
| 11d379cbce | |||
| 101f471829 | |||
| b9e3e55384 | |||
| b981f254de | |||
| fa8fc80b83 | |||
| 17f856849d | |||
| 06fdbaf73d | |||
| aa2b8e82ff | |||
| cf5d64dd49 | |||
| ed39b68f13 | |||
| b1e7bfd8f8 | |||
| ed3249818e | |||
| 68c4d248d9 | |||
| 6e8ab67a48 | |||
| 7c94ac42cc |
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["es2015-loose"],
|
||||
"plugins": ["transform-es3-property-literals", "transform-es3-member-expression-literals", "inline-json"]
|
||||
}
|
||||
@@ -28,5 +28,6 @@ test/coverage/*
|
||||
.sass-cache
|
||||
|
||||
dist/*
|
||||
es5/*
|
||||
|
||||
.idea/
|
||||
|
||||
-47
@@ -1,47 +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",
|
||||
|
||||
"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/**
|
||||
|
||||
+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
|
||||
|
||||
+116
@@ -6,6 +6,122 @@ _(none)_
|
||||
|
||||
--------------------
|
||||
|
||||
## 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.7 (2016-09-28)
|
||||
* @gkatsev checked throwIfWhitespace first in hasElClass ([view](https://github.com/videojs/video.js/pull/3640))
|
||||
* @misteroneill pinned grunt-contrib-uglify to ~0.11 to pin uglify to ~2.6 ([view](https://github.com/videojs/video.js/pull/3634))
|
||||
* @gkatsev set playerId on new el created for movingMediaElementInDOM. Fixes #3283 ([view](https://github.com/videojs/video.js/pull/3648))
|
||||
|
||||
## 5.11.6 (2016-08-25)
|
||||
* @imbcmdth Added exception handling to event dispatcher ([view](https://github.com/videojs/video.js/pull/3580))
|
||||
|
||||
## 5.11.5 (2016-08-25)
|
||||
* @misteroneill fixed wrapping native and emulated MediaErrors ([view](https://github.com/videojs/video.js/pull/3562))
|
||||
* @snyderizer fixed switching between audio tracks. Fixes #3510 ([view](https://github.com/videojs/video.js/pull/3538))
|
||||
* @jbarabander added title attribute to audio button. Fixes #3528 ([view](https://github.com/videojs/video.js/pull/3565))
|
||||
* @misteroneill fixed IE8 media error test failure ([view](https://github.com/videojs/video.js/pull/3568))
|
||||
|
||||
## 5.11.4 (2016-08-16)
|
||||
_(none)_
|
||||
|
||||
## 5.11.3 (2016-08-15)
|
||||
* @vdeshpande fixed control text for fullscreen button ([view](https://github.com/videojs/video.js/pull/3485))
|
||||
* @mister-ben fixed android treating swipe as a tap ([view](https://github.com/videojs/video.js/pull/3514))
|
||||
* @mboles updated duration() method documentation ([view](https://github.com/videojs/video.js/pull/3515))
|
||||
* @mister-ben silenced chrome's play() request was interrupted by pause() error ([view](https://github.com/videojs/video.js/pull/3518))
|
||||
|
||||
## 5.11.2 (2016-08-09)
|
||||
_(none)_
|
||||
|
||||
## 5.11.1 (2016-08-08)
|
||||
* @vxsx fixed legend selector to be more specific. Fixes #3492 ([view](https://github.com/videojs/video.js/pull/3494))
|
||||
|
||||
## 5.11.0 (2016-07-22)
|
||||
* @BrandonOCasey Document audio/video track usage ([view](https://github.com/videojs/video.js/pull/3295))
|
||||
* @hartman Correct documentation to refer to nativeTextTracks option ([view](https://github.com/videojs/video.js/pull/3309))
|
||||
* @nickygerritsen Also pass tech options to canHandleSource ([view](https://github.com/videojs/video.js/pull/3303))
|
||||
* @misteroneill Un-deprecate the videojs.players property ([view](https://github.com/videojs/video.js/pull/3299))
|
||||
* @nickygerritsen Add title to all clickable components ([view](https://github.com/videojs/video.js/pull/3296))
|
||||
* @nickygerritsen Update Dutch language file ([view](https://github.com/videojs/video.js/pull/3297))
|
||||
* @hartman Add descriptions and audio button to adaptive classes ([view](https://github.com/videojs/video.js/pull/3312))
|
||||
* @MattiasBuelens Retain details from tech error ([view](https://github.com/videojs/video.js/pull/3313))
|
||||
* @nickygerritsen Fix test for tooltips in IE8 ([view](https://github.com/videojs/video.js/pull/3327))
|
||||
* @mboles added loadstart event to jsdoc ([view](https://github.com/videojs/video.js/pull/3370))
|
||||
* @hartman added default print styling ([view](https://github.com/videojs/video.js/pull/3304))
|
||||
* @ldayananda updated videojs to not do anything if no src is set ([view](https://github.com/videojs/video.js/pull/3378))
|
||||
* @nickygerritsen removed unused tracks when changing sources. Fixes #3000 ([view](https://github.com/videojs/video.js/pull/3002))
|
||||
* @vit-koumar updated Flash tech to return Infinity from duration instead of -1 ([view](https://github.com/videojs/video.js/pull/3128))
|
||||
* @alex-phillips added ontextdata to Flash tech ([view](https://github.com/videojs/video.js/pull/2748))
|
||||
* @MattiasBuelens updated components to use durationchange only ([view](https://github.com/videojs/video.js/pull/3349))
|
||||
* @misteroneill improved Logging for IE < 11 ([view](https://github.com/videojs/video.js/pull/3356))
|
||||
* @vdeshpande updated control text of modal dialog ([view](https://github.com/videojs/video.js/pull/3400))
|
||||
* @ldayananda fixed mouse handling on menus by using mouseleave over mouseout ([view](https://github.com/videojs/video.js/pull/3404))
|
||||
* @mister-ben updated language to inherit correctly and respect the attribute on the player ([view](https://github.com/videojs/video.js/pull/3426))
|
||||
* @sashyro fixed nativeControlsForTouch option ([view](https://github.com/videojs/video.js/pull/3410))
|
||||
* @tbasse fixed techCall null check against tech ([view](https://github.com/videojs/video.js/pull/2676))
|
||||
* @rbran100 checked src and currentSrc in handleTechReady to work around mixed content issues in chrome ([view](https://github.com/videojs/video.js/pull/3287))
|
||||
* @OwenEdwards fixed caption settings dialog labels for accessibility ([view](https://github.com/videojs/video.js/pull/3281))
|
||||
* @OwenEdwards removed spurious head tags in the simple-embed example ([view](https://github.com/videojs/video.js/pull/3438))
|
||||
* @ntadej added a null check to errorDisplay usage ([view](https://github.com/videojs/video.js/pull/3440))
|
||||
* @misteroneill fixed logging issues on IE by separating fn.apply and stringify checks ([view](https://github.com/videojs/video.js/pull/3444))
|
||||
* @misteroneill fixed npm test from running coveralls locally ([view](https://github.com/videojs/video.js/pull/3449))
|
||||
* @gkatsev added es6-shim to tests. Fixes Flash duration test ([view](https://github.com/videojs/video.js/pull/3453))
|
||||
* @misteroneill corrects test assertions for older IEs in the log module ([view](https://github.com/videojs/video.js/pull/3454))
|
||||
* @gkatsev fixed setting lang by looping through loop element variable and not constant tag ([view](https://github.com/videojs/video.js/pull/3455))
|
||||
|
||||
## 5.10.8 (2016-08-08)
|
||||
* @gkatsev re-published to make sure that the audio button has css
|
||||
|
||||
## 5.10.7 (2016-06-27)
|
||||
* @gkatsev pinned node-sass to 3.4 ([view](https://github.com/videojs/video.js/pull/3401))
|
||||
* @mister-ben added try catch to volume and playbackrate checks. Fixes #3315 ([view](https://github.com/videojs/video.js/pull/3320))
|
||||
* @m14t removed unused loadEvent property in ControlBar options ([view](https://github.com/videojs/video.js/pull/3363))
|
||||
* @bklava updated pt-BR language file ([view](https://github.com/videojs/video.js/pull/3373))
|
||||
* @mister-ben updated menus to use default videojs font-family ([view](https://github.com/videojs/video.js/pull/3384))
|
||||
* @vdeshpande fixed chapters getting duplicated each time a track is loaded ([view](https://github.com/videojs/video.js/pull/3354))
|
||||
|
||||
## 5.10.6 (2016-06-20)
|
||||
* @gkatsev fix not fully minified video.min.js file.
|
||||
|
||||
## 5.10.5 (2016-06-07)
|
||||
* @gkatsev pinned dependencies to direct versions ([view](https://github.com/videojs/video.js/pull/3338))
|
||||
* @gkatsev fixed minified vjs in ie8 when initialized with id string ([view](https://github.com/videojs/video.js/pull/3357))
|
||||
* @IJsLauw fixed unhandled exception in deleting poster on ios7 ([view](https://github.com/videojs/video.js/pull/3337))
|
||||
|
||||
## 5.10.4 (2016-05-31)
|
||||
* Patch release to fix dist on npm
|
||||
|
||||
## 5.10.3 (2016-05-27)
|
||||
* @BrandonOCasey fixed source handlers being disposed multiple times when a video is put into the video element directly ([view](https://github.com/videojs/video.js/pull/3343))
|
||||
|
||||
## 5.10.2 (2016-05-12)
|
||||
* @gkatsev nulled out currentSource_ in setSource ([view](https://github.com/videojs/video.js/pull/3314))
|
||||
|
||||
|
||||
+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.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
# [Video.js - HTML5 Video Player](http://videojs.com)
|
||||
[](https://travis-ci.org/videojs/video.js)
|
||||
[](https://coveralls.io/github/videojs/video.js?branch=master)
|
||||
[](http://slack.videojs.com)
|
||||
|
||||
|
||||
[](https://nodei.co/npm/video.js/)
|
||||
|
||||
|
||||
+75
-58
@@ -1,4 +1,6 @@
|
||||
import {gruntCustomizer, gruntOptionsMaker} from './options-customizer.js';
|
||||
import chg from 'chg';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
@@ -16,29 +18,10 @@ module.exports = function(grunt) {
|
||||
|
||||
const browserifyGruntDefaults = {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: 'videojs'
|
||||
},
|
||||
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 +30,7 @@ module.exports = function(grunt) {
|
||||
release: {
|
||||
tag_name: 'v'+ version.full,
|
||||
name: version.full,
|
||||
body: require('chg').find(version.full).changesRaw
|
||||
body: chg.find(version.full).changesRaw
|
||||
},
|
||||
},
|
||||
files: {
|
||||
@@ -111,22 +94,11 @@ 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',
|
||||
mangle: true,
|
||||
compress: {
|
||||
@@ -159,7 +131,11 @@ module.exports = function(grunt) {
|
||||
},
|
||||
skin: {
|
||||
files: ['src/css/**/*'],
|
||||
tasks: ['sass']
|
||||
tasks: ['skin']
|
||||
},
|
||||
babel: {
|
||||
files: ['src/js/**/*.js'],
|
||||
tasks: ['babel:es5']
|
||||
},
|
||||
jshint: {
|
||||
files: ['src/**/*', 'test/unit/**/*.js', 'Gruntfile.js'],
|
||||
@@ -325,11 +301,21 @@ module.exports = function(grunt) {
|
||||
}
|
||||
})
|
||||
},
|
||||
babel: {
|
||||
es5: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: 'src/js/',
|
||||
src: ['**/*.js', '!base-styles.js'],
|
||||
dest: 'es5/'
|
||||
}]
|
||||
}
|
||||
},
|
||||
browserify: {
|
||||
options: browserifyGruntOptions(),
|
||||
build: {
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/video.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
@@ -342,7 +328,7 @@ module.exports = function(grunt) {
|
||||
]
|
||||
}),
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/video.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -351,14 +337,15 @@ module.exports = function(grunt) {
|
||||
keepAlive: true
|
||||
},
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
'build/temp/video.js': ['es5/video.js']
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
options: {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: false
|
||||
verbose: true,
|
||||
standalone: false,
|
||||
transform: ['babelify']
|
||||
},
|
||||
plugin: [
|
||||
['proxyquireify/plugin']
|
||||
@@ -375,14 +362,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,25 +373,30 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
separator: '\n'
|
||||
},
|
||||
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',
|
||||
dest: 'build/temp/video.js'
|
||||
},
|
||||
ie8_addition: {
|
||||
src: ['build/temp/video-js.css', 'src/css/ie8.css'],
|
||||
dest: 'build/temp/video-js.css'
|
||||
}
|
||||
},
|
||||
concurrent: {
|
||||
options: {
|
||||
logConcurrentOutput: true
|
||||
},
|
||||
tests: [
|
||||
'watch:babel',
|
||||
'browserify:tests'
|
||||
],
|
||||
// Run multiple watch tasks in parallel
|
||||
// Needed so watchify can cache intelligently
|
||||
watchAll: [
|
||||
@@ -443,6 +427,32 @@ module.exports = function(grunt) {
|
||||
src: ['build/temp/video.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
shell: {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -450,20 +460,21 @@ module.exports = function(grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
grunt.loadNpmTasks('videojs-doc-generator');
|
||||
grunt.loadNpmTasks('chg');
|
||||
grunt.loadNpmTasks('gkatsev-grunt-sass');
|
||||
|
||||
const buildDependents = [
|
||||
'shell:lint',
|
||||
'clean:build',
|
||||
|
||||
'jshint',
|
||||
'babel:es5',
|
||||
'browserify:build',
|
||||
'exorcise:build',
|
||||
'concat:novtt',
|
||||
'concat:vtt',
|
||||
'usebanner:novtt',
|
||||
'usebanner:vtt',
|
||||
'uglify',
|
||||
|
||||
'sass',
|
||||
'skin',
|
||||
'version:css',
|
||||
'cssmin',
|
||||
|
||||
@@ -488,12 +499,18 @@ module.exports = function(grunt) {
|
||||
'zip:dist'
|
||||
]);
|
||||
|
||||
grunt.registerTask('skin', ['sass']);
|
||||
grunt.registerTask('skin', ['sass', 'concat:ie8_addition']);
|
||||
|
||||
// Default task - build and test
|
||||
grunt.registerTask('default', ['test']);
|
||||
|
||||
grunt.registerTask('test', ['build', 'karma:defaults']);
|
||||
// The test script includes coveralls only when the TRAVIS env var is set.
|
||||
grunt.registerTask('test', [
|
||||
'build',
|
||||
'shell:noderequire',
|
||||
'shell:browserify',
|
||||
'shell:webpack',
|
||||
'karma:defaults'].concat(process.env.TRAVIS && 'coveralls').filter(Boolean));
|
||||
|
||||
// Run while developing
|
||||
grunt.registerTask('dev', ['build', 'connect:dev', 'concurrent:watchSandbox']);
|
||||
|
||||
@@ -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
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.10.2",
|
||||
"version": "5.12.2",
|
||||
"keywords": [
|
||||
"videojs",
|
||||
"html5",
|
||||
|
||||
externo
+36
-16
@@ -437,7 +437,8 @@ body.vjs-full-window {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: auto; }
|
||||
overflow: auto;
|
||||
font-family: Arial, Helvetica, sans-serif; }
|
||||
|
||||
.vjs-scrubbing .vjs-menu-button:hover .vjs-menu {
|
||||
display: none; }
|
||||
@@ -584,9 +585,6 @@ body.vjs-full-window {
|
||||
-moz-transition: visibility 1s, opacity 1s;
|
||||
-o-transition: visibility 1s, opacity 1s;
|
||||
transition: visibility 1s, opacity 1s; }
|
||||
@media \0screen {
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
visibility: hidden; } }
|
||||
|
||||
.vjs-controls-disabled .vjs-control-bar,
|
||||
.vjs-using-native-controls .vjs-control-bar,
|
||||
@@ -597,10 +595,6 @@ body.vjs-full-window {
|
||||
opacity: 1;
|
||||
visibility: visible; }
|
||||
|
||||
@media \0screen {
|
||||
.vjs-user-inactive.vjs-playing .vjs-control-bar :before {
|
||||
content: ""; } }
|
||||
|
||||
.vjs-has-started.vjs-no-flex .vjs-control-bar {
|
||||
display: table; }
|
||||
|
||||
@@ -1184,19 +1178,22 @@ video::-webkit-media-text-track-display {
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-playback-rate, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-progress-control,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-control, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-menu-button,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-playback-rate,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-control, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-menu-button,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-playback-rate,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-volume-control,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.vjs-caption-settings {
|
||||
@@ -1207,13 +1204,13 @@ video::-webkit-media-text-track-display {
|
||||
color: #fff;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
height: 15em;
|
||||
height: 16em;
|
||||
font-size: 12px;
|
||||
width: 40em; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksettings {
|
||||
top: 0;
|
||||
bottom: 2em;
|
||||
bottom: 1em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
@@ -1236,16 +1233,20 @@ video::-webkit-media-text-track-display {
|
||||
.vjs-caption-settings .vjs-tracksetting {
|
||||
margin: 5px;
|
||||
padding: 3px;
|
||||
min-height: 40px; }
|
||||
min-height: 40px;
|
||||
border: none; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting label {
|
||||
.vjs-caption-settings .vjs-tracksetting label,
|
||||
.vjs-caption-settings .vjs-tracksetting legend {
|
||||
display: block;
|
||||
width: 100px;
|
||||
margin-bottom: 5px; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting span {
|
||||
display: inline;
|
||||
margin-left: 5px; }
|
||||
margin-left: 5px;
|
||||
vertical-align: top;
|
||||
float: right; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting > div {
|
||||
margin-bottom: 5px;
|
||||
@@ -1259,6 +1260,21 @@ video::-webkit-media-text-track-display {
|
||||
.vjs-caption-settings label > input {
|
||||
margin-right: 10px; }
|
||||
|
||||
.vjs-caption-settings fieldset {
|
||||
margin-top: 1em;
|
||||
margin-left: .5em; }
|
||||
|
||||
.vjs-caption-settings fieldset .vjs-label {
|
||||
position: absolute;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
/* for Internet Explorer */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden; }
|
||||
|
||||
.vjs-caption-settings input[type="button"] {
|
||||
width: 40px;
|
||||
height: 40px; }
|
||||
@@ -1273,3 +1289,7 @@ video::-webkit-media-text-track-display {
|
||||
line-height: 1.5;
|
||||
padding: 20px 24px;
|
||||
z-index: 1; }
|
||||
|
||||
@media print {
|
||||
.video-js > *:not(.vjs-tech):not(.vjs-poster) {
|
||||
visibility: hidden; } }
|
||||
|
||||
externo
+1
-1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+20432
-19769
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+13
-11
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
-1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
-3
@@ -3,14 +3,11 @@
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
<title>Video.js | HTML5 Video Player</title>
|
||||
<link href="http://vjs.zencdn.net/5.0.2/video-js.css" rel="stylesheet">
|
||||
<script src="http://vjs.zencdn.net/ie8/1.1.0/videojs-ie8.min.js"></script>
|
||||
<script src="http://vjs.zencdn.net/5.0.2/video.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
externo
+7
-1
@@ -30,5 +30,11 @@ videojs.addLanguage("de",{
|
||||
"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",
|
||||
"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"
|
||||
});
|
||||
externo
+3
@@ -18,13 +18,16 @@ videojs.addLanguage("en",{
|
||||
"Captions": "Captions",
|
||||
"captions off": "captions off",
|
||||
"Chapters": "Chapters",
|
||||
"Close Modal Dialog": "Close Modal Dialog",
|
||||
"Descriptions": "Descriptions",
|
||||
"descriptions off": "descriptions off",
|
||||
"Audio Track": "Audio Track",
|
||||
"You aborted the media playback": "You aborted the media playback",
|
||||
"A network error caused the media download to fail part-way.": "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 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",
|
||||
|
||||
externo
+16
-3
@@ -15,12 +15,25 @@ videojs.addLanguage("fr",{
|
||||
"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.",
|
||||
"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é"
|
||||
});
|
||||
externo
+15
-4
@@ -14,13 +14,24 @@ videojs.addLanguage("nl",{
|
||||
"Unmute": "Geluid aan",
|
||||
"Playback Rate": "Weergavesnelheid",
|
||||
"Subtitles": "Ondertiteling",
|
||||
"subtitles off": "Ondertiteling uit",
|
||||
"Captions": "Ondertiteling",
|
||||
"captions off": "Ondertiteling uit",
|
||||
"subtitles off": "ondertiteling uit",
|
||||
"Captions": "Bijschriften",
|
||||
"captions off": "bijschriften uit",
|
||||
"Chapters": "Hoofdstukken",
|
||||
"Descriptions": "Beschrijvingen",
|
||||
"descriptions off": "beschrijvingen off",
|
||||
"You aborted the media playback": "U hebt de mediaweergave afgebroken.",
|
||||
"A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, vanwege een server- of netwerkfout of doordat het formaat niet wordt ondersteund.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De mediaweergave is afgebroken vanwege beschadigde data of het mediabestand gebruikt functies die niet door uw browser worden ondersteund.",
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden."
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden.",
|
||||
"Play Video": "Video Afspelen",
|
||||
"Close": "Sluiten",
|
||||
"Modal Window": "Modal Venster",
|
||||
"This is a modal window": "Dit is een modaal venster",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Dit modaal venster kan gesloten worden door op Escape te drukken of de 'sluiten' knop te activeren.",
|
||||
", opens captions settings dialog": ", opent bijschriften instellingen venster",
|
||||
", opens subtitles settings dialog": ", opent ondertiteling instellingen venster",
|
||||
", opens descriptions settings dialog": ", opent beschrijvingen instellingen venster",
|
||||
", selected": ", selected"
|
||||
});
|
||||
externo
+3
-3
@@ -1,6 +1,6 @@
|
||||
videojs.addLanguage("pt-BR",{
|
||||
"Play": "Tocar",
|
||||
"Pause": "Pause",
|
||||
"Pause": "Pausar",
|
||||
"Current Time": "Tempo",
|
||||
"Duration Time": "Duração",
|
||||
"Remaining Time": "Tempo Restante",
|
||||
@@ -18,9 +18,9 @@ videojs.addLanguage("pt-BR",{
|
||||
"Captions": "Anotações",
|
||||
"captions off": "Sem Anotações",
|
||||
"Chapters": "Capítulos",
|
||||
"You aborted the media playback": "Você parou a execução de vídeo.",
|
||||
"You aborted the media playback": "Você parou a execução do vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar 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 com sua rede ou pelo formato do vídeo não ser suportado.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A Execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
|
||||
"No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível."
|
||||
});
|
||||
externo
BIN
Arquivo binário não exibido.
externo
BIN
Arquivo binário não exibido.
externo
+67
-16
@@ -437,7 +437,8 @@ body.vjs-full-window {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: auto; }
|
||||
overflow: auto;
|
||||
font-family: Arial, Helvetica, sans-serif; }
|
||||
|
||||
.vjs-scrubbing .vjs-menu-button:hover .vjs-menu {
|
||||
display: none; }
|
||||
@@ -584,9 +585,6 @@ body.vjs-full-window {
|
||||
-moz-transition: visibility 1s, opacity 1s;
|
||||
-o-transition: visibility 1s, opacity 1s;
|
||||
transition: visibility 1s, opacity 1s; }
|
||||
@media \0screen {
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
visibility: hidden; } }
|
||||
|
||||
.vjs-controls-disabled .vjs-control-bar,
|
||||
.vjs-using-native-controls .vjs-control-bar,
|
||||
@@ -597,10 +595,6 @@ body.vjs-full-window {
|
||||
opacity: 1;
|
||||
visibility: visible; }
|
||||
|
||||
@media \0screen {
|
||||
.vjs-user-inactive.vjs-playing .vjs-control-bar :before {
|
||||
content: ""; } }
|
||||
|
||||
.vjs-has-started.vjs-no-flex .vjs-control-bar {
|
||||
display: table; }
|
||||
|
||||
@@ -1184,19 +1178,22 @@ video::-webkit-media-text-track-display {
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-playback-rate, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-progress-control,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-control, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-menu-button,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-playback-rate,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-control, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-menu-button,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button, .video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-current-time, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-time-divider, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-duration, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-remaining-time,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-playback-rate,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-mute-control, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-volume-control,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button {
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-descriptions-button, .video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button,
|
||||
.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button .vjs-audio-button {
|
||||
display: none; }
|
||||
|
||||
.vjs-caption-settings {
|
||||
@@ -1207,13 +1204,13 @@ video::-webkit-media-text-track-display {
|
||||
color: #fff;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
height: 15em;
|
||||
height: 16em;
|
||||
font-size: 12px;
|
||||
width: 40em; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksettings {
|
||||
top: 0;
|
||||
bottom: 2em;
|
||||
bottom: 1em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
@@ -1236,16 +1233,20 @@ video::-webkit-media-text-track-display {
|
||||
.vjs-caption-settings .vjs-tracksetting {
|
||||
margin: 5px;
|
||||
padding: 3px;
|
||||
min-height: 40px; }
|
||||
min-height: 40px;
|
||||
border: none; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting label {
|
||||
.vjs-caption-settings .vjs-tracksetting label,
|
||||
.vjs-caption-settings .vjs-tracksetting legend {
|
||||
display: block;
|
||||
width: 100px;
|
||||
margin-bottom: 5px; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting span {
|
||||
display: inline;
|
||||
margin-left: 5px; }
|
||||
margin-left: 5px;
|
||||
vertical-align: top;
|
||||
float: right; }
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting > div {
|
||||
margin-bottom: 5px;
|
||||
@@ -1259,6 +1260,21 @@ video::-webkit-media-text-track-display {
|
||||
.vjs-caption-settings label > input {
|
||||
margin-right: 10px; }
|
||||
|
||||
.vjs-caption-settings fieldset {
|
||||
margin-top: 1em;
|
||||
margin-left: .5em; }
|
||||
|
||||
.vjs-caption-settings fieldset .vjs-label {
|
||||
position: absolute;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
/* for Internet Explorer */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden; }
|
||||
|
||||
.vjs-caption-settings input[type="button"] {
|
||||
width: 40px;
|
||||
height: 40px; }
|
||||
@@ -1273,3 +1289,38 @@ video::-webkit-media-text-track-display {
|
||||
line-height: 1.5;
|
||||
padding: 20px 24px;
|
||||
z-index: 1; }
|
||||
|
||||
@media print {
|
||||
.video-js > *:not(.vjs-tech):not(.vjs-poster) {
|
||||
visibility: hidden; } }
|
||||
|
||||
/**
|
||||
// IE 8 hack for media queries which the sass parser can encounter problems with
|
||||
$ie8screen: "\\0screen";
|
||||
|
||||
// original home: css/components/_control-bar.scss
|
||||
// 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: ""; }
|
||||
}
|
||||
|
||||
// Video has started playing AND user is inactive
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
|
||||
// Make controls hidden in IE8 for now
|
||||
@media #{$ie8screen} {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@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; } }
|
||||
|
||||
externo
+1
-1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
BIN
Arquivo binário não exibido.
externo
+20431
-19768
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
-321
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+14
-11
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
-1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -3,14 +3,11 @@
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
<title>Video.js | HTML5 Video Player</title>
|
||||
<link href="http://vjs.zencdn.net/5.0.2/video-js.css" rel="stylesheet">
|
||||
<script src="http://vjs.zencdn.net/ie8/1.1.0/videojs-ie8.min.js"></script>
|
||||
<script src="http://vjs.zencdn.net/5.0.2/video.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# 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:
|
||||
|
||||
- **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
|
||||
|
||||
## 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
|
||||
|
||||
## Adding to Video.js
|
||||
|
||||
> Right now adding audio tracks in the HTML is unsupported. Audio Tracks must be added programatically.
|
||||
|
||||
You must add audio tracks [programatically](#api) for the time being.
|
||||
|
||||
## Attributes
|
||||
Audio Track propertites and settings
|
||||
|
||||
### 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();
|
||||
|
||||
// listen to the change event
|
||||
tracks.addEventListener('change', function() {
|
||||
|
||||
// print the currently enabled AudioTrack label
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
|
||||
if (track.enabled) {
|
||||
console.log(track.label);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### `player.audioTracks().addTrack(AudioTrack)`
|
||||
Add an existing AudioTrack to the players internal list of AudioTracks.
|
||||
|
||||
### `player.audioTracks().removeTrack(AudioTrack)`
|
||||
Remove a track from the AudioTrackList currently on the player. if no track exists this will do nothing.
|
||||
+13
-159
@@ -32,6 +32,7 @@ A sample dictionary for Spanish `['es']` would look as follows:
|
||||
"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.",
|
||||
@@ -80,6 +81,7 @@ NOTE: These need to be added after the core Video.js script.
|
||||
"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.",
|
||||
@@ -108,6 +110,10 @@ NOTE: These need to be added after the core Video.js script.
|
||||
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.
|
||||
|
||||
Updating default translations
|
||||
-----------------------------
|
||||
|
||||
A list of the current translations and any strings that need translation are at [docs/translations-needed.md](../translations-needed.md). After updating the language files in /lang/ running `grunt check-languages` will update that list.
|
||||
|
||||
Setting Default Language in a Video.js Player
|
||||
---------------------------------------------
|
||||
@@ -129,14 +135,14 @@ During a Video.js player instantiation you can force it to localize to a specifi
|
||||
Determining Player Language
|
||||
---------------------------
|
||||
|
||||
The player language is set to one of the following in descending priority
|
||||
The player language is set to one of the following in descending priority:
|
||||
|
||||
* The language set in setup options as above
|
||||
* The document language (`lang` attribute of the `html` element)
|
||||
* Browser language preference
|
||||
* 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'
|
||||
|
||||
That can be overridden after instantiation with `language('fr')`.
|
||||
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.
|
||||
|
||||
Language selection
|
||||
------------------
|
||||
@@ -155,159 +161,7 @@ var details = '<div class="vjs-errors-details">' + player.localize('Technical de
|
||||
|
||||
Language Codes
|
||||
--------------
|
||||
The following is a list of official language codes.
|
||||
A list of languages codes can be found [here](http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
|
||||
|
||||
**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.
|
||||
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.
|
||||
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# 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:
|
||||
|
||||
- **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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
You can also add tracks [programatically](#api).
|
||||
|
||||
## 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">
|
||||
</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.
|
||||
|
||||
### kind
|
||||
One of the five track types listed above. Kind defaults to subtitles if no kind is included.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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).
|
||||
|
||||
## 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();
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
|
||||
// find the captions track that's in english
|
||||
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:
|
||||
```js
|
||||
let tracks = player.textTracks();
|
||||
let metadataTrack;
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
|
||||
// find the metadata track that's labeled ads
|
||||
if (track.kind === 'captions' && track.label === 'ads') {
|
||||
track.mode = 'hidden';
|
||||
// store it for usage outside of the loop
|
||||
metadataTrack = track;
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
html5: {
|
||||
nativeTextTracks: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 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:
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
// make the text track settings dialog not initialize
|
||||
textTrackSettings: false
|
||||
});
|
||||
```
|
||||
```css
|
||||
/* 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.
|
||||
|
||||
## 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.
|
||||
|
||||
### `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.
|
||||
|
||||
### `player.remoteTextTrackEls() -> HTMLTrackElementList`
|
||||
Another helper method, this is a list of all the `track` elements associated with the player. Both emulated or otherwise.
|
||||
|
||||
### `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.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`
|
||||
|
||||
### `player.removeRemoteTextTrack(HTMLTrackElement|TextTrack)`
|
||||
This function takes either an HTMLTrackElement or a TextTrack object and removes it from the player.
|
||||
+4
-182
@@ -1,184 +1,6 @@
|
||||
# Tracks
|
||||
There are currently three types of 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:
|
||||
|
||||
- **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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
You can also add tracks [programatically](#api).
|
||||
|
||||
## 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">
|
||||
</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.
|
||||
|
||||
### kind
|
||||
One of the five track types listed above. Kind defaults to subtitles if no kind is included.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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).
|
||||
|
||||
## 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();
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
|
||||
// find the captions track that's in english
|
||||
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:
|
||||
```js
|
||||
let tracks = player.textTracks();
|
||||
let metadataTrack;
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
|
||||
// find the metadata track that's labeled ads
|
||||
if (track.kind === 'captions' && track.label === 'ads') {
|
||||
track.mode = 'hidden';
|
||||
// store it for usage outside of the loop
|
||||
metadataTrack = track;
|
||||
}
|
||||
}
|
||||
|
||||
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 `nativeTextTrack` option to the tech like so:
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
html5: {
|
||||
nativeTextTrack: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 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:
|
||||
```js
|
||||
let player = videojs('myvideo', {
|
||||
// make the text track settings dialog not initialize
|
||||
textTrackSettings: false
|
||||
});
|
||||
```
|
||||
```css
|
||||
/* 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.
|
||||
|
||||
## 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.
|
||||
|
||||
### `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.
|
||||
|
||||
### `player.remoteTextTrackEls() -> HTMLTrackElementList`
|
||||
Another helper method, this is a list of all the `track` elements associated with the player. Both emulated or otherwise.
|
||||
|
||||
### `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.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`
|
||||
|
||||
### `player.removeRemoteTextTrack(HTMLTrackElement|TextTrack)`
|
||||
This function takes either an HTMLTrackElement or a TextTrack object and removes it from the player.
|
||||
* [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.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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:
|
||||
|
||||
- **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
|
||||
|
||||
## 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
|
||||
|
||||
## Adding to Video.js
|
||||
|
||||
> Right now adding video tracks in the HTML is unsupported. Video Tracks must be added programatically.
|
||||
|
||||
You must add video tracks [programatically](#api) for the time being.
|
||||
|
||||
## Attributes
|
||||
Video Track propertites and settings
|
||||
|
||||
### 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 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();
|
||||
|
||||
// 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);
|
||||
});
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### `player.videoTracks().addTrack(VideoTrack)`
|
||||
Add an existing VideoTrack to the players internal list of VideoTracks.
|
||||
|
||||
### `player.videoTracks().removeTrack(VideoTrack)`
|
||||
Remove a track from the VideoTrackList currently on the player. if no track exists this will do nothing.
|
||||
|
||||
### `player.videoTracks().selectedIndex`
|
||||
The current index for the selected track
|
||||
+1
-1
@@ -14,7 +14,7 @@ There are two categories of docs: [Guides](./guides/) and [API docs](./api/). Gu
|
||||
|
||||
* [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) - Text Tracks are used to display subtitles and captions, and add a menu for navigating between chapters in a video.
|
||||
* [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.
|
||||
|
||||
### Customizing
|
||||
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
# Translations needed
|
||||
|
||||
The currently available translations are in the lang dir. This table shows the completeness of those translations. Anything not listed does not exist yet, so go ahead and create it by copying `en.json`.
|
||||
|
||||
If you add or update a translation run `grunt check-translations` to update the list and include this modified doc in the pull request.
|
||||
|
||||
## Status of translations
|
||||
|
||||
<!-- START langtable -->
|
||||
| Language file | Missing translations |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------- |
|
||||
| ar.json (missing 6) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| | , opens descriptions settings dialog |
|
||||
| ba.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| bg.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| ca.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| cs.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| da.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| de.json (Complete) | |
|
||||
| el.json (missing 7) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| | Play Video |
|
||||
| | , opens descriptions settings dialog |
|
||||
| es.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| fa.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| fi.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| fr.json (missing 1) | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| hr.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| hu.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| it.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| ja.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| ko.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| nb.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| nl.json (missing 3) | Close Modal Dialog |
|
||||
| | Audio Track |
|
||||
| | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| nn.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| pl.json (missing 7) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| | Play Video |
|
||||
| | , opens descriptions settings dialog |
|
||||
| pt-BR.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| ru.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| sr.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| sv.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| tr.json (missing 6) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | The media is encrypted and we do not have the keys to decrypt it. |
|
||||
| | , opens descriptions settings dialog |
|
||||
| uk.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| vi.json (missing 14) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| zh-CN.json (missing 13) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
| zh-TW.json (missing 13) | Close Modal Dialog |
|
||||
| | Descriptions |
|
||||
| | descriptions off |
|
||||
| | Audio Track |
|
||||
| | 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. |
|
||||
| | , opens captions settings dialog |
|
||||
| | , opens subtitles settings dialog |
|
||||
| | , opens descriptions settings dialog |
|
||||
| | , selected |
|
||||
<!-- END langtable -->
|
||||
+7
-1
@@ -30,5 +30,11 @@
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
"Captions": "Captions",
|
||||
"captions off": "captions off",
|
||||
"Chapters": "Chapters",
|
||||
"Close Modal Dialog": "Close Modal Dialog",
|
||||
"Descriptions": "Descriptions",
|
||||
"descriptions off": "descriptions off",
|
||||
"Audio Track": "Audio Track",
|
||||
"You aborted the media playback": "You aborted the media playback",
|
||||
"A network error caused the media download to fail part-way.": "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 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",
|
||||
|
||||
+16
-3
@@ -15,12 +15,25 @@
|
||||
"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.",
|
||||
"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é"
|
||||
}
|
||||
|
||||
+15
-4
@@ -14,13 +14,24 @@
|
||||
"Unmute": "Geluid aan",
|
||||
"Playback Rate": "Weergavesnelheid",
|
||||
"Subtitles": "Ondertiteling",
|
||||
"subtitles off": "Ondertiteling uit",
|
||||
"Captions": "Ondertiteling",
|
||||
"captions off": "Ondertiteling uit",
|
||||
"subtitles off": "ondertiteling uit",
|
||||
"Captions": "Bijschriften",
|
||||
"captions off": "bijschriften uit",
|
||||
"Chapters": "Hoofdstukken",
|
||||
"Descriptions": "Beschrijvingen",
|
||||
"descriptions off": "beschrijvingen off",
|
||||
"You aborted the media playback": "U hebt de mediaweergave afgebroken.",
|
||||
"A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, vanwege een server- of netwerkfout of doordat het formaat niet wordt ondersteund.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De mediaweergave is afgebroken vanwege beschadigde data of het mediabestand gebruikt functies die niet door uw browser worden ondersteund.",
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden."
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden.",
|
||||
"Play Video": "Video Afspelen",
|
||||
"Close": "Sluiten",
|
||||
"Modal Window": "Modal Venster",
|
||||
"This is a modal window": "Dit is een modaal venster",
|
||||
"This modal can be closed by pressing the Escape key or activating the close button.": "Dit modaal venster kan gesloten worden door op Escape te drukken of de 'sluiten' knop te activeren.",
|
||||
", opens captions settings dialog": ", opent bijschriften instellingen venster",
|
||||
", opens subtitles settings dialog": ", opent ondertiteling instellingen venster",
|
||||
", opens descriptions settings dialog": ", opent beschrijvingen instellingen venster",
|
||||
", selected": ", selected"
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"Play": "Tocar",
|
||||
"Pause": "Pause",
|
||||
"Pause": "Pausar",
|
||||
"Current Time": "Tempo",
|
||||
"Duration Time": "Duração",
|
||||
"Remaining Time": "Tempo Restante",
|
||||
@@ -18,9 +18,9 @@
|
||||
"Captions": "Anotações",
|
||||
"captions off": "Sem Anotações",
|
||||
"Chapters": "Capítulos",
|
||||
"You aborted the media playback": "Você parou a execução de vídeo.",
|
||||
"You aborted the media playback": "Você parou a execução do vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar 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 com sua rede ou pelo formato do vídeo não ser suportado.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A Execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
|
||||
"No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível."
|
||||
}
|
||||
|
||||
+63
-40
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.10.2",
|
||||
"version": "5.12.2",
|
||||
"main": "./es5/video.js",
|
||||
"style": "./dist/video-js.css",
|
||||
"copyright": "Copyright Brightcove, Inc. <https://www.brightcove.com/>",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
@@ -14,89 +16,110 @@
|
||||
"homepage": "http://videojs.com",
|
||||
"author": "Steve Heffernan",
|
||||
"scripts": {
|
||||
"test": "grunt test && if [ '$TRAVIS' ]; then grunt coveralls; fi;"
|
||||
"build": "grunt dist",
|
||||
"change": "grunt chg-add",
|
||||
"clean": "grunt clean",
|
||||
"lint": "vjsstandard",
|
||||
"start": "grunt watchAll",
|
||||
"test": "grunt test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/videojs/video.js.git"
|
||||
},
|
||||
"main": "./dist/video.js",
|
||||
"style": "./dist/video-js.css",
|
||||
"dependencies": {
|
||||
"global": "^4.3.0",
|
||||
"lodash-compat": "^3.9.3",
|
||||
"object.assign": "^4.0.1",
|
||||
"safe-json-parse": "^4.0.0",
|
||||
"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-font": "2.0.0",
|
||||
"videojs-ie8": "1.1.2",
|
||||
"videojs-swf": "5.0.1",
|
||||
"videojs-vtt.js": "^0.12.1",
|
||||
"xhr": "~2.2.0"
|
||||
"videojs-swf": "5.1.0",
|
||||
"videojs-vtt.js": "0.12.1",
|
||||
"xhr": "2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "^5.2.2",
|
||||
"babelify": "^6.0.1",
|
||||
"babel-cli": "^6.11.4",
|
||||
"babel-plugin-inline-json": "^1.1.1",
|
||||
"babel-plugin-transform-es3-member-expression-literals": "^6.8.0",
|
||||
"babel-plugin-transform-es3-property-literals": "^6.8.0",
|
||||
"babel-plugin-transform-runtime": "^6.9.0",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"babel-preset-es2015-loose": "^7.0.0",
|
||||
"babel-register": "^6.9.0",
|
||||
"babelify": "^7.3.0",
|
||||
"blanket": "^1.1.6",
|
||||
"browserify-derequire": "^0.9.4",
|
||||
"browserify-istanbul": "^0.2.1",
|
||||
"browserify-versionify": "^1.0.4",
|
||||
"chg": "^0.3.2",
|
||||
"css": "^2.2.0",
|
||||
"es5-shim": "^4.1.3",
|
||||
"es6-shim": "^0.35.1",
|
||||
"ghooks": "^1.3.2",
|
||||
"gkatsev-grunt-sass": "^1.1.1",
|
||||
"grunt": "^0.4.4",
|
||||
"grunt-aws-s3": "^0.12.1",
|
||||
"grunt-babel": "^6.0.0",
|
||||
"grunt-banner": "^0.4.0",
|
||||
"grunt-browserify": "3.5.1",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"grunt-concurrent": "^1.0.0",
|
||||
"grunt-concurrent": "^2.3.1",
|
||||
"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.8.0",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-contrib-watch": "~0.1.4",
|
||||
"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-sass": "^1.0.0",
|
||||
"grunt-karma": "^2.0.0",
|
||||
"grunt-shell": "^1.3.0",
|
||||
"grunt-version": "~0.3.0",
|
||||
"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",
|
||||
"karma": "^1.2.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.1.0",
|
||||
"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",
|
||||
"markdown-table": "^1.0.0",
|
||||
"proxyquireify": "^3.0.0",
|
||||
"qunitjs": "^1.18.0",
|
||||
"qunitjs": "^1.23.1",
|
||||
"sinon": "^1.16.1",
|
||||
"time-grunt": "^1.1.1",
|
||||
"uglify-js": "~2.3.6",
|
||||
"videojs-doc-generator": "0.0.1"
|
||||
"uglify-js": "~2.7.3",
|
||||
"videojs-doc-generator": "0.0.1",
|
||||
"videojs-standard": "^5.2.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@media print {
|
||||
.video-js > *:not(.vjs-tech):not(.vjs-poster) {
|
||||
visibility:hidden;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
.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-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
|
||||
// When the player is x-small, display nothing but:
|
||||
@@ -19,7 +20,8 @@
|
||||
.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-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
|
||||
|
||||
@@ -33,5 +35,6 @@
|
||||
.vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
|
||||
.vjs-playback-rate,
|
||||
.vjs-mute-control, .vjs-volume-control,
|
||||
.vjs-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button .vjs-audio-button { display: none; }
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
color: $primary-foreground-color;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
height: 15em;
|
||||
height: 16em;
|
||||
font-size: 12px;
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
.vjs-caption-settings .vjs-tracksettings {
|
||||
top: 0;
|
||||
bottom: 2em;
|
||||
bottom: 1em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
@@ -40,8 +40,10 @@
|
||||
margin: 5px;
|
||||
padding: 3px;
|
||||
min-height: 40px;
|
||||
border: none;
|
||||
}
|
||||
.vjs-caption-settings .vjs-tracksetting label {
|
||||
.vjs-caption-settings .vjs-tracksetting label,
|
||||
.vjs-caption-settings .vjs-tracksetting legend {
|
||||
display: block;
|
||||
width: 100px;
|
||||
margin-bottom: 5px;
|
||||
@@ -50,6 +52,8 @@
|
||||
.vjs-caption-settings .vjs-tracksetting span {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
vertical-align: top;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.vjs-caption-settings .vjs-tracksetting > div {
|
||||
@@ -67,6 +71,23 @@
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.vjs-caption-settings fieldset {
|
||||
margin-top: 1em;
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
// Hide labels within fieldsets, so they are only for screen reader users
|
||||
.vjs-caption-settings fieldset .vjs-label {
|
||||
position: absolute;
|
||||
clip: rect(1px 1px 1px 1px); /* for Internet Explorer */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vjs-caption-settings input[type="button"] {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,8 +1,6 @@
|
||||
.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 {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
// We need an increased hit area on hover
|
||||
.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
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
.vjs-menu .vjs-menu-content {
|
||||
display: block;
|
||||
padding: 0; margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
font-family: $text-font-family;
|
||||
}
|
||||
|
||||
// prevent menus from opening while scrubbing (FF, IE)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
// IE 8 hack for media queries which the sass parser can encounter problems with
|
||||
$ie8screen: "\\0screen";
|
||||
|
||||
// original home: css/components/_control-bar.scss
|
||||
// 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: ""; }
|
||||
}
|
||||
|
||||
// Video has started playing AND user is inactive
|
||||
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
|
||||
// Make controls hidden in IE8 for now
|
||||
@media #{$ie8screen} {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@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; } }
|
||||
@@ -39,3 +39,5 @@
|
||||
@import "components/adaptive";
|
||||
@import "components/captions-settings";
|
||||
@import "components/modal-dialog";
|
||||
|
||||
@import "print";
|
||||
|
||||
@@ -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);
|
||||
+16
-12
@@ -3,10 +3,7 @@
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -32,7 +29,7 @@ class Button extends ClickableComponent {
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
*/
|
||||
createEl(tag='button', props={}, attributes={}) {
|
||||
createEl(tag = 'button', props = {}, attributes = {}) {
|
||||
props = assign({
|
||||
className: this.buildCSSClass()
|
||||
}, props);
|
||||
@@ -53,11 +50,15 @@ class Button extends ClickableComponent {
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -73,8 +74,9 @@ class Button extends ClickableComponent {
|
||||
* @deprecated
|
||||
* @method addChild
|
||||
*/
|
||||
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
|
||||
@@ -87,13 +89,15 @@ class Button extends ClickableComponent {
|
||||
* @method handleKeyPress
|
||||
*/
|
||||
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
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass keypress handling up for unsupported keys
|
||||
super.handleKeyPress(event);
|
||||
}
|
||||
}
|
||||
|
||||
Component.registerComponent('Button', Button);
|
||||
|
||||
@@ -39,7 +39,7 @@ class ClickableComponent extends Component {
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
*/
|
||||
createEl(tag='div', props={}, attributes={}) {
|
||||
createEl(tag = 'div', props = {}, attributes = {}) {
|
||||
props = assign({
|
||||
className: this.buildCSSClass(),
|
||||
tabIndex: 0
|
||||
@@ -51,11 +51,13 @@ 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);
|
||||
const el = super.createEl(tag, props, attributes);
|
||||
|
||||
this.createControlTextEl(el);
|
||||
|
||||
@@ -78,7 +80,7 @@ class ClickableComponent extends Component {
|
||||
el.appendChild(this.controlTextEl_);
|
||||
}
|
||||
|
||||
this.controlText(this.controlText_);
|
||||
this.controlText(this.controlText_, el);
|
||||
|
||||
return this.controlTextEl_;
|
||||
}
|
||||
@@ -86,15 +88,21 @@ class ClickableComponent extends Component {
|
||||
/**
|
||||
* Controls text - both request and localize
|
||||
*
|
||||
* @param {String} text Text for element
|
||||
* @param {String} text Text for element
|
||||
* @param {Element=} el Element to set the title on
|
||||
* @return {String}
|
||||
* @method controlText
|
||||
*/
|
||||
controlText(text) {
|
||||
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 = this.localize(this.controlText_);
|
||||
this.controlTextEl_.innerHTML = localizedText;
|
||||
el.setAttribute('title', localizedText);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -117,14 +125,14 @@ class ClickableComponent extends Component {
|
||||
* @return {Component} The child component (created by this process if a string was used)
|
||||
* @method addChild
|
||||
*/
|
||||
addChild(child, options={}) {
|
||||
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.`);
|
||||
// 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);
|
||||
}
|
||||
@@ -175,12 +183,15 @@ class ClickableComponent extends Component {
|
||||
* @method handleKeyPress
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-41
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* Player Component - Base class for all UI objects
|
||||
*/
|
||||
|
||||
import window from 'global/window';
|
||||
import * as Dom from './utils/dom.js';
|
||||
import * as Fn from './utils/fn.js';
|
||||
@@ -11,10 +10,8 @@ import * as Guid from './utils/guid.js';
|
||||
import * as Events from './utils/events.js';
|
||||
import log from './utils/log.js';
|
||||
import toTitleCase from './utils/to-title-case.js';
|
||||
import assign from 'object.assign';
|
||||
import mergeOptions from './utils/merge-options.js';
|
||||
|
||||
|
||||
/**
|
||||
* Base UI Component class
|
||||
* Components are embeddable UI objects that are represented by both a
|
||||
@@ -32,7 +29,7 @@ import mergeOptions from './utils/merge-options.js';
|
||||
* ```
|
||||
* Components are also event targets.
|
||||
* ```js
|
||||
* button.on('click', function(){
|
||||
* button.on('click', function() {
|
||||
* console.log('Button Clicked!');
|
||||
* });
|
||||
* button.trigger('customevent');
|
||||
@@ -66,7 +63,7 @@ class Component {
|
||||
// If there was no ID from the options, generate one
|
||||
if (!this.id_) {
|
||||
// Don't require the player ID function in the case of mock players
|
||||
let id = player && player.id && player.id() || 'no_player';
|
||||
const id = player && player.id && player.id() || 'no_player';
|
||||
|
||||
this.id_ = `${id}_component_${Guid.newGUID()}`;
|
||||
}
|
||||
@@ -219,21 +216,21 @@ class Component {
|
||||
}
|
||||
|
||||
localize(string) {
|
||||
let code = this.player_.language && this.player_.language();
|
||||
let languages = this.player_.languages && this.player_.languages();
|
||||
const code = this.player_.language && this.player_.language();
|
||||
const languages = this.player_.languages && this.player_.languages();
|
||||
|
||||
if (!code || !languages) {
|
||||
return string;
|
||||
}
|
||||
|
||||
let language = languages[code];
|
||||
const language = languages[code];
|
||||
|
||||
if (language && language[string]) {
|
||||
return language[string];
|
||||
}
|
||||
|
||||
let primaryCode = code.split('-')[0];
|
||||
let primaryLang = languages[primaryCode];
|
||||
const primaryCode = code.split('-')[0];
|
||||
const primaryLang = languages[primaryCode];
|
||||
|
||||
if (primaryLang && primaryLang[string]) {
|
||||
return primaryLang[string];
|
||||
@@ -340,7 +337,7 @@ class Component {
|
||||
* @return {Component} The child component (created by this process if a string was used)
|
||||
* @method addChild
|
||||
*/
|
||||
addChild(child, options={}, index=this.children_.length) {
|
||||
addChild(child, options = {}, index = this.children_.length) {
|
||||
let component;
|
||||
let componentName;
|
||||
|
||||
@@ -361,14 +358,14 @@ class Component {
|
||||
|
||||
// If no componentClass in options, assume componentClass is the name lowercased
|
||||
// (e.g. playButton)
|
||||
let componentClassName = options.componentClass || toTitleCase(componentName);
|
||||
const componentClassName = options.componentClass || toTitleCase(componentName);
|
||||
|
||||
// Set name through options
|
||||
options.name = componentName;
|
||||
|
||||
// Create a new object & element for this controls set
|
||||
// If there's no .player_, this is a player
|
||||
let ComponentClass = Component.getComponent(componentClassName);
|
||||
const ComponentClass = Component.getComponent(componentClassName);
|
||||
|
||||
if (!ComponentClass) {
|
||||
throw new Error(`Component ${componentClassName} does not exist`);
|
||||
@@ -406,8 +403,9 @@ class Component {
|
||||
// Add the UI object's element to the container div (box)
|
||||
// Having an element is not required
|
||||
if (typeof component.el === 'function' && component.el()) {
|
||||
let childNodes = this.contentEl().children;
|
||||
let refNode = childNodes[index] || null;
|
||||
const childNodes = this.contentEl().children;
|
||||
const refNode = childNodes[index] || null;
|
||||
|
||||
this.contentEl().insertBefore(component.el(), refNode);
|
||||
}
|
||||
|
||||
@@ -448,7 +446,7 @@ class Component {
|
||||
this.childIndex_[component.id()] = null;
|
||||
this.childNameIndex_[component.name()] = null;
|
||||
|
||||
let compEl = component.el();
|
||||
const compEl = component.el();
|
||||
|
||||
if (compEl && compEl.parentNode === this.contentEl()) {
|
||||
this.contentEl().removeChild(component.el());
|
||||
@@ -502,14 +500,14 @@ class Component {
|
||||
* @method initChildren
|
||||
*/
|
||||
initChildren() {
|
||||
let children = this.options_.children;
|
||||
const children = this.options_.children;
|
||||
|
||||
if (children) {
|
||||
// `this` is `parent`
|
||||
let parentOptions = this.options_;
|
||||
const parentOptions = this.options_;
|
||||
|
||||
let handleAdd = (child) => {
|
||||
let name = child.name;
|
||||
const handleAdd = (child) => {
|
||||
const name = child.name;
|
||||
let opts = child.opts;
|
||||
|
||||
// Allow options for children to be set at the parent options
|
||||
@@ -539,7 +537,8 @@ class Component {
|
||||
// Add a direct reference to the child by name on the parent instance.
|
||||
// If two of the same component are used, different names should be supplied
|
||||
// for each
|
||||
let newChild = this.addChild(name, opts);
|
||||
const newChild = this.addChild(name, opts);
|
||||
|
||||
if (newChild) {
|
||||
this[name] = newChild;
|
||||
}
|
||||
@@ -547,7 +546,7 @@ class Component {
|
||||
|
||||
// Allow for an array of children details to passed in the options
|
||||
let workingChildren;
|
||||
let Tech = Component.getComponent('Tech');
|
||||
const Tech = Component.getComponent('Tech');
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
workingChildren = children;
|
||||
@@ -563,13 +562,13 @@ class Component {
|
||||
return !workingChildren.some(function(wchild) {
|
||||
if (typeof wchild === 'string') {
|
||||
return child === wchild;
|
||||
} else {
|
||||
return child === wchild.name;
|
||||
}
|
||||
return child === wchild.name;
|
||||
});
|
||||
}))
|
||||
.map((child) => {
|
||||
let name, opts;
|
||||
let name;
|
||||
let opts;
|
||||
|
||||
if (typeof child === 'string') {
|
||||
name = child;
|
||||
@@ -585,8 +584,9 @@ class Component {
|
||||
// we have to make sure that child.name isn't in the techOrder since
|
||||
// techs are registerd as Components but can't aren't compatible
|
||||
// See https://github.com/videojs/video.js/issues/2772
|
||||
let c = Component.getComponent(child.opts.componentClass ||
|
||||
const c = Component.getComponent(child.opts.componentClass ||
|
||||
toTitleCase(child.name));
|
||||
|
||||
return c && !Tech.isTech(c);
|
||||
})
|
||||
.forEach(handleAdd);
|
||||
@@ -608,7 +608,7 @@ class Component {
|
||||
/**
|
||||
* Add an event listener to this component's element
|
||||
* ```js
|
||||
* var myFunc = function(){
|
||||
* var myFunc = function() {
|
||||
* var myComponent = this;
|
||||
* // Do something when the event is fired
|
||||
* };
|
||||
@@ -797,7 +797,7 @@ class Component {
|
||||
* @return {Component}
|
||||
* @method ready
|
||||
*/
|
||||
ready(fn, sync=false) {
|
||||
ready(fn, sync = false) {
|
||||
if (fn) {
|
||||
if (this.isReady_) {
|
||||
if (sync) {
|
||||
@@ -824,14 +824,14 @@ class Component {
|
||||
this.isReady_ = true;
|
||||
|
||||
// Ensure ready is triggerd asynchronously
|
||||
this.setTimeout(function(){
|
||||
let readyQueue = this.readyQueue_;
|
||||
this.setTimeout(function() {
|
||||
const readyQueue = this.readyQueue_;
|
||||
|
||||
// Reset Ready Queue
|
||||
this.readyQueue_ = [];
|
||||
|
||||
if (readyQueue && readyQueue.length > 0) {
|
||||
readyQueue.forEach(function(fn){
|
||||
readyQueue.forEach(function(fn) {
|
||||
fn.call(this);
|
||||
}, this);
|
||||
}
|
||||
@@ -1077,8 +1077,8 @@ class Component {
|
||||
}
|
||||
|
||||
// Get dimension value from style
|
||||
let val = this.el_.style[widthOrHeight];
|
||||
let pxIndex = val.indexOf('px');
|
||||
const val = this.el_.style[widthOrHeight];
|
||||
const pxIndex = val.indexOf('px');
|
||||
|
||||
if (pxIndex !== -1) {
|
||||
// Return the pixel value with no 'px'
|
||||
@@ -1106,11 +1106,13 @@ class Component {
|
||||
|
||||
if (typeof window.getComputedStyle === 'function') {
|
||||
const computedStyle = window.getComputedStyle(this.el_);
|
||||
|
||||
computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
|
||||
} else if (this.el_.currentStyle) {
|
||||
// ie 8 doesn't support computed style, shim it
|
||||
// return clientWidth or clientHeight instead for better accuracy
|
||||
const rule = `offset${toTitleCase(widthOrHeight)}`;
|
||||
|
||||
computedWidthOrHeight = this.el_[rule];
|
||||
}
|
||||
|
||||
@@ -1176,8 +1178,11 @@ class Component {
|
||||
this.on('touchstart', function(event) {
|
||||
// If more than one finger, don't consider treating this as a click
|
||||
if (event.touches.length === 1) {
|
||||
// Copy the touches object to prevent modifying the original
|
||||
firstTouch = assign({}, event.touches[0]);
|
||||
// Copy pageX/pageY from the object
|
||||
firstTouch = {
|
||||
pageX: event.touches[0].pageX,
|
||||
pageY: event.touches[0].pageY
|
||||
};
|
||||
// Record start time so we can detect a tap vs. "touch and hold"
|
||||
touchStart = new Date().getTime();
|
||||
// Reset couldBeTap tracking
|
||||
@@ -1194,7 +1199,7 @@ class Component {
|
||||
// So, if we moved only a small distance, this could still be a tap
|
||||
const xdiff = event.touches[0].pageX - firstTouch.pageX;
|
||||
const ydiff = event.touches[0].pageY - firstTouch.pageY;
|
||||
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||||
|
||||
if (touchDistance > tapMovementThreshold) {
|
||||
couldBeTap = false;
|
||||
@@ -1297,7 +1302,7 @@ class Component {
|
||||
fn = Fn.bind(this, fn);
|
||||
|
||||
// window.setTimeout would be preferable here, but due to some bizarre issue with Sinon and/or Phantomjs, we can't.
|
||||
let timeoutId = window.setTimeout(fn, timeout);
|
||||
const timeoutId = window.setTimeout(fn, timeout);
|
||||
|
||||
const disposeFn = function() {
|
||||
this.clearTimeout(timeoutId);
|
||||
@@ -1340,7 +1345,7 @@ class Component {
|
||||
setInterval(fn, interval) {
|
||||
fn = Fn.bind(this, fn);
|
||||
|
||||
let intervalId = window.setInterval(fn, interval);
|
||||
const intervalId = window.setInterval(fn, interval);
|
||||
|
||||
const disposeFn = function() {
|
||||
this.clearInterval(intervalId);
|
||||
@@ -1425,7 +1430,7 @@ class Component {
|
||||
// Set up the constructor using the supplied init method
|
||||
// or using the init of the parent object
|
||||
// Make sure to check the unobfuscated version for external libs
|
||||
let init = props.init || props.init || this.prototype.init || this.prototype.init || function() {};
|
||||
const init = props.init || props.init || this.prototype.init || this.prototype.init || function() {};
|
||||
// In Resig's simple class inheritance (previously used) the constructor
|
||||
// is a function that calls `this.init.apply(arguments)`
|
||||
// However that would prevent us from using `ParentObject.call(this);`
|
||||
@@ -1435,7 +1440,7 @@ class Component {
|
||||
// `ParentObject.prototype.init.apply(this, arguments);`
|
||||
// Bleh. We're not creating a _super() function, so it's good to keep
|
||||
// the parent constructor reference simple.
|
||||
let subObj = function() {
|
||||
const subObj = function() {
|
||||
init.apply(this, arguments);
|
||||
};
|
||||
|
||||
@@ -1449,7 +1454,7 @@ class Component {
|
||||
subObj.extend = Component.extend;
|
||||
|
||||
// Extend subObj's prototype with functions and other properties from props
|
||||
for (let name in props) {
|
||||
for (const name in props) {
|
||||
if (props.hasOwnProperty(name)) {
|
||||
subObj.prototype[name] = props[name];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -40,25 +39,25 @@ class AudioTrackButton extends TrackButton {
|
||||
* @method createItems
|
||||
*/
|
||||
createItems(items = []) {
|
||||
let tracks = this.player_.audioTracks && this.player_.audioTracks();
|
||||
const tracks = this.player_.audioTracks && this.player_.audioTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
AudioTrackButton.prototype.controlText_ = 'Audio Track';
|
||||
Component.registerComponent('AudioTrackButton', AudioTrackButton);
|
||||
export default AudioTrackButton;
|
||||
|
||||
@@ -15,8 +15,8 @@ import * as Fn from '../../utils/fn.js';
|
||||
*/
|
||||
class AudioTrackMenuItem extends MenuItem {
|
||||
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';
|
||||
@@ -27,7 +27,7 @@ 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', () => {
|
||||
@@ -42,18 +42,18 @@ class AudioTrackMenuItem extends MenuItem {
|
||||
* @method handleClick
|
||||
*/
|
||||
handleClick(event) {
|
||||
let tracks = this.player_.audioTracks();
|
||||
const tracks = this.player_.audioTracks();
|
||||
|
||||
super.handleClick(event);
|
||||
|
||||
if (!tracks) return;
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
const track = tracks[i];
|
||||
|
||||
if (track === this.track) {
|
||||
track.enabled = true;
|
||||
}
|
||||
track.enabled = track === this.track;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,24 @@
|
||||
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-control/volume-control.js';
|
||||
import './volume-menu-button.js';
|
||||
import './mute-toggle.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
|
||||
@@ -42,13 +42,13 @@ class ControlBar extends Component {
|
||||
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'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ControlBar.prototype.options_ = {
|
||||
loadEvent: 'play',
|
||||
children: [
|
||||
'playToggle',
|
||||
'volumeMenuButton',
|
||||
|
||||
@@ -12,6 +12,11 @@ import Component from '../component.js';
|
||||
*/
|
||||
class FullscreenToggle extends Button {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sub components to stack CSS class names
|
||||
*
|
||||
@@ -21,7 +26,18 @@ class FullscreenToggle extends Button {
|
||||
buildCSSClass() {
|
||||
return `vjs-fullscreen-control ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Fullscreenchange on the component and change control text accordingly
|
||||
*
|
||||
* @method handleFullscreenChange
|
||||
*/
|
||||
handleFullscreenChange() {
|
||||
if (this.player_.isFullscreen()) {
|
||||
this.controlText('Non-Fullscreen');
|
||||
} else {
|
||||
this.controlText('Fullscreen');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handles click for full screen
|
||||
*
|
||||
@@ -30,10 +46,8 @@ class FullscreenToggle extends Button {
|
||||
handleClick() {
|
||||
if (!this.player_.isFullscreen()) {
|
||||
this.player_.requestFullscreen();
|
||||
this.controlText('Non-Fullscreen');
|
||||
} else {
|
||||
this.player_.exitFullscreen();
|
||||
this.controlText('Fullscreen');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class LiveDisplay extends Component {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
var el = super.createEl('div', {
|
||||
const el = super.createEl('div', {
|
||||
className: 'vjs-live-control vjs-control'
|
||||
});
|
||||
|
||||
|
||||
@@ -21,14 +21,15 @@ class MuteToggle extends Button {
|
||||
this.on(player, 'volumechange', this.update);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
|
||||
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.
|
||||
// We need to update the button to account for a default muted state.
|
||||
this.update();
|
||||
|
||||
if (player.tech_['featuresVolumeControl'] === false) {
|
||||
if (player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
@@ -52,7 +53,7 @@ class MuteToggle extends Button {
|
||||
* @method handleClick
|
||||
*/
|
||||
handleClick() {
|
||||
this.player_.muted( this.player_.muted() ? false : true );
|
||||
this.player_.muted(this.player_.muted() ? false : true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,8 +62,8 @@ class MuteToggle extends Button {
|
||||
* @method update
|
||||
*/
|
||||
update() {
|
||||
var vol = this.player_.volume(),
|
||||
level = 3;
|
||||
const vol = this.player_.volume();
|
||||
let level = 3;
|
||||
|
||||
if (vol === 0 || this.player_.muted()) {
|
||||
level = 0;
|
||||
@@ -75,13 +76,14 @@ 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++) {
|
||||
// TODO improve muted icon classes
|
||||
for (let i = 0; i < 4; i++) {
|
||||
Dom.removeElClass(this.el_, `vjs-vol-${i}`);
|
||||
}
|
||||
Dom.addElClass(this.el_, `vjs-vol-${level}`);
|
||||
|
||||
@@ -14,7 +14,7 @@ import Component from '../component.js';
|
||||
*/
|
||||
class PlayToggle extends Button {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'play', this.handlePlay);
|
||||
@@ -52,7 +52,8 @@ class PlayToggle extends Button {
|
||||
handlePlay() {
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +64,8 @@ class PlayToggle extends Button {
|
||||
handlePause() {
|
||||
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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import * as Dom from '../../utils/dom.js';
|
||||
*/
|
||||
class PlaybackRateMenuButton extends MenuButton {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.updateVisibility();
|
||||
@@ -34,7 +34,7 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl();
|
||||
const el = super.createEl();
|
||||
|
||||
this.labelEl_ = Dom.createEl('div', {
|
||||
className: 'vjs-playback-rate-value',
|
||||
@@ -63,13 +63,13 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
* @method createMenu
|
||||
*/
|
||||
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'})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -94,12 +94,13 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
*/
|
||||
handleClick() {
|
||||
// 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;
|
||||
@@ -115,7 +116,7 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
* @method playbackRates
|
||||
*/
|
||||
playbackRates() {
|
||||
return this.options_['playbackRates'] || (this.options_.playerOptions && this.options_.playerOptions['playbackRates']);
|
||||
return this.options_.playbackRates || (this.options_.playerOptions && this.options_.playerOptions.playbackRates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,10 +127,10 @@ class PlaybackRateMenuButton extends MenuButton {
|
||||
* @method playbackRateSupported
|
||||
*/
|
||||
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
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ import Component from '../../component.js';
|
||||
*/
|
||||
class PlaybackRateMenuItem extends MenuItem {
|
||||
|
||||
constructor(player, options){
|
||||
let label = options['rate'];
|
||||
let rate = parseFloat(label, 10);
|
||||
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;
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as Dom from '../../utils/dom.js';
|
||||
*/
|
||||
class LoadProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'progress', this.update);
|
||||
}
|
||||
@@ -38,14 +38,16 @@ class LoadProgressBar extends Component {
|
||||
* @method update
|
||||
*/
|
||||
update() {
|
||||
let buffered = this.player_.buffered();
|
||||
let duration = this.player_.duration();
|
||||
let bufferedEnd = this.player_.bufferedEnd();
|
||||
let children = this.el_.children;
|
||||
const buffered = this.player_.buffered();
|
||||
const duration = this.player_.duration();
|
||||
const bufferedEnd = this.player_.bufferedEnd();
|
||||
const children = this.el_.children;
|
||||
|
||||
// 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,8 +56,8 @@ 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) {
|
||||
@@ -69,7 +71,7 @@ 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,24 +55,24 @@ 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;
|
||||
const duration = this.player_.duration();
|
||||
const newTime = this.calculateDistance(event) * duration;
|
||||
const position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
|
||||
|
||||
this.update(newTime, position);
|
||||
}
|
||||
|
||||
update(newTime, position) {
|
||||
let time = formatTime(newTime, this.player_.duration());
|
||||
const 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;
|
||||
const clampedPosition = this.clampPosition_(position);
|
||||
const difference = position - clampedPosition + 1;
|
||||
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
|
||||
const tooltipWidthHalf = tooltipWidth / 2;
|
||||
|
||||
this.tooltip.innerHTML = time;
|
||||
this.tooltip.style.right = `-${tooltipWidthHalf - difference}px`;
|
||||
@@ -98,9 +98,9 @@ class MouseTimeDisplay extends Component {
|
||||
return position;
|
||||
}
|
||||
|
||||
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
|
||||
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
|
||||
let tooltipWidthHalf = tooltipWidth / 2;
|
||||
const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
|
||||
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
|
||||
const tooltipWidthHalf = tooltipWidth / 2;
|
||||
let actualPosition = position;
|
||||
|
||||
if (position < tooltipWidthHalf) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
|
||||
*/
|
||||
class PlayProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.updateDataAttr();
|
||||
this.on(player, 'timeupdate', this.updateDataAttr);
|
||||
@@ -48,7 +47,8 @@ class PlayProgressBar extends Component {
|
||||
}
|
||||
|
||||
updateDataAttr() {
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
|
||||
this.el_.setAttribute('data-current-time', formatTime(time, this.player_.duration()));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
* @file progress-control.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import SeekBar from './seek-bar.js';
|
||||
import MouseTimeDisplay from './mouse-time-display.js';
|
||||
|
||||
import './seek-bar.js';
|
||||
import './mouse-time-display.js';
|
||||
|
||||
/**
|
||||
* The Progress Control component contains the seek bar, load progress,
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
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 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 './tooltip-progress-bar.js';
|
||||
|
||||
/**
|
||||
* Seek Bar and holder for the progress bars
|
||||
@@ -21,7 +21,7 @@ import assign from 'object.assign';
|
||||
*/
|
||||
class SeekBar extends Slider {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'timeupdate', this.updateProgress);
|
||||
this.on(player, 'ended', this.updateProgress);
|
||||
@@ -65,9 +65,10 @@ class SeekBar extends Slider {
|
||||
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;
|
||||
const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
|
||||
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width);
|
||||
const 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`;
|
||||
@@ -76,9 +77,12 @@ class SeekBar extends Slider {
|
||||
|
||||
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)
|
||||
el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2));
|
||||
// human readable value of progress bar (time complete)
|
||||
el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +92,8 @@ class SeekBar extends Slider {
|
||||
* @method getPercent
|
||||
*/
|
||||
getPercent() {
|
||||
let percent = this.player_.currentTime() / this.player_.duration();
|
||||
const percent = this.player_.currentTime() / this.player_.duration();
|
||||
|
||||
return percent >= 1 ? 1 : percent;
|
||||
}
|
||||
|
||||
@@ -115,7 +120,9 @@ class SeekBar extends Slider {
|
||||
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);
|
||||
@@ -141,7 +148,8 @@ class SeekBar extends Slider {
|
||||
* @method stepForward
|
||||
*/
|
||||
stepForward() {
|
||||
this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
|
||||
// more quickly fast forward for keyboard-only users
|
||||
this.player_.currentTime(this.player_.currentTime() + 5);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +158,8 @@ class SeekBar extends Slider {
|
||||
* @method stepBack
|
||||
*/
|
||||
stepBack() {
|
||||
this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
|
||||
// more quickly rewind for keyboard-only users
|
||||
this.player_.currentTime(this.player_.currentTime() - 5);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -161,7 +170,7 @@ SeekBar.prototype.options_ = {
|
||||
'mouseTimeDisplay',
|
||||
'playProgressBar'
|
||||
],
|
||||
'barName': 'playProgressBar'
|
||||
barName: 'playProgressBar'
|
||||
};
|
||||
|
||||
SeekBar.prototype.playerEvent = 'timeupdate';
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
|
||||
*/
|
||||
class TooltipProgressBar extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.updateDataAttr();
|
||||
this.on(player, 'timeupdate', this.updateDataAttr);
|
||||
@@ -30,7 +29,7 @@ class TooltipProgressBar extends Component {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl('div', {
|
||||
const 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>`
|
||||
@@ -42,8 +41,9 @@ class TooltipProgressBar extends Component {
|
||||
}
|
||||
|
||||
updateDataAttr() {
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
let formattedTime = formatTime(time, this.player_.duration());
|
||||
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
const formattedTime = formatTime(time, this.player_.duration());
|
||||
|
||||
this.el_.setAttribute('data-current-time', formattedTime);
|
||||
this.tooltip.innerHTML = formattedTime;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ class CustomControlSpacer extends Spacer {
|
||||
* @method createEl
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -12,24 +12,24 @@ import Component from '../../component.js';
|
||||
* @extends TextTrackMenuItem
|
||||
* @class CaptionSettingsMenuItem
|
||||
*/
|
||||
class CaptionSettingsMenuItem extends TextTrackMenuItem {
|
||||
class CaptionSettingsMenuItem extends TextTrackMenuItem {
|
||||
|
||||
constructor(player, options) {
|
||||
options['track'] = {
|
||||
'kind': options['kind'],
|
||||
'player': player,
|
||||
'label': options['kind'] + ' settings',
|
||||
'selectable': false,
|
||||
'default': false,
|
||||
options.track = {
|
||||
player,
|
||||
kind: options.kind,
|
||||
label: options.kind + ' settings',
|
||||
selectable: false,
|
||||
default: false,
|
||||
mode: 'disabled'
|
||||
};
|
||||
|
||||
// CaptionSettingsMenuItem has no concept of 'selected'
|
||||
options['selectable'] = false;
|
||||
options.selectable = false;
|
||||
|
||||
super(player, options);
|
||||
this.addClass('vjs-texttrack-settings');
|
||||
this.controlText(', opens ' + options['kind'] + ' settings dialog');
|
||||
this.controlText(', opens ' + options.kind + ' settings dialog');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,9 +16,9 @@ import CaptionSettingsMenuItem from './caption-settings-menu-item.js';
|
||||
*/
|
||||
class CaptionsButton extends TextTrackButton {
|
||||
|
||||
constructor(player, options, ready){
|
||||
constructor(player, options, ready) {
|
||||
super(player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Captions Menu');
|
||||
this.el_.setAttribute('aria-label', 'Captions Menu');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,10 +38,11 @@ class CaptionsButton extends TextTrackButton {
|
||||
*/
|
||||
update() {
|
||||
let threshold = 2;
|
||||
|
||||
super.update();
|
||||
|
||||
// if native, then threshold is 1 because no settings button
|
||||
if (this.player().tech_ && this.player().tech_['featuresNativeTextTracks']) {
|
||||
if (this.player().tech_ && this.player().tech_.featuresNativeTextTracks) {
|
||||
threshold = 1;
|
||||
}
|
||||
|
||||
@@ -59,10 +60,10 @@ class CaptionsButton extends TextTrackButton {
|
||||
* @method createItems
|
||||
*/
|
||||
createItems() {
|
||||
let items = [];
|
||||
const items = [];
|
||||
|
||||
if (!(this.player().tech_ && this.player().tech_['featuresNativeTextTracks'])) {
|
||||
items.push(new CaptionSettingsMenuItem(this.player_, { 'kind': this.kind_ }));
|
||||
if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
|
||||
items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.kind_}));
|
||||
}
|
||||
|
||||
return super.createItems(items);
|
||||
|
||||
@@ -7,9 +7,7 @@ import TextTrackMenuItem from './text-track-menu-item.js';
|
||||
import ChaptersTrackMenuItem from './chapters-track-menu-item.js';
|
||||
import Menu from '../../menu/menu.js';
|
||||
import * as Dom from '../../utils/dom.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import toTitleCase from '../../utils/to-title-case.js';
|
||||
import window from 'global/window';
|
||||
|
||||
/**
|
||||
* The button component for toggling and selecting chapters
|
||||
@@ -24,9 +22,9 @@ import window from 'global/window';
|
||||
*/
|
||||
class ChaptersButton extends TextTrackButton {
|
||||
|
||||
constructor(player, options, ready){
|
||||
constructor(player, options, ready) {
|
||||
super(player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Chapters Menu');
|
||||
this.el_.setAttribute('aria-label', 'Chapters Menu');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,20 +44,18 @@ class ChaptersButton extends TextTrackButton {
|
||||
* @method createItems
|
||||
*/
|
||||
createItems() {
|
||||
let items = [];
|
||||
|
||||
let tracks = this.player_.textTracks();
|
||||
const items = [];
|
||||
const tracks = this.player_.textTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
if (track['kind'] === this.kind_) {
|
||||
items.push(new TextTrackMenuItem(this.player_, {
|
||||
'track': track
|
||||
}));
|
||||
const track = tracks[i];
|
||||
|
||||
if (track.kind === this.kind_) {
|
||||
items.push(new TextTrackMenuItem(this.player_, {track}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,14 +69,16 @@ class ChaptersButton extends TextTrackButton {
|
||||
* @method createMenu
|
||||
*/
|
||||
createMenu() {
|
||||
let tracks = this.player_.textTracks() || [];
|
||||
const tracks = this.player_.textTracks() || [];
|
||||
let chaptersTrack;
|
||||
let items = this.items = [];
|
||||
let items = this.items || [];
|
||||
|
||||
for (let i = 0, length = tracks.length; i < length; i++) {
|
||||
let track = tracks[i];
|
||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
||||
|
||||
if (track['kind'] === this.kind_) {
|
||||
// We will always choose the last track as our chaptersTrack
|
||||
const track = tracks[i];
|
||||
|
||||
if (track.kind === this.kind_) {
|
||||
chaptersTrack = track;
|
||||
|
||||
break;
|
||||
@@ -88,21 +86,30 @@ class ChaptersButton extends TextTrackButton {
|
||||
}
|
||||
|
||||
let menu = this.menu;
|
||||
|
||||
if (menu === undefined) {
|
||||
menu = new Menu(this.player_);
|
||||
let title = Dom.createEl('li', {
|
||||
|
||||
const title = Dom.createEl('li', {
|
||||
className: 'vjs-menu-title',
|
||||
innerHTML: toTitleCase(this.kind_),
|
||||
tabIndex: -1
|
||||
});
|
||||
|
||||
menu.children_.unshift(title);
|
||||
Dom.insertElFirst(title, menu.contentEl());
|
||||
} else {
|
||||
// We will empty out the menu children each time because we want a
|
||||
// fresh new menu child list each time
|
||||
items.forEach(item => menu.removeChild(item));
|
||||
// Empty out the ChaptersButton menu items because we no longer need them
|
||||
items = [];
|
||||
}
|
||||
|
||||
if (chaptersTrack && chaptersTrack.cues == null) {
|
||||
chaptersTrack['mode'] = 'hidden';
|
||||
if (chaptersTrack && (chaptersTrack.cues === null || chaptersTrack.cues === undefined)) {
|
||||
chaptersTrack.mode = 'hidden';
|
||||
|
||||
let remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack);
|
||||
const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack);
|
||||
|
||||
if (remoteTextTrackEl) {
|
||||
remoteTextTrackEl.addEventListener('load', (event) => this.update());
|
||||
@@ -110,31 +117,29 @@ class ChaptersButton extends TextTrackButton {
|
||||
}
|
||||
|
||||
if (chaptersTrack && chaptersTrack.cues && chaptersTrack.cues.length > 0) {
|
||||
let cues = chaptersTrack['cues'], cue;
|
||||
const cues = chaptersTrack.cues;
|
||||
|
||||
for (let i = 0, l = cues.length; i < l; i++) {
|
||||
cue = cues[i];
|
||||
const cue = cues[i];
|
||||
|
||||
let mi = new ChaptersTrackMenuItem(this.player_, {
|
||||
'track': chaptersTrack,
|
||||
'cue': cue
|
||||
const mi = new ChaptersTrackMenuItem(this.player_, {
|
||||
cue,
|
||||
track: chaptersTrack
|
||||
});
|
||||
|
||||
items.push(mi);
|
||||
|
||||
menu.addChild(mi);
|
||||
}
|
||||
|
||||
this.addChild(menu);
|
||||
}
|
||||
|
||||
if (this.items.length > 0) {
|
||||
if (items.length > 0) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
// Assigning the value of items back to this.items for next iteration
|
||||
this.items = items;
|
||||
return menu;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ChaptersButton.prototype.kind_ = 'chapters';
|
||||
|
||||
@@ -15,14 +15,14 @@ import * as Fn from '../../utils/fn.js';
|
||||
*/
|
||||
class ChaptersTrackMenuItem extends MenuItem {
|
||||
|
||||
constructor(player, options){
|
||||
let track = options['track'];
|
||||
let cue = options['cue'];
|
||||
let currentTime = player.currentTime();
|
||||
constructor(player, options) {
|
||||
const track = options.track;
|
||||
const cue = options.cue;
|
||||
const currentTime = player.currentTime();
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = cue.text;
|
||||
options['selected'] = (cue['startTime'] <= currentTime && currentTime < cue['endTime']);
|
||||
options.label = cue.text;
|
||||
options.selected = (cue.startTime <= currentTime && currentTime < cue.endTime);
|
||||
super(player, options);
|
||||
|
||||
this.track = track;
|
||||
@@ -47,11 +47,11 @@ class ChaptersTrackMenuItem extends MenuItem {
|
||||
* @method update
|
||||
*/
|
||||
update() {
|
||||
let cue = this.cue;
|
||||
let currentTime = this.player_.currentTime();
|
||||
const cue = this.cue;
|
||||
const currentTime = this.player_.currentTime();
|
||||
|
||||
// vjs.log(currentTime, cue.startTime);
|
||||
this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']);
|
||||
this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ import * as Fn from '../../utils/fn.js';
|
||||
*/
|
||||
class DescriptionsButton extends TextTrackButton {
|
||||
|
||||
constructor(player, options, ready){
|
||||
constructor(player, options, ready) {
|
||||
super(player, options, ready);
|
||||
this.el_.setAttribute('aria-label', 'Descriptions Menu');
|
||||
|
||||
let tracks = player.textTracks();
|
||||
const tracks = player.textTracks();
|
||||
|
||||
if (tracks) {
|
||||
let changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', function() {
|
||||
@@ -37,14 +37,15 @@ class DescriptionsButton extends TextTrackButton {
|
||||
*
|
||||
* @method handleTracksChange
|
||||
*/
|
||||
handleTracksChange(event){
|
||||
let tracks = this.player().textTracks();
|
||||
handleTracksChange(event) {
|
||||
const tracks = this.player().textTracks();
|
||||
let disabled = false;
|
||||
|
||||
// Check whether a track of a different kind is showing
|
||||
for (let i = 0, l = tracks.length; i < l; i++) {
|
||||
let track = tracks[i];
|
||||
if (track['kind'] !== this.kind_ && track['mode'] === 'showing') {
|
||||
const track = tracks[i];
|
||||
|
||||
if (track.kind !== this.kind_ && track.mode === 'showing') {
|
||||
disabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -14,19 +14,19 @@ import Component from '../../component.js';
|
||||
*/
|
||||
class OffTextTrackMenuItem extends TextTrackMenuItem {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
// Create pseudo track info
|
||||
// Requires options['kind']
|
||||
options['track'] = {
|
||||
'kind': options['kind'],
|
||||
'player': player,
|
||||
'label': options['kind'] + ' off',
|
||||
'default': false,
|
||||
'mode': 'disabled'
|
||||
options.track = {
|
||||
player,
|
||||
kind: options.kind,
|
||||
label: options.kind + ' off',
|
||||
default: false,
|
||||
mode: 'disabled'
|
||||
};
|
||||
|
||||
// MenuItem is selectable
|
||||
options['selectable'] = true;
|
||||
options.selectable = true;
|
||||
|
||||
super(player, options);
|
||||
this.selected(true);
|
||||
@@ -38,13 +38,14 @@ class OffTextTrackMenuItem extends TextTrackMenuItem {
|
||||
* @param {Object} event Event object
|
||||
* @method handleTracksChange
|
||||
*/
|
||||
handleTracksChange(event){
|
||||
let tracks = this.player().textTracks();
|
||||
handleTracksChange(event) {
|
||||
const tracks = this.player().textTracks();
|
||||
let selected = true;
|
||||
|
||||
for (let i = 0, l = tracks.length; i < l; i++) {
|
||||
let track = tracks[i];
|
||||
if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') {
|
||||
const track = tracks[i];
|
||||
|
||||
if (track.kind === this.track.kind && track.mode === 'showing') {
|
||||
selected = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ import Component from '../../component.js';
|
||||
*/
|
||||
class SubtitlesButton extends TextTrackButton {
|
||||
|
||||
constructor(player, options, ready){
|
||||
constructor(player, options, ready) {
|
||||
super(player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Subtitles Menu');
|
||||
this.el_.setAttribute('aria-label', 'Subtitles Menu');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
import TrackButton from '../track-button.js';
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
import TextTrackMenuItem from './text-track-menu-item.js';
|
||||
import OffTextTrackMenuItem from './off-text-track-menu-item.js';
|
||||
|
||||
@@ -17,7 +16,7 @@ import OffTextTrackMenuItem from './off-text-track-menu-item.js';
|
||||
*/
|
||||
class TextTrackButton extends TrackButton {
|
||||
|
||||
constructor(player, options = {}){
|
||||
constructor(player, options = {}) {
|
||||
options.tracks = player.textTracks();
|
||||
|
||||
super(player, options);
|
||||
@@ -29,25 +28,25 @@ class TextTrackButton extends TrackButton {
|
||||
* @return {Array} Array of menu items
|
||||
* @method createItems
|
||||
*/
|
||||
createItems(items=[]) {
|
||||
createItems(items = []) {
|
||||
// Add an OFF menu item to turn all tracks off
|
||||
items.push(new OffTextTrackMenuItem(this.player_, { 'kind': this.kind_ }));
|
||||
items.push(new OffTextTrackMenuItem(this.player_, {kind: this.kind_}));
|
||||
|
||||
let tracks = this.player_.textTracks();
|
||||
const tracks = this.player_.textTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
const track = tracks[i];
|
||||
|
||||
// only add tracks that are of the appropriate kind and have a label
|
||||
if (track['kind'] === this.kind_) {
|
||||
if (track.kind === this.kind_) {
|
||||
items.push(new TextTrackMenuItem(this.player_, {
|
||||
track,
|
||||
// MenuItem is selectable
|
||||
'selectable': true,
|
||||
'track': track
|
||||
selectable: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,20 @@ import document from 'global/document';
|
||||
*/
|
||||
class TextTrackMenuItem extends MenuItem {
|
||||
|
||||
constructor(player, options){
|
||||
let track = options['track'];
|
||||
let tracks = player.textTracks();
|
||||
constructor(player, options) {
|
||||
const track = options.track;
|
||||
const tracks = player.textTracks();
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = track['label'] || track['language'] || 'Unknown';
|
||||
options['selected'] = track['default'] || track['mode'] === 'showing';
|
||||
options.label = track.label || track.language || 'Unknown';
|
||||
options.selected = track.default || track.mode === 'showing';
|
||||
|
||||
super(player, options);
|
||||
|
||||
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', function() {
|
||||
@@ -52,7 +52,9 @@ class TextTrackMenuItem extends MenuItem {
|
||||
// Android 2.3 throws an Illegal Constructor error for window.Event
|
||||
try {
|
||||
event = new window.Event('change');
|
||||
} catch(err){}
|
||||
} catch (err) {
|
||||
// continue regardless of error
|
||||
}
|
||||
}
|
||||
|
||||
if (!event) {
|
||||
@@ -71,24 +73,26 @@ class TextTrackMenuItem extends MenuItem {
|
||||
* @method handleClick
|
||||
*/
|
||||
handleClick(event) {
|
||||
let kind = this.track['kind'];
|
||||
let tracks = this.player_.textTracks();
|
||||
const kind = this.track.kind;
|
||||
const tracks = this.player_.textTracks();
|
||||
|
||||
super.handleClick(event);
|
||||
|
||||
if (!tracks) return;
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
let track = tracks[i];
|
||||
const track = tracks[i];
|
||||
|
||||
if (track['kind'] !== kind) {
|
||||
if (track.kind !== kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (track === this.track) {
|
||||
track['mode'] = 'showing';
|
||||
track.mode = 'showing';
|
||||
} else {
|
||||
track['mode'] = 'disabled';
|
||||
track.mode = 'disabled';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,8 +102,8 @@ class TextTrackMenuItem extends MenuItem {
|
||||
*
|
||||
* @method handleTracksChange
|
||||
*/
|
||||
handleTracksChange(event){
|
||||
this.selected(this.track['mode'] === 'showing');
|
||||
handleTracksChange(event) {
|
||||
this.selected(this.track.mode === 'showing');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
|
||||
*/
|
||||
class CurrentTimeDisplay extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
@@ -28,14 +28,14 @@ class CurrentTimeDisplay extends Component {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl('div', {
|
||||
const el = super.createEl('div', {
|
||||
className: 'vjs-current-time vjs-time-control vjs-control'
|
||||
});
|
||||
|
||||
this.contentEl_ = Dom.createEl('div', {
|
||||
className: 'vjs-current-time-display',
|
||||
// label the current time for screen reader users
|
||||
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00',
|
||||
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00'
|
||||
}, {
|
||||
// tell screen readers not to automatically read the time as it changes
|
||||
'aria-live': 'off'
|
||||
@@ -52,9 +52,10 @@ class CurrentTimeDisplay extends Component {
|
||||
*/
|
||||
updateContent() {
|
||||
// Allows for smooth scrubbing, when player can't keep up.
|
||||
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
let localizedText = this.localize('Current Time');
|
||||
let formattedTime = formatTime(time, this.player_.duration());
|
||||
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
|
||||
const localizedText = this.localize('Current Time');
|
||||
const formattedTime = formatTime(time, this.player_.duration());
|
||||
|
||||
if (formattedTime !== this.formattedTime_) {
|
||||
this.formattedTime_ = formattedTime;
|
||||
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`;
|
||||
|
||||
@@ -15,16 +15,10 @@ import formatTime from '../../utils/format-time.js';
|
||||
*/
|
||||
class DurationDisplay extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
// this might need to be changed to 'durationchange' instead of 'timeupdate' eventually,
|
||||
// however the durationchange event fires before this.player_.duration() is set,
|
||||
// so the value cannot be written out using this method.
|
||||
// Once the order of durationchange and this.player_.duration() being set is figured out,
|
||||
// this can be updated.
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
this.on(player, 'loadedmetadata', this.updateContent);
|
||||
this.on(player, 'durationchange', this.updateContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +28,7 @@ class DurationDisplay extends Component {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl('div', {
|
||||
const el = super.createEl('div', {
|
||||
className: 'vjs-duration vjs-time-control vjs-control'
|
||||
});
|
||||
|
||||
@@ -57,12 +51,15 @@ class DurationDisplay extends Component {
|
||||
* @method updateContent
|
||||
*/
|
||||
updateContent() {
|
||||
let duration = this.player_.duration();
|
||||
const duration = this.player_.duration();
|
||||
|
||||
if (duration && this.duration_ !== duration) {
|
||||
this.duration_ = duration;
|
||||
let localizedText = this.localize('Duration Time');
|
||||
let formattedTime = formatTime(duration);
|
||||
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`; // label the duration time for screen reader users
|
||||
const localizedText = this.localize('Duration Time');
|
||||
const formattedTime = formatTime(duration);
|
||||
|
||||
// label the duration time for screen reader users
|
||||
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,11 @@ import formatTime from '../../utils/format-time.js';
|
||||
*/
|
||||
class RemainingTimeDisplay extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
this.on(player, 'durationchange', this.updateContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,14 +29,14 @@ class RemainingTimeDisplay extends Component {
|
||||
* @method createEl
|
||||
*/
|
||||
createEl() {
|
||||
let el = super.createEl('div', {
|
||||
const el = super.createEl('div', {
|
||||
className: 'vjs-remaining-time vjs-time-control vjs-control'
|
||||
});
|
||||
|
||||
this.contentEl_ = Dom.createEl('div', {
|
||||
className: 'vjs-remaining-time-display',
|
||||
// label the remaining time for screen reader users
|
||||
innerHTML: `<span class="vjs-control-text">${this.localize('Remaining Time')}</span> -0:00`,
|
||||
innerHTML: `<span class="vjs-control-text">${this.localize('Remaining Time')}</span> -0:00`
|
||||
}, {
|
||||
// tell screen readers not to automatically read the time as it changes
|
||||
'aria-live': 'off'
|
||||
@@ -54,6 +55,7 @@ class RemainingTimeDisplay extends Component {
|
||||
if (this.player_.duration()) {
|
||||
const localizedText = this.localize('Remaining Time');
|
||||
const formattedTime = formatTime(this.player_.remainingTime());
|
||||
|
||||
if (formattedTime !== this.formattedTime_) {
|
||||
this.formattedTime_ = formattedTime;
|
||||
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> -${formattedTime}`;
|
||||
|
||||
@@ -15,8 +15,8 @@ import * as Fn from '../utils/fn.js';
|
||||
*/
|
||||
class TrackButton extends MenuButton {
|
||||
|
||||
constructor(player, options){
|
||||
let tracks = options.tracks;
|
||||
constructor(player, options) {
|
||||
const tracks = options.tracks;
|
||||
|
||||
super(player, options);
|
||||
|
||||
@@ -28,7 +28,8 @@ class TrackButton extends MenuButton {
|
||||
return;
|
||||
}
|
||||
|
||||
let updateHandler = Fn.bind(this, this.update);
|
||||
const updateHandler = Fn.bind(this, this.update);
|
||||
|
||||
tracks.addEventListener('removetrack', updateHandler);
|
||||
tracks.addEventListener('addtrack', updateHandler);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
|
||||
// Required children
|
||||
import VolumeLevel from './volume-level.js';
|
||||
import './volume-level.js';
|
||||
|
||||
/**
|
||||
* The bar that contains the volume level and can be clicked on to adjust the level
|
||||
@@ -18,7 +18,7 @@ import VolumeLevel from './volume-level.js';
|
||||
*/
|
||||
class VolumeBar extends Slider {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.on(player, 'volumechange', this.updateARIAAttributes);
|
||||
player.ready(Fn.bind(this, this.updateARIAAttributes));
|
||||
@@ -63,9 +63,8 @@ class VolumeBar extends Slider {
|
||||
getPercent() {
|
||||
if (this.player_.muted()) {
|
||||
return 0;
|
||||
} else {
|
||||
return this.player_.volume();
|
||||
}
|
||||
return this.player_.volume();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +94,8 @@ class VolumeBar extends Slider {
|
||||
*/
|
||||
updateARIAAttributes() {
|
||||
// Current value of volume bar as a percentage
|
||||
let volume = (this.player_.volume() * 100).toFixed(2);
|
||||
const volume = (this.player_.volume() * 100).toFixed(2);
|
||||
|
||||
this.el_.setAttribute('aria-valuenow', volume);
|
||||
this.el_.setAttribute('aria-valuetext', volume + '%');
|
||||
}
|
||||
@@ -106,7 +106,7 @@ VolumeBar.prototype.options_ = {
|
||||
children: [
|
||||
'volumeLevel'
|
||||
],
|
||||
'barName': 'volumeLevel'
|
||||
barName: 'volumeLevel'
|
||||
};
|
||||
|
||||
VolumeBar.prototype.playerEvent = 'volumechange';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import Component from '../../component.js';
|
||||
|
||||
// Required children
|
||||
import VolumeBar from './volume-bar.js';
|
||||
import './volume-bar.js';
|
||||
|
||||
/**
|
||||
* The component for controlling the volume level
|
||||
@@ -16,15 +16,15 @@ import VolumeBar from './volume-bar.js';
|
||||
*/
|
||||
class VolumeControl extends Component {
|
||||
|
||||
constructor(player, options){
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
// hide volume controls when they're not supported by the current tech
|
||||
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
|
||||
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
this.on(player, 'loadstart', function(){
|
||||
if (player.tech_['featuresVolumeControl'] === false) {
|
||||
this.on(player, 'loadstart', function() {
|
||||
if (player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
|
||||
@@ -18,7 +18,7 @@ import VolumeBar from './volume-control/volume-bar.js';
|
||||
*/
|
||||
class VolumeMenuButton extends PopupButton {
|
||||
|
||||
constructor(player, options={}){
|
||||
constructor(player, options = {}) {
|
||||
// Default to inline
|
||||
if (options.inline === undefined) {
|
||||
options.inline = true;
|
||||
@@ -48,7 +48,7 @@ class VolumeMenuButton extends PopupButton {
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
function updateVisibility() {
|
||||
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
|
||||
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
@@ -58,19 +58,19 @@ class VolumeMenuButton extends PopupButton {
|
||||
updateVisibility.call(this);
|
||||
this.on(player, 'loadstart', updateVisibility);
|
||||
|
||||
this.on(this.volumeBar, ['slideractive', 'focus'], function(){
|
||||
this.on(this.volumeBar, ['slideractive', 'focus'], function() {
|
||||
this.addClass('vjs-slider-active');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['sliderinactive', 'blur'], function(){
|
||||
this.on(this.volumeBar, ['sliderinactive', 'blur'], function() {
|
||||
this.removeClass('vjs-slider-active');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['focus'], function(){
|
||||
this.on(this.volumeBar, ['focus'], function() {
|
||||
this.addClass('vjs-lock-showing');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['blur'], function(){
|
||||
this.on(this.volumeBar, ['blur'], function() {
|
||||
this.removeClass('vjs-lock-showing');
|
||||
});
|
||||
}
|
||||
@@ -83,7 +83,8 @@ class VolumeMenuButton extends PopupButton {
|
||||
*/
|
||||
buildCSSClass() {
|
||||
let orientationClass = '';
|
||||
if (!!this.options_.vertical) {
|
||||
|
||||
if (this.options_.vertical) {
|
||||
orientationClass = 'vjs-volume-menu-button-vertical';
|
||||
} else {
|
||||
orientationClass = 'vjs-volume-menu-button-horizontal';
|
||||
@@ -99,11 +100,11 @@ class VolumeMenuButton extends PopupButton {
|
||||
* @method createPopup
|
||||
*/
|
||||
createPopup() {
|
||||
let popup = new Popup(this.player_, {
|
||||
const popup = new Popup(this.player_, {
|
||||
contentElType: 'div'
|
||||
});
|
||||
|
||||
let vb = new VolumeBar(this.player_, this.options_.volumeBar);
|
||||
const vb = new VolumeBar(this.player_, this.options_.volumeBar);
|
||||
|
||||
popup.addChild(vb);
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
*/
|
||||
import Component from './component';
|
||||
import ModalDialog from './modal-dialog';
|
||||
|
||||
import * as Dom from './utils/dom';
|
||||
import mergeOptions from './utils/merge-options';
|
||||
|
||||
/**
|
||||
@@ -45,7 +43,8 @@ class ErrorDisplay extends ModalDialog {
|
||||
* @return {String|Null}
|
||||
*/
|
||||
content() {
|
||||
let error = this.player().error();
|
||||
const error = this.player().error();
|
||||
|
||||
return error ? this.localize(error.message) : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,41 +3,43 @@
|
||||
*/
|
||||
import * as Events from './utils/events.js';
|
||||
|
||||
var EventTarget = function() {};
|
||||
const EventTarget = function() {};
|
||||
|
||||
EventTarget.prototype.allowedEvents_ = {};
|
||||
|
||||
EventTarget.prototype.on = function(type, fn) {
|
||||
// Remove the addEventListener alias before calling Events.on
|
||||
// so we don't get into an infinite type loop
|
||||
let ael = this.addEventListener;
|
||||
const ael = this.addEventListener;
|
||||
|
||||
this.addEventListener = () => {};
|
||||
Events.on(this, type, fn);
|
||||
this.addEventListener = ael;
|
||||
};
|
||||
|
||||
EventTarget.prototype.addEventListener = EventTarget.prototype.on;
|
||||
|
||||
EventTarget.prototype.off = function(type, fn) {
|
||||
Events.off(this, type, fn);
|
||||
};
|
||||
|
||||
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
|
||||
|
||||
EventTarget.prototype.one = function(type, fn) {
|
||||
// Remove the addEventListener alias before calling Events.on
|
||||
// so we don't get into an infinite type loop
|
||||
let ael = this.addEventListener;
|
||||
const ael = this.addEventListener;
|
||||
|
||||
this.addEventListener = () => {};
|
||||
Events.one(this, type, fn);
|
||||
this.addEventListener = ael;
|
||||
};
|
||||
|
||||
EventTarget.prototype.trigger = function(event) {
|
||||
let type = event.type || event;
|
||||
const type = event.type || event;
|
||||
|
||||
if (typeof event === 'string') {
|
||||
event = {
|
||||
type: type
|
||||
};
|
||||
event = {type};
|
||||
}
|
||||
event = Events.fixEvent(event);
|
||||
|
||||
@@ -47,6 +49,7 @@ EventTarget.prototype.trigger = function(event) {
|
||||
|
||||
Events.trigger(this, event);
|
||||
};
|
||||
|
||||
// The standard DOM EventTarget.dispatchEvent() is aliased to trigger()
|
||||
EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
|
||||
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ import log from './utils/log';
|
||||
* Both work the same but node adds `super_` to the subClass
|
||||
* and Bable adds the superClass as __proto__. Both seem useful.
|
||||
*/
|
||||
const _inherits = function (subClass, superClass) {
|
||||
const _inherits = function(subClass, superClass) {
|
||||
if (typeof superClass !== 'function' && superClass !== null) {
|
||||
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
|
||||
}
|
||||
@@ -44,10 +44,11 @@ const _inherits = function (subClass, superClass) {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
const extendFn = function(superClass, subClassMethods={}) {
|
||||
const extendFn = function(superClass, subClassMethods = {}) {
|
||||
let subClass = function() {
|
||||
superClass.apply(this, arguments);
|
||||
};
|
||||
|
||||
let methods = {};
|
||||
|
||||
if (typeof subClassMethods === 'object') {
|
||||
@@ -66,7 +67,7 @@ const extendFn = function(superClass, subClassMethods={}) {
|
||||
_inherits(subClass, superClass);
|
||||
|
||||
// Extend subObj's prototype with functions and other properties from props
|
||||
for (var name in methods) {
|
||||
for (const name in methods) {
|
||||
if (methods.hasOwnProperty(name)) {
|
||||
subClass.prototype[name] = methods[name];
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import document from 'global/document';
|
||||
* @type {Object|undefined}
|
||||
* @private
|
||||
*/
|
||||
let FullscreenApi = {};
|
||||
const FullscreenApi = {};
|
||||
|
||||
// browser API methods
|
||||
// map approach from Screenful.js - https://github.com/sindresorhus/screenfull.js
|
||||
@@ -60,7 +60,7 @@ const apiMap = [
|
||||
]
|
||||
];
|
||||
|
||||
let specApi = apiMap[0];
|
||||
const specApi = apiMap[0];
|
||||
let browserApi;
|
||||
|
||||
// determine the supported set of functions
|
||||
@@ -74,7 +74,7 @@ for (let i = 0; i < apiMap.length; i++) {
|
||||
|
||||
// map the browser API names to the spec API names
|
||||
if (browserApi) {
|
||||
for (let i=0; i<browserApi.length; i++) {
|
||||
for (let i = 0; i < browserApi.length; i++) {
|
||||
FullscreenApi[specApi[i]] = browserApi[i];
|
||||
}
|
||||
}
|
||||
|
||||
+40
-17
@@ -4,24 +4,45 @@
|
||||
import assign from 'object.assign';
|
||||
|
||||
/*
|
||||
* Custom MediaError to mimic the HTML5 MediaError
|
||||
* Custom MediaError class which mimics the standard HTML5 MediaError class.
|
||||
*
|
||||
* @param {Number} code The media error code
|
||||
* @param {Number|String|Object|MediaError} value
|
||||
* This can be of multiple types:
|
||||
* - Number: should be a standard error code
|
||||
* - String: an error message (the code will be 0)
|
||||
* - Object: arbitrary properties
|
||||
* - MediaError (native): used to populate a video.js MediaError object
|
||||
* - MediaError (video.js): will return itself if it's already a
|
||||
* video.js MediaError object.
|
||||
*/
|
||||
let MediaError = function(code){
|
||||
if (typeof code === 'number') {
|
||||
this.code = code;
|
||||
} else if (typeof code === 'string') {
|
||||
function MediaError(value) {
|
||||
|
||||
// Allow redundant calls to this constructor to avoid having `instanceof`
|
||||
// checks peppered around the code.
|
||||
if (value instanceof MediaError) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
this.code = value;
|
||||
} else if (typeof value === 'string') {
|
||||
// default code is zero, so this is a custom error
|
||||
this.message = code;
|
||||
} else if (typeof code === 'object') { // object
|
||||
assign(this, code);
|
||||
this.message = value;
|
||||
} else if (typeof value === 'object') {
|
||||
|
||||
// We assign the `code` property manually because native MediaError objects
|
||||
// do not expose it as an own/enumerable property of the object.
|
||||
if (typeof value.code === 'number') {
|
||||
this.code = value.code;
|
||||
}
|
||||
|
||||
assign(this, value);
|
||||
}
|
||||
|
||||
if (!this.message) {
|
||||
this.message = MediaError.defaultMessages[this.code] || '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* The error code that refers two one of the defined
|
||||
@@ -52,13 +73,15 @@ MediaError.prototype.message = '';
|
||||
*/
|
||||
MediaError.prototype.status = null;
|
||||
|
||||
// These errors are indexed by the W3C standard numeric value. The order
|
||||
// should not be changed!
|
||||
MediaError.errorTypes = [
|
||||
'MEDIA_ERR_CUSTOM', // = 0
|
||||
'MEDIA_ERR_ABORTED', // = 1
|
||||
'MEDIA_ERR_NETWORK', // = 2
|
||||
'MEDIA_ERR_DECODE', // = 3
|
||||
'MEDIA_ERR_SRC_NOT_SUPPORTED', // = 4
|
||||
'MEDIA_ERR_ENCRYPTED' // = 5
|
||||
'MEDIA_ERR_CUSTOM',
|
||||
'MEDIA_ERR_ABORTED',
|
||||
'MEDIA_ERR_NETWORK',
|
||||
'MEDIA_ERR_DECODE',
|
||||
'MEDIA_ERR_SRC_NOT_SUPPORTED',
|
||||
'MEDIA_ERR_ENCRYPTED'
|
||||
];
|
||||
|
||||
MediaError.defaultMessages = {
|
||||
@@ -71,7 +94,7 @@ MediaError.defaultMessages = {
|
||||
|
||||
// Add types as properties on MediaError
|
||||
// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
||||
for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
|
||||
for (let errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
|
||||
MediaError[MediaError.errorTypes[errNum]] = errNum;
|
||||
// values should be accessible on both the class and instance
|
||||
MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
|
||||
|
||||
@@ -18,7 +18,7 @@ import toTitleCase from '../utils/to-title-case.js';
|
||||
*/
|
||||
class MenuButton extends ClickableComponent {
|
||||
|
||||
constructor(player, options={}){
|
||||
constructor(player, options = {}) {
|
||||
super(player, options);
|
||||
|
||||
this.update();
|
||||
@@ -36,7 +36,7 @@ class MenuButton extends ClickableComponent {
|
||||
* @method update
|
||||
*/
|
||||
update() {
|
||||
let menu = this.createMenu();
|
||||
const menu = this.createMenu();
|
||||
|
||||
if (this.menu) {
|
||||
this.removeChild(this.menu);
|
||||
@@ -68,24 +68,25 @@ class MenuButton extends ClickableComponent {
|
||||
* @method createMenu
|
||||
*/
|
||||
createMenu() {
|
||||
var menu = new Menu(this.player_);
|
||||
const menu = new Menu(this.player_);
|
||||
|
||||
// Add a title list item to the top
|
||||
if (this.options_.title) {
|
||||
let title = Dom.createEl('li', {
|
||||
const title = Dom.createEl('li', {
|
||||
className: 'vjs-menu-title',
|
||||
innerHTML: toTitleCase(this.options_.title),
|
||||
tabIndex: -1
|
||||
});
|
||||
|
||||
menu.children_.unshift(title);
|
||||
Dom.insertElFirst(title, menu.contentEl());
|
||||
}
|
||||
|
||||
this.items = this['createItems']();
|
||||
this.items = this.createItems();
|
||||
|
||||
if (this.items) {
|
||||
// Add menu items to the menu
|
||||
for (var i = 0; i < this.items.length; i++) {
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
menu.addItem(this.items[i]);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +99,7 @@ class MenuButton extends ClickableComponent {
|
||||
*
|
||||
* @method createItems
|
||||
*/
|
||||
createItems(){}
|
||||
createItems() {}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
@@ -119,7 +120,7 @@ class MenuButton extends ClickableComponent {
|
||||
* @method buildCSSClass
|
||||
*/
|
||||
buildCSSClass() {
|
||||
var menuButtonClass = 'vjs-menu-button';
|
||||
let menuButtonClass = 'vjs-menu-button';
|
||||
|
||||
// If the inline option is passed, we want to use different styles altogether.
|
||||
if (this.options_.inline === true) {
|
||||
@@ -141,11 +142,11 @@ class MenuButton extends ClickableComponent {
|
||||
* @method handleClick
|
||||
*/
|
||||
handleClick() {
|
||||
this.one('mouseout', Fn.bind(this, function(){
|
||||
this.menu.unlockShowing();
|
||||
this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function(e) {
|
||||
this.unpressButton();
|
||||
this.el_.blur();
|
||||
}));
|
||||
if (this.buttonPressed_){
|
||||
if (this.buttonPressed_) {
|
||||
this.unpressButton();
|
||||
} else {
|
||||
this.pressButton();
|
||||
@@ -189,8 +190,8 @@ class MenuButton extends ClickableComponent {
|
||||
handleSubmenuKeyPress(event) {
|
||||
|
||||
// Escape (27) key or Tab (9) key unpress the 'button'
|
||||
if (event.which === 27 || event.which === 9){
|
||||
if (this.buttonPressed_){
|
||||
if (event.which === 27 || event.which === 9) {
|
||||
if (this.buttonPressed_) {
|
||||
this.unpressButton();
|
||||
}
|
||||
// Don't preventDefault for Tab key - we still want to lose focus
|
||||
@@ -210,7 +211,8 @@ class MenuButton extends ClickableComponent {
|
||||
this.buttonPressed_ = true;
|
||||
this.menu.lockShowing();
|
||||
this.el_.setAttribute('aria-expanded', 'true');
|
||||
this.menu.focus(); // set the focus into the submenu
|
||||
// set the focus into the submenu
|
||||
this.menu.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +226,8 @@ class MenuButton extends ClickableComponent {
|
||||
this.buttonPressed_ = false;
|
||||
this.menu.unlockShowing();
|
||||
this.el_.setAttribute('aria-expanded', 'false');
|
||||
this.el_.focus(); // Set focus back to this menu button
|
||||
// Set focus back to this menu button
|
||||
this.el_.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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