4 |
5 | VideoJS Tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Brightcove, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "video.js",
3 | "description": "An HTML5 and Flash video player with a common API and skin for both.",
4 | "version": "5.9.0",
5 | "keywords": [
6 | "videojs",
7 | "html5",
8 | "flash",
9 | "video",
10 | "player"
11 | ],
12 | "scripts": ["dist/video-js/video.dev.js"],
13 | "styles": ["dist/video-js/video-js.css"],
14 | "files": ["dist/video-js/video-js.swf"],
15 | "fonts": [
16 | "dist/video-js/font/vjs.eot",
17 | "dist/video-js/font/vjs.svg",
18 | "dist/video-js/font/vjs.ttf",
19 | "dist/video-js/font/vjs.woff"
20 | ],
21 | "main": "dist/video-js/video.dev.js"
22 | }
23 |
--------------------------------------------------------------------------------
/test/unit/button.test.js:
--------------------------------------------------------------------------------
1 | import Button from '../../src/js/button.js';
2 | import TestHelpers from './test-helpers.js';
3 |
4 | q.module('Button');
5 |
6 | test('should localize its text', function(){
7 | expect(2);
8 |
9 | var player, testButton, el;
10 |
11 | player = TestHelpers.makePlayer({
12 | 'language': 'es',
13 | 'languages': {
14 | 'es': {
15 | 'Play': 'Juego'
16 | }
17 | }
18 | });
19 |
20 | testButton = new Button(player);
21 | testButton.controlText_ = 'Play';
22 | el = testButton.createEl();
23 |
24 | ok(el.nodeName.toLowerCase().match('button'));
25 | ok(el.innerHTML.match('Juego'));
26 | });
27 |
--------------------------------------------------------------------------------
/test/globals-shim.js:
--------------------------------------------------------------------------------
1 | import document from 'global/document';
2 | import window from 'global/window';
3 | import sinon from 'sinon';
4 |
5 | window.q = QUnit;
6 | window.sinon = sinon;
7 |
8 | // There's nowhere we require completing xhr requests
9 | // and raynos/xhr doesn't want to make stubbing easy (Raynos/xhr#11)
10 | // so we need to stub XHR before the xhr module is included anywhere else.
11 | window.xhr = sinon.useFakeXMLHttpRequest();
12 |
13 | // This may not be needed anymore, but double check before removing
14 | window.fixture = document.createElement('div');
15 | window.fixture.id = 'qunit-fixture';
16 | document.body.appendChild(window.fixture);
17 |
--------------------------------------------------------------------------------
/src/css/components/_error.scss:
--------------------------------------------------------------------------------
1 | .vjs-error .vjs-error-display .vjs-modal-dialog-content {
2 | font-size: 1.4em;
3 | text-align: center;
4 | }
5 |
6 | .vjs-error .vjs-error-display:before {
7 | color: #fff;
8 | content: 'X';
9 | font-family: $text-font-family;
10 | font-size: 4em;
11 | left: 0;
12 |
13 | // In order to center the play icon vertically we need to set the line height
14 | // to the same as the button height
15 | line-height: 1;
16 | margin-top: -0.5em;
17 | position: absolute;
18 | text-shadow: 0.05em 0.05em 0.1em #000;
19 | text-align: center; // Needed for IE8
20 | top: 50%;
21 | vertical-align: middle;
22 | width: 100%;
23 | }
24 |
--------------------------------------------------------------------------------
/src/css/_private-variables.scss:
--------------------------------------------------------------------------------
1 | // Text, icons, hover states
2 | $primary-foreground-color: #fff !default;
3 |
4 | // Control backgrounds (control bar, big play, menus)
5 | $primary-background-color: #2B333F !default;
6 | $primary-background-transparency: 0.7 !default;
7 |
8 | // Hover states, slider backgrounds
9 | $secondary-background-color: lighten($primary-background-color, 33%) !default;
10 | $secondary-background-transparency: 0.5 !default;
11 |
12 | $text-font-family: Arial, Helvetica, sans-serif !default;
13 |
14 | // Using the '--' naming for component-specific styles
15 | $big-play-button--width: 3em !default;
16 | $big-play-button--height: 1.5em !default;
17 | $big-play-button--transparency: 0.8 !default;
18 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 | Please describe the change as necessary.
3 | If it's a feature or enhancement please be as detailed as possible.
4 | If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail.
5 |
6 |
7 | ## Specific Changes proposed
8 | Please list the specific changes involved in this pull request.
9 |
10 | ## Requirements Checklist
11 | - [ ] Feature implemented / Bug fixed
12 | - [ ] If necessary, more likely in a feature request than a bug fix
13 | - [ ] Unit Tests updated or fixed
14 | - [ ] Docs/guides updated
15 | - [ ] Example created ([starter template on JSBin](http://jsbin.com/axedog/edit?html,output))
16 | - [ ] Reviewed by Two Core Contributors
17 |
--------------------------------------------------------------------------------
/src/js/loading-spinner.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file loading-spinner.js
3 | */
4 | import Component from './component';
5 |
6 | /* Loading Spinner
7 | ================================================================================ */
8 | /**
9 | * Loading spinner for waiting events
10 | *
11 | * @extends Component
12 | * @class LoadingSpinner
13 | */
14 | class LoadingSpinner extends Component {
15 |
16 | /**
17 | * Create the component's DOM element
18 | *
19 | * @method createEl
20 | */
21 | createEl() {
22 | return super.createEl('div', {
23 | className: 'vjs-loading-spinner',
24 | dir: 'ltr'
25 | });
26 | }
27 | }
28 |
29 | Component.registerComponent('LoadingSpinner', LoadingSpinner);
30 | export default LoadingSpinner;
31 |
--------------------------------------------------------------------------------
/test/unit/utils/merge-options.test.js:
--------------------------------------------------------------------------------
1 | import mergeOptions from '../../../src/js/utils/merge-options.js';
2 |
3 | q.module('merge-options');
4 |
5 | test('should merge options objects', function(){
6 | var ob1, ob2, ob3;
7 |
8 | ob1 = {
9 | a: true,
10 | b: { b1: true, b2: true, b3: true },
11 | c: true
12 | };
13 |
14 | ob2 = {
15 | // override value
16 | a: false,
17 | // merge sub-option values
18 | b: { b1: true, b2: false, b4: true },
19 | // add new option
20 | d: true
21 | };
22 |
23 | ob3 = mergeOptions(ob1, ob2);
24 |
25 | deepEqual(ob3, {
26 | a: false,
27 | b: { b1: true, b2: false, b3: true, b4: true },
28 | c: true,
29 | d: true
30 | }, 'options objects merged correctly');
31 | });
32 |
--------------------------------------------------------------------------------
/src/js/close-button.js:
--------------------------------------------------------------------------------
1 | import Button from './button';
2 | import Component from './component';
3 |
4 | /**
5 | * The `CloseButton` component is a button which fires a "close" event
6 | * when it is activated.
7 | *
8 | * @extends Button
9 | * @class CloseButton
10 | */
11 | class CloseButton extends Button {
12 |
13 | constructor(player, options) {
14 | super(player, options);
15 | this.controlText(options && options.controlText || this.localize('Close'));
16 | }
17 |
18 | buildCSSClass() {
19 | return `vjs-close-button ${super.buildCSSClass()}`;
20 | }
21 |
22 | handleClick() {
23 | this.trigger({type: 'close', bubbles: false});
24 | }
25 | }
26 |
27 | Component.registerComponent('CloseButton', CloseButton);
28 | export default CloseButton;
29 |
--------------------------------------------------------------------------------
/src/js/control-bar/volume-control/volume-level.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file volume-level.js
3 | */
4 | import Component from '../../component.js';
5 |
6 | /**
7 | * Shows volume level
8 | *
9 | * @param {Player|Object} player
10 | * @param {Object=} options
11 | * @extends Component
12 | * @class VolumeLevel
13 | */
14 | class VolumeLevel extends Component {
15 |
16 | /**
17 | * Create the component's DOM element
18 | *
19 | * @return {Element}
20 | * @method createEl
21 | */
22 | createEl() {
23 | return super.createEl('div', {
24 | className: 'vjs-volume-level',
25 | innerHTML: ''
26 | });
27 | }
28 |
29 | }
30 |
31 | Component.registerComponent('VolumeLevel', VolumeLevel);
32 | export default VolumeLevel;
33 |
--------------------------------------------------------------------------------
/src/css/components/menu/_menu-popup.scss:
--------------------------------------------------------------------------------
1 | .vjs-menu-button-popup .vjs-menu {
2 | display: none;
3 | position: absolute;
4 | bottom: 0;
5 | width: 10em;
6 | left: -3em; // (Width of vjs-menu - width of button) / 2
7 | height: 0em;
8 | margin-bottom: 1.5em;
9 | border-top-color: rgba($primary-background-color, $primary-background-transparency); // Same as ul background
10 | }
11 |
12 | // Button Pop-up Menu
13 | .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
14 | @include background-color-with-alpha($primary-background-color, $primary-background-transparency);
15 |
16 | position: absolute;
17 | width: 100%;
18 | bottom: 1.5em; // Same bottom as vjs-menu border-top
19 | max-height: 15em;
20 | }
21 |
22 | .vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu,
23 | .vjs-menu-button-popup .vjs-menu.vjs-lock-showing {
24 | display: block;
25 | }
26 |
--------------------------------------------------------------------------------
/src/js/control-bar/time-controls/time-divider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file time-divider.js
3 | */
4 | import Component from '../../component.js';
5 |
6 | /**
7 | * The separator between the current time and duration.
8 | * Can be hidden if it's not needed in the design.
9 | *
10 | * @param {Player|Object} player
11 | * @param {Object=} options
12 | * @extends Component
13 | * @class TimeDivider
14 | */
15 | class TimeDivider extends Component {
16 |
17 | /**
18 | * Create the component's DOM element
19 | *
20 | * @return {Element}
21 | * @method createEl
22 | */
23 | createEl() {
24 | return super.createEl('div', {
25 | className: 'vjs-time-control vjs-time-divider',
26 | innerHTML: '
/
'
27 | });
28 | }
29 |
30 | }
31 |
32 | Component.registerComponent('TimeDivider', TimeDivider);
33 | export default TimeDivider;
34 |
--------------------------------------------------------------------------------
/src/css/components/_time.scss:
--------------------------------------------------------------------------------
1 | .video-js .vjs-time-control {
2 | @include flex(none);
3 | font-size: 1em;
4 | line-height: 3em;
5 | min-width: 2em;
6 | width: auto;
7 | padding-left: 1em;
8 | padding-right: 1em;
9 | }
10 |
11 | .vjs-live .vjs-time-control {
12 | display: none;
13 | }
14 |
15 | // We need the extra specificity that referencing .vjs-no-flex provides.
16 | .video-js .vjs-current-time,
17 | .vjs-no-flex .vjs-current-time {
18 | display: none;
19 | }
20 |
21 | .video-js .vjs-duration,
22 | .vjs-no-flex .vjs-duration {
23 | display: none;
24 | }
25 |
26 | .vjs-time-divider {
27 | display: none;
28 | line-height: 3em;
29 | }
30 |
31 | .vjs-live .vjs-time-divider {
32 | // Already the default, but we want to ensure when the player is live
33 | // this hides in the same way as the other time controls for other skins
34 | display: none;
35 | }
36 |
--------------------------------------------------------------------------------
/docs/examples/shared/example-captions.vtt:
--------------------------------------------------------------------------------
1 | WEBVTT
2 |
3 | 00:00.700 --> 00:04.110
4 | Captions describe all relevant audio for the hearing impaired.
5 | [ Heroic music playing for a seagull ]
6 |
7 | 00:04.500 --> 00:05.000
8 | [ Splash!!! ]
9 |
10 | 00:05.100 --> 00:06.000
11 | [ Sploosh!!! ]
12 |
13 | 00:08.000 --> 00:09.225
14 | [ Splash...splash...splash splash splash ]
15 |
16 | 00:10.525 --> 00:11.255
17 | [ Splash, Sploosh again ]
18 |
19 | 00:13.500 --> 00:14.984
20 | Dolphin: eeeEEEEEeeee!
21 |
22 | 00:14.984 --> 00:16.984
23 | Dolphin: Squawk! eeeEEE?
24 |
25 | 00:25.000 --> 00:28.284
26 | [ A whole ton of splashes ]
27 |
28 | 00:29.500 --> 00:31.000
29 | Mine. Mine. Mine.
30 |
31 | 00:34.300 --> 00:36.000
32 | Shark: Chomp
33 |
34 | 00:36.800 --> 00:37.900
35 | Shark: CHOMP!!!
36 |
37 | 00:37.861 --> 00:41.193
38 | EEEEEEOOOOOOOOOOWHALENOISE
39 |
40 | 00:42.593 --> 00:45.611
41 | [ BIG SPLASH ]
--------------------------------------------------------------------------------
/docs/examples/elephantsdream/chapters.en.vtt:
--------------------------------------------------------------------------------
1 | WEBVTT
2 |
3 | NOTE Created by Owen Edwards 2015. http://creativecommons.org/licenses/by/2.5/
4 | NOTE Based on 'finalbreakdown.rtf', part of the prepoduction notes, which are:
5 | NOTE (c) Copyright 2006, Blender Foundation /
6 | NOTE Netherlands Media Art Institute /
7 | NOTE www.elephantsdream.org
8 |
9 | 1
10 | 00:00:00.000 --> 00:00:27.500
11 | Prologue
12 |
13 | 2
14 | 00:00:27.500 --> 00:01:10.000
15 | Switchboard trap
16 |
17 | 3
18 | 00:01:10.000 --> 00:03:25.000
19 | Telephone/Lecture
20 |
21 | 4
22 | 00:03:25.000 --> 00:04:52.000
23 | Typewriter
24 |
25 | 5
26 | 00:04:52.000 --> 00:06:19.500
27 | Proog shows Emo stuff
28 |
29 | 6
30 | 00:06:19.500 --> 00:07:09.000
31 | Which way
32 |
33 | 7
34 | 00:07:09.000 --> 00:07:45.000
35 | Emo flips out
36 |
37 | 8
38 | 00:07:45.000 --> 00:09:25.000
39 | Emo creates
40 |
41 | 9
42 | 00:09:25.000 --> 00:10:53.000
43 | Closing credits
44 |
45 |
--------------------------------------------------------------------------------
/src/css/components/_control.scss:
--------------------------------------------------------------------------------
1 | // vjs-control might be better named vjs-button now.
2 | // It's used on both real buttons (play button)
3 | // and div buttons (menu buttons)
4 | .video-js .vjs-control {
5 | outline: none;
6 | position: relative;
7 | text-align: center;
8 | margin: 0;
9 | padding: 0;
10 | height: 100%;
11 | width: 4em;
12 | @include flex(none);
13 |
14 | &:before {
15 | font-size: 1.8em;
16 | line-height: 1.67;
17 |
18 | @extend %icon-default;
19 | }
20 | }
21 |
22 | // Replacement for focus outline
23 | .video-js .vjs-control:focus:before,
24 | .video-js .vjs-control:hover:before,
25 | .video-js .vjs-control:focus {
26 | text-shadow: 0em 0em 1em rgba($primary-foreground-color, 1);
27 | }
28 |
29 | // Hide control text visually, but have it available for screenreaders
30 | .video-js .vjs-control-text {
31 | @include hide-visually;
32 | }
33 |
34 | // IE 8 + 9 Support
35 | .vjs-no-flex .vjs-control {
36 | display: table-cell;
37 | vertical-align: middle;
38 | }
39 |
--------------------------------------------------------------------------------
/src/js/control-bar/spacer-controls/spacer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file spacer.js
3 | */
4 | import Component from '../../component.js';
5 |
6 | /**
7 | * Just an empty spacer element that can be used as an append point for plugins, etc.
8 | * Also can be used to create space between elements when necessary.
9 | *
10 | * @extends Component
11 | * @class Spacer
12 | */
13 | class Spacer extends Component {
14 |
15 | /**
16 | * Allow sub components to stack CSS class names
17 | *
18 | * @return {String} The constructed class name
19 | * @method buildCSSClass
20 | */
21 | buildCSSClass() {
22 | return `vjs-spacer ${super.buildCSSClass()}`;
23 | }
24 |
25 | /**
26 | * Create the component's DOM element
27 | *
28 | * @return {Element}
29 | * @method createEl
30 | */
31 | createEl() {
32 | return super.createEl('div', {
33 | className: this.buildCSSClass()
34 | });
35 | }
36 | }
37 |
38 | Component.registerComponent('Spacer', Spacer);
39 |
40 | export default Spacer;
41 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 | Briefly describe the issue.
3 | Include a [reduced test case](https://css-tricks.com/reduced-test-cases/), we have a [starter template](http://jsbin.com/axedog/edit?html,output) on JSBin you can use.
4 |
5 | ## Steps to reproduce
6 | Explain in detail the exact steps necessary to reproduce the issue.
7 |
8 | 1.
9 | 2.
10 | 3.
11 |
12 | ## Results
13 | ### Expected
14 | Please describe what you expected to see.
15 |
16 | ### Actual
17 | Please describe what actually happened.
18 |
19 | ### Error output
20 | If there are any errors at all, please include them here.
21 |
22 | ## Additional Information
23 | Please include any additional information necessary here. Including the following:
24 | ### versions
25 | #### videojs
26 | what version of videojs does this occur with?
27 | #### browsers
28 | what browser are affected?
29 | #### OSes
30 | what platforms (operating systems and devices) are affected?
31 | ### plugins
32 | are any videojs plugins being used on the page? If so, please list them below.
33 |
--------------------------------------------------------------------------------
/src/js/control-bar/progress-control/progress-control.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file progress-control.js
3 | */
4 | import Component from '../../component.js';
5 | import SeekBar from './seek-bar.js';
6 | import MouseTimeDisplay from './mouse-time-display.js';
7 |
8 | /**
9 | * The Progress Control component contains the seek bar, load progress,
10 | * and play progress
11 | *
12 | * @param {Player|Object} player
13 | * @param {Object=} options
14 | * @extends Component
15 | * @class ProgressControl
16 | */
17 | class ProgressControl extends Component {
18 |
19 | /**
20 | * Create the component's DOM element
21 | *
22 | * @return {Element}
23 | * @method createEl
24 | */
25 | createEl() {
26 | return super.createEl('div', {
27 | className: 'vjs-progress-control vjs-control'
28 | });
29 | }
30 | }
31 |
32 | ProgressControl.prototype.options_ = {
33 | children: [
34 | 'seekBar'
35 | ]
36 | };
37 |
38 | Component.registerComponent('ProgressControl', ProgressControl);
39 | export default ProgressControl;
40 |
--------------------------------------------------------------------------------
/src/js/utils/buffer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file buffer.js
3 | */
4 | import { createTimeRange } from './time-ranges.js';
5 |
6 | /**
7 | * Compute how much your video has been buffered
8 | *
9 | * @param {Object} Buffered object
10 | * @param {Number} Total duration
11 | * @return {Number} Percent buffered of the total duration
12 | * @private
13 | * @function bufferedPercent
14 | */
15 | export function bufferedPercent(buffered, duration) {
16 | var bufferedDuration = 0,
17 | start, end;
18 |
19 | if (!duration) {
20 | return 0;
21 | }
22 |
23 | if (!buffered || !buffered.length) {
24 | buffered = createTimeRange(0, 0);
25 | }
26 |
27 | for (let i = 0; i < buffered.length; i++){
28 | start = buffered.start(i);
29 | end = buffered.end(i);
30 |
31 | // buffered end can be bigger than duration by a very small fraction
32 | if (end > duration) {
33 | end = duration;
34 | }
35 |
36 | bufferedDuration += end - start;
37 | }
38 |
39 | return bufferedDuration / duration;
40 | }
41 |
--------------------------------------------------------------------------------
/src/js/tracks/text-track-enums.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file text-track-enums.js
3 | */
4 |
5 | /**
6 | * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
7 | *
8 | * enum TextTrackMode { "disabled", "hidden", "showing" };
9 | */
10 | const TextTrackMode = {
11 | disabled: 'disabled',
12 | hidden: 'hidden',
13 | showing: 'showing'
14 | };
15 |
16 | /**
17 | * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackkind
18 | *
19 | * enum TextTrackKind {
20 | * "subtitles",
21 | * "captions",
22 | * "descriptions",
23 | * "chapters",
24 | * "metadata"
25 | * };
26 | */
27 | const TextTrackKind = {
28 | subtitles: 'subtitles',
29 | captions: 'captions',
30 | descriptions: 'descriptions',
31 | chapters: 'chapters',
32 | metadata: 'metadata'
33 | };
34 |
35 | /* jshint ignore:start */
36 | // we ignore jshint here because it does not see
37 | // TextTrackMode or TextTrackKind as defined here somehow...
38 | export { TextTrackMode, TextTrackKind };
39 | /* jshint ignore:end */
40 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "evil" : true,
3 | "validthis": true,
4 | "node" : true,
5 | "debug" : true,
6 | "boss" : true,
7 | "expr" : true,
8 | "eqnull" : true,
9 | "quotmark" : "single",
10 | "sub" : true,
11 | "trailing" : true,
12 | "undef" : true,
13 | "laxbreak" : true,
14 | "esnext" : true,
15 | "eqeqeq" : true,
16 | "predef" : [
17 | "_V_",
18 | "goog",
19 | "console",
20 |
21 | "require",
22 | "define",
23 | "module",
24 | "exports",
25 | "process",
26 |
27 | "q",
28 | "asyncTest",
29 | "deepEqual",
30 | "equal",
31 | "expect",
32 | "module",
33 | "notDeepEqual",
34 | "notEqual",
35 | "notStrictEqual",
36 | "ok",
37 | "throws",
38 | "QUnit",
39 | "raises",
40 | "start",
41 | "stop",
42 | "strictEqual",
43 | "test",
44 | "throws",
45 | "sinon"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/src/css/components/_text-track.scss:
--------------------------------------------------------------------------------
1 | // Emulated tracks
2 | .vjs-text-track-display {
3 | position: absolute;
4 | bottom: 3em;
5 | left: 0;
6 | right: 0;
7 | top: 0;
8 | pointer-events: none;
9 | }
10 |
11 | // Move captions down when controls aren't being shown
12 | .video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display {
13 | bottom: 1em;
14 | }
15 |
16 | // Individual tracks
17 | .video-js .vjs-text-track {
18 | font-size: 1.4em;
19 | text-align: center;
20 | margin-bottom: 0.1em;
21 | // Transparent black background, or fallback to all black (oldIE)
22 | @include background-color-with-alpha(#000, 0.5);
23 | }
24 |
25 | .vjs-subtitles { color: #fff; } // Subtitles are white
26 | .vjs-captions { color: #fc6; } // Captions are yellow
27 | .vjs-tt-cue { display: block; }
28 |
29 | // Native tracks
30 | video::-webkit-media-text-track-display {
31 | @include transform(translateY(-3em));
32 | }
33 |
34 | // Move captions down when controls aren't being shown
35 | .video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display {
36 | @include transform(translateY(-1.5em));
37 | }
38 |
--------------------------------------------------------------------------------
/lang/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "播放",
3 | "Pause": "暂停",
4 | "Current Time": "当前时间",
5 | "Duration Time": "时长",
6 | "Remaining Time": "剩余时间",
7 | "Stream Type": "媒体流类型",
8 | "LIVE": "直播",
9 | "Loaded": "加载完毕",
10 | "Progress": "进度",
11 | "Fullscreen": "全屏",
12 | "Non-Fullscreen": "退出全屏",
13 | "Mute": "静音",
14 | "Unmute": "取消静音",
15 | "Playback Rate": "播放码率",
16 | "Subtitles": "字幕",
17 | "subtitles off": "字幕关闭",
18 | "Captions": "内嵌字幕",
19 | "captions off": "内嵌字幕关闭",
20 | "Chapters": "节目段落",
21 | "You aborted the media playback": "视频播放被终止",
22 | "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
25 | "No compatible source was found for this media.": "无法找到此视频兼容的源。",
26 | "The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。"
27 | }
28 |
--------------------------------------------------------------------------------
/lang/zh-TW.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "播放",
3 | "Pause": "暫停",
4 | "Current Time": "目前時間",
5 | "Duration Time": "總共時間",
6 | "Remaining Time": "剩餘時間",
7 | "Stream Type": "串流類型",
8 | "LIVE": "直播",
9 | "Loaded": "載入完畢",
10 | "Progress": "進度",
11 | "Fullscreen": "全螢幕",
12 | "Non-Fullscreen": "退出全螢幕",
13 | "Mute": "靜音",
14 | "Unmute": "取消靜音",
15 | "Playback Rate": " 播放速率",
16 | "Subtitles": "字幕",
17 | "subtitles off": "關閉字幕",
18 | "Captions": "內嵌字幕",
19 | "captions off": "關閉內嵌字幕",
20 | "Chapters": "章節",
21 | "You aborted the media playback": "影片播放已終止",
22 | "A network error caused the media download to fail part-way.": "網路錯誤導致影片下載失敗。",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "影片因格式不支援或者伺服器或網路的問題無法載入。",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由於影片檔案損毀或是該影片使用了您的瀏覽器不支援的功能,播放終止。",
25 | "No compatible source was found for this media.": "無法找到相容此影片的來源。",
26 | "The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。"
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/lang/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "재생",
3 | "Pause": "일시중지",
4 | "Current Time": "현재 시간",
5 | "Duration Time": "지정 기간",
6 | "Remaining Time": "남은 시간",
7 | "Stream Type": "스트리밍 유형",
8 | "LIVE": "라이브",
9 | "Loaded": "로드됨",
10 | "Progress": "진행",
11 | "Fullscreen": "전체 화면",
12 | "Non-Fullscreen": "전체 화면 해제",
13 | "Mute": "음소거",
14 | "Unmute": "음소거 해제",
15 | "Playback Rate": "재생 비율",
16 | "Subtitles": "서브타이틀",
17 | "subtitles off": "서브타이틀 끄기",
18 | "Captions": "자막",
19 | "captions off": "자막 끄기",
20 | "Chapters": "챕터",
21 | "You aborted the media playback": "비디오 재생을 취소했습니다.",
22 | "A network error caused the media download to fail part-way.": "네트워크 오류로 인하여 비디오 일부를 다운로드하지 못 했습니다.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "비디오를 로드할 수 없습니다. 서버 혹은 네트워크 오류 때문이거나 지원되지 않는 형식 때문일 수 있습니다.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "비디오 재생이 취소됐습니다. 비디오가 손상되었거나 비디오가 사용하는 기능을 브라우저에서 지원하지 않는 것 같습니다.",
25 | "No compatible source was found for this media.": "비디오에 호환되지 않는 소스가 있습니다."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/control-bar/fullscreen-toggle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file fullscreen-toggle.js
3 | */
4 | import Button from '../button.js';
5 | import Component from '../component.js';
6 |
7 | /**
8 | * Toggle fullscreen video
9 | *
10 | * @extends Button
11 | * @class FullscreenToggle
12 | */
13 | class FullscreenToggle extends Button {
14 |
15 | /**
16 | * Allow sub components to stack CSS class names
17 | *
18 | * @return {String} The constructed class name
19 | * @method buildCSSClass
20 | */
21 | buildCSSClass() {
22 | return `vjs-fullscreen-control ${super.buildCSSClass()}`;
23 | }
24 |
25 | /**
26 | * Handles click for full screen
27 | *
28 | * @method handleClick
29 | */
30 | handleClick() {
31 | if (!this.player_.isFullscreen()) {
32 | this.player_.requestFullscreen();
33 | this.controlText('Non-Fullscreen');
34 | } else {
35 | this.player_.exitFullscreen();
36 | this.controlText('Fullscreen');
37 | }
38 | }
39 |
40 | }
41 |
42 | FullscreenToggle.prototype.controlText_ = 'Fullscreen';
43 |
44 | Component.registerComponent('FullscreenToggle', FullscreenToggle);
45 | export default FullscreenToggle;
46 |
--------------------------------------------------------------------------------
/lang/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "再生",
3 | "Pause": "一時停止",
4 | "Current Time": "現在の時間",
5 | "Duration Time": "長さ",
6 | "Remaining Time": "残りの時間",
7 | "Stream Type": "ストリームの種類",
8 | "LIVE": "ライブ",
9 | "Loaded": "ロード済み",
10 | "Progress": "進行状況",
11 | "Fullscreen": "フルスクリーン",
12 | "Non-Fullscreen": "フルスクリーン以外",
13 | "Mute": "ミュート",
14 | "Unmute": "ミュート解除",
15 | "Playback Rate": "再生レート",
16 | "Subtitles": "サブタイトル",
17 | "subtitles off": "サブタイトル オフ",
18 | "Captions": "キャプション",
19 | "captions off": "キャプション オフ",
20 | "Chapters": "チャプター",
21 | "You aborted the media playback": "動画再生を中止しました",
22 | "A network error caused the media download to fail part-way.": "ネットワーク エラーにより動画のダウンロードが途中で失敗しました",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "サーバーまたはネットワークのエラー、またはフォーマットがサポートされていないため、動画をロードできませんでした",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "破損の問題、またはお使いのブラウザがサポートしていない機能が動画に使用されていたため、動画の再生が中止されました",
25 | "No compatible source was found for this media.": "この動画に対して互換性のあるソースが見つかりませんでした"
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/big-play-button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file big-play-button.js
3 | */
4 | import Button from './button.js';
5 | import Component from './component.js';
6 |
7 | /**
8 | * Initial play button. Shows before the video has played. The hiding of the
9 | * big play button is done via CSS and player states.
10 | *
11 | * @param {Object} player Main Player
12 | * @param {Object=} options Object of option names and values
13 | * @extends Button
14 | * @class BigPlayButton
15 | */
16 | class BigPlayButton extends Button {
17 |
18 | constructor(player, options) {
19 | super(player, options);
20 | }
21 |
22 | /**
23 | * Allow sub components to stack CSS class names
24 | *
25 | * @return {String} The constructed class name
26 | * @method buildCSSClass
27 | */
28 | buildCSSClass() {
29 | return 'vjs-big-play-button';
30 | }
31 |
32 | /**
33 | * Handles click for play
34 | *
35 | * @method handleClick
36 | */
37 | handleClick() {
38 | this.player_.play();
39 | }
40 |
41 | }
42 |
43 | BigPlayButton.prototype.controlText_ = 'Play Video';
44 |
45 | Component.registerComponent('BigPlayButton', BigPlayButton);
46 | export default BigPlayButton;
47 |
--------------------------------------------------------------------------------
/src/css/components/_poster.scss:
--------------------------------------------------------------------------------
1 | .vjs-poster {
2 | display: inline-block;
3 | vertical-align: middle;
4 | background-repeat: no-repeat;
5 | background-position: 50% 50%;
6 | background-size: contain;
7 | cursor: pointer;
8 | margin: 0;
9 | padding: 0;
10 | position: absolute;
11 | top: 0;
12 | right: 0;
13 | bottom: 0;
14 | left: 0;
15 | height: 100%;
16 | }
17 |
18 | // Used for IE8 fallback
19 | .vjs-poster img {
20 | display: block;
21 | vertical-align: middle;
22 | margin: 0 auto;
23 | max-height: 100%;
24 | padding: 0;
25 | width: 100%;
26 | }
27 |
28 | // Hide the poster after the video has started playing
29 | .vjs-has-started .vjs-poster {
30 | display: none;
31 | }
32 |
33 | // Don't hide the poster if we're playing audio
34 | .vjs-audio.vjs-has-started .vjs-poster {
35 | display: block;
36 | }
37 |
38 | // Hide the poster when controls are disabled because it's clickable
39 | // and the native poster can take over
40 | .vjs-controls-disabled .vjs-poster {
41 | display: none;
42 | }
43 |
44 | // Hide the poster when native controls are used otherwise it covers them
45 | .vjs-using-native-controls .vjs-poster {
46 | display: none;
47 | }
48 |
--------------------------------------------------------------------------------
/lang/fa.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "پخش",
3 | "Pause": "وقفه",
4 | "Current Time": "زمان کنونی",
5 | "Duration Time": "مدت زمان",
6 | "Remaining Time": "زمان باقیمانده",
7 | "Stream Type": "نوع استریم",
8 | "LIVE": "زنده",
9 | "Loaded": "فراخوانی شده",
10 | "Progress": "پیشرفت",
11 | "Fullscreen": "تمام صفحه",
12 | "Non-Fullscreen": "نمایش عادی",
13 | "Mute": "بی صدا",
14 | "Unmute": "بهمراه صدا",
15 | "Playback Rate": "سرعت پخش",
16 | "Subtitles": "زیرنویس",
17 | "subtitles off": "بدون زیرنویس",
18 | "Captions": "عنوان",
19 | "captions off": "بدون عنوان",
20 | "Chapters": "فصل",
21 | "You aborted the media playback": "شما پخش را متوقف کردید.",
22 | "A network error caused the media download to fail part-way.": "مشکل در دریافت ویدئو ...",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "فرمت پشتیبانی نمیشود یا خطایی روی داده است.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "مشکل در دریافت ویدئو ...",
25 | "No compatible source was found for this media.": "هیچ ورودی ای برای این رسانه شناسایی نشد."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/control-bar/text-track-controls/subtitles-button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file subtitles-button.js
3 | */
4 | import TextTrackButton from './text-track-button.js';
5 | import Component from '../../component.js';
6 |
7 | /**
8 | * The button component for toggling and selecting subtitles
9 | *
10 | * @param {Object} player Player object
11 | * @param {Object=} options Object of option names and values
12 | * @param {Function=} ready Ready callback function
13 | * @extends TextTrackButton
14 | * @class SubtitlesButton
15 | */
16 | class SubtitlesButton extends TextTrackButton {
17 |
18 | constructor(player, options, ready){
19 | super(player, options, ready);
20 | this.el_.setAttribute('aria-label','Subtitles Menu');
21 | }
22 |
23 | /**
24 | * Allow sub components to stack CSS class names
25 | *
26 | * @return {String} The constructed class name
27 | * @method buildCSSClass
28 | */
29 | buildCSSClass() {
30 | return `vjs-subtitles-button ${super.buildCSSClass()}`;
31 | }
32 |
33 | }
34 |
35 | SubtitlesButton.prototype.kind_ = 'subtitles';
36 | SubtitlesButton.prototype.controlText_ = 'Subtitles';
37 |
38 | Component.registerComponent('SubtitlesButton', SubtitlesButton);
39 | export default SubtitlesButton;
40 |
--------------------------------------------------------------------------------
/src/js/control-bar/spacer-controls/custom-control-spacer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file custom-control-spacer.js
3 | */
4 | import Spacer from './spacer.js';
5 | import Component from '../../component.js';
6 |
7 | /**
8 | * Spacer specifically meant to be used as an insertion point for new plugins, etc.
9 | *
10 | * @extends Spacer
11 | * @class CustomControlSpacer
12 | */
13 | class CustomControlSpacer extends Spacer {
14 |
15 | /**
16 | * Allow sub components to stack CSS class names
17 | *
18 | * @return {String} The constructed class name
19 | * @method buildCSSClass
20 | */
21 | buildCSSClass() {
22 | return `vjs-custom-control-spacer ${super.buildCSSClass()}`;
23 | }
24 |
25 | /**
26 | * Create the component's DOM element
27 | *
28 | * @return {Element}
29 | * @method createEl
30 | */
31 | createEl() {
32 | let el = super.createEl({
33 | className: this.buildCSSClass(),
34 | });
35 |
36 | // No-flex/table-cell mode requires there be some content
37 | // in the cell to fill the remaining space of the table.
38 | el.innerHTML = ' ';
39 | return el;
40 | }
41 | }
42 |
43 | Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
44 | export default CustomControlSpacer;
45 |
--------------------------------------------------------------------------------
/test/unit/utils/format-time.test.js:
--------------------------------------------------------------------------------
1 | import formatTime from '../../../src/js/utils/format-time.js';
2 |
3 | q.module('format-time');
4 |
5 | test('should format time as a string', function(){
6 | ok(formatTime(1) === '0:01');
7 | ok(formatTime(10) === '0:10');
8 | ok(formatTime(60) === '1:00');
9 | ok(formatTime(600) === '10:00');
10 | ok(formatTime(3600) === '1:00:00');
11 | ok(formatTime(36000) === '10:00:00');
12 | ok(formatTime(360000) === '100:00:00');
13 |
14 | // Using guide should provide extra leading zeros
15 | ok(formatTime(1,1) === '0:01');
16 | ok(formatTime(1,10) === '0:01');
17 | ok(formatTime(1,60) === '0:01');
18 | ok(formatTime(1,600) === '00:01');
19 | ok(formatTime(1,3600) === '0:00:01');
20 | // Don't do extra leading zeros for hours
21 | ok(formatTime(1,36000) === '0:00:01');
22 | ok(formatTime(1,360000) === '0:00:01');
23 |
24 | // Do not display negative time
25 | ok(formatTime(-1) === '0:00');
26 | ok(formatTime(-1,3600) === '0:00:00');
27 | });
28 |
29 | test('should format invalid times as dashes', function(){
30 | equal(formatTime(Infinity, 90), '-:-');
31 | equal(formatTime(NaN), '-:-');
32 | // equal(formatTime(NaN, 216000), '-:--:--');
33 | equal(formatTime(10, Infinity), '0:00:10');
34 | equal(formatTime(90, NaN), '1:30');
35 | });
36 |
--------------------------------------------------------------------------------
/lang/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Přehrát",
3 | "Pause": "Pauza",
4 | "Current Time": "Aktuální čas",
5 | "Duration Time": "Doba trvání",
6 | "Remaining Time": "Zbývající čas",
7 | "Stream Type": "Stream Type",
8 | "LIVE": "ŽIVĚ",
9 | "Loaded": "Načteno",
10 | "Progress": "Stav",
11 | "Fullscreen": "Celá obrazovka",
12 | "Non-Fullscreen": "Zmenšená obrazovka",
13 | "Mute": "Ztlumit zvuk",
14 | "Unmute": "Přehrát zvuk",
15 | "Playback Rate": "Rychlost přehrávání",
16 | "Subtitles": "Titulky",
17 | "subtitles off": "Titulky vypnuty",
18 | "Captions": "Popisky",
19 | "captions off": "Popisky vypnuty",
20 | "Chapters": "Kapitoly",
21 | "You aborted the media playback": "Přehrávání videa je přerušeno.",
22 | "A network error caused the media download to fail part-way.": "Video nemohlo být načteno, kvůli chybě v síti.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video nemohlo být načteno, buď kvůli chybě serveru nebo sítě nebo proto, že daný formát není podporován.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Váš prohlížeč nepodporuje formát videa.",
25 | "No compatible source was found for this media.": "Špatně zadaný zdroj videa."
26 | }
27 |
--------------------------------------------------------------------------------
/src/css/video-js.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 | @import "private-variables";
3 | @import "utilities";
4 |
5 | @if $icon-codepoints {
6 | @import "node_modules/videojs-font/scss/icons-codepoints";
7 | } @else {
8 | @import "node_modules/videojs-font/scss/icons";
9 | }
10 |
11 | @import "components/layout";
12 | @import "components/big-play";
13 | @import "components/button";
14 | @import "components/close-button";
15 |
16 | @import "components/menu/menu";
17 | @import "components/menu/menu-popup";
18 | @import "components/menu/menu-inline";
19 |
20 | @import "components/control-bar";
21 | @import "components/control";
22 | @import "components/control-spacer";
23 |
24 | @import "components/progress";
25 | @import "components/slider";
26 |
27 | @import "components/volume";
28 |
29 | @import "components/poster";
30 | @import "components/live";
31 | @import "components/time";
32 | @import "components/play-pause";
33 | @import "components/text-track";
34 | @import "components/fullscreen";
35 | @import "components/playback-rate";
36 | @import "components/error";
37 | @import "components/loading";
38 | @import "components/captions";
39 | @import "components/chapters";
40 | @import "components/descriptions";
41 | @import "components/subtitles";
42 | @import "components/adaptive";
43 | @import "components/captions-settings";
44 | @import "components/modal-dialog";
45 |
--------------------------------------------------------------------------------
/lang/vi.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Phát",
3 | "Pause": "Tạm dừng",
4 | "Current Time": "Thời gian hiện tại",
5 | "Duration Time": "Độ dài",
6 | "Remaining Time": "Thời gian còn lại",
7 | "Stream Type": "Kiểu Stream",
8 | "LIVE": "TRỰC TIẾP",
9 | "Loaded": "Đã tải",
10 | "Progress": "Tiến trình",
11 | "Fullscreen": "Toàn màn hình",
12 | "Non-Fullscreen": "Thoát toàn màn hình",
13 | "Mute": "Tắt tiếng",
14 | "Unmute": "Bật âm thanh",
15 | "Playback Rate": "Tốc độ phát",
16 | "Subtitles": "Phụ đề",
17 | "subtitles off": "Tắt phụ đề",
18 | "Captions": "Chú thích",
19 | "captions off": "Tắt chú thích",
20 | "Chapters": "Chương",
21 | "You aborted the media playback": "Bạn đã hủy việc phát media.",
22 | "A network error caused the media download to fail part-way.": "Một lỗi mạng dẫn đến việc tải media bị lỗi.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video không tải được, mạng hay server có lỗi hoặc định dạng không được hỗ trợ.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Phát media đã bị hủy do một sai lỗi hoặc media sử dụng những tính năng trình duyệt không hỗ trợ.",
25 | "No compatible source was found for this media.": "Không có nguồn tương thích cho media này."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/utils/fn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file fn.js
3 | */
4 | import { newGUID } from './guid.js';
5 |
6 | /**
7 | * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
8 | * It also stores a unique id on the function so it can be easily removed from events
9 | *
10 | * @param {*} context The object to bind as scope
11 | * @param {Function} fn The function to be bound to a scope
12 | * @param {Number=} uid An optional unique ID for the function to be set
13 | * @return {Function}
14 | * @private
15 | * @method bind
16 | */
17 | export const bind = function(context, fn, uid) {
18 | // Make sure the function has a unique ID
19 | if (!fn.guid) { fn.guid = newGUID(); }
20 |
21 | // Create the new function that changes the context
22 | let ret = function() {
23 | return fn.apply(context, arguments);
24 | };
25 |
26 | // Allow for the ability to individualize this function
27 | // Needed in the case where multiple objects might share the same prototype
28 | // IF both items add an event listener with the same function, then you try to remove just one
29 | // it will remove both because they both have the same guid.
30 | // when using this, you need to use the bind method when you remove the listener as well.
31 | // currently used in text tracks
32 | ret.guid = (uid) ? uid + '_' + fn.guid : fn.guid;
33 |
34 | return ret;
35 | };
36 |
--------------------------------------------------------------------------------
/lang/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Tocar",
3 | "Pause": "Pause",
4 | "Current Time": "Tempo",
5 | "Duration Time": "Duração",
6 | "Remaining Time": "Tempo Restante",
7 | "Stream Type": "Tipo de Stream",
8 | "LIVE": "AO VIVO",
9 | "Loaded": "Carregado",
10 | "Progress": "Progresso",
11 | "Fullscreen": "Tela Cheia",
12 | "Non-Fullscreen": "Tela Normal",
13 | "Mute": "Mudo",
14 | "Unmute": "Habilitar Som",
15 | "Playback Rate": "Velocidade",
16 | "Subtitles": "Legendas",
17 | "subtitles off": "Sem Legendas",
18 | "Captions": "Anotações",
19 | "captions off": "Sem Anotações",
20 | "Chapters": "Capítulos",
21 | "You aborted the media playback": "Você parou a execução de vídeo.",
22 | "A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar parcialmente.",
23 | "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.",
24 | "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.",
25 | "No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/ba.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Pusti",
3 | "Pause": "Pauza",
4 | "Current Time": "Trenutno vrijeme",
5 | "Duration Time": "Vrijeme trajanja",
6 | "Remaining Time": "Preostalo vrijeme",
7 | "Stream Type": "Način strimovanja",
8 | "LIVE": "UŽIVO",
9 | "Loaded": "Učitan",
10 | "Progress": "Progres",
11 | "Fullscreen": "Puni ekran",
12 | "Non-Fullscreen": "Mali ekran",
13 | "Mute": "Prigušen",
14 | "Unmute": "Ne-prigušen",
15 | "Playback Rate": "Stopa reprodukcije",
16 | "Subtitles": "Podnaslov",
17 | "subtitles off": "Podnaslov deaktiviran",
18 | "Captions": "Titlovi",
19 | "captions off": "Titlovi deaktivirani",
20 | "Chapters": "Poglavlja",
21 | "You aborted the media playback": "Isključili ste reprodukciju videa.",
22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/hr.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Pusti",
3 | "Pause": "Pauza",
4 | "Current Time": "Trenutno vrijeme",
5 | "Duration Time": "Vrijeme trajanja",
6 | "Remaining Time": "Preostalo vrijeme",
7 | "Stream Type": "Način strimovanja",
8 | "LIVE": "UŽIVO",
9 | "Loaded": "Učitan",
10 | "Progress": "Progres",
11 | "Fullscreen": "Puni ekran",
12 | "Non-Fullscreen": "Mali ekran",
13 | "Mute": "Prigušen",
14 | "Unmute": "Ne-prigušen",
15 | "Playback Rate": "Stopa reprodukcije",
16 | "Subtitles": "Podnaslov",
17 | "subtitles off": "Podnaslov deaktiviran",
18 | "Captions": "Titlovi",
19 | "captions off": "Titlovi deaktivirani",
20 | "Chapters": "Poglavlja",
21 | "You aborted the media playback": "Isključili ste reprodukciju videa.",
22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/sr.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Pusti",
3 | "Pause": "Pauza",
4 | "Current Time": "Trenutno vrijeme",
5 | "Duration Time": "Vrijeme trajanja",
6 | "Remaining Time": "Preostalo vrijeme",
7 | "Stream Type": "Način strimovanja",
8 | "LIVE": "UŽIVO",
9 | "Loaded": "Učitan",
10 | "Progress": "Progres",
11 | "Fullscreen": "Puni ekran",
12 | "Non-Fullscreen": "Mali ekran",
13 | "Mute": "Prigušen",
14 | "Unmute": "Ne-prigušen",
15 | "Playback Rate": "Stopa reprodukcije",
16 | "Subtitles": "Podnaslov",
17 | "subtitles off": "Podnaslov deaktiviran",
18 | "Captions": "Titlovi",
19 | "captions off": "Titlovi deaktivirani",
20 | "Chapters": "Poglavlja",
21 | "You aborted the media playback": "Isključili ste reprodukciju videa.",
22 | "A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
25 | "No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
26 | }
27 |
--------------------------------------------------------------------------------
/docs/examples/simple-embed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Video.js | HTML5 Video Player
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/lang/fi.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Toisto",
3 | "Pause": "Tauko",
4 | "Current Time": "Tämänhetkinen aika",
5 | "Duration Time": "Kokonaisaika",
6 | "Remaining Time": "Jäljellä oleva aika",
7 | "Stream Type": "Lähetystyyppi",
8 | "LIVE": "LIVE",
9 | "Loaded": "Ladattu",
10 | "Progress": "Edistyminen",
11 | "Fullscreen": "Koko näyttö",
12 | "Non-Fullscreen": "Koko näyttö pois",
13 | "Mute": "Ääni pois",
14 | "Unmute": "Ääni päällä",
15 | "Playback Rate": "Toistonopeus",
16 | "Subtitles": "Tekstitys",
17 | "subtitles off": "Tekstitys pois",
18 | "Captions": "Tekstitys",
19 | "captions off": "Tekstitys pois",
20 | "Chapters": "Kappaleet",
21 | "You aborted the media playback": "Olet keskeyttänyt videotoiston.",
22 | "A network error caused the media download to fail part-way.": "Verkkovirhe keskeytti videon latauksen.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videon lataus ei onnistunut joko palvelin- tai verkkovirheestä tai väärästä formaatista johtuen.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videon toisto keskeytyi, koska media on vaurioitunut tai käyttää käyttää toimintoja, joita selaimesi ei tue.",
25 | "No compatible source was found for this media.": "Tälle videolle ei löytynyt yhteensopivaa lähdettä."
26 | }
27 |
--------------------------------------------------------------------------------
/test/unit/clickable-component.test.js:
--------------------------------------------------------------------------------
1 | import ClickableComponent from '../../src/js/clickable-component.js';
2 | import TestHelpers from './test-helpers.js';
3 |
4 | q.module('ClickableComponent');
5 |
6 | q.test('should create a div with role="button"', function(){
7 | expect(2);
8 |
9 | let player = TestHelpers.makePlayer({});
10 |
11 | let testClickableComponent = new ClickableComponent(player);
12 | let el = testClickableComponent.createEl();
13 |
14 | equal(el.nodeName.toLowerCase(), 'div', 'the name of the element is "div"');
15 | equal(el.getAttribute('role').toLowerCase(), 'button', 'the role of the element is "button"');
16 |
17 | testClickableComponent.dispose();
18 | player.dispose();
19 | });
20 |
21 | q.test('should be enabled/disabled', function(){
22 | expect(3);
23 |
24 | let player = TestHelpers.makePlayer({});
25 |
26 | let testClickableComponent = new ClickableComponent(player);
27 |
28 | equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent defaults to enabled');
29 |
30 | testClickableComponent.disable();
31 |
32 | equal(testClickableComponent.hasClass('vjs-disabled'), true, 'ClickableComponent is disabled');
33 |
34 | testClickableComponent.enable();
35 |
36 | equal(testClickableComponent.hasClass('vjs-disabled'), false, 'ClickableComponent is enabled');
37 |
38 | testClickableComponent.dispose();
39 | player.dispose();
40 | });
41 |
--------------------------------------------------------------------------------
/src/js/control-bar/live-display.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file live-display.js
3 | */
4 | import Component from '../component';
5 | import * as Dom from '../utils/dom.js';
6 |
7 | /**
8 | * Displays the live indicator
9 | * TODO - Future make it click to snap to live
10 | *
11 | * @extends Component
12 | * @class LiveDisplay
13 | */
14 | class LiveDisplay extends Component {
15 |
16 | constructor(player, options) {
17 | super(player, options);
18 |
19 | this.updateShowing();
20 | this.on(this.player(), 'durationchange', this.updateShowing);
21 | }
22 |
23 | /**
24 | * Create the component's DOM element
25 | *
26 | * @return {Element}
27 | * @method createEl
28 | */
29 | createEl() {
30 | var el = super.createEl('div', {
31 | className: 'vjs-live-control vjs-control'
32 | });
33 |
34 | this.contentEl_ = Dom.createEl('div', {
35 | className: 'vjs-live-display',
36 | innerHTML: `${this.localize('Stream Type')}${this.localize('LIVE')}`
37 | }, {
38 | 'aria-live': 'off'
39 | });
40 |
41 | el.appendChild(this.contentEl_);
42 | return el;
43 | }
44 |
45 | updateShowing() {
46 | if (this.player().duration() === Infinity) {
47 | this.show();
48 | } else {
49 | this.hide();
50 | }
51 | }
52 |
53 | }
54 |
55 | Component.registerComponent('LiveDisplay', LiveDisplay);
56 | export default LiveDisplay;
57 |
--------------------------------------------------------------------------------
/lang/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Lejátszás",
3 | "Pause": "Szünet",
4 | "Current Time": "Aktuális időpont",
5 | "Duration Time": "Hossz",
6 | "Remaining Time": "Hátralévő idő",
7 | "Stream Type": "Adatfolyam típusa",
8 | "LIVE": "ÉLŐ",
9 | "Loaded": "Betöltve",
10 | "Progress": "Állapot",
11 | "Fullscreen": "Teljes képernyő",
12 | "Non-Fullscreen": "Normál méret",
13 | "Mute": "Némítás",
14 | "Unmute": "Némítás kikapcsolva",
15 | "Playback Rate": "Lejátszási sebesség",
16 | "Subtitles": "Feliratok",
17 | "subtitles off": "Feliratok kikapcsolva",
18 | "Captions": "Magyarázó szöveg",
19 | "captions off": "Magyarázó szöveg kikapcsolva",
20 | "Chapters": "Fejezetek",
21 | "You aborted the media playback": "Leállította a lejátszást",
22 | "A network error caused the media download to fail part-way.": "Hálózati hiba miatt a videó részlegesen töltődött le.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "A videó nem tölthető be hálózati vagy kiszolgálói hiba miatt, vagy a formátuma nem támogatott.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A lejátszás adatsérülés miatt leállt, vagy a videó egyes tulajdonságait a böngészője nem támogatja.",
25 | "No compatible source was found for this media.": "Nincs kompatibilis forrás ehhez a videóhoz."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/nn.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Spel",
3 | "Pause": "Pause",
4 | "Current Time": "Aktuell tid",
5 | "Duration Time": "Varigheit",
6 | "Remaining Time": "Tid attende",
7 | "Stream Type": "Type straum",
8 | "LIVE": "DIREKTE",
9 | "Loaded": "Lasta inn",
10 | "Progress": "Status",
11 | "Fullscreen": "Fullskjerm",
12 | "Non-Fullscreen": "Stenga fullskjerm",
13 | "Mute": "Ljod av",
14 | "Unmute": "Ljod på",
15 | "Playback Rate": "Avspelingsrate",
16 | "Subtitles": "Teksting på",
17 | "subtitles off": "Teksting av",
18 | "Captions": "Teksting for høyrselshemma på",
19 | "captions off": "Teksting for høyrselshemma av",
20 | "Chapters": "Kapitel",
21 | "You aborted the media playback": "Du avbraut avspelinga.",
22 | "A network error caused the media download to fail part-way.": "Ein nettverksfeil avbraut nedlasting av videoen.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikkje lastas ned, på grunn av ein nettverksfeil eller serverfeil, eller av di formatet ikkje er stoda.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoavspelinga blei broten på grunn av øydelagde data eller av di videoen ville gjera noe som nettlesaren din ikkje stodar.",
25 | "No compatible source was found for this media.": "Fant ikke en kompatibel kilde for dette mediainnholdet."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Spela",
3 | "Pause": "Pausa",
4 | "Current Time": "Aktuell tid",
5 | "Duration Time": "Total tid",
6 | "Remaining Time": "Återstående tid",
7 | "Stream Type": "Strömningstyp",
8 | "LIVE": "LIVE",
9 | "Loaded": "Laddad",
10 | "Progress": "Förlopp",
11 | "Fullscreen": "Fullskärm",
12 | "Non-Fullscreen": "Ej fullskärm",
13 | "Mute": "Ljud av",
14 | "Unmute": "Ljud på",
15 | "Playback Rate": "Uppspelningshastighet",
16 | "Subtitles": "Text på",
17 | "subtitles off": "Text av",
18 | "Captions": "Text på",
19 | "captions off": "Text av",
20 | "Chapters": "Kapitel",
21 | "You aborted the media playback": "Du har avbrutit videouppspelningen.",
22 | "A network error caused the media download to fail part-way.": "Ett nätverksfel gjorde att nedladdningen av videon avbröts.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Det gick inte att ladda videon, antingen på grund av ett server- eller nätverksfel, eller för att formatet inte stöds.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Uppspelningen avbröts på grund av att videon är skadad, eller också för att videon använder funktioner som din webbläsare inte stöder.",
25 | "No compatible source was found for this media.": "Det gick inte att hitta någon kompatibel källa för den här videon."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/uk.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Відтворити",
3 | "Pause": "Призупинити",
4 | "Current Time": "Поточний час",
5 | "Duration Time": "Тривалість",
6 | "Remaining Time": "Час, що залишився",
7 | "Stream Type": "Тип потоку",
8 | "LIVE": "НАЖИВО",
9 | "Loaded": "Завантаження",
10 | "Progress": "Прогрес",
11 | "Fullscreen": "Повноекранний режим",
12 | "Non-Fullscreen": "Неповноекранний режим",
13 | "Mute": "Без звуку",
14 | "Unmute": "Зі звуком",
15 | "Playback Rate": "Швидкість відтворення",
16 | "Subtitles": "Субтитри",
17 | "subtitles off": "Без субтитрів",
18 | "Captions": "Підписи",
19 | "captions off": "Без підписів",
20 | "Chapters": "Розділи",
21 | "You aborted the media playback": "Ви припинили відтворення відео",
22 | "A network error caused the media download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.",
25 | "No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/event-target.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file event-target.js
3 | */
4 | import * as Events from './utils/events.js';
5 |
6 | var EventTarget = function() {};
7 |
8 | EventTarget.prototype.allowedEvents_ = {};
9 |
10 | EventTarget.prototype.on = function(type, fn) {
11 | // Remove the addEventListener alias before calling Events.on
12 | // so we don't get into an infinite type loop
13 | let ael = this.addEventListener;
14 | this.addEventListener = Function.prototype;
15 | Events.on(this, type, fn);
16 | this.addEventListener = ael;
17 | };
18 | EventTarget.prototype.addEventListener = EventTarget.prototype.on;
19 |
20 | EventTarget.prototype.off = function(type, fn) {
21 | Events.off(this, type, fn);
22 | };
23 | EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
24 |
25 | EventTarget.prototype.one = function(type, fn) {
26 | Events.one(this, type, fn);
27 | };
28 |
29 | EventTarget.prototype.trigger = function(event) {
30 | let type = event.type || event;
31 |
32 | if (typeof event === 'string') {
33 | event = {
34 | type: type
35 | };
36 | }
37 | event = Events.fixEvent(event);
38 |
39 | if (this.allowedEvents_[type] && this['on' + type]) {
40 | this['on' + type](event);
41 | }
42 |
43 | Events.trigger(this, event);
44 | };
45 | // The standard DOM EventTarget.dispatchEvent() is aliased to trigger()
46 | EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
47 |
48 | export default EventTarget;
49 |
--------------------------------------------------------------------------------
/lang/nb.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Spill",
3 | "Pause": "Pause",
4 | "Current Time": "Aktuell tid",
5 | "Duration Time": "Varighet",
6 | "Remaining Time": "Gjenstående tid",
7 | "Stream Type": "Type strøm",
8 | "LIVE": "DIREKTE",
9 | "Loaded": "Lastet inn",
10 | "Progress": "Status",
11 | "Fullscreen": "Fullskjerm",
12 | "Non-Fullscreen": "Lukk fullskjerm",
13 | "Mute": "Lyd av",
14 | "Unmute": "Lyd på",
15 | "Playback Rate": "Avspillingsrate",
16 | "Subtitles": "Undertekst på",
17 | "subtitles off": "Undertekst av",
18 | "Captions": "Undertekst for hørselshemmede på",
19 | "captions off": "Undertekst for hørselshemmede av",
20 | "Chapters": "Kapitler",
21 | "You aborted the media playback": "Du avbrøt avspillingen.",
22 | "A network error caused the media download to fail part-way.": "En nettverksfeil avbrøt nedlasting av videoen.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikke lastes ned, på grunn av nettverksfeil eller serverfeil, eller fordi formatet ikke er støttet.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoavspillingen ble avbrudt på grunn av ødelagte data eller fordi videoen ville gjøre noe som nettleseren din ikke har støtte for.",
25 | "No compatible source was found for this media.": "Fant ikke en kompatibel kilde for dette mediainnholdet."
26 | }
27 |
--------------------------------------------------------------------------------
/test/unit/close-button.test.js:
--------------------------------------------------------------------------------
1 | import CloseButton from '../../src/js/close-button';
2 | import TestHelpers from './test-helpers';
3 |
4 | q.module('CloseButton', {
5 |
6 | beforeEach: function() {
7 | this.player = TestHelpers.makePlayer();
8 | this.btn = new CloseButton(this.player);
9 | },
10 |
11 | afterEach: function() {
12 | this.player.dispose();
13 | this.btn.dispose();
14 | }
15 | });
16 |
17 | q.test('should create the expected element', function(assert) {
18 | let elAssertions = TestHelpers.assertEl(assert, this.btn.el(), {
19 | tagName: 'button',
20 | classes: [
21 | 'vjs-button',
22 | 'vjs-close-button',
23 | 'vjs-control'
24 | ]
25 | });
26 |
27 | assert.expect(elAssertions.count + 1);
28 | elAssertions();
29 | assert.strictEqual(this.btn.el().querySelector('.vjs-control-text').innerHTML, 'Close');
30 | });
31 |
32 | q.test('should allow setting the controlText_ property as an option', function(assert) {
33 | var text = 'OK!';
34 | var btn = new CloseButton(this.player, {controlText: text});
35 |
36 | assert.expect(1);
37 | assert.strictEqual(btn.controlText_, text, 'set the controlText_ property');
38 | });
39 |
40 | q.test('should trigger an event on activation', function(assert) {
41 | var spy = sinon.spy();
42 |
43 | this.btn.on('close', spy);
44 | this.btn.trigger('click');
45 | assert.expect(1);
46 | assert.strictEqual(spy.callCount, 1, 'the "close" event was triggered');
47 | });
48 |
--------------------------------------------------------------------------------
/lang/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Afspil",
3 | "Pause": "Pause",
4 | "Current Time": "Aktuel tid",
5 | "Duration Time": "Varighed",
6 | "Remaining Time": "Resterende tid",
7 | "Stream Type": "Stream-type",
8 | "LIVE": "LIVE",
9 | "Loaded": "Indlæst",
10 | "Progress": "Status",
11 | "Fullscreen": "Fuldskærm",
12 | "Non-Fullscreen": "Luk fuldskærm",
13 | "Mute": "Uden lyd",
14 | "Unmute": "Med lyd",
15 | "Playback Rate": "Afspilningsrate",
16 | "Subtitles": "Undertekster",
17 | "subtitles off": "Uden undertekster",
18 | "Captions": "Undertekster for hørehæmmede",
19 | "captions off": "Uden undertekster for hørehæmmede",
20 | "Chapters": "Kapitler",
21 | "You aborted the media playback": "Du afbrød videoafspilningen.",
22 | "A network error caused the media download to fail part-way.": "En netværksfejl fik download af videoen til at fejle.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikke indlæses, enten fordi serveren eller netværket fejlede, eller fordi formatet ikke er understøttet.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoafspilningen blev afbrudt på grund af ødelagte data eller fordi videoen benyttede faciliteter som din browser ikke understøtter.",
25 | "No compatible source was found for this media.": "Fandt ikke en kompatibel kilde for denne media."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Воспроизвести",
3 | "Pause": "Приостановить",
4 | "Current Time": "Текущее время",
5 | "Duration Time": "Продолжительность",
6 | "Remaining Time": "Оставшееся время",
7 | "Stream Type": "Тип потока",
8 | "LIVE": "ОНЛАЙН",
9 | "Loaded": "Загрузка",
10 | "Progress": "Прогресс",
11 | "Fullscreen": "Полноэкранный режим",
12 | "Non-Fullscreen": "Неполноэкранный режим",
13 | "Mute": "Без звука",
14 | "Unmute": "Со звуком",
15 | "Playback Rate": "Скорость воспроизведения",
16 | "Subtitles": "Субтитры",
17 | "subtitles off": "Субтитры выкл.",
18 | "Captions": "Подписи",
19 | "captions off": "Подписи выкл.",
20 | "Chapters": "Главы",
21 | "You aborted the media playback": "Вы прервали воспроизведение видео",
22 | "A network error caused the media download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.",
25 | "No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/control-bar/text-track-controls/caption-settings-menu-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file caption-settings-menu-item.js
3 | */
4 | import TextTrackMenuItem from './text-track-menu-item.js';
5 | import Component from '../../component.js';
6 |
7 | /**
8 | * The menu item for caption track settings menu
9 | *
10 | * @param {Player|Object} player
11 | * @param {Object=} options
12 | * @extends TextTrackMenuItem
13 | * @class CaptionSettingsMenuItem
14 | */
15 | class CaptionSettingsMenuItem extends TextTrackMenuItem {
16 |
17 | constructor(player, options) {
18 | options['track'] = {
19 | 'kind': options['kind'],
20 | 'player': player,
21 | 'label': options['kind'] + ' settings',
22 | 'selectable': false,
23 | 'default': false,
24 | mode: 'disabled'
25 | };
26 |
27 | // CaptionSettingsMenuItem has no concept of 'selected'
28 | options['selectable'] = false;
29 |
30 | super(player, options);
31 | this.addClass('vjs-texttrack-settings');
32 | this.controlText(', opens ' + options['kind'] + ' settings dialog');
33 | }
34 |
35 | /**
36 | * Handle click on menu item
37 | *
38 | * @method handleClick
39 | */
40 | handleClick() {
41 | this.player().getChild('textTrackSettings').show();
42 | this.player().getChild('textTrackSettings').el_.focus();
43 | }
44 |
45 | }
46 |
47 | Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
48 | export default CaptionSettingsMenuItem;
49 |
--------------------------------------------------------------------------------
/lang/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Възпроизвеждане",
3 | "Pause": "Пауза",
4 | "Current Time": "Текущо време",
5 | "Duration Time": "Продължителност",
6 | "Remaining Time": "Оставащо време",
7 | "Stream Type": "Тип на потока",
8 | "LIVE": "НА ЖИВО",
9 | "Loaded": "Заредено",
10 | "Progress": "Прогрес",
11 | "Fullscreen": "Цял екран",
12 | "Non-Fullscreen": "Спиране на цял екран",
13 | "Mute": "Без звук",
14 | "Unmute": "Със звук",
15 | "Playback Rate": "Скорост на възпроизвеждане",
16 | "Subtitles": "Субтитри",
17 | "subtitles off": "Спряни субтитри",
18 | "Captions": "Аудио надписи",
19 | "captions off": "Спряни аудио надписи",
20 | "Chapters": "Глави",
21 | "You aborted the media playback": "Спряхте възпроизвеждането на видеото",
22 | "A network error caused the media download to fail part-way.": "Грешка в мрежата провали изтеглянето на видеото.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Видеото не може да бъде заредено заради проблем със сървъра или мрежата или защото този формат не е поддържан.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Възпроизвеждането на видеото беше прекъснато заради проблем с файла или защото видеото използва опции които браузърът Ви не поддържа.",
25 | "No compatible source was found for this media.": "Не беше намерен съвместим източник за това видео."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Afspelen",
3 | "Pause": "Pauze",
4 | "Current Time": "Huidige tijd",
5 | "Duration Time": "Looptijd",
6 | "Remaining Time": "Resterende tijd",
7 | "Stream Type": "Streamtype",
8 | "LIVE": "LIVE",
9 | "Loaded": "Geladen",
10 | "Progress": "Status",
11 | "Fullscreen": "Volledig scherm",
12 | "Non-Fullscreen": "Geen volledig scherm",
13 | "Mute": "Geluid uit",
14 | "Unmute": "Geluid aan",
15 | "Playback Rate": "Weergavesnelheid",
16 | "Subtitles": "Ondertiteling",
17 | "subtitles off": "Ondertiteling uit",
18 | "Captions": "Ondertiteling",
19 | "captions off": "Ondertiteling uit",
20 | "Chapters": "Hoofdstukken",
21 | "You aborted the media playback": "U hebt de mediaweergave afgebroken.",
22 | "A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
23 | "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.",
24 | "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.",
25 | "No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden."
26 | }
27 |
--------------------------------------------------------------------------------
/src/css/components/menu/_menu.scss:
--------------------------------------------------------------------------------
1 | .vjs-menu-button {
2 | cursor: pointer;
3 | }
4 |
5 | // Change cursor back to default if the menu button is disabled
6 | .vjs-menu-button.vjs-disabled {
7 | cursor: default;
8 | }
9 |
10 | // prevent menus from opening while disabled
11 | .vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu {
12 | display: none;
13 | }
14 |
15 | .vjs-menu .vjs-menu-content {
16 | display: block;
17 | padding: 0; margin: 0;
18 | overflow: auto;
19 | }
20 |
21 | // prevent menus from opening while scrubbing (FF, IE)
22 | .vjs-scrubbing .vjs-menu-button:hover .vjs-menu {
23 | display: none;
24 | }
25 |
26 | .vjs-menu li {
27 | list-style: none;
28 | margin: 0;
29 | padding: 0.2em 0;
30 | line-height: 1.4em;
31 | font-size: 1.2em;
32 | text-align: center;
33 | text-transform: lowercase;
34 | }
35 |
36 | .vjs-menu li:focus,
37 | .vjs-menu li:hover {
38 | outline: 0;
39 | @include background-color-with-alpha($secondary-background-color, $secondary-background-transparency);
40 | }
41 |
42 | .vjs-menu li.vjs-selected,
43 | .vjs-menu li.vjs-selected:focus,
44 | .vjs-menu li.vjs-selected:hover {
45 | background-color: $primary-foreground-color;
46 | color: $primary-background-color;
47 | }
48 |
49 | .vjs-menu li.vjs-menu-title {
50 | text-align: center;
51 | text-transform: uppercase;
52 | font-size: 1em;
53 | line-height: 2em;
54 | padding: 0;
55 | margin: 0 0 0.3em 0;
56 | font-weight: bold;
57 | cursor: default;
58 | }
59 |
--------------------------------------------------------------------------------
/src/js/control-bar/volume-control/volume-control.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file volume-control.js
3 | */
4 | import Component from '../../component.js';
5 |
6 | // Required children
7 | import VolumeBar from './volume-bar.js';
8 |
9 | /**
10 | * The component for controlling the volume level
11 | *
12 | * @param {Player|Object} player
13 | * @param {Object=} options
14 | * @extends Component
15 | * @class VolumeControl
16 | */
17 | class VolumeControl extends Component {
18 |
19 | constructor(player, options){
20 | super(player, options);
21 |
22 | // hide volume controls when they're not supported by the current tech
23 | if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
24 | this.addClass('vjs-hidden');
25 | }
26 | this.on(player, 'loadstart', function(){
27 | if (player.tech_['featuresVolumeControl'] === false) {
28 | this.addClass('vjs-hidden');
29 | } else {
30 | this.removeClass('vjs-hidden');
31 | }
32 | });
33 | }
34 |
35 | /**
36 | * Create the component's DOM element
37 | *
38 | * @return {Element}
39 | * @method createEl
40 | */
41 | createEl() {
42 | return super.createEl('div', {
43 | className: 'vjs-volume-control vjs-control'
44 | });
45 | }
46 |
47 | }
48 |
49 | VolumeControl.prototype.options_ = {
50 | children: [
51 | 'volumeBar'
52 | ]
53 | };
54 |
55 | Component.registerComponent('VolumeControl', VolumeControl);
56 | export default VolumeControl;
57 |
--------------------------------------------------------------------------------
/test/unit/utils/time-ranges.test.js:
--------------------------------------------------------------------------------
1 | import { createTimeRanges, createTimeRange } from '../../../src/js/utils/time-ranges.js';
2 |
3 | q.module('time-ranges');
4 |
5 | test('should export the deprecated createTimeRange function', function(){
6 | equal(createTimeRange, createTimeRanges, 'createTimeRange is an alias to createTimeRanges');
7 | });
8 |
9 | test('should create a fake single timerange', function(assert){
10 | var tr = createTimeRanges(0, 10);
11 |
12 | equal(tr.length, 1, 'length should be 1');
13 | equal(tr.start(0), 0, 'works if start is called with valid index');
14 | equal(tr.end(0), 10, 'works if end is called with with valid index');
15 | assert.throws(()=>tr.start(1), /Failed to execute 'start'/, 'fails if start is called with an invalid index');
16 | assert.throws(()=>tr.end(1), /Failed to execute 'end'/, 'fails if end is called with with an invalid index');
17 | });
18 |
19 | test('should create a fake multiple timerange', function(assert){
20 | var tr = createTimeRanges([
21 | [0, 10],
22 | [11, 20]
23 | ]);
24 |
25 | equal(tr.length, 2, 'length should equal 2');
26 | equal(tr.start(1), 11, 'works if start is called with valid index');
27 | equal(tr.end(1), 20, 'works if end is called with with valid index');
28 | assert.throws(()=>tr.start(-1), /Failed to execute 'start'/, 'fails if start is called with an invalid index');
29 | assert.throws(()=>tr.end(-1), /Failed to execute 'end'/, 'fails if end is called with with an invalid index');
30 | });
31 |
--------------------------------------------------------------------------------
/src/js/control-bar/playback-rate-menu/playback-rate-menu-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file playback-rate-menu-item.js
3 | */
4 | import MenuItem from '../../menu/menu-item.js';
5 | import Component from '../../component.js';
6 |
7 | /**
8 | * The specific menu item type for selecting a playback rate
9 | *
10 | * @param {Player|Object} player
11 | * @param {Object=} options
12 | * @extends MenuItem
13 | * @class PlaybackRateMenuItem
14 | */
15 | class PlaybackRateMenuItem extends MenuItem {
16 |
17 | constructor(player, options){
18 | let label = options['rate'];
19 | let rate = parseFloat(label, 10);
20 |
21 | // Modify options for parent MenuItem class's init.
22 | options['label'] = label;
23 | options['selected'] = rate === 1;
24 | super(player, options);
25 |
26 | this.label = label;
27 | this.rate = rate;
28 |
29 | this.on(player, 'ratechange', this.update);
30 | }
31 |
32 | /**
33 | * Handle click on menu item
34 | *
35 | * @method handleClick
36 | */
37 | handleClick() {
38 | super.handleClick();
39 | this.player().playbackRate(this.rate);
40 | }
41 |
42 | /**
43 | * Update playback rate with selected rate
44 | *
45 | * @method update
46 | */
47 | update() {
48 | this.selected(this.player().playbackRate() === this.rate);
49 | }
50 |
51 | }
52 |
53 | PlaybackRateMenuItem.prototype.contentElType = 'button';
54 |
55 | Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
56 | export default PlaybackRateMenuItem;
57 |
--------------------------------------------------------------------------------
/src/js/utils/format-time.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file format-time.js
3 | *
4 | * Format seconds as a time string, H:MM:SS or M:SS
5 | * Supplying a guide (in seconds) will force a number of leading zeros
6 | * to cover the length of the guide
7 | *
8 | * @param {Number} seconds Number of seconds to be turned into a string
9 | * @param {Number} guide Number (in seconds) to model the string after
10 | * @return {String} Time formatted as H:MM:SS or M:SS
11 | * @private
12 | * @function formatTime
13 | */
14 | function formatTime(seconds, guide=seconds) {
15 | seconds = seconds < 0 ? 0 : seconds;
16 | let s = Math.floor(seconds % 60);
17 | let m = Math.floor(seconds / 60 % 60);
18 | let h = Math.floor(seconds / 3600);
19 | const gm = Math.floor(guide / 60 % 60);
20 | const gh = Math.floor(guide / 3600);
21 |
22 | // handle invalid times
23 | if (isNaN(seconds) || seconds === Infinity) {
24 | // '-' is false for all relational operators (e.g. <, >=) so this setting
25 | // will add the minimum number of fields specified by the guide
26 | h = m = s = '-';
27 | }
28 |
29 | // Check if we need to show hours
30 | h = (h > 0 || gh > 0) ? h + ':' : '';
31 |
32 | // If hours are showing, we may need to add a leading zero.
33 | // Always show at least one digit of minutes.
34 | m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':';
35 |
36 | // Check if leading zero is need for seconds
37 | s = (s < 10) ? '0' + s : s;
38 |
39 | return h + m + s;
40 | }
41 |
42 | export default formatTime;
43 |
--------------------------------------------------------------------------------
/lang/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Play",
3 | "Pause": "Pausa",
4 | "Current Time": "Orario attuale",
5 | "Duration Time": "Durata",
6 | "Remaining Time": "Tempo rimanente",
7 | "Stream Type": "Tipo del Streaming",
8 | "LIVE": "LIVE",
9 | "Loaded": "Caricato",
10 | "Progress": "Stato",
11 | "Fullscreen": "Schermo intero",
12 | "Non-Fullscreen": "Chiudi schermo intero",
13 | "Mute": "Muto",
14 | "Unmute": "Audio",
15 | "Playback Rate": "Tasso di riproduzione",
16 | "Subtitles": "Sottotitoli",
17 | "subtitles off": "Senza sottotitoli",
18 | "Captions": "Sottotitoli non udenti",
19 | "captions off": "Senza sottotitoli non udenti",
20 | "Chapters": "Capitolo",
21 | "You aborted the media playback": "La riproduzione del filmato è stata interrotta.",
22 | "A network error caused the media download to fail part-way.": "Il download del filmato è stato interrotto a causa di un problema rete.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Il filmato non può essere caricato a causa di un errore nel server o nella rete o perché il formato non viene supportato.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La riproduzione del filmato è stata interrotta a causa di un file danneggiato o per l’utilizzo di impostazioni non supportate dal browser.",
25 | "No compatible source was found for this media.": "Non ci sono fonti compatibili per questo filmato."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/ca.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Reproducció",
3 | "Pause": "Pausa",
4 | "Current Time": "Temps reproduït",
5 | "Duration Time": "Durada total",
6 | "Remaining Time": "Temps restant",
7 | "Stream Type": "Tipus de seqüència",
8 | "LIVE": "EN DIRECTE",
9 | "Loaded": "Carregat",
10 | "Progress": "Progrés",
11 | "Fullscreen": "Pantalla completa",
12 | "Non-Fullscreen": "Pantalla no completa",
13 | "Mute": "Silencia",
14 | "Unmute": "Amb so",
15 | "Playback Rate": "Velocitat de reproducció",
16 | "Subtitles": "Subtítols",
17 | "subtitles off": "Subtítols desactivats",
18 | "Captions": "Llegendes",
19 | "captions off": "Llegendes desactivades",
20 | "Chapters": "Capítols",
21 | "You aborted the media playback": "Heu interromput la reproducció del vídeo.",
22 | "A network error caused the media download to fail part-way.": "Un error de la xarxa ha interromput la baixada del vídeo.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "No s'ha pogut carregar el vídeo perquè el servidor o la xarxa han fallat, o bé perquè el seu format no és compatible.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducció de vídeo s'ha interrumput per un problema de corrupció de dades o bé perquè el vídeo demanava funcions que el vostre navegador no ofereix.",
25 | "No compatible source was found for this media.": "No s'ha trobat cap font compatible amb el vídeo."
26 | }
27 |
--------------------------------------------------------------------------------
/lang/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Lecture",
3 | "Pause": "Pause",
4 | "Current Time": "Temps actuel",
5 | "Duration Time": "Durée",
6 | "Remaining Time": "Temps restant",
7 | "Stream Type": "Type de flux",
8 | "LIVE": "EN DIRECT",
9 | "Loaded": "Chargé",
10 | "Progress": "Progression",
11 | "Fullscreen": "Plein écran",
12 | "Non-Fullscreen": "Fenêtré",
13 | "Mute": "Sourdine",
14 | "Unmute": "Son activé",
15 | "Playback Rate": "Vitesse de lecture",
16 | "Subtitles": "Sous-titres",
17 | "subtitles off": "Sous-titres désactivés",
18 | "Captions": "Sous-titres",
19 | "captions off": "Sous-titres désactivés",
20 | "Chapters": "Chapitres",
21 | "You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.",
22 | "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.",
23 | "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.",
24 | "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.",
25 | "No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo."
26 | }
27 |
--------------------------------------------------------------------------------
/src/css/components/_adaptive.scss:
--------------------------------------------------------------------------------
1 | // When the player is absurdly tiny, display nothing but:
2 | // - Play button
3 | // - Fullscreen Button
4 | .video-js.vjs-layout-tiny:not(.vjs-fullscreen) {
5 | .vjs-custom-control-spacer { @include flex(auto); }
6 | &.vjs-no-flex .vjs-custom-control-spacer { width: auto; }
7 |
8 | .vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
9 | .vjs-playback-rate, .vjs-progress-control,
10 | .vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
11 | .vjs-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
12 | }
13 |
14 | // When the player is x-small, display nothing but:
15 | // - Play button
16 | // - Progress bar
17 | // - Fullscreen Button
18 | .video-js.vjs-layout-x-small:not(.vjs-fullscreen) {
19 | .vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
20 | .vjs-playback-rate,
21 | .vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
22 | .vjs-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
23 | }
24 |
25 |
26 | // When the player is small, display nothing but:
27 | // - Play button
28 | // - Progress bar
29 | // - Volume menu button
30 | // - Captions Button
31 | // - Fullscreen button
32 | .video-js.vjs-layout-small:not(.vjs-fullscreen) {
33 | .vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
34 | .vjs-playback-rate,
35 | .vjs-mute-control, .vjs-volume-control,
36 | .vjs-chapters-button, .vjs-captions-button, .vjs-subtitles-button { display: none; }
37 | }
38 |
--------------------------------------------------------------------------------
/lang/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Reproducción",
3 | "Pause": "Pausa",
4 | "Current Time": "Tiempo reproducido",
5 | "Duration Time": "Duración total",
6 | "Remaining Time": "Tiempo restante",
7 | "Stream Type": "Tipo de secuencia",
8 | "LIVE": "DIRECTO",
9 | "Loaded": "Cargado",
10 | "Progress": "Progreso",
11 | "Fullscreen": "Pantalla completa",
12 | "Non-Fullscreen": "Pantalla no completa",
13 | "Mute": "Silenciar",
14 | "Unmute": "No silenciado",
15 | "Playback Rate": "Velocidad de reproducción",
16 | "Subtitles": "Subtítulos",
17 | "subtitles off": "Subtítulos desactivados",
18 | "Captions": "Subtítulos especiales",
19 | "captions off": "Subtítulos especiales desactivados",
20 | "Chapters": "Capítulos",
21 | "You aborted the media playback": "Ha interrumpido la reproducción del vídeo.",
22 | "A network error caused the media download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.",
23 | "The media 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.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.",
25 | "No compatible source was found for this media.": "No se ha encontrado ninguna fuente compatible con este vídeo."
26 | }
27 |
--------------------------------------------------------------------------------
/src/js/error-display.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file error-display.js
3 | */
4 | import Component from './component';
5 | import ModalDialog from './modal-dialog';
6 |
7 | import * as Dom from './utils/dom';
8 | import mergeOptions from './utils/merge-options';
9 |
10 | /**
11 | * Display that an error has occurred making the video unplayable.
12 | *
13 | * @extends ModalDialog
14 | * @class ErrorDisplay
15 | */
16 | class ErrorDisplay extends ModalDialog {
17 |
18 | /**
19 | * Constructor for error display modal.
20 | *
21 | * @param {Player} player
22 | * @param {Object} [options]
23 | */
24 | constructor(player, options) {
25 | super(player, options);
26 | this.on(player, 'error', this.open);
27 | }
28 |
29 | /**
30 | * Include the old class for backward-compatibility.
31 | *
32 | * This can be removed in 6.0.
33 | *
34 | * @method buildCSSClass
35 | * @deprecated
36 | * @return {String}
37 | */
38 | buildCSSClass() {
39 | return `vjs-error-display ${super.buildCSSClass()}`;
40 | }
41 |
42 | /**
43 | * Generates the modal content based on the player error.
44 | *
45 | * @return {String|Null}
46 | */
47 | content() {
48 | let error = this.player().error();
49 | return error ? this.localize(error.message) : '';
50 | }
51 | }
52 |
53 | ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
54 | fillAlways: true,
55 | temporary: false,
56 | uncloseable: true
57 | });
58 |
59 | Component.registerComponent('ErrorDisplay', ErrorDisplay);
60 | export default ErrorDisplay;
61 |
--------------------------------------------------------------------------------
/src/js/utils/create-deprecation-proxy.js:
--------------------------------------------------------------------------------
1 | import log from './log.js';
2 |
3 | /**
4 | * Object containing the default behaviors for available handler methods.
5 | *
6 | * @private
7 | * @type {Object}
8 | */
9 | const defaultBehaviors = {
10 | get(obj, key) {
11 | return obj[key];
12 | },
13 | set(obj, key, value) {
14 | obj[key] = value;
15 | return true;
16 | }
17 | };
18 |
19 | /**
20 | * Expose private objects publicly using a Proxy to log deprecation warnings.
21 | *
22 | * Browsers that do not support Proxy objects will simply return the `target`
23 | * object, so it can be directly exposed.
24 | *
25 | * @param {Object} target The target object.
26 | * @param {Object} messages Messages to display from a Proxy. Only operations
27 | * with an associated message will be proxied.
28 | * @param {String} [messages.get]
29 | * @param {String} [messages.set]
30 | * @return {Object} A Proxy if supported or the `target` argument.
31 | */
32 | export default (target, messages={}) => {
33 | if (typeof Proxy === 'function') {
34 | let handler = {};
35 |
36 | // Build a handler object based on those keys that have both messages
37 | // and default behaviors.
38 | Object.keys(messages).forEach(key => {
39 | if (defaultBehaviors.hasOwnProperty(key)) {
40 | handler[key] = function() {
41 | log.warn(messages[key]);
42 | return defaultBehaviors[key].apply(this, arguments);
43 | };
44 | }
45 | });
46 |
47 | return new Proxy(target, handler);
48 | }
49 | return target;
50 | };
51 |
--------------------------------------------------------------------------------
/src/js/popup/popup.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file popup.js
3 | */
4 | import Component from '../component.js';
5 | import * as Dom from '../utils/dom.js';
6 | import * as Fn from '../utils/fn.js';
7 | import * as Events from '../utils/events.js';
8 |
9 | /**
10 | * The Popup component is used to build pop up controls.
11 | *
12 | * @extends Component
13 | * @class Popup
14 | */
15 | class Popup extends Component {
16 |
17 | /**
18 | * Add a popup item to the popup
19 | *
20 | * @param {Object|String} component Component or component type to add
21 | * @method addItem
22 | */
23 | addItem(component) {
24 | this.addChild(component);
25 | component.on('click', Fn.bind(this, function(){
26 | this.unlockShowing();
27 | }));
28 | }
29 |
30 | /**
31 | * Create the component's DOM element
32 | *
33 | * @return {Element}
34 | * @method createEl
35 | */
36 | createEl() {
37 | let contentElType = this.options_.contentElType || 'ul';
38 | this.contentEl_ = Dom.createEl(contentElType, {
39 | className: 'vjs-menu-content'
40 | });
41 | var el = super.createEl('div', {
42 | append: this.contentEl_,
43 | className: 'vjs-menu'
44 | });
45 | el.appendChild(this.contentEl_);
46 |
47 | // Prevent clicks from bubbling up. Needed for Popup Buttons,
48 | // where a click on the parent is significant
49 | Events.on(el, 'click', function(event){
50 | event.preventDefault();
51 | event.stopImmediatePropagation();
52 | });
53 |
54 | return el;
55 | }
56 | }
57 |
58 | Component.registerComponent('Popup', Popup);
59 | export default Popup;
60 |
--------------------------------------------------------------------------------
/src/js/control-bar/text-track-controls/off-text-track-menu-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file off-text-track-menu-item.js
3 | */
4 | import TextTrackMenuItem from './text-track-menu-item.js';
5 | import Component from '../../component.js';
6 |
7 | /**
8 | * A special menu item for turning of a specific type of text track
9 | *
10 | * @param {Player|Object} player
11 | * @param {Object=} options
12 | * @extends TextTrackMenuItem
13 | * @class OffTextTrackMenuItem
14 | */
15 | class OffTextTrackMenuItem extends TextTrackMenuItem {
16 |
17 | constructor(player, options){
18 | // Create pseudo track info
19 | // Requires options['kind']
20 | options['track'] = {
21 | 'kind': options['kind'],
22 | 'player': player,
23 | 'label': options['kind'] + ' off',
24 | 'default': false,
25 | 'mode': 'disabled'
26 | };
27 |
28 | // MenuItem is selectable
29 | options['selectable'] = true;
30 |
31 | super(player, options);
32 | this.selected(true);
33 | }
34 |
35 | /**
36 | * Handle text track change
37 | *
38 | * @param {Object} event Event object
39 | * @method handleTracksChange
40 | */
41 | handleTracksChange(event){
42 | let tracks = this.player().textTracks();
43 | let selected = true;
44 |
45 | for (let i = 0, l = tracks.length; i < l; i++) {
46 | let track = tracks[i];
47 | if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') {
48 | selected = false;
49 | break;
50 | }
51 | }
52 |
53 | this.selected(selected);
54 | }
55 |
56 | }
57 |
58 | Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
59 | export default OffTextTrackMenuItem;
60 |
--------------------------------------------------------------------------------
/src/js/control-bar/progress-control/tooltip-progress-bar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file play-progress-bar.js
3 | */
4 | import Component from '../../component.js';
5 | import * as Fn from '../../utils/fn.js';
6 | import * as Dom from '../../utils/dom.js';
7 | import formatTime from '../../utils/format-time.js';
8 |
9 | /**
10 | * Shows play progress
11 | *
12 | * @param {Player|Object} player
13 | * @param {Object=} options
14 | * @extends Component
15 | * @class PlayProgressBar
16 | */
17 | class TooltipProgressBar extends Component {
18 |
19 | constructor(player, options){
20 | super(player, options);
21 | this.updateDataAttr();
22 | this.on(player, 'timeupdate', this.updateDataAttr);
23 | player.ready(Fn.bind(this, this.updateDataAttr));
24 | }
25 |
26 | /**
27 | * Create the component's DOM element
28 | *
29 | * @return {Element}
30 | * @method createEl
31 | */
32 | createEl() {
33 | let el = super.createEl('div', {
34 | className: 'vjs-tooltip-progress-bar vjs-slider-bar',
35 | innerHTML: `
36 | ${this.localize('Progress')}: 0%`
37 | });
38 |
39 | this.tooltip = el.querySelector('.vjs-time-tooltip');
40 |
41 | return el;
42 | }
43 |
44 | updateDataAttr() {
45 | let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
46 | let formattedTime = formatTime(time, this.player_.duration());
47 | this.el_.setAttribute('data-current-time', formattedTime);
48 | this.tooltip.innerHTML = formattedTime;
49 | }
50 |
51 | }
52 |
53 | Component.registerComponent('TooltipProgressBar', TooltipProgressBar);
54 | export default TooltipProgressBar;
55 |
--------------------------------------------------------------------------------
/src/css/components/_control-bar.scss:
--------------------------------------------------------------------------------
1 | .video-js .vjs-control-bar {
2 | display: none;
3 | width: 100%;
4 | position: absolute;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | height: 3.0em;
9 |
10 | @include background-color-with-alpha($primary-background-color, $primary-background-transparency);
11 | }
12 |
13 | // Video has started playing
14 | .vjs-has-started .vjs-control-bar {
15 | @include display-flex;
16 | visibility: visible;
17 | opacity: 1;
18 |
19 | $trans: visibility 0.1s, opacity 0.1s; // Var needed because of comma
20 | @include transition($trans);
21 | }
22 |
23 | // Video has started playing AND user is inactive
24 | .vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
25 | visibility: hidden;
26 | opacity: 0;
27 |
28 | $trans: visibility 1.0s, opacity 1.0s;
29 | @include transition($trans);
30 | }
31 |
32 | .vjs-controls-disabled .vjs-control-bar,
33 | .vjs-using-native-controls .vjs-control-bar,
34 | .vjs-error .vjs-control-bar {
35 | // !important is ok in this context.
36 | display: none !important;
37 | }
38 |
39 | // Don't hide the control bar if it's audio
40 | .vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
41 | opacity: 1;
42 | visibility: visible;
43 | }
44 |
45 | // IE8 is flakey with fonts, and you have to change the actual content to force
46 | // fonts to show/hide properly.
47 | // - "\9" IE8 hack didn't work for this
48 | // Found in XP IE8 from http://modern.ie. Does not show up in "IE8 mode" in IE9
49 | $ie8screen: "\\0screen";
50 | .vjs-user-inactive.vjs-playing .vjs-control-bar :before {
51 | @media #{$ie8screen} { content: ""; }
52 | }
53 |
54 | // IE 8 + 9 Support
55 | .vjs-has-started.vjs-no-flex .vjs-control-bar {
56 | display: table;
57 | }
58 |
--------------------------------------------------------------------------------
/src/js/control-bar/text-track-controls/chapters-track-menu-item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file chapters-track-menu-item.js
3 | */
4 | import MenuItem from '../../menu/menu-item.js';
5 | import Component from '../../component.js';
6 | import * as Fn from '../../utils/fn.js';
7 |
8 | /**
9 | * The chapter track menu item
10 | *
11 | * @param {Player|Object} player
12 | * @param {Object=} options
13 | * @extends MenuItem
14 | * @class ChaptersTrackMenuItem
15 | */
16 | class ChaptersTrackMenuItem extends MenuItem {
17 |
18 | constructor(player, options){
19 | let track = options['track'];
20 | let cue = options['cue'];
21 | let currentTime = player.currentTime();
22 |
23 | // Modify options for parent MenuItem class's init.
24 | options['label'] = cue.text;
25 | options['selected'] = (cue['startTime'] <= currentTime && currentTime < cue['endTime']);
26 | super(player, options);
27 |
28 | this.track = track;
29 | this.cue = cue;
30 | track.addEventListener('cuechange', Fn.bind(this, this.update));
31 | }
32 |
33 | /**
34 | * Handle click on menu item
35 | *
36 | * @method handleClick
37 | */
38 | handleClick() {
39 | super.handleClick();
40 | this.player_.currentTime(this.cue.startTime);
41 | this.update(this.cue.startTime);
42 | }
43 |
44 | /**
45 | * Update chapter menu item
46 | *
47 | * @method update
48 | */
49 | update() {
50 | let cue = this.cue;
51 | let currentTime = this.player_.currentTime();
52 |
53 | // vjs.log(currentTime, cue.startTime);
54 | this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']);
55 | }
56 |
57 | }
58 |
59 | Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
60 | export default ChaptersTrackMenuItem;
61 |
--------------------------------------------------------------------------------
/test/unit/tech/tech-faker.js:
--------------------------------------------------------------------------------
1 | // Fake a media playback tech controller so that player tests
2 | // can run without HTML5 or Flash, of which PhantomJS supports neither.
3 |
4 | import Tech from '../../../src/js/tech/tech.js';
5 |
6 | /**
7 | * @constructor
8 | */
9 | class TechFaker extends Tech {
10 |
11 | constructor(options, handleReady){
12 | super(options, handleReady);
13 | this.triggerReady();
14 | }
15 |
16 | createEl() {
17 | var el = super.createEl('div', {
18 | className: 'vjs-tech'
19 | });
20 |
21 | /*if (this.player().poster()) {
22 | // transfer the poster image to mimic HTML
23 | el.poster = this.player().poster();
24 | }*/
25 |
26 | return el;
27 | }
28 |
29 | // fake a poster attribute to mimic the video element
30 | poster() { return this.el().poster; }
31 | setPoster(val) { this.el().poster = val; }
32 |
33 | setControls(val) {}
34 |
35 | currentTime() { return 0; }
36 | seeking() { return false; }
37 | src() { return 'movie.mp4'; }
38 | volume() { return 0; }
39 | muted() { return false; }
40 | pause() { return false; }
41 | paused() { return true; }
42 | play() { this.trigger('play'); }
43 | supportsFullScreen() { return false; }
44 | buffered() { return {}; }
45 | duration() { return {}; }
46 | networkState() { return 0; }
47 | readyState() { return 0; }
48 | controls() { return false; }
49 |
50 | // Support everything except for "video/unsupported-format"
51 | static isSupported() { return true; }
52 | static canPlayType(type) { return (type !== 'video/unsupported-format' ? 'maybe' : ''); }
53 | static canPlaySource(srcObj) { return srcObj.type !== 'video/unsupported-format'; }
54 | }
55 |
56 | Tech.registerTech('TechFaker', TechFaker);
57 | export default TechFaker;
58 |
--------------------------------------------------------------------------------
/src/js/tracks/html-track-element-list.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file html-track-element-list.js
3 | */
4 |
5 | import * as browser from '../utils/browser.js';
6 | import document from 'global/document';
7 |
8 | class HtmlTrackElementList {
9 | constructor(trackElements = []) {
10 | let list = this;
11 |
12 | if (browser.IS_IE8) {
13 | list = document.createElement('custom');
14 |
15 | for (let prop in HtmlTrackElementList.prototype) {
16 | if (prop !== 'constructor') {
17 | list[prop] = HtmlTrackElementList.prototype[prop];
18 | }
19 | }
20 | }
21 |
22 | list.trackElements_ = [];
23 |
24 | Object.defineProperty(list, 'length', {
25 | get() {
26 | return this.trackElements_.length;
27 | }
28 | });
29 |
30 | for (let i = 0, length = trackElements.length; i < length; i++) {
31 | list.addTrackElement_(trackElements[i]);
32 | }
33 |
34 | if (browser.IS_IE8) {
35 | return list;
36 | }
37 | }
38 |
39 | addTrackElement_(trackElement) {
40 | this.trackElements_.push(trackElement);
41 | }
42 |
43 | getTrackElementByTrack_(track) {
44 | let trackElement_;
45 |
46 | for (let i = 0, length = this.trackElements_.length; i < length; i++) {
47 | if (track === this.trackElements_[i].track) {
48 | trackElement_ = this.trackElements_[i];
49 |
50 | break;
51 | }
52 | }
53 |
54 | return trackElement_;
55 | }
56 |
57 | removeTrackElement_(trackElement) {
58 | for (let i = 0, length = this.trackElements_.length; i < length; i++) {
59 | if (trackElement === this.trackElements_[i]) {
60 | this.trackElements_.splice(i, 1);
61 |
62 | break;
63 | }
64 | }
65 | }
66 | }
67 |
68 | export default HtmlTrackElementList;
69 |
--------------------------------------------------------------------------------
/test/unit/utils/create-deprecation-proxy.test.js:
--------------------------------------------------------------------------------
1 | import createDeprecationProxy from '../../../src/js/utils/create-deprecation-proxy.js';
2 | import log from '../../../src/js/utils/log.js';
3 |
4 | const proxySupported = typeof Proxy === 'function';
5 |
6 | test('should return a Proxy object when supported or the target object by reference', function() {
7 | let target = {foo: 1};
8 | let subject = createDeprecationProxy(target, {
9 | get: 'get message',
10 | set: 'set message'
11 | });
12 |
13 | // Testing for a Proxy is really difficult because Proxy objects by their
14 | // nature disguise the fact that they are in fact Proxy objects. So, this
15 | // tests that the log.warn method gets called on property get/set operations
16 | // to detect the Proxy.
17 | if (proxySupported) {
18 | sinon.stub(log, 'warn');
19 |
20 | subject.foo; // Triggers a "get"
21 | subject.foo = 2; // Triggers a "set"
22 |
23 | equal(log.warn.callCount, 2, 'proxied operations cause deprecation warnings');
24 | ok(log.warn.calledWith('get message'), 'proxied get logs expected message');
25 | ok(log.warn.calledWith('set message'), 'proxied set logs expected message');
26 |
27 | log.warn.restore();
28 | } else {
29 | strictEqual(target, subject, 'identical to target');
30 | }
31 | });
32 |
33 | // Tests run only in Proxy-supporting environments.
34 | if (proxySupported) {
35 | test('no deprecation warning is logged for operations without a message', function() {
36 | let subject = createDeprecationProxy({}, {
37 | get: 'get message'
38 | });
39 |
40 | sinon.stub(log, 'warn');
41 | subject.foo = 'bar'; // Triggers a "set," but not a "get"
42 | equal(log.warn.callCount, 0, 'no deprecation warning expected');
43 | log.warn.restore();
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/lang/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Oynat",
3 | "Pause": "Duraklat",
4 | "Current Time": "Süre",
5 | "Duration Time": "Toplam Süre",
6 | "Remaining Time": "Kalan Süre",
7 | "Stream Type": "Yayın Tipi",
8 | "LIVE": "CANLI",
9 | "Loaded": "Yüklendi",
10 | "Progress": "Yükleniyor",
11 | "Fullscreen": "Tam Ekran",
12 | "Non-Fullscreen": "Küçük Ekran",
13 | "Mute": "Ses Kapa",
14 | "Unmute": "Ses Aç",
15 | "Playback Rate": "Oynatma Hızı",
16 | "Subtitles": "Altyazı",
17 | "subtitles off": "Altyazı Kapalı",
18 | "Captions": "Ek Açıklamalar",
19 | "captions off": "Ek Açıklamalar Kapalı",
20 | "Chapters": "Bölümler",
21 | "You aborted the media playback": "Video oynatmayı iptal ettiniz",
22 | "A network error caused the media download to fail part-way.": "Video indirilirken bağlantı sorunu oluştu.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video oynatılamadı, ağ ya da sunucu hatası veya belirtilen format desteklenmiyor.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Tarayıcınız desteklemediği için videoda hata oluştu.",
25 | "No compatible source was found for this media.": "Video için kaynak bulunamadı.",
26 | "Play Video": "Videoyu Oynat",
27 | "Close": "Kapat",
28 | "Modal Window": "Modal Penceresi",
29 | "This is a modal window": "Bu bir modal penceresidir",
30 | "This modal can be closed by pressing the Escape key or activating the close button.": "Bu modal ESC tuşuna basarak ya da kapata tıklanarak kapatılabilir.",
31 | ", opens captions settings dialog": ", ek açıklama ayarları menüsünü açar",
32 | ", opens subtitles settings dialog": ", altyazı ayarları menüsünü açar",
33 | ", selected": ", seçildi"
34 | }
35 |
--------------------------------------------------------------------------------
/src/css/components/_captions-settings.scss:
--------------------------------------------------------------------------------
1 | .vjs-caption-settings {
2 | position: relative;
3 | top: 1em;
4 | background-color: $primary-background-color;
5 | background-color: rgba($primary-background-color, 0.75);
6 | color: $primary-foreground-color;
7 | margin: 0 auto;
8 | padding: 0.5em;
9 | height: 15em;
10 | font-size: 12px;
11 | width: 40em;
12 | }
13 |
14 | .vjs-caption-settings .vjs-tracksettings {
15 | top: 0;
16 | bottom: 2em;
17 | left: 0;
18 | right: 0;
19 | position: absolute;
20 | overflow: auto;
21 | }
22 |
23 | .vjs-caption-settings .vjs-tracksettings-colors,
24 | .vjs-caption-settings .vjs-tracksettings-font {
25 | float: left;
26 | }
27 | .vjs-caption-settings .vjs-tracksettings-colors:after,
28 | .vjs-caption-settings .vjs-tracksettings-font:after,
29 | .vjs-caption-settings .vjs-tracksettings-controls:after {
30 | clear: both;
31 | }
32 |
33 | .vjs-caption-settings .vjs-tracksettings-controls {
34 | position: absolute;
35 | bottom: 1em;
36 | right: 1em;
37 | }
38 |
39 | .vjs-caption-settings .vjs-tracksetting {
40 | margin: 5px;
41 | padding: 3px;
42 | min-height: 40px;
43 | }
44 | .vjs-caption-settings .vjs-tracksetting label {
45 | display: block;
46 | width: 100px;
47 | margin-bottom: 5px;
48 | }
49 |
50 | .vjs-caption-settings .vjs-tracksetting span {
51 | display: inline;
52 | margin-left: 5px;
53 | }
54 |
55 | .vjs-caption-settings .vjs-tracksetting > div {
56 | margin-bottom: 5px;
57 | min-height: 20px;
58 | }
59 |
60 | .vjs-caption-settings .vjs-tracksetting > div:last-child {
61 | margin-bottom: 0;
62 | padding-bottom: 0;
63 | min-height: 0;
64 | }
65 |
66 | .vjs-caption-settings label > input {
67 | margin-right: 10px;
68 | }
69 |
70 | .vjs-caption-settings input[type="button"] {
71 | width: 40px;
72 | height: 40px;
73 | }
74 |
--------------------------------------------------------------------------------
/lang/ar.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "تشغيل",
3 | "Pause": "ايقاف",
4 | "Current Time": "الوقت الحالي",
5 | "Duration Time": "Dauer",
6 | "Remaining Time": "الوقت المتبقي",
7 | "Stream Type": "نوع التيار",
8 | "LIVE": "مباشر",
9 | "Loaded": "تم التحميل",
10 | "Progress": "التقدم",
11 | "Fullscreen": "ملء الشاشة",
12 | "Non-Fullscreen": "غير ملء الشاشة",
13 | "Mute": "صامت",
14 | "Unmute": "غير الصامت",
15 | "Playback Rate": "معدل التشغيل",
16 | "Subtitles": "الترجمة",
17 | "subtitles off": "ايقاف الترجمة",
18 | "Captions": "التعليقات",
19 | "captions off": "ايقاف التعليقات",
20 | "Chapters": "فصول",
21 | "You aborted the media playback": "لقد ألغيت تشغيل الفيديو",
22 | "A network error caused the media download to fail part-way.": "تسبب خطأ في الشبكة بفشل تحميل الفيديو بالكامل.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "لا يمكن تحميل الفيديو بسبب فشل في الخادم أو الشبكة ، أو فشل بسبب عدم امكانية قراءة تنسيق الفيديو.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "تم ايقاف تشغيل الفيديو بسبب مشكلة فساد أو لأن الفيديو المستخدم يستخدم ميزات غير مدعومة من متصفحك.",
25 | "No compatible source was found for this media.": "فشل العثور على أي مصدر متوافق مع هذا الفيديو.",
26 | "Play Video": "تشغيل الفيديو",
27 | "Close": "أغلق",
28 | "Modal Window": "نافذة مشروطة",
29 | "This is a modal window": "هذه نافذة مشروطة",
30 | "This modal can be closed by pressing the Escape key or activating the close button.": "يمكن غلق هذه النافذة المشروطة عن طريق الضغط على زر الخروج أو تفعيل زر الإغلاق",
31 | ", opens captions settings dialog": ", تفتح نافذة خيارات التعليقات",
32 | ", opens subtitles settings dialog": ", تفتح نافذة خيارات الترجمة",
33 | ", selected": ", مختار"
34 | }
35 |
--------------------------------------------------------------------------------
/src/css/components/_big-play.scss:
--------------------------------------------------------------------------------
1 | .video-js .vjs-big-play-button {
2 | font-size: 3em;
3 | line-height: $big-play-button--height;
4 | height: $big-play-button--height;
5 | width: $big-play-button--width; // Firefox bug: For some reason without width the icon wouldn't show up. Switched to using width and removed padding.
6 | display: block;
7 | position: absolute;
8 | top: 10px;
9 | left: 10px;
10 | padding: 0;
11 | cursor: pointer;
12 | opacity: 1;
13 | border: 0.06666em solid $primary-foreground-color;
14 |
15 | // Need a slightly gray bg so it can be seen on black backgrounds
16 | @include background-color-with-alpha($primary-background-color, $primary-background-transparency);
17 | @include border-radius(0.3em);
18 | @include transition(all 0.4s);
19 |
20 | @extend .vjs-icon-play;
21 |
22 | // Since the big play button doesn't inherit from vjs-control, we need to specify a bit more than
23 | // other buttons for the icon.
24 | &:before {
25 | @extend %icon-default;
26 | }
27 | }
28 |
29 | // Allow people that hate their poster image to center the big play button.
30 | .vjs-big-play-centered .vjs-big-play-button {
31 | top: 50%;
32 | left: 50%;
33 | margin-top: -($big-play-button--height / 2);
34 | margin-left: -($big-play-button--width / 2);
35 | }
36 |
37 | .video-js:hover .vjs-big-play-button,
38 | .video-js .vjs-big-play-button:focus {
39 | outline: 0;
40 | border-color: $primary-foreground-color;
41 |
42 | @include background-color-with-alpha($secondary-background-color, $secondary-background-transparency);
43 | @include transition(all 0s);
44 | }
45 |
46 | // Hide if controls are disabled, the video is playing, or native controls are used.
47 | .vjs-controls-disabled .vjs-big-play-button,
48 | .vjs-has-started .vjs-big-play-button,
49 | .vjs-using-native-controls .vjs-big-play-button,
50 | .vjs-error .vjs-big-play-button {
51 | display: none;
52 | }
53 |
--------------------------------------------------------------------------------
/src/css/components/menu/_menu-inline.scss:
--------------------------------------------------------------------------------
1 | .video-js .vjs-menu-button-inline {
2 | @include transition(all 0.4s);
3 | overflow: hidden;
4 | }
5 |
6 | .video-js .vjs-menu-button-inline:before {
7 | // Icon pseudoelement has a different base font size (1.8em), so we need to
8 | // account for that in the width. 4em (standard button width) divided by 1.8
9 | // to get the same button width as normal.
10 | width: 2.222222222em;
11 | }
12 |
13 | // Hover state
14 | .video-js .vjs-menu-button-inline:hover,
15 | .video-js .vjs-menu-button-inline:focus,
16 | .video-js .vjs-menu-button-inline.vjs-slider-active,
17 | .video-js.vjs-no-flex .vjs-menu-button-inline {
18 | // This width is currently specific to the inline volume bar.
19 | width: 12em;
20 | }
21 | // Don't transition when tabbing in reverse to the volume menu
22 | // because it looks weird
23 | .video-js .vjs-menu-button-inline.vjs-slider-active {
24 | @include transition(none);
25 | }
26 |
27 | .vjs-menu-button-inline .vjs-menu {
28 | opacity: 0;
29 | height: 100%;
30 | width: auto;
31 |
32 | position: absolute;
33 | left: 4em;
34 | top: 0;
35 |
36 | padding: 0;
37 | margin: 0;
38 |
39 | @include transition(all 0.4s);
40 | }
41 |
42 | .vjs-menu-button-inline:hover .vjs-menu,
43 | .vjs-menu-button-inline:focus .vjs-menu,
44 | .vjs-menu-button-inline.vjs-slider-active .vjs-menu {
45 | display: block;
46 | opacity: 1;
47 | }
48 |
49 | .vjs-no-flex .vjs-menu-button-inline .vjs-menu {
50 | display: block;
51 | opacity: 1;
52 | position: relative;
53 | width: auto;
54 | }
55 |
56 | .vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu,
57 | .vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu,
58 | .vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu {
59 | width: auto;
60 | }
61 |
62 | .vjs-menu-button-inline .vjs-menu-content {
63 | width: auto;
64 | height: 100%;
65 | margin: 0;
66 | overflow: hidden;
67 | }
68 |
--------------------------------------------------------------------------------
/sandbox/plugin.html.example:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video.js Plugin Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
This page shows you how to create, register and initialize a Video.js plugin.
22 |
23 |
29 |
30 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/js/control-bar/play-toggle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file play-toggle.js
3 | */
4 | import Button from '../button.js';
5 | import Component from '../component.js';
6 |
7 | /**
8 | * Button to toggle between play and pause
9 | *
10 | * @param {Player|Object} player
11 | * @param {Object=} options
12 | * @extends Button
13 | * @class PlayToggle
14 | */
15 | class PlayToggle extends Button {
16 |
17 | constructor(player, options){
18 | super(player, options);
19 |
20 | this.on(player, 'play', this.handlePlay);
21 | this.on(player, 'pause', this.handlePause);
22 | }
23 |
24 | /**
25 | * Allow sub components to stack CSS class names
26 | *
27 | * @return {String} The constructed class name
28 | * @method buildCSSClass
29 | */
30 | buildCSSClass() {
31 | return `vjs-play-control ${super.buildCSSClass()}`;
32 | }
33 |
34 | /**
35 | * Handle click to toggle between play and pause
36 | *
37 | * @method handleClick
38 | */
39 | handleClick() {
40 | if (this.player_.paused()) {
41 | this.player_.play();
42 | } else {
43 | this.player_.pause();
44 | }
45 | }
46 |
47 | /**
48 | * Add the vjs-playing class to the element so it can change appearance
49 | *
50 | * @method handlePlay
51 | */
52 | handlePlay() {
53 | this.removeClass('vjs-paused');
54 | this.addClass('vjs-playing');
55 | this.controlText('Pause'); // change the button text to "Pause"
56 | }
57 |
58 | /**
59 | * Add the vjs-paused class to the element so it can change appearance
60 | *
61 | * @method handlePause
62 | */
63 | handlePause() {
64 | this.removeClass('vjs-playing');
65 | this.addClass('vjs-paused');
66 | this.controlText('Play'); // change the button text to "Play"
67 | }
68 |
69 | }
70 |
71 | PlayToggle.prototype.controlText_ = 'Play';
72 |
73 | Component.registerComponent('PlayToggle', PlayToggle);
74 | export default PlayToggle;
75 |
--------------------------------------------------------------------------------
/src/js/control-bar/progress-control/play-progress-bar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file play-progress-bar.js
3 | */
4 | import Component from '../../component.js';
5 | import * as Fn from '../../utils/fn.js';
6 | import * as Dom from '../../utils/dom.js';
7 | import formatTime from '../../utils/format-time.js';
8 |
9 | /**
10 | * Shows play progress
11 | *
12 | * @param {Player|Object} player
13 | * @param {Object=} options
14 | * @extends Component
15 | * @class PlayProgressBar
16 | */
17 | class PlayProgressBar extends Component {
18 |
19 | constructor(player, options){
20 | super(player, options);
21 | this.updateDataAttr();
22 | this.on(player, 'timeupdate', this.updateDataAttr);
23 | player.ready(Fn.bind(this, this.updateDataAttr));
24 |
25 | if (options.playerOptions &&
26 | options.playerOptions.controlBar &&
27 | options.playerOptions.controlBar.progressControl &&
28 | options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
29 | this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
30 | }
31 |
32 | if (this.keepTooltipsInside) {
33 | this.addClass('vjs-keep-tooltips-inside');
34 | }
35 | }
36 |
37 | /**
38 | * Create the component's DOM element
39 | *
40 | * @return {Element}
41 | * @method createEl
42 | */
43 | createEl() {
44 | return super.createEl('div', {
45 | className: 'vjs-play-progress vjs-slider-bar',
46 | innerHTML: `${this.localize('Progress')}: 0%`
47 | });
48 | }
49 |
50 | updateDataAttr() {
51 | let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
52 | this.el_.setAttribute('data-current-time', formatTime(time, this.player_.duration()));
53 | }
54 |
55 | }
56 |
57 | Component.registerComponent('PlayProgressBar', PlayProgressBar);
58 | export default PlayProgressBar;
59 |
--------------------------------------------------------------------------------
/test/unit/utils/log.test.js:
--------------------------------------------------------------------------------
1 | import log from '../../../src/js/utils/log.js';
2 | import window from 'global/window';
3 |
4 | q.module('log');
5 |
6 | test('should confirm logging functions work', function() {
7 | let origConsole = window['console'];
8 | // replace the native console for testing
9 | // in ie8 console.log is apparently not a 'function' so sinon chokes on it
10 | // https://github.com/cjohansen/Sinon.JS/issues/386
11 | // instead we'll temporarily replace them with no-op functions
12 | let console = window['console'] = {
13 | log: function(){},
14 | warn: function(){},
15 | error: function(){}
16 | };
17 |
18 | // stub the global log functions
19 | let logStub = sinon.stub(console, 'log');
20 | let errorStub = sinon.stub(console, 'error');
21 | let warnStub = sinon.stub(console, 'warn');
22 |
23 | // Reset the log history
24 | log.history = [];
25 |
26 | log('log1', 'log2');
27 | log.warn('warn1', 'warn2');
28 | log.error('error1', 'error2');
29 |
30 | ok(logStub.called, 'log was called');
31 | equal(logStub.firstCall.args[0], 'VIDEOJS:');
32 | equal(logStub.firstCall.args[1], 'log1');
33 | equal(logStub.firstCall.args[2], 'log2');
34 |
35 | ok(warnStub.called, 'warn was called');
36 | equal(warnStub.firstCall.args[0], 'VIDEOJS:');
37 | equal(warnStub.firstCall.args[1], 'WARN:');
38 | equal(warnStub.firstCall.args[2], 'warn1');
39 | equal(warnStub.firstCall.args[3], 'warn2');
40 |
41 | ok(errorStub.called, 'error was called');
42 | equal(errorStub.firstCall.args[0], 'VIDEOJS:');
43 | equal(errorStub.firstCall.args[1], 'ERROR:');
44 | equal(errorStub.firstCall.args[2], 'error1');
45 | equal(errorStub.firstCall.args[3], 'error2');
46 |
47 | equal(log.history.length, 3, 'there should be three messages in the log history');
48 |
49 | // tear down sinon
50 | logStub.restore();
51 | errorStub.restore();
52 | warnStub.restore();
53 |
54 | // restore the native console
55 | window['console'] = origConsole;
56 | });
57 |
--------------------------------------------------------------------------------
/src/js/tech/loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file loader.js
3 | */
4 | import Component from '../component.js';
5 | import Tech from './tech.js';
6 | import window from 'global/window';
7 | import toTitleCase from '../utils/to-title-case.js';
8 |
9 | /**
10 | * The Media Loader is the component that decides which playback technology to load
11 | * when the player is initialized.
12 | *
13 | * @param {Object} player Main Player
14 | * @param {Object=} options Object of option names and values
15 | * @param {Function=} ready Ready callback function
16 | * @extends Component
17 | * @class MediaLoader
18 | */
19 | class MediaLoader extends Component {
20 |
21 | constructor(player, options, ready){
22 | super(player, options, ready);
23 |
24 | // If there are no sources when the player is initialized,
25 | // load the first supported playback technology.
26 |
27 | if (!options.playerOptions['sources'] || options.playerOptions['sources'].length === 0) {
28 | for (let i=0, j=options.playerOptions['techOrder']; i maxIndex) {
57 | throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is greater than or equal to the maximum bound (${maxIndex}).`);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/docs/guides/api.md:
--------------------------------------------------------------------------------
1 | API
2 | ===
3 |
4 | The Video.js API allows you to interact with the video through JavaScript, whether the browser is playing the video through HTML5 video, Flash, or any other supported playback technologies.
5 |
6 | Referencing the Player
7 | ----------------------
8 | To use the API functions, you need access to the player object. Luckily this is easy to get. You just need to make sure your video tag has an ID. The example embed code has an ID of "example\_video_1". If you have multiple videos on one page, make sure every video tag has a unique ID.
9 |
10 | ```js
11 | var myPlayer = videojs('example_video_1');
12 | ```
13 |
14 | (If the player hasn't been initialized yet via the data-setup attribute or another method, this will also initialize the player.)
15 |
16 | Wait Until the Player is Ready
17 | ------------------------------
18 | The time it takes Video.js to set up the video and API will vary depending on the playback technology being used (HTML5 will often be much faster to load than Flash). For that reason we want to use the player's 'ready' function to trigger any code that requires the player's API.
19 |
20 | ```js
21 | videojs("example_video_1").ready(function(){
22 | var myPlayer = this;
23 |
24 | // EXAMPLE: Start playing the video.
25 | myPlayer.play();
26 |
27 | });
28 | ```
29 |
30 | API Methods
31 | -----------
32 | Now that you have access to a ready player, you can control the video, get values, or respond to video events. The Video.js API function names follow the [HTML5 media API](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html). The main difference is that getter/setter functions are used for video properties.
33 |
34 | ```js
35 |
36 | // setting a property on a bare HTML5 video element
37 | myVideoElement.currentTime = "120";
38 |
39 | // setting a property on a Video.js player
40 | myPlayer.currentTime(120);
41 |
42 | ```
43 |
44 | The full list of player API methods and events can be found in the [player API docs](http://docs.videojs.com/docs/api/index.html).
45 |
--------------------------------------------------------------------------------
/lang/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Odtwarzaj",
3 | "Pause": "Pauza",
4 | "Current Time": "Aktualny czas",
5 | "Duration Time": "Czas trwania",
6 | "Remaining Time": "Pozostały czas",
7 | "Stream Type": "Typ strumienia",
8 | "LIVE": "NA ŻYWO",
9 | "Loaded": "Załadowany",
10 | "Progress": "Status",
11 | "Fullscreen": "Pełny ekran",
12 | "Non-Fullscreen": "Pełny ekran niedostępny",
13 | "Mute": "Wyłącz dźwięk",
14 | "Unmute": "Włącz dźwięk",
15 | "Playback Rate": "Szybkość odtwarzania",
16 | "Subtitles": "Napisy",
17 | "subtitles off": "Napisy wyłączone",
18 | "Captions": "Transkrypcja",
19 | "captions off": "Transkrypcja wyłączona",
20 | "Chapters": "Rozdziały",
21 | "You aborted the media playback": "Odtwarzanie zostało przerwane",
22 | "A network error caused the media download to fail part-way.": "Problemy z siecią spowodowały błąd przy pobieraniu materiału wideo.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Materiał wideo nie może być załadowany, ponieważ wystąpił problem z siecią lub format nie jest obsługiwany",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Odtwarzanie materiału wideo zostało przerwane z powodu uszkodzonego pliku wideo lub z powodu błędu funkcji, które nie są wspierane przez przeglądarkę.",
25 | "No compatible source was found for this media.": "Dla tego materiału wideo nie znaleziono kompatybilnego źródła.",
26 | "Play video": "Odtwarzaj wideo",
27 | "Close": "Zamknij",
28 | "Modal Window": "Okno Modala",
29 | "This is a modal window": "To jest okno modala",
30 | "This modal can be closed by pressing the Escape key or activating the close button.": "Ten modal możesz zamknąć naciskając przycisk Escape albo wybierając przycisk Zamknij.",
31 | ", opens captions settings dialog": ", otwiera okno dialogowe ustawień transkrypcji",
32 | ", opens subtitles settings dialog": ", otwiera okno dialogowe napisów",
33 | ", selected": ", zaznaczone"
34 | }
35 |
--------------------------------------------------------------------------------
/src/js/utils/merge-options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file merge-options.js
3 | */
4 | import merge from 'lodash-compat/object/merge';
5 |
6 | function isPlain(obj) {
7 | return !!obj
8 | && typeof obj === 'object'
9 | && obj.toString() === '[object Object]'
10 | && obj.constructor === Object;
11 | }
12 |
13 | /**
14 | * Merge customizer. video.js simply overwrites non-simple objects
15 | * (like arrays) instead of attempting to overlay them.
16 | * @see https://lodash.com/docs#merge
17 | */
18 | const customizer = function(destination, source) {
19 | // If we're not working with a plain object, copy the value as is
20 | // If source is an array, for instance, it will replace destination
21 | if (!isPlain(source)) {
22 | return source;
23 | }
24 |
25 | // If the new value is a plain object but the first object value is not
26 | // we need to create a new object for the first object to merge with.
27 | // This makes it consistent with how merge() works by default
28 | // and also protects from later changes the to first object affecting
29 | // the second object's values.
30 | if (!isPlain(destination)) {
31 | return mergeOptions(source);
32 | }
33 | };
34 |
35 | /**
36 | * Merge one or more options objects, recursively merging **only**
37 | * plain object properties. Previously `deepMerge`.
38 | *
39 | * @param {...Object} source One or more objects to merge
40 | * @returns {Object} a new object that is the union of all
41 | * provided objects
42 | * @function mergeOptions
43 | */
44 | export default function mergeOptions() {
45 | // contruct the call dynamically to handle the variable number of
46 | // objects to merge
47 | let args = Array.prototype.slice.call(arguments);
48 |
49 | // unshift an empty object into the front of the call as the target
50 | // of the merge
51 | args.unshift({});
52 |
53 | // customize conflict resolution to match our historical merge behavior
54 | args.push(customizer);
55 |
56 | merge.apply(null, args);
57 |
58 | // return the mutated result object
59 | return args[0];
60 | }
61 |
--------------------------------------------------------------------------------
/src/js/utils/log.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file log.js
3 | */
4 | import window from 'global/window';
5 |
6 | /**
7 | * Log plain debug messages
8 | */
9 | const log = function(){
10 | _logType(null, arguments);
11 | };
12 |
13 | /**
14 | * Keep a history of log messages
15 | * @type {Array}
16 | */
17 | log.history = [];
18 |
19 | /**
20 | * Log error messages
21 | */
22 | log.error = function(){
23 | _logType('error', arguments);
24 | };
25 |
26 | /**
27 | * Log warning messages
28 | */
29 | log.warn = function(){
30 | _logType('warn', arguments);
31 | };
32 |
33 | /**
34 | * Log messages to the console and history based on the type of message
35 | *
36 | * @param {String} type The type of message, or `null` for `log`
37 | * @param {Object} args The args to be passed to the log
38 | * @private
39 | * @method _logType
40 | */
41 | function _logType(type, args){
42 | // convert args to an array to get array functions
43 | let argsArray = Array.prototype.slice.call(args);
44 | // if there's no console then don't try to output messages
45 | // they will still be stored in log.history
46 | // Was setting these once outside of this function, but containing them
47 | // in the function makes it easier to test cases where console doesn't exist
48 | let noop = function(){};
49 |
50 | let console = window['console'] || {
51 | 'log': noop,
52 | 'warn': noop,
53 | 'error': noop
54 | };
55 |
56 | if (type) {
57 | // add the type to the front of the message
58 | argsArray.unshift(type.toUpperCase()+':');
59 | } else {
60 | // default to log with no prefix
61 | type = 'log';
62 | }
63 |
64 | // add to history
65 | log.history.push(argsArray);
66 |
67 | // add console prefix after adding to history
68 | argsArray.unshift('VIDEOJS:');
69 |
70 | // call appropriate log function
71 | if (console[type].apply) {
72 | console[type].apply(console, argsArray);
73 | } else {
74 | // ie8 doesn't allow error.apply, but it will just join() the array anyway
75 | console[type](argsArray.join(' '));
76 | }
77 | }
78 |
79 | export default log;
80 |
--------------------------------------------------------------------------------
/lang/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Play",
3 | "Pause": "Pause",
4 | "Current Time": "Current Time",
5 | "Duration Time": "Duration Time",
6 | "Remaining Time": "Remaining Time",
7 | "Stream Type": "Stream Type",
8 | "LIVE": "LIVE",
9 | "Loaded": "Loaded",
10 | "Progress": "Progress",
11 | "Fullscreen": "Fullscreen",
12 | "Non-Fullscreen": "Non-Fullscreen",
13 | "Mute": "Mute",
14 | "Unmute": "Unmute",
15 | "Playback Rate": "Playback Rate",
16 | "Subtitles": "Subtitles",
17 | "subtitles off": "subtitles off",
18 | "Captions": "Captions",
19 | "captions off": "captions off",
20 | "Chapters": "Chapters",
21 | "Descriptions": "Descriptions",
22 | "descriptions off": "descriptions off",
23 | "You aborted the media playback": "You aborted the media playback",
24 | "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
25 | "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.",
26 | "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.",
27 | "No compatible source was found for this media.": "No compatible source was found for this media.",
28 | "Play Video": "Play Video",
29 | "Close": "Close",
30 | "Modal Window": "Modal Window",
31 | "This is a modal window": "This is a modal window",
32 | "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
33 | ", opens captions settings dialog": ", opens captions settings dialog",
34 | ", opens subtitles settings dialog": ", opens subtitles settings dialog",
35 | ", opens descriptions settings dialog": ", opens descriptions settings dialog",
36 | ", selected": ", selected"
37 | }
38 |
--------------------------------------------------------------------------------
/lang/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "Play": "Wiedergabe",
3 | "Pause": "Pause",
4 | "Current Time": "Aktueller Zeitpunkt",
5 | "Duration Time": "Dauer",
6 | "Remaining Time": "Verbleibende Zeit",
7 | "Stream Type": "Streamtyp",
8 | "LIVE": "LIVE",
9 | "Loaded": "Geladen",
10 | "Progress": "Status",
11 | "Fullscreen": "Vollbild",
12 | "Non-Fullscreen": "Kein Vollbild",
13 | "Mute": "Ton aus",
14 | "Unmute": "Ton ein",
15 | "Playback Rate": "Wiedergabegeschwindigkeit",
16 | "Subtitles": "Untertitel",
17 | "subtitles off": "Untertitel aus",
18 | "Captions": "Untertitel",
19 | "captions off": "Untertitel aus",
20 | "Chapters": "Kapitel",
21 | "You aborted the media playback": "Sie haben die Videowiedergabe abgebrochen.",
22 | "A network error caused the media download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.",
23 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.",
24 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.",
25 | "No compatible source was found for this media.": "Für dieses Video wurde keine kompatible Quelle gefunden.",
26 | "Play Video": "Video abspielen",
27 | "Close": "Schließen",
28 | "Modal Window": "Modales Fenster",
29 | "This is a modal window": "Dies ist ein modales Fenster",
30 | "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.",
31 | ", opens captions settings dialog": ", öffnet Einstellungen für Untertitel",
32 | ", opens subtitles settings dialog": ", öffnet Einstellungen für Untertitel",
33 | ", selected": " (ausgewählt)"
34 | }
35 |
--------------------------------------------------------------------------------
/src/js/fullscreen-api.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file fullscreen-api.js
3 | */
4 | import document from 'global/document';
5 |
6 | /*
7 | * Store the browser-specific methods for the fullscreen API
8 | * @type {Object|undefined}
9 | * @private
10 | */
11 | let FullscreenApi = {};
12 |
13 | // browser API methods
14 | // map approach from Screenful.js - https://github.com/sindresorhus/screenfull.js
15 | const apiMap = [
16 | // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
17 | [
18 | 'requestFullscreen',
19 | 'exitFullscreen',
20 | 'fullscreenElement',
21 | 'fullscreenEnabled',
22 | 'fullscreenchange',
23 | 'fullscreenerror'
24 | ],
25 | // WebKit
26 | [
27 | 'webkitRequestFullscreen',
28 | 'webkitExitFullscreen',
29 | 'webkitFullscreenElement',
30 | 'webkitFullscreenEnabled',
31 | 'webkitfullscreenchange',
32 | 'webkitfullscreenerror'
33 | ],
34 | // Old WebKit (Safari 5.1)
35 | [
36 | 'webkitRequestFullScreen',
37 | 'webkitCancelFullScreen',
38 | 'webkitCurrentFullScreenElement',
39 | 'webkitCancelFullScreen',
40 | 'webkitfullscreenchange',
41 | 'webkitfullscreenerror'
42 | ],
43 | // Mozilla
44 | [
45 | 'mozRequestFullScreen',
46 | 'mozCancelFullScreen',
47 | 'mozFullScreenElement',
48 | 'mozFullScreenEnabled',
49 | 'mozfullscreenchange',
50 | 'mozfullscreenerror'
51 | ],
52 | // Microsoft
53 | [
54 | 'msRequestFullscreen',
55 | 'msExitFullscreen',
56 | 'msFullscreenElement',
57 | 'msFullscreenEnabled',
58 | 'MSFullscreenChange',
59 | 'MSFullscreenError'
60 | ]
61 | ];
62 |
63 | let specApi = apiMap[0];
64 | let browserApi;
65 |
66 | // determine the supported set of functions
67 | for (let i = 0; i < apiMap.length; i++) {
68 | // check for exitFullscreen function
69 | if (apiMap[i][1] in document) {
70 | browserApi = apiMap[i];
71 | break;
72 | }
73 | }
74 |
75 | // map the browser API names to the spec API names
76 | if (browserApi) {
77 | for (let i=0; iCurrent Time ' + '0:00',
39 | }, {
40 | // tell screen readers not to automatically read the time as it changes
41 | 'aria-live': 'off'
42 | });
43 |
44 | el.appendChild(this.contentEl_);
45 | return el;
46 | }
47 |
48 | /**
49 | * Update current time display
50 | *
51 | * @method updateContent
52 | */
53 | updateContent() {
54 | // Allows for smooth scrubbing, when player can't keep up.
55 | let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
56 | let localizedText = this.localize('Current Time');
57 | let formattedTime = formatTime(time, this.player_.duration());
58 | if (formattedTime !== this.formattedTime_) {
59 | this.formattedTime_ = formattedTime;
60 | this.contentEl_.innerHTML = `${localizedText} ${formattedTime}`;
61 | }
62 | }
63 |
64 | }
65 |
66 | Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
67 | export default CurrentTimeDisplay;
68 |
--------------------------------------------------------------------------------
/sandbox/index.html.example:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video.js Sandbox
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example (so don't edit or add those files). To get started make a copy of index.html.example and rename it to index.html.