Comparar commits
45 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| a75bee2682 | |||
| 0e1c424e48 | |||
| d1316a39ab | |||
| 210caea7f0 | |||
| 0da23c1d8d | |||
| 2627e86db5 | |||
| ea2e274a30 | |||
| f2fa8f8687 | |||
| cd800b02ea | |||
| 89d860fb84 | |||
| ed5ed0ded4 | |||
| 977eedda79 | |||
| 4e395b6b02 | |||
| a25c4c98f3 | |||
| b1e863677f | |||
| 98f241d614 | |||
| 5d754c911d | |||
| fa2f08ad66 | |||
| 0b32893612 | |||
| d0efd16b49 | |||
| c24a3a8173 | |||
| e6f5d6b585 | |||
| 42e33ca537 | |||
| 655e14a733 | |||
| 871dac9c3a | |||
| 6e25aef060 | |||
| 5f5357d535 | |||
| 86f23d39fd | |||
| 51f1863adc | |||
| ab88bcdde3 | |||
| 589cab7fa7 | |||
| c85b526a1c | |||
| 4598b38291 | |||
| 93e079718e | |||
| 34742e09a5 | |||
| d47a8ab2dc | |||
| 6f3826a7ef | |||
| 1834d39c2f | |||
| 40b6ad3050 | |||
| e58c2266de | |||
| 7ce59efa7d | |||
| aab9246253 | |||
| da09a44eda | |||
| 6beb0eb299 | |||
| a56781945a |
@@ -34,12 +34,14 @@
|
||||
"notEqual",
|
||||
"notStrictEqual",
|
||||
"ok",
|
||||
"throws",
|
||||
"QUnit",
|
||||
"raises",
|
||||
"start",
|
||||
"stop",
|
||||
"strictEqual",
|
||||
"test",
|
||||
"throws",
|
||||
"sinon"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,42 @@ _(none)_
|
||||
|
||||
--------------------
|
||||
|
||||
## 5.2.0 (2015-11-10)
|
||||
* @gkatsev made initListeners more general and added Tech.isTech. Fixes #2767 ([view](https://github.com/videojs/video.js/pull/2773))
|
||||
* @dmlap updated swf to 5.0.1 ([view](https://github.com/videojs/video.js/pull/2795))
|
||||
* @gkatsev added a tech registry. Fixes #2772 ([view](https://github.com/videojs/video.js/pull/2782))
|
||||
* @Lillemanden impoved logic for dividing RTMP paths ([view](https://github.com/videojs/video.js/pull/2787))
|
||||
* @bdeitte added a test for improved RTMP path dividing logic ([view](https://github.com/videojs/video.js/pull/2794))
|
||||
* @paladox updated grunt-cli dependency ([view](https://github.com/videojs/video.js/pull/2555))
|
||||
* @paladox updated grunt-contrib-jshint ([view](https://github.com/videojs/video.js/pull/2554))
|
||||
* @siebrand updated dutch translations ([view](https://github.com/videojs/video.js/pull/2556))
|
||||
* @misteroneill exposed DOM helpers ([view](https://github.com/videojs/video.js/pull/2754))
|
||||
* @incompl fixed broken link to reduced test cases article ([view](https://github.com/videojs/video.js/pull/2801))
|
||||
* @zjruan updated text track prototype loops to blacklist constructor for IE8 ([view](https://github.com/videojs/video.js/pull/2565))
|
||||
* @gkatsev fixed usage of textTracksToJson ([view](https://github.com/videojs/video.js/pull/2797))
|
||||
* @gkatsev updated contrib.json to use / as branch-name separator in feature-accept ([view](https://github.com/videojs/video.js/pull/2803))
|
||||
* @gkatsev updated MediaLoader to check for techs in their registry ([view](https://github.com/videojs/video.js/pull/2798))
|
||||
|
||||
## 5.1.0 (2015-11-02)
|
||||
* @typcn bumped grunt-sass to ^1.0.0 to support node 4.x ([view](https://github.com/videojs/video.js/pull/2645))
|
||||
* @gkatsev removed unhelpful isCrossOrigin test ([view](https://github.com/videojs/video.js/pull/2715))
|
||||
* @forbesjo updated karma to use all installed browsers for unit tests ([view](https://github.com/videojs/video.js/pull/2708))
|
||||
* @forbesjo removed android/ios tests to increase build stability ([view](https://github.com/videojs/video.js/pull/2739))
|
||||
* @nickygerritsen added canPlayType method to player ([view](https://github.com/videojs/video.js/pull/2709))
|
||||
* @gkatsev fixes track tests and ignored empty properties in tracks converter ([view](https://github.com/videojs/video.js/pull/2744))
|
||||
* @misteroneill added a modal dialog ([view](https://github.com/videojs/video.js/pull/2668))
|
||||
* @misteroneill removed z-index from big play button ([view](https://github.com/videojs/video.js/pull/2639))
|
||||
* @DaveVoyles updated URL to player API docs ([view](https://github.com/videojs/video.js/pull/2685))
|
||||
* @ ([view](https://github.com/videojs/video.js/pull/2691))
|
||||
* @kahwee Fixed sandbox plugin example to work in Video.js 5 ([view](https://github.com/videojs/video.js/pull/2691))
|
||||
* @Soviut Fixed argument names in some API docs ([view](https://github.com/videojs/video.js/pull/2714))
|
||||
* @forbesjo Added Microsoft Caption Maker link ([view](https://github.com/videojs/video.js/pull/2618))
|
||||
* @misteroneill updated modal dialog CSS ([view](https://github.com/videojs/video.js/pull/2756))
|
||||
* @misteroneill Add browserify
|
||||
* @brkattk updated emulateTextTrack to exit early if no textTracks ([view](https://github.com/videojs/video.js/pull/2426))
|
||||
* @chemoish Fix captions sticking to bottom for webkit browsers. Fixes #2193 ([view](https://github.com/videojs/video.js/pull/2702))
|
||||
* @imbcmdth Deferred the implementation of select functions in the tech to source handlers if they provide them ([view](https://github.com/videojs/video.js/pull/2760))
|
||||
|
||||
## 5.0.2 (2015-10-23)
|
||||
* @imbcmdth fixed an issue with emulateTextTracks being called before the tech dom was ready ([view](https://github.com/videojs/video.js/pull/2692))
|
||||
* @gkatsev bumped obj.assign to fix uncaught SecurityError in iframes. Fixes #2703 ([view](https://github.com/videojs/video.js/pull/2721))
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ Guidelines for bug reports:
|
||||
|
||||
2. Check if the issue has already been fixed — try to reproduce it using the latest `master` branch in the repository.
|
||||
|
||||
3. Isolate the problem — **create a [reduced test case](http://css-tricks.com/6263-reduced-test-cases/)** with a live example. You can possibly use [this JSBin example](http://jsbin.com/axedog/7/edit) as a starting point.
|
||||
3. Isolate the problem — **create a [reduced test case](https://css-tricks.com/reduced-test-cases/)** with a live example. You can possibly use [this JSBin example](http://jsbin.com/axedog/7/edit) as a starting point.
|
||||
|
||||
A good bug report should be as detailed as possible, so that others won't have to follow up for the essential details.
|
||||
|
||||
|
||||
+5
-10
@@ -1,20 +1,16 @@
|
||||
## Please Note!
|
||||
The master branch is now the development branch for 5.0 and should be considered unstable until the first 5.0 release. If you're looking for the most recent stable release, please refer to the [stable branch](https://github.com/videojs/video.js/tree/stable).
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
# [Video.js - HTML5 Video Player](http://videojs.com) [](https://travis-ci.org/videojs/video.js)
|
||||
|
||||
> Video.js is a web video player built from the ground up for an HTML5 world. It supports HTML5 and Flash video, as well as YouTube and Vimeo (through [plugins](https://github.com/videojs/video.js/wiki/Plugins)). It supports video playback on desktops and mobile devices. This project was started mid 2010, and the player is now used on over ~~50,000~~ 100,000 websites.
|
||||
> Video.js is a web video player built from the ground up for an HTML5 world. It supports HTML5 and Flash video, as well as YouTube and Vimeo (through [plugins](https://github.com/videojs/video.js/wiki/Plugins)). It supports video playback on desktops and mobile devices. This project was started mid 2010, and the player is now used on over ~~50,000~~ ~~100,000~~ 200,000 websites.
|
||||
|
||||
## Quick start
|
||||
Thanks to the awesome folks over at [Fastly](http://www.fastly.com/), there's a free, CDN hosted version of Video.js that anyone can use. Simply add these includes to your document's
|
||||
`<head>`:
|
||||
|
||||
```html
|
||||
<link href="http://vjs.zencdn.net/4.12/video-js.css" rel="stylesheet">
|
||||
<script src="http://vjs.zencdn.net/4.12/video.js"></script>
|
||||
<link href="http://vjs.zencdn.net/5.0/video-js.min.css" rel="stylesheet">
|
||||
<script src="http://vjs.zencdn.net/5.0/video.min.js"></script>
|
||||
```
|
||||
|
||||
Then, whenever you want to use Video.js you can simply use the `<video>` element as your normally would, but with an additional `data-setup` attribute containing any Video.js options. These options
|
||||
@@ -48,8 +44,7 @@ var player = videojs('really-cool-video', { /* Options */ }, function() {
|
||||
});
|
||||
```
|
||||
|
||||
If you're ready to dive in, the [documentation](docs/index.md) is the first place to go for more information. Generally the
|
||||
[player API docs](docs/api/vjs.Player.md) are the most pertinent.
|
||||
If you're ready to dive in, the [documentation](http://docs.videojs.com) is the first place to go for more information.
|
||||
|
||||
## Contributing
|
||||
Video.js is a free and open source library, and we appreciate any help you're willing to give. Check out the [contributing guide](CONTRIBUTING.md).
|
||||
|
||||
+100
-39
@@ -13,9 +13,71 @@ module.exports = function(grunt) {
|
||||
patch: verParts[2]
|
||||
};
|
||||
|
||||
const browserifyGruntDefaults = {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: 'videojs'
|
||||
},
|
||||
plugin: [
|
||||
['browserify-derequire']
|
||||
],
|
||||
transform: [
|
||||
require('babelify').configure({
|
||||
sourceMapRelative: './',
|
||||
loose: ['all']
|
||||
}),
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION__',
|
||||
version: pkg.version
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION_NO_PATCH__',
|
||||
version: version.majorMinor
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__SWF_VERSION__',
|
||||
version: pkg.dependencies['videojs-swf']
|
||||
}]
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Customizes _.merge behavior in `browserifyGruntOptions` to concatenate
|
||||
* arrays. This can be overridden on a per-call basis to
|
||||
*
|
||||
* @see https://lodash.com/docs#merge
|
||||
* @function browserifyGruntCustomizer
|
||||
* @private
|
||||
* @param {Mixed} objectValue
|
||||
* @param {Mixed} sourceValue
|
||||
* @return {Object}
|
||||
*/
|
||||
function browserifyGruntCustomizer(objectValue, sourceValue) {
|
||||
if (Array.isArray(objectValue)) {
|
||||
return objectValue.concat(sourceValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique object of Browserify Grunt task options.
|
||||
*
|
||||
* @function browserifyGruntOptions
|
||||
* @private
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [customizer=browserifyGruntCustomizer]
|
||||
* If the default array-concatenation behavior is not desireable,
|
||||
* pass _.noop or a unique customizer (https://lodash.com/docs#merge).
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
function browserifyGruntOptions(options = null, customizer = browserifyGruntCustomizer) {
|
||||
return _.merge({}, browserifyGruntDefaults, options, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates processor functions for license banners.
|
||||
*
|
||||
* @function createLicenseProcessor
|
||||
* @private
|
||||
* @param {Object} data Custom data overriding `bannerCommonData`. Will
|
||||
* not be mutated.
|
||||
@@ -23,11 +85,13 @@ module.exports = function(grunt) {
|
||||
* using an object constructed from `bannerCommonData`
|
||||
* and the `data` argument.
|
||||
*/
|
||||
let createLicenseProcessor = (data) => function () {
|
||||
return grunt.template.process(license, {
|
||||
data: _.merge({}, bannerCommonData, data)
|
||||
});
|
||||
};
|
||||
function createLicenseProcessor(data) {
|
||||
return () => {
|
||||
return grunt.template.process(license, {
|
||||
data: _.merge({}, bannerCommonData, data)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
version.majorMinor = `${version.major}.${version.minor}`;
|
||||
grunt.vjsVersion = version;
|
||||
@@ -131,7 +195,12 @@ module.exports = function(grunt) {
|
||||
configFile: 'test/karma.conf.js'
|
||||
},
|
||||
|
||||
defaults: {},
|
||||
defaults: {
|
||||
detectBrowsers: {
|
||||
enabled: !process.env.TRAVIS,
|
||||
usePhantomJS: false
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
autoWatch: true,
|
||||
@@ -154,9 +223,7 @@ module.exports = function(grunt) {
|
||||
ie11_bs: { browsers: ['ie11_bs'] },
|
||||
ie10_bs: { browsers: ['ie10_bs'] },
|
||||
ie9_bs: { browsers: ['ie9_bs'] },
|
||||
ie8_bs: { browsers: ['ie8_bs'] },
|
||||
android_bs: { browsers: ['android_bs'] },
|
||||
ios_bs: { browsers: ['ios_bs'] }
|
||||
ie8_bs: { browsers: ['ie8_bs'] }
|
||||
},
|
||||
vjsdocs: {
|
||||
all: {
|
||||
@@ -239,38 +306,25 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
browserify: {
|
||||
options: {
|
||||
browserifyOptions: {
|
||||
debug: true,
|
||||
standalone: 'videojs'
|
||||
},
|
||||
plugin: [
|
||||
['browserify-derequire']
|
||||
],
|
||||
transform: [
|
||||
require('babelify').configure({
|
||||
sourceMapRelative: './',
|
||||
loose: ['all']
|
||||
}),
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION__',
|
||||
version: pkg.version
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__VERSION_NO_PATCH__',
|
||||
version: version.majorMinor
|
||||
}],
|
||||
['browserify-versionify', {
|
||||
placeholder: '__SWF_VERSION__',
|
||||
version: pkg.dependencies['videojs-swf']
|
||||
}]
|
||||
]
|
||||
},
|
||||
options: browserifyGruntOptions(),
|
||||
build: {
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
}
|
||||
},
|
||||
dist: {
|
||||
options: browserifyGruntOptions({
|
||||
transform: [
|
||||
['browserify-versionify', {
|
||||
placeholder: '../node_modules/vtt.js/dist/vtt.js',
|
||||
version: 'https://cdn.rawgit.com/gkatsev/vtt.js/vjs-v0.12.1/dist/vtt.min.js'
|
||||
}],
|
||||
]
|
||||
}),
|
||||
files: {
|
||||
'build/temp/video.js': ['src/js/video.js']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
watch: true,
|
||||
@@ -377,7 +431,7 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('videojs-doc-generator');
|
||||
grunt.loadNpmTasks('chg');
|
||||
|
||||
grunt.registerTask('build', [
|
||||
const buildDependents = [
|
||||
'clean:build',
|
||||
|
||||
'jshint',
|
||||
@@ -397,11 +451,18 @@ module.exports = function(grunt) {
|
||||
'copy:swf',
|
||||
'copy:ie8',
|
||||
'vjslanguages'
|
||||
]);
|
||||
];
|
||||
|
||||
grunt.registerTask('build', buildDependents);
|
||||
|
||||
grunt.registerTask(
|
||||
'build:dist',
|
||||
buildDependents.map(task => task === 'browserify:build' ? 'browserify:dist' : task)
|
||||
);
|
||||
|
||||
grunt.registerTask('dist', [
|
||||
'clean:dist',
|
||||
'build',
|
||||
'build:dist',
|
||||
'copy:dist',
|
||||
'copy:examples',
|
||||
'zip:dist'
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.0.2",
|
||||
"version": "5.2.0",
|
||||
"keywords": [
|
||||
"videojs",
|
||||
"html5",
|
||||
|
||||
+4
-4
@@ -285,7 +285,7 @@
|
||||
[ "grunt chg-release:{{version}}", "Update the changelog with the new release" ],
|
||||
[ "git commit -am 'v{{version}}'", "Add and commit the package changes" ],
|
||||
[ "git push upstream stable", "Push the release branch changes to the repo" ],
|
||||
[ "git checkout -b temp-release-branch master","Create a temporary branch for the dist" ],
|
||||
[ "git checkout -b temp-release-branch stable","Create a temporary branch for the dist" ],
|
||||
[ "grunt dist", "Build the dist" ],
|
||||
[ "git add dist --force", "Add the (otherwise ignored) release files" ],
|
||||
[ "git commit -m 'v{{version}} dist'", "Commit the dist changes" ],
|
||||
@@ -398,7 +398,7 @@
|
||||
{ "prompt": "text", "id": "prNum", "desc": "What is the the pull request number?" },
|
||||
{ "get": "{{meta.urls.repo_api}}/pulls/{{prNum}}", "desc": "Get the PR information", "id": "pr" },
|
||||
{ "get": "{{meta.urls.repo_api}}/pulls/{{prNum}}/commits", "desc": "Get the PR commits to access author info", "id": "prCommits" },
|
||||
[ "git checkout -b {{pr.user.login}}-{{pr.head.ref}} {{pr.base.ref}}", "Create a new branch for merging the changes" ],
|
||||
[ "git checkout -b {{pr.user.login}}/{{pr.head.ref}} {{pr.base.ref}}", "Create a new branch for merging the changes" ],
|
||||
[ "git fetch {{pr.head.repo.ssh_url}} {{pr.head.ref}}", "Fetch the changes" ],
|
||||
[ "git merge --no-commit --squash FETCH_HEAD", "Merge the changes in without committing so they can be squashed" ],
|
||||
[ "grunt test", "Run tests to make sure they still pass" ],
|
||||
@@ -408,10 +408,10 @@
|
||||
[ "git commit -a --author='{{prCommits.[0].commit.author.name}} <{{prCommits.[0].commit.author.email}}>' -m '{{line}}. closes #{{prNum}}'", "Commit the changes" ],
|
||||
{ "prompt": "confirm", "desc": "Does everything look ok?" },
|
||||
[ "git checkout {{pr.base.ref}}", "Check out the base branch" ],
|
||||
[ "git merge {{pr.user.login}}-{{pr.head.ref}}", "Merge the changes" ],
|
||||
[ "git merge {{pr.user.login}}/{{pr.head.ref}}", "Merge the changes" ],
|
||||
[ "git push origin {{pr.base.ref}}", "Push the changes to your remote copy of the project" ],
|
||||
[ "git push upstream {{pr.base.ref}}", "Push the changes to the main project" ],
|
||||
[ "git branch -D {{pr.user.login}}-{{pr.head.ref}}", "Delete the local branch used for merging" ]
|
||||
[ "git branch -D {{pr.user.login}}/{{pr.head.ref}}", "Delete the local branch used for merging" ]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
externo
+19146
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+16
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+41
@@ -0,0 +1,41 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.700 --> 00:04.110
|
||||
Captions describe all relevant audio for the hearing impaired.
|
||||
[ Heroic music playing for a seagull ]
|
||||
|
||||
00:04.500 --> 00:05.000
|
||||
[ Splash!!! ]
|
||||
|
||||
00:05.100 --> 00:06.000
|
||||
[ Sploosh!!! ]
|
||||
|
||||
00:08.000 --> 00:09.225
|
||||
[ Splash...splash...splash splash splash ]
|
||||
|
||||
00:10.525 --> 00:11.255
|
||||
[ Splash, Sploosh again ]
|
||||
|
||||
00:13.500 --> 00:14.984
|
||||
Dolphin: eeeEEEEEeeee!
|
||||
|
||||
00:14.984 --> 00:16.984
|
||||
Dolphin: Squawk! eeeEEE?
|
||||
|
||||
00:25.000 --> 00:28.284
|
||||
[ A whole ton of splashes ]
|
||||
|
||||
00:29.500 --> 00:31.000
|
||||
Mine. Mine. Mine.
|
||||
|
||||
00:34.300 --> 00:36.000
|
||||
Shark: Chomp
|
||||
|
||||
00:36.800 --> 00:37.900
|
||||
Shark: CHOMP!!!
|
||||
|
||||
00:37.861 --> 00:41.193
|
||||
EEEEEEOOOOOOOOOOWHALENOISE
|
||||
|
||||
00:42.593 --> 00:45.611
|
||||
[ BIG SPLASH ]
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video.js | HTML5 Video Player</title>
|
||||
|
||||
<!-- Chang URLs to wherever Video.js files will be hosted -->
|
||||
<!-- Default URLs assume the examples folder is included alongside video.js -->
|
||||
<link href="../../video-js.min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Include ES5 shim, sham and html5 shiv for ie8 support -->
|
||||
<!-- Exclude this if you don't need ie8 support -->
|
||||
<script src="../../ie8/videojs-ie8.min.js"></script>
|
||||
|
||||
<!-- video.js must be in the <head> for older IEs to work. -->
|
||||
<script src="../../video.min.js"></script>
|
||||
|
||||
<!-- Unless using the CDN hosted version, update the URL to the Flash SWF -->
|
||||
<script>
|
||||
videojs.options.flash.swf = "../../video-js.swf";
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264"
|
||||
poster="http://video-js.zencoder.com/oceans-clip.png"
|
||||
data-setup="{}">
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
<track kind="captions" src="../shared/example-captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
|
||||
<track kind="subtitles" src="../shared/example-captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
|
||||
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
|
||||
</video>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
externo
BIN
Arquivo binário não exibido.
externo
+99
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<font id="VideoJS" horiz-adv-x="1792">
|
||||
<font-face font-family="VideoJS"
|
||||
units-per-em="1792" ascent="1792"
|
||||
descent="0" />
|
||||
<missing-glyph horiz-adv-x="0" />
|
||||
<glyph glyph-name="play"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M597.3333333333334 1418.6666666666665V373.3333333333333L1418.6666666666667 896z" />
|
||||
<glyph glyph-name="play-circle"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M746.6666666666667 560L1194.6666666666667 896L746.6666666666667 1232V560zM896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665z" />
|
||||
<glyph glyph-name="pause"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M448 373.3333333333333H746.6666666666667V1418.6666666666665H448V373.3333333333333zM1045.3333333333335 1418.6666666666665V373.3333333333333H1344V1418.6666666666665H1045.3333333333335z" />
|
||||
<glyph glyph-name="volume-mute"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1232 896C1232 1027.7866666666666 1155.8400000000001 1141.6533333333332 1045.3333333333335 1196.5333333333333V1031.52L1228.6399999999999 848.2133333333334C1230.88 863.8933333333334 1232 879.9466666666667 1232 896.0000000000001zM1418.6666666666667 896C1418.6666666666667 825.8133333333333 1403.3600000000001 759.7333333333333 1378.3466666666668 698.8799999999999L1491.466666666667 585.7599999999998C1540 678.72 1568 783.9999999999999 1568 896C1568 1215.5733333333333 1344.3733333333334 1482.88 1045.3333333333335 1550.8266666666666V1396.6399999999999C1261.1200000000001 1332.4266666666667 1418.6666666666667 1132.6933333333332 1418.6666666666667 896zM319.2000000000001 1568L224 1472.8L576.8 1120H224V672H522.6666666666667L896 298.6666666666665V800.8L1213.7066666666667 483.0933333333332C1163.68 444.6399999999999 1107.3066666666666 413.6533333333332 1045.3333333333335 394.9866666666665V240.7999999999998C1148 264.32 1241.7066666666667 311.3599999999997 1320.48 375.9466666666663L1472.8000000000002 224L1568 319.1999999999998L896 991.1999999999998L319.2000000000001 1568zM896 1493.3333333333333L739.9466666666667 1337.28L896 1181.2266666666667V1493.3333333333333z" />
|
||||
<glyph glyph-name="volume-low"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M522.6666666666667 1120V672H821.3333333333334L1194.6666666666667 298.6666666666665V1493.3333333333333L821.3333333333334 1120H522.6666666666667z" />
|
||||
<glyph glyph-name="volume-mid"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1381.3333333333335 896C1381.3333333333335 1027.7866666666666 1305.1733333333334 1141.6533333333332 1194.6666666666667 1196.5333333333333V595.0933333333332C1305.1733333333334 650.3466666666666 1381.3333333333335 764.2133333333331 1381.3333333333335 896zM373.3333333333334 1120V672H672L1045.3333333333335 298.6666666666665V1493.3333333333333L672 1120H373.3333333333334z" />
|
||||
<glyph glyph-name="volume-high"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M224 1120V672H522.6666666666667L896 298.6666666666665V1493.3333333333333L522.6666666666667 1120H224zM1232 896C1232 1027.7866666666666 1155.8400000000001 1141.6533333333332 1045.3333333333335 1196.5333333333333V595.0933333333332C1155.8400000000001 650.3466666666666 1232 764.2133333333331 1232 896zM1045.3333333333335 1550.8266666666666V1396.6399999999999C1261.1200000000001 1332.4266666666667 1418.6666666666667 1132.6933333333332 1418.6666666666667 896S1261.1200000000001 459.5733333333333 1045.3333333333335 395.3600000000002V241.1733333333332C1344.3733333333334 309.1199999999999 1568 576.0533333333333 1568 896S1344.3733333333334 1482.88 1045.3333333333335 1550.8266666666666z" />
|
||||
<glyph glyph-name="fullscreen-enter"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M522.6666666666667 746.6666666666665H373.3333333333334V373.3333333333333H746.6666666666667V522.6666666666665H522.6666666666667V746.6666666666665zM373.3333333333334 1045.3333333333333H522.6666666666667V1269.3333333333333H746.6666666666667V1418.6666666666665H373.3333333333334V1045.3333333333333zM1269.3333333333335 522.6666666666665H1045.3333333333335V373.3333333333333H1418.6666666666667V746.6666666666665H1269.3333333333335V522.6666666666665zM1045.3333333333335 1418.6666666666665V1269.3333333333333H1269.3333333333335V1045.3333333333333H1418.6666666666667V1418.6666666666665H1045.3333333333335z" />
|
||||
<glyph glyph-name="fullscreen-exit"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M373.3333333333334 597.3333333333333H597.3333333333334V373.3333333333333H746.6666666666667V746.6666666666665H373.3333333333334V597.3333333333333zM597.3333333333334 1194.6666666666665H373.3333333333334V1045.3333333333333H746.6666666666667V1418.6666666666665H597.3333333333334V1194.6666666666665zM1045.3333333333335 373.3333333333333H1194.6666666666667V597.3333333333333H1418.6666666666667V746.6666666666665H1045.3333333333335V373.3333333333333zM1194.6666666666667 1194.6666666666665V1418.6666666666665H1045.3333333333335V1045.3333333333333H1418.6666666666667V1194.6666666666665H1194.6666666666667z" />
|
||||
<glyph glyph-name="square"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1344 1493.3333333333333H448C365.4933333333334 1493.3333333333333 298.6666666666667 1426.5066666666667 298.6666666666667 1344V448C298.6666666666667 365.4933333333331 365.4933333333334 298.6666666666665 448 298.6666666666665H1344C1426.506666666667 298.6666666666665 1493.3333333333335 365.4933333333331 1493.3333333333335 448V1344C1493.3333333333335 1426.5066666666667 1426.506666666667 1493.3333333333333 1344 1493.3333333333333zM1344 448H448V1344H1344V448z" />
|
||||
<glyph glyph-name="spinner"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M701.8666666666668 1008L1057.6533333333334 1624.3733333333334C1005.7600000000002 1635.9466666666667 951.6266666666666 1642.6666666666667 896 1642.6666666666667C716.8000000000001 1642.6666666666667 552.9066666666668 1579.5733333333333 424.1066666666667 1474.2933333333333L697.76 1000.5333333333334L701.8666666666666 1008zM1608.32 1120C1539.6266666666666 1338.4 1373.1200000000001 1512.7466666666667 1160.6933333333332 1593.3866666666668L887.4133333333334 1120H1608.32zM1627.7333333333336 1045.3333333333333H1068.48L1090.1333333333334 1008L1445.92 392C1567.6266666666668 524.9066666666668 1642.6666666666667 701.4933333333333 1642.6666666666667 896C1642.6666666666667 947.1466666666666 1637.44 997.1733333333332 1627.7333333333336 1045.3333333333333zM637.2800000000001 896L346.08 1400C224.3733333333333 1267.0933333333332 149.3333333333334 1090.5066666666667 149.3333333333334 896C149.3333333333334 844.8533333333332 154.56 794.8266666666666 164.2666666666667 746.6666666666665H723.5200000000001L637.2800000000002 896zM183.68 672C252.3733333333334 453.5999999999999 418.88 279.2533333333334 631.3066666666667 198.6133333333332L904.5866666666668 672H183.68zM1025.1733333333334 672L733.9733333333334 167.6266666666666C786.24 156.0533333333333 840.3733333333334 149.3333333333333 896 149.3333333333333C1075.2 149.3333333333333 1239.0933333333332 212.4266666666665 1367.8933333333334 317.7066666666665L1094.24 791.4666666666666L1025.1733333333334 672z" />
|
||||
<glyph glyph-name="subtitles"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1493.3333333333335 1493.3333333333333H298.6666666666667C216.16 1493.3333333333333 149.3333333333334 1426.5066666666667 149.3333333333334 1344V448C149.3333333333334 365.4933333333331 216.16 298.6666666666665 298.6666666666667 298.6666666666665H1493.3333333333335C1575.8400000000001 298.6666666666665 1642.6666666666667 365.4933333333331 1642.6666666666667 448V1344C1642.6666666666667 1426.5066666666667 1575.8400000000001 1493.3333333333333 1493.3333333333335 1493.3333333333333zM298.6666666666667 896H597.3333333333334V746.6666666666665H298.6666666666667V896zM1045.3333333333335 448H298.6666666666667V597.3333333333333H1045.3333333333335V448zM1493.3333333333335 448H1194.6666666666667V597.3333333333333H1493.3333333333335V448zM1493.3333333333335 746.6666666666665H746.6666666666667V896H1493.3333333333335V746.6666666666665z" />
|
||||
<glyph glyph-name="captions"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1418.6666666666667 1493.3333333333333H373.3333333333334C290.8266666666667 1493.3333333333333 224 1426.5066666666667 224 1344V448C224 365.4933333333331 290.8266666666667 298.6666666666665 373.3333333333334 298.6666666666665H1418.6666666666667C1501.1733333333334 298.6666666666665 1568 365.4933333333331 1568 448V1344C1568 1426.5066666666667 1501.1733333333334 1493.3333333333333 1418.6666666666667 1493.3333333333333zM821.3333333333334 970.6666666666666H709.3333333333334V1008H560V783.9999999999999H709.3333333333334V821.3333333333333H821.3333333333334V746.6666666666665C821.3333333333334 705.5999999999999 788.1066666666667 672 746.6666666666667 672H522.6666666666667C481.2266666666667 672 448 705.5999999999999 448 746.6666666666665V1045.3333333333333C448 1086.4 481.2266666666667 1120 522.6666666666667 1120H746.6666666666667C788.1066666666667 1120 821.3333333333334 1086.4 821.3333333333334 1045.3333333333333V970.6666666666666zM1344 970.6666666666666H1232V1008H1082.6666666666667V783.9999999999999H1232V821.3333333333333H1344V746.6666666666665C1344 705.5999999999999 1310.7733333333333 672 1269.3333333333335 672H1045.3333333333335C1003.8933333333334 672 970.6666666666669 705.5999999999999 970.6666666666669 746.6666666666665V1045.3333333333333C970.6666666666669 1086.4 1003.8933333333334 1120 1045.3333333333335 1120H1269.3333333333335C1310.7733333333333 1120 1344 1086.4 1344 1045.3333333333333V970.6666666666666z" />
|
||||
<glyph glyph-name="chapters"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M224 821.3333333333333H373.3333333333334V970.6666666666666H224V821.3333333333333zM224 522.6666666666665H373.3333333333334V672H224V522.6666666666665zM224 1120H373.3333333333334V1269.3333333333333H224V1120zM522.6666666666667 821.3333333333333H1568V970.6666666666666H522.6666666666667V821.3333333333333zM522.6666666666667 522.6666666666665H1568V672H522.6666666666667V522.6666666666665zM522.6666666666667 1269.3333333333333V1120H1568V1269.3333333333333H522.6666666666667z" />
|
||||
<glyph glyph-name="share"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1344 590.9866666666665C1287.2533333333333 590.9866666666665 1236.1066666666668 568.9599999999998 1197.2800000000002 533.4933333333331L665.2800000000001 843.7333333333333C669.3866666666667 860.5333333333333 672 878.08 672 896S669.3866666666667 931.4666666666666 665.2800000000001 948.2666666666667L1191.68 1255.52C1231.6266666666668 1218.1866666666665 1285.0133333333335 1195.04 1344 1195.04C1467.5733333333335 1195.04 1568 1295.4666666666665 1568 1419.04S1467.5733333333335 1643.04 1344 1643.04S1120 1542.6133333333332 1120 1419.04C1120 1401.12 1122.6133333333335 1383.5733333333333 1126.72 1366.773333333333L600.3199999999999 1059.5199999999998C560.3733333333333 1096.853333333333 506.9866666666666 1119.9999999999998 448 1119.9999999999998C324.4266666666666 1119.9999999999998 224 1019.5733333333332 224 895.9999999999998S324.4266666666666 671.9999999999998 448 671.9999999999998C506.9866666666666 671.9999999999998 560.3733333333333 695.1466666666665 600.3199999999999 732.4799999999998L1132.32 422.2399999999998C1128.5866666666666 406.5599999999997 1126.3466666666666 390.133333333333 1126.3466666666666 373.3333333333331C1126.3466666666666 253.1199999999997 1223.7866666666669 155.6799999999996 1344 155.6799999999996S1561.6533333333334 253.1199999999997 1561.6533333333334 373.3333333333331S1464.2133333333334 590.9866666666662 1344 590.9866666666662z" />
|
||||
<glyph glyph-name="cog"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1450.7733333333333 823.1999999999999C1453.76 847.0933333333334 1456 871.3599999999999 1456 896S1453.76 944.9066666666666 1450.7733333333333 968.8L1608.6933333333336 1092.3733333333332C1622.8800000000003 1103.5733333333333 1626.986666666667 1123.7333333333331 1617.6533333333336 1140.1599999999999L1468.3200000000004 1398.8799999999999C1458.986666666667 1414.9333333333334 1439.5733333333335 1421.6533333333332 1422.7733333333338 1414.9333333333334L1236.8533333333337 1339.8933333333332C1198.4000000000003 1369.3866666666668 1156.2133333333338 1394.3999999999999 1110.6666666666672 1413.44L1082.6666666666667 1611.3066666666666C1079.3066666666668 1628.8533333333332 1064 1642.6666666666667 1045.3333333333335 1642.6666666666667H746.6666666666667C728 1642.6666666666667 712.6933333333334 1628.8533333333332 709.7066666666668 1611.3066666666666L681.7066666666668 1413.44C636.1600000000002 1394.4 593.9733333333335 1369.76 555.5200000000001 1339.8933333333332L369.6 1414.9333333333334C352.8000000000001 1421.28 333.3866666666667 1414.9333333333334 324.0533333333334 1398.88L174.72 1140.1599999999999C165.3866666666667 1124.1066666666666 169.4933333333334 1103.9466666666667 183.68 1092.3733333333332L341.2266666666667 968.8C338.2400000000001 944.9066666666666 336 920.64 336 896S338.2400000000001 847.0933333333334 341.2266666666667 823.1999999999999L183.68 699.6266666666668C169.4933333333334 688.4266666666667 165.3866666666667 668.2666666666667 174.72 651.8399999999999L324.0533333333334 393.1199999999999C333.3866666666667 377.0666666666666 352.8 370.3466666666666 369.6 377.0666666666666L555.5200000000001 452.1066666666666C593.9733333333334 422.6133333333333 636.16 397.5999999999999 681.7066666666668 378.56L709.7066666666668 180.6933333333334C712.6933333333334 163.1466666666668 728 149.3333333333333 746.6666666666667 149.3333333333333H1045.3333333333335C1064 149.3333333333333 1079.3066666666668 163.1466666666665 1082.2933333333333 180.6933333333334L1110.2933333333333 378.56C1155.84 397.5999999999999 1198.0266666666666 422.24 1236.48 452.1066666666666L1422.3999999999999 377.0666666666666C1439.2 370.7199999999998 1458.6133333333332 377.0666666666666 1467.9466666666665 393.1199999999999L1617.2799999999997 651.8399999999999C1626.6133333333332 667.8933333333332 1622.5066666666664 688.0533333333333 1608.3199999999997 699.6266666666668L1450.773333333333 823.1999999999999zM896 634.6666666666665C751.52 634.6666666666665 634.6666666666667 751.52 634.6666666666667 896S751.52 1157.3333333333333 896 1157.3333333333333S1157.3333333333335 1040.48 1157.3333333333335 896S1040.48 634.6666666666665 896 634.6666666666665z" />
|
||||
<glyph glyph-name="circle"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M149.3333333333334 896C149.3333333333334 483.6273867930074 483.6273867930075 149.3333333333333 896 149.3333333333333C1308.3726132069926 149.3333333333333 1642.6666666666667 483.6273867930074 1642.6666666666667 896C1642.6666666666667 1308.3726132069926 1308.3726132069926 1642.6666666666667 896 1642.6666666666667C483.6273867930075 1642.6666666666667 149.3333333333334 1308.3726132069926 149.3333333333334 896z" />
|
||||
<glyph glyph-name="circle-outline"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665z" />
|
||||
<glyph glyph-name="circle-inner-circle"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M896 1642.6666666666667C484.2133333333334 1642.6666666666667 149.3333333333334 1307.7866666666666 149.3333333333334 896S484.2133333333334 149.3333333333333 896 149.3333333333333S1642.6666666666667 484.2133333333331 1642.6666666666667 896S1307.7866666666669 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665zM1120 896C1120 772.4266666666666 1019.5733333333334 672 896 672S672 772.4266666666666 672 896S772.4266666666667 1120 896 1120S1120 1019.5733333333332 1120 896z" />
|
||||
<glyph glyph-name="hd"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1418.6666666666667 1568H373.3333333333334C290.4533333333333 1568 224 1500.8 224 1418.6666666666665V373.3333333333333C224 291.1999999999998 290.4533333333334 224 373.3333333333334 224H1418.6666666666667C1500.8000000000002 224 1568 291.1999999999998 1568 373.3333333333333V1418.6666666666665C1568 1500.8 1500.8000000000002 1568 1418.6666666666667 1568zM821.3333333333334 672H709.3333333333334V821.3333333333333H560V672H448V1120H560V933.3333333333331H709.3333333333334V1120H821.3333333333334V672zM970.6666666666669 1120H1269.3333333333335C1310.4 1120 1344 1086.4 1344 1045.3333333333333V746.6666666666665C1344 705.5999999999999 1310.4 672 1269.3333333333335 672H970.6666666666669V1120zM1082.6666666666667 783.9999999999999H1232V1008H1082.6666666666667V783.9999999999999z" />
|
||||
<glyph glyph-name="cancel"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM1269.3333333333335 628.3199999999999L1163.68 522.6666666666665L896 790.3466666666667L628.3199999999999 522.6666666666665L522.6666666666667 628.3199999999999L790.3466666666668 896L522.6666666666667 1163.68L628.3199999999999 1269.3333333333333L896 1001.6533333333332L1163.68 1269.3333333333333L1269.3333333333335 1163.68L1001.6533333333334 896L1269.3333333333335 628.3199999999999z" />
|
||||
<glyph glyph-name="replay"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M896 1418.6666666666665V1717.3333333333333L522.6666666666667 1344L896 970.6666666666666V1269.3333333333333C1143.52 1269.3333333333333 1344 1068.8533333333332 1344 821.3333333333333S1143.52 373.3333333333333 896 373.3333333333333S448 573.813333333333 448 821.3333333333333H298.6666666666667C298.6666666666667 491.3066666666664 565.9733333333334 224 896 224S1493.3333333333335 491.3066666666664 1493.3333333333335 821.3333333333333S1226.0266666666669 1418.6666666666665 896 1418.6666666666665z" />
|
||||
<glyph glyph-name="facebook"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1343 1780V1516H1186Q1100 1516 1070 1480T1040 1372V1183H1333L1294 887H1040V128H734V887H479V1183H734V1401Q734 1587 838 1689.5T1115 1792Q1262 1792 1343 1780z" />
|
||||
<glyph glyph-name="gplus"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M799 996Q799 960 831 925.5T908.5 857.5T999 784T1076 680T1108 538Q1108 448 1060 365Q988 243 849 185.5T551 128Q419 128 304.5 169.5T133 307Q96 367 96 438Q96 519 140.5 588T259 703Q390 785 663 803Q631 845 615.5 877T600 950Q600 986 621 1035Q575 1031 553 1031Q405 1031 303.5 1127.5T202 1372Q202 1454 238 1531T337 1662Q414 1728 519.5 1760T737 1792H1155L1017 1704H886Q960 1641 998 1571T1036 1411Q1036 1339 1011.5 1281.5T952.5 1188.5T883 1123.5T823.5 1062T799 996zM653 1092Q691 1092 731 1108.5T797 1152Q850 1209 850 1311Q850 1369 833 1436T784.5 1565.5T700 1669T583 1710Q541 1710 500.5 1690.5T435 1638Q388 1579 388 1478Q388 1432 398 1380.5T429.5 1277.5T481.5 1185T556.5 1118T653 1092zM655 219Q713 219 766.5 232T865.5 271T938.5 344T966 453Q966 478 959 502T944.5 544T917.5 585.5T888 620.5T849.5 655T813 684T771.5 714T735 740Q719 742 687 742Q634 742 582 735T474.5 710T377.5 664T309 589.5T282 484Q282 414 317 360.5T408.5 277.5T527.5 233.5T655 219zM1465 1095H1678V987H1465V768H1360V987H1148V1095H1360V1312H1465V1095z" />
|
||||
<glyph glyph-name="linkedin"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M477 1167V176H147V1167H477zM498 1473Q499 1400 447.5 1351T312 1302H310Q228 1302 178 1351T128 1473Q128 1547 179.5 1595.5T314 1644T447 1595.5T498 1473zM1664 744V176H1335V706Q1335 811 1294.5 870.5T1168 930Q1105 930 1062.5 895.5T999 810Q988 780 988 729V176H659Q661 575 661 823T660 1119L659 1167H988V1023H986Q1006 1055 1027 1079T1083.5 1131T1170.5 1174.5T1285 1190Q1456 1190 1560 1076.5T1664 744z" />
|
||||
<glyph glyph-name="twitter"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1684 1384Q1617 1286 1522 1217Q1523 1203 1523 1175Q1523 1045 1485 915.5T1369.5 667T1185 456.5T927 310.5T604 256Q333 256 108 401Q143 397 186 397Q411 397 587 535Q482 537 399 599.5T285 759Q318 754 346 754Q389 754 431 765Q319 788 245.5 876.5T172 1082V1086Q240 1048 318 1045Q252 1089 213 1160T174 1314Q174 1402 218 1477Q339 1328 512.5 1238.5T884 1139Q876 1177 876 1213Q876 1347 970.5 1441.5T1199 1536Q1339 1536 1435 1434Q1544 1455 1640 1512Q1603 1397 1498 1334Q1591 1344 1684 1384z" />
|
||||
<glyph glyph-name="tumblr"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1328 463L1408 226Q1385 191 1297 160T1120 128Q1016 126 929.5 154T787 228T692 334T636.5 454T620 572V1116H452V1331Q524 1357 581 1400.5T672 1490.5T730 1592.5T764 1691.5T779 1780Q780 1785 783.5 1788.5T791 1792H1035V1368H1368V1116H1034V598Q1034 568 1040.5 542T1063 489.5T1112.5 448T1194 434Q1272 436 1328 463z" />
|
||||
<glyph glyph-name="pinterest"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M1664 896Q1664 687 1561 510.5T1281.5 231T896 128Q785 128 678 160Q737 253 756 324Q765 358 810 535Q830 496 883 467.5T997 439Q1118 439 1213 507.5T1360 696T1412 966Q1412 1080 1352.5 1180T1180 1343T925 1406Q820 1406 729 1377T574.5 1300T465.5 1189.5T398.5 1060T377 926Q377 822 417 743T534 632Q564 620 572 652Q574 659 580 683T588 713Q594 736 577 756Q526 817 526 907Q526 1058 630.5 1166.5T904 1275Q1055 1275 1139.5 1193T1224 980Q1224 810 1155.5 691T980 572Q919 572 882 615.5T859 720Q867 755 885.5 813.5T915.5 916.5T927 992Q927 1042 900 1075T823 1108Q761 1108 718 1051T675 909Q675 836 700 787L601 369Q584 299 588 192Q382 283 255 473T128 896Q128 1105 231 1281.5T510.5 1561T896 1664T1281.5 1561T1561 1281.5T1664 896z" />
|
||||
<glyph glyph-name="audio-description"
|
||||
unicode=""
|
||||
horiz-adv-x="1792" d=" M795.5138904615 457.270933L795.5138904615 1221.5248286325C971.84576475 1225.085121904 1107.39330415 1232.12360523 1207.223857 1161.5835220499998C1303.033991 1093.8857027 1377.7922305 962.20560625 1364.3373135 792.9476205000001C1350.102593 613.9029365000001 1219.6655764999998 463.4600215 1050.12389545 448.2843645000001C965.8259268 440.7398275000001 798.21890505 448.2843645000001 798.21890505 448.2843645000001C798.21890505 448.2843645000001 795.2791410655 453.016494 795.5138904615 457.270933M966.1564647 649.0863960000001C1076.16084135 644.6767075 1152.385591 707.3020429999999 1163.8910079999998 807.9351875C1179.2994744999999 942.71878505 1089.73043585 1030.3691748 960.74508635 1020.7227954L960.74508635 658.08043C960.6196169500002 652.9482330000001 962.7606933 650.3134680000001 966.1564647 649.0863960000001 M1343.2299685 457.3517725000002C1389.9059734 444.3690160000001 1404.0840274999998 496.0596970000001 1424.48294065 532.2791494999999C1469.0084255 611.2788500000001 1502.5101322 712.8584189999999 1503.0416912 828.9881705C1503.8147453000001 995.5680973 1438.8404296 1117.7973688000002 1378.4383305 1200.62456881045L1348.652139905 1200.62456881045C1346.6001063899998 1187.06858424 1356.44474056 1175.024791325 1362.18395859 1164.6588891000001C1408.2649952 1081.49431985 1450.96645015 966.7230041 1451.57490975 834.9817034999999C1452.27106325 683.8655425000002 1402.00636065 557.5072264999999 1343.2299685 457.3517725000002 M1488.0379675 457.3517725000002C1534.7139723999999 444.3690160000001 1548.8825828 496.0671625 1569.29093965 532.2791494999999C1613.8164245 611.2788500000001 1647.3113856500001 712.8584189999999 1647.8496902000002 828.9881705C1648.6227442999998 995.5680973 1583.6484286 1117.7973688000002 1523.2463295 1200.62456881045L1493.460138905 1200.62456881045C1491.40810539 1187.06858424 1501.250041305 1175.021805755 1506.9919575899999 1164.6588891000001C1553.0729942 1081.49431985 1595.7757984 966.7230041 1596.3829087499998 834.9817034999999C1597.07906225 683.8655425000002 1546.8143596500001 557.5072264999999 1488.0379675 457.3517725000002 M1631.9130380000001 457.3517725000002C1678.5890429 444.3690160000001 1692.7576533 496.0671625 1713.1660101500001 532.2791494999999C1757.691495 611.2788500000001 1791.1864561500001 712.8584189999999 1791.7247607000002 828.9881705C1792.4978148 995.5680973 1727.5234991000002 1117.7973688000002 1667.1214 1200.62456881045L1637.3352094050001 1200.62456881045C1635.28317589 1187.06858424 1645.1251118050002 1175.02329854 1650.86702809 1164.6588891000001C1696.9480647 1081.49431985 1739.64951965 966.7230041 1740.25797925 834.9817034999999C1740.95413275 683.8655425000002 1690.6894301500001 557.5072264999999 1631.9130380000001 457.3517725000002 M15.66796875 451.481947L254.03034755 451.481947L319.0356932 551.1747990000001L543.6261075 551.6487970000001C543.6261075 551.6487970000001 543.8541115 483.7032095 543.8541115 451.481947L714.4993835 451.481947L714.4993835 1230.9210795L508.643051 1230.9210795C488.8579955 1197.5411595 15.66796875 451.481947 15.66796875 451.481947L15.66796875 451.481947zM550.0048155000001 959.9708615L550.0048155000001 710.916297L408.4199 711.8642895L550.0048155000001 959.9708615L550.0048155000001 959.9708615z" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 25 KiB |
externo
BIN
Arquivo binário não exibido.
externo
BIN
Arquivo binário não exibido.
externo
+1975
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
externo
+1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ar",{
|
||||
"Play": "تشغيل",
|
||||
"Pause": "ايقاف",
|
||||
"Current Time": "الوقت الحالي",
|
||||
"Duration Time": "Dauer",
|
||||
"Remaining Time": "الوقت المتبقي",
|
||||
"Stream Type": "نوع التيار",
|
||||
"LIVE": "مباشر",
|
||||
"Loaded": "تم التحميل",
|
||||
"Progress": "التقدم",
|
||||
"Fullscreen": "ملء الشاشة",
|
||||
"Non-Fullscreen": "غير ملء الشاشة",
|
||||
"Mute": "صامت",
|
||||
"Unmuted": "غير الصامت",
|
||||
"Playback Rate": "معدل التشغيل",
|
||||
"Subtitles": "الترجمة",
|
||||
"subtitles off": "ايقاف الترجمة",
|
||||
"Captions": "التعليقات",
|
||||
"captions off": "ايقاف التعليقات",
|
||||
"Chapters": "فصول",
|
||||
"You aborted the media playback": "لقد ألغيت تشغيل الفيديو",
|
||||
"A network error caused the media download to fail part-way.": "تسبب خطأ في الشبكة بفشل تحميل الفيديو بالكامل.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "لا يمكن تحميل الفيديو بسبب فشل في الخادم أو الشبكة ، أو فشل بسبب عدم امكانية قراءة تنسيق الفيديو.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "تم ايقاف تشغيل الفيديو بسبب مشكلة فساد أو لأن الفيديو المستخدم يستخدم ميزات غير مدعومة من متصفحك.",
|
||||
"No compatible source was found for this media.": "فشل العثور على أي مصدر متوافق مع هذا الفيديو."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ba",{
|
||||
"Play": "Pusti",
|
||||
"Pause": "Pauza",
|
||||
"Current Time": "Trenutno vrijeme",
|
||||
"Duration Time": "Vrijeme trajanja",
|
||||
"Remaining Time": "Preostalo vrijeme",
|
||||
"Stream Type": "Način strimovanja",
|
||||
"LIVE": "UŽIVO",
|
||||
"Loaded": "Učitan",
|
||||
"Progress": "Progres",
|
||||
"Fullscreen": "Puni ekran",
|
||||
"Non-Fullscreen": "Mali ekran",
|
||||
"Mute": "Prigušen",
|
||||
"Unmuted": "Ne-prigušen",
|
||||
"Playback Rate": "Stopa reprodukcije",
|
||||
"Subtitles": "Podnaslov",
|
||||
"subtitles off": "Podnaslov deaktiviran",
|
||||
"Captions": "Titlovi",
|
||||
"captions off": "Titlovi deaktivirani",
|
||||
"Chapters": "Poglavlja",
|
||||
"You aborted the media playback": "Isključili ste reprodukciju videa.",
|
||||
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("bg",{
|
||||
"Play": "Възпроизвеждане",
|
||||
"Pause": "Пауза",
|
||||
"Current Time": "Текущо време",
|
||||
"Duration Time": "Продължителност",
|
||||
"Remaining Time": "Оставащо време",
|
||||
"Stream Type": "Тип на потока",
|
||||
"LIVE": "НА ЖИВО",
|
||||
"Loaded": "Заредено",
|
||||
"Progress": "Прогрес",
|
||||
"Fullscreen": "Цял екран",
|
||||
"Non-Fullscreen": "Спиране на цял екран",
|
||||
"Mute": "Без звук",
|
||||
"Unmuted": "Със звук",
|
||||
"Playback Rate": "Скорост на възпроизвеждане",
|
||||
"Subtitles": "Субтитри",
|
||||
"subtitles off": "Спряни субтитри",
|
||||
"Captions": "Аудио надписи",
|
||||
"captions off": "Спряни аудио надписи",
|
||||
"Chapters": "Глави",
|
||||
"You aborted the media playback": "Спряхте възпроизвеждането на видеото",
|
||||
"A network error caused the media download to fail part-way.": "Грешка в мрежата провали изтеглянето на видеото.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Видеото не може да бъде заредено заради проблем със сървъра или мрежата или защото този формат не е поддържан.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Възпроизвеждането на видеото беше прекъснато заради проблем с файла или защото видеото използва опции които браузърът Ви не поддържа.",
|
||||
"No compatible source was found for this media.": "Не беше намерен съвместим източник за това видео."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ca",{
|
||||
"Play": "Reproducció",
|
||||
"Pause": "Pausa",
|
||||
"Current Time": "Temps reproduït",
|
||||
"Duration Time": "Durada total",
|
||||
"Remaining Time": "Temps restant",
|
||||
"Stream Type": "Tipus de seqüència",
|
||||
"LIVE": "EN DIRECTE",
|
||||
"Loaded": "Carregat",
|
||||
"Progress": "Progrés",
|
||||
"Fullscreen": "Pantalla completa",
|
||||
"Non-Fullscreen": "Pantalla no completa",
|
||||
"Mute": "Silencia",
|
||||
"Unmuted": "Amb so",
|
||||
"Playback Rate": "Velocitat de reproducció",
|
||||
"Subtitles": "Subtítols",
|
||||
"subtitles off": "Subtítols desactivats",
|
||||
"Captions": "Llegendes",
|
||||
"captions off": "Llegendes desactivades",
|
||||
"Chapters": "Capítols",
|
||||
"You aborted the media playback": "Heu interromput la reproducció del vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Un error de la xarxa ha interromput la baixada del vídeo.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "No s'ha trobat cap font compatible amb el vídeo."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("cs",{
|
||||
"Play": "Přehrát",
|
||||
"Pause": "Pauza",
|
||||
"Current Time": "Aktuální čas",
|
||||
"Duration Time": "Doba trvání",
|
||||
"Remaining Time": "Zbývající čas",
|
||||
"Stream Type": "Stream Type",
|
||||
"LIVE": "ŽIVĚ",
|
||||
"Loaded": "Načteno",
|
||||
"Progress": "Stav",
|
||||
"Fullscreen": "Celá obrazovka",
|
||||
"Non-Fullscreen": "Zmenšená obrazovka",
|
||||
"Mute": "Ztlumit zvuk",
|
||||
"Unmuted": "Přehrát zvuk",
|
||||
"Playback Rate": "Rychlost přehrávání",
|
||||
"Subtitles": "Titulky",
|
||||
"subtitles off": "Titulky vypnuty",
|
||||
"Captions": "Popisky",
|
||||
"captions off": "Popisky vypnuty",
|
||||
"Chapters": "Kapitoly",
|
||||
"You aborted the media playback": "Přehrávání videa je přerušeno.",
|
||||
"A network error caused the media download to fail part-way.": "Video nemohlo být načteno, kvůli chybě v síti.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Špatně zadaný zdroj videa."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("da",{
|
||||
"Play": "Afspil",
|
||||
"Pause": "Pause",
|
||||
"Current Time": "Aktuel tid",
|
||||
"Duration Time": "Varighed",
|
||||
"Remaining Time": "Resterende tid",
|
||||
"Stream Type": "Stream-type",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Indlæst",
|
||||
"Progress": "Status",
|
||||
"Fullscreen": "Fuldskærm",
|
||||
"Non-Fullscreen": "Luk fuldskærm",
|
||||
"Mute": "Uden lyd",
|
||||
"Unmuted": "Med lyd",
|
||||
"Playback Rate": "Afspilningsrate",
|
||||
"Subtitles": "Undertekster",
|
||||
"subtitles off": "Uden undertekster",
|
||||
"Captions": "Undertekster for hørehæmmede",
|
||||
"captions off": "Uden undertekster for hørehæmmede",
|
||||
"Chapters": "Kapitler",
|
||||
"You aborted the media playback": "Du afbrød videoafspilningen.",
|
||||
"A network error caused the media download to fail part-way.": "En netværksfejl fik download af videoen til at fejle.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Fandt ikke en kompatibel kilde for denne media."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("de",{
|
||||
"Play": "Wiedergabe",
|
||||
"Pause": "Pause",
|
||||
"Current Time": "Aktueller Zeitpunkt",
|
||||
"Duration Time": "Dauer",
|
||||
"Remaining Time": "Verbleibende Zeit",
|
||||
"Stream Type": "Streamtyp",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Geladen",
|
||||
"Progress": "Status",
|
||||
"Fullscreen": "Vollbild",
|
||||
"Non-Fullscreen": "Kein Vollbild",
|
||||
"Mute": "Ton aus",
|
||||
"Unmuted": "Ton ein",
|
||||
"Playback Rate": "Wiedergabegeschwindigkeit",
|
||||
"Subtitles": "Untertitel",
|
||||
"subtitles off": "Untertitel aus",
|
||||
"Captions": "Untertitel",
|
||||
"captions off": "Untertitel aus",
|
||||
"Chapters": "Kapitel",
|
||||
"You aborted the media playback": "Sie haben die Videowiedergabe abgebrochen.",
|
||||
"A network error caused the media download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Für dieses Video wurde keine kompatible Quelle gefunden."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("es",{
|
||||
"Play": "Reproducción",
|
||||
"Pause": "Pausa",
|
||||
"Current Time": "Tiempo reproducido",
|
||||
"Duration Time": "Duración total",
|
||||
"Remaining Time": "Tiempo restante",
|
||||
"Stream Type": "Tipo de secuencia",
|
||||
"LIVE": "DIRECTO",
|
||||
"Loaded": "Cargado",
|
||||
"Progress": "Progreso",
|
||||
"Fullscreen": "Pantalla completa",
|
||||
"Non-Fullscreen": "Pantalla no completa",
|
||||
"Mute": "Silenciar",
|
||||
"Unmuted": "No silenciado",
|
||||
"Playback Rate": "Velocidad de reproducción",
|
||||
"Subtitles": "Subtítulos",
|
||||
"subtitles off": "Subtítulos desactivados",
|
||||
"Captions": "Subtítulos especiales",
|
||||
"captions off": "Subtítulos especiales desactivados",
|
||||
"Chapters": "Capítulos",
|
||||
"You aborted the media playback": "Ha interrumpido la reproducción del vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "No se ha encontrado ninguna fuente compatible con este vídeo."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("fi",{
|
||||
"Play": "Toisto",
|
||||
"Pause": "Tauko",
|
||||
"Current Time": "Tämänhetkinen aika",
|
||||
"Duration Time": "Kokonaisaika",
|
||||
"Remaining Time": "Jäljellä oleva aika",
|
||||
"Stream Type": "Lähetystyyppi",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Ladattu",
|
||||
"Progress": "Edistyminen",
|
||||
"Fullscreen": "Koko näyttö",
|
||||
"Non-Fullscreen": "Koko näyttö pois",
|
||||
"Mute": "Ääni pois",
|
||||
"Unmuted": "Ääni päällä",
|
||||
"Playback Rate": "Toistonopeus",
|
||||
"Subtitles": "Tekstitys",
|
||||
"subtitles off": "Tekstitys pois",
|
||||
"Captions": "Tekstitys",
|
||||
"captions off": "Tekstitys pois",
|
||||
"Chapters": "Kappaleet",
|
||||
"You aborted the media playback": "Olet keskeyttänyt videotoiston.",
|
||||
"A network error caused the media download to fail part-way.": "Verkkovirhe keskeytti videon latauksen.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Tälle videolle ei löytynyt yhteensopivaa lähdettä."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("fr",{
|
||||
"Play": "Lecture",
|
||||
"Pause": "Pause",
|
||||
"Current Time": "Temps actuel",
|
||||
"Duration Time": "Durée",
|
||||
"Remaining Time": "Temps restant",
|
||||
"Stream Type": "Type de flux",
|
||||
"LIVE": "EN DIRECT",
|
||||
"Loaded": "Chargé",
|
||||
"Progress": "Progression",
|
||||
"Fullscreen": "Plein écran",
|
||||
"Non-Fullscreen": "Fenêtré",
|
||||
"Mute": "Sourdine",
|
||||
"Unmuted": "Son activé",
|
||||
"Playback Rate": "Vitesse de lecture",
|
||||
"Subtitles": "Sous-titres",
|
||||
"subtitles off": "Sous-titres désactivés",
|
||||
"Captions": "Sous-titres",
|
||||
"captions off": "Sous-titres désactivés",
|
||||
"Chapters": "Chapitres",
|
||||
"You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.",
|
||||
"A network error caused the media download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.",
|
||||
"No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("hr",{
|
||||
"Play": "Pusti",
|
||||
"Pause": "Pauza",
|
||||
"Current Time": "Trenutno vrijeme",
|
||||
"Duration Time": "Vrijeme trajanja",
|
||||
"Remaining Time": "Preostalo vrijeme",
|
||||
"Stream Type": "Način strimovanja",
|
||||
"LIVE": "UŽIVO",
|
||||
"Loaded": "Učitan",
|
||||
"Progress": "Progres",
|
||||
"Fullscreen": "Puni ekran",
|
||||
"Non-Fullscreen": "Mali ekran",
|
||||
"Mute": "Prigušen",
|
||||
"Unmuted": "Ne-prigušen",
|
||||
"Playback Rate": "Stopa reprodukcije",
|
||||
"Subtitles": "Podnaslov",
|
||||
"subtitles off": "Podnaslov deaktiviran",
|
||||
"Captions": "Titlovi",
|
||||
"captions off": "Titlovi deaktivirani",
|
||||
"Chapters": "Poglavlja",
|
||||
"You aborted the media playback": "Isključili ste reprodukciju videa.",
|
||||
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("hu",{
|
||||
"Play": "Lejátszás",
|
||||
"Pause": "Szünet",
|
||||
"Current Time": "Aktuális időpont",
|
||||
"Duration Time": "Hossz",
|
||||
"Remaining Time": "Hátralévő idő",
|
||||
"Stream Type": "Adatfolyam típusa",
|
||||
"LIVE": "ÉLŐ",
|
||||
"Loaded": "Betöltve",
|
||||
"Progress": "Állapot",
|
||||
"Fullscreen": "Teljes képernyő",
|
||||
"Non-Fullscreen": "Normál méret",
|
||||
"Mute": "Némítás",
|
||||
"Unmuted": "Némítás kikapcsolva",
|
||||
"Playback Rate": "Lejátszási sebesség",
|
||||
"Subtitles": "Feliratok",
|
||||
"subtitles off": "Feliratok kikapcsolva",
|
||||
"Captions": "Magyarázó szöveg",
|
||||
"captions off": "Magyarázó szöveg kikapcsolva",
|
||||
"Chapters": "Fejezetek",
|
||||
"You aborted the media playback": "Leállította a lejátszást",
|
||||
"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.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Nincs kompatibilis forrás ehhez a videóhoz."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("it",{
|
||||
"Play": "Play",
|
||||
"Pause": "Pausa",
|
||||
"Current Time": "Orario attuale",
|
||||
"Duration Time": "Durata",
|
||||
"Remaining Time": "Tempo rimanente",
|
||||
"Stream Type": "Tipo del Streaming",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Caricato",
|
||||
"Progress": "Stato",
|
||||
"Fullscreen": "Schermo intero",
|
||||
"Non-Fullscreen": "Chiudi schermo intero",
|
||||
"Mute": "Muto",
|
||||
"Unmuted": "Audio",
|
||||
"Playback Rate": "Tasso di riproduzione",
|
||||
"Subtitles": "Sottotitoli",
|
||||
"subtitles off": "Senza sottotitoli",
|
||||
"Captions": "Sottotitoli non udenti",
|
||||
"captions off": "Senza sottotitoli non udenti",
|
||||
"Chapters": "Capitolo",
|
||||
"You aborted the media playback": "La riproduzione del filmato è stata interrotta.",
|
||||
"A network error caused the media download to fail part-way.": "Il download del filmato è stato interrotto a causa di un problema rete.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Non ci sono fonti compatibili per questo filmato."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ja",{
|
||||
"Play": "再生",
|
||||
"Pause": "一時停止",
|
||||
"Current Time": "現在の時間",
|
||||
"Duration Time": "長さ",
|
||||
"Remaining Time": "残りの時間",
|
||||
"Stream Type": "ストリームの種類",
|
||||
"LIVE": "ライブ",
|
||||
"Loaded": "ロード済み",
|
||||
"Progress": "進行状況",
|
||||
"Fullscreen": "フルスクリーン",
|
||||
"Non-Fullscreen": "フルスクリーン以外",
|
||||
"Mute": "ミュート",
|
||||
"Unmuted": "ミュート解除",
|
||||
"Playback Rate": "再生レート",
|
||||
"Subtitles": "サブタイトル",
|
||||
"subtitles off": "サブタイトル オフ",
|
||||
"Captions": "キャプション",
|
||||
"captions off": "キャプション オフ",
|
||||
"Chapters": "チャプター",
|
||||
"You aborted the media playback": "動画再生を中止しました",
|
||||
"A network error caused the media download to fail part-way.": "ネットワーク エラーにより動画のダウンロードが途中で失敗しました",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "サーバーまたはネットワークのエラー、またはフォーマットがサポートされていないため、動画をロードできませんでした",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "破損の問題、またはお使いのブラウザがサポートしていない機能が動画に使用されていたため、動画の再生が中止されました",
|
||||
"No compatible source was found for this media.": "この動画に対して互換性のあるソースが見つかりませんでした"
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ko",{
|
||||
"Play": "재생",
|
||||
"Pause": "일시중지",
|
||||
"Current Time": "현재 시간",
|
||||
"Duration Time": "지정 기간",
|
||||
"Remaining Time": "남은 시간",
|
||||
"Stream Type": "스트리밍 유형",
|
||||
"LIVE": "라이브",
|
||||
"Loaded": "로드됨",
|
||||
"Progress": "진행",
|
||||
"Fullscreen": "전체 화면",
|
||||
"Non-Fullscreen": "전체 화면 해제",
|
||||
"Mute": "음소거",
|
||||
"Unmuted": "음소거 해제",
|
||||
"Playback Rate": "재생 비율",
|
||||
"Subtitles": "서브타이틀",
|
||||
"subtitles off": "서브타이틀 끄기",
|
||||
"Captions": "자막",
|
||||
"captions off": "자막 끄기",
|
||||
"Chapters": "챕터",
|
||||
"You aborted the media playback": "비디오 재생을 취소했습니다.",
|
||||
"A network error caused the media download to fail part-way.": "네트워크 오류로 인하여 비디오 일부를 다운로드하지 못 했습니다.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "비디오를 로드할 수 없습니다. 서버 혹은 네트워크 오류 때문이거나 지원되지 않는 형식 때문일 수 있습니다.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "비디오 재생이 취소됐습니다. 비디오가 손상되었거나 비디오가 사용하는 기능을 브라우저에서 지원하지 않는 것 같습니다.",
|
||||
"No compatible source was found for this media.": "비디오에 호환되지 않는 소스가 있습니다."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("nl",{
|
||||
"Play": "Afspelen",
|
||||
"Pause": "Pauze",
|
||||
"Current Time": "Huidige tijd",
|
||||
"Duration Time": "Looptijd",
|
||||
"Remaining Time": "Resterende tijd",
|
||||
"Stream Type": "Streamtype",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Geladen",
|
||||
"Progress": "Status",
|
||||
"Fullscreen": "Volledig scherm",
|
||||
"Non-Fullscreen": "Geen volledig scherm",
|
||||
"Mute": "Geluid uit",
|
||||
"Unmuted": "Geluid aan",
|
||||
"Playback Rate": "Weergavesnelheid",
|
||||
"Subtitles": "Ondertiteling",
|
||||
"subtitles off": "Ondertiteling uit",
|
||||
"Captions": "Ondertiteling",
|
||||
"captions off": "Ondertiteling uit",
|
||||
"Chapters": "Hoofdstukken",
|
||||
"You aborted the media playback": "U hebt de mediaweergave afgebroken.",
|
||||
"A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, vanwege een server- of netwerkfout of doordat het formaat niet wordt ondersteund.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De mediaweergave is afgebroken vanwege beschadigde data of het mediabestand gebruikt functies die niet door uw browser worden ondersteund.",
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("pt-BR",{
|
||||
"Play": "Tocar",
|
||||
"Pause": "Pause",
|
||||
"Current Time": "Tempo",
|
||||
"Duration Time": "Duração",
|
||||
"Remaining Time": "Tempo Restante",
|
||||
"Stream Type": "Tipo de Stream",
|
||||
"LIVE": "AO VIVO",
|
||||
"Loaded": "Carregado",
|
||||
"Progress": "Progresso",
|
||||
"Fullscreen": "Tela Cheia",
|
||||
"Non-Fullscreen": "Tela Normal",
|
||||
"Mute": "Mudo",
|
||||
"Unmuted": "Habilitar Som",
|
||||
"Playback Rate": "Velocidade",
|
||||
"Subtitles": "Legendas",
|
||||
"subtitles off": "Sem Legendas",
|
||||
"Captions": "Anotações",
|
||||
"captions off": "Sem Anotações",
|
||||
"Chapters": "Capítulos",
|
||||
"You aborted the media playback": "Você parou a execução de vídeo.",
|
||||
"A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar parcialmente.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "O vídeo não pode ser carregado, ou porque houve um problema com sua rede ou pelo formato do vídeo não ser suportado.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A Execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
|
||||
"No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("ru",{
|
||||
"Play": "Воспроизвести",
|
||||
"Pause": "Приостановить",
|
||||
"Current Time": "Текущее время",
|
||||
"Duration Time": "Продолжительность",
|
||||
"Remaining Time": "Оставшееся время",
|
||||
"Stream Type": "Тип потока",
|
||||
"LIVE": "ОНЛАЙН",
|
||||
"Loaded": "Загрузка",
|
||||
"Progress": "Прогресс",
|
||||
"Fullscreen": "Полноэкранный режим",
|
||||
"Non-Fullscreen": "Неполноэкранный режим",
|
||||
"Mute": "Без звука",
|
||||
"Unmuted": "Со звуком",
|
||||
"Playback Rate": "Скорость воспроизведения",
|
||||
"Subtitles": "Субтитры",
|
||||
"subtitles off": "Субтитры выкл.",
|
||||
"Captions": "Подписи",
|
||||
"captions off": "Подписи выкл.",
|
||||
"Chapters": "Главы",
|
||||
"You aborted the media playback": "Вы прервали воспроизведение видео",
|
||||
"A network error caused the media download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.",
|
||||
"No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("sr",{
|
||||
"Play": "Pusti",
|
||||
"Pause": "Pauza",
|
||||
"Current Time": "Trenutno vrijeme",
|
||||
"Duration Time": "Vrijeme trajanja",
|
||||
"Remaining Time": "Preostalo vrijeme",
|
||||
"Stream Type": "Način strimovanja",
|
||||
"LIVE": "UŽIVO",
|
||||
"Loaded": "Učitan",
|
||||
"Progress": "Progres",
|
||||
"Fullscreen": "Puni ekran",
|
||||
"Non-Fullscreen": "Mali ekran",
|
||||
"Mute": "Prigušen",
|
||||
"Unmuted": "Ne-prigušen",
|
||||
"Playback Rate": "Stopa reprodukcije",
|
||||
"Subtitles": "Podnaslov",
|
||||
"subtitles off": "Podnaslov deaktiviran",
|
||||
"Captions": "Titlovi",
|
||||
"captions off": "Titlovi deaktivirani",
|
||||
"Chapters": "Poglavlja",
|
||||
"You aborted the media playback": "Isključili ste reprodukciju videa.",
|
||||
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("sv",{
|
||||
"Play": "Spela",
|
||||
"Pause": "Pausa",
|
||||
"Current Time": "Aktuell tid",
|
||||
"Duration Time": "Total tid",
|
||||
"Remaining Time": "Återstående tid",
|
||||
"Stream Type": "Strömningstyp",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Laddad",
|
||||
"Progress": "Förlopp",
|
||||
"Fullscreen": "Fullskärm",
|
||||
"Non-Fullscreen": "Ej fullskärm",
|
||||
"Mute": "Ljud av",
|
||||
"Unmuted": "Ljud på",
|
||||
"Playback Rate": "Uppspelningshastighet",
|
||||
"Subtitles": "Text på",
|
||||
"subtitles off": "Text av",
|
||||
"Captions": "Text på",
|
||||
"captions off": "Text av",
|
||||
"Chapters": "Kapitel",
|
||||
"You aborted the media playback": "Du har avbrutit videouppspelningen.",
|
||||
"A network error caused the media download to fail part-way.": "Ett nätverksfel gjorde att nedladdningen av videon avbröts.",
|
||||
"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.",
|
||||
"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.",
|
||||
"No compatible source was found for this media.": "Det gick inte att hitta någon kompatibel källa för den här videon."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("tr",{
|
||||
"Play": "Oynat",
|
||||
"Pause": "Duraklat",
|
||||
"Current Time": "Süre",
|
||||
"Duration Time": "Toplam Süre",
|
||||
"Remaining Time": "Kalan Süre",
|
||||
"Stream Type": "Yayın Tipi",
|
||||
"LIVE": "CANLI",
|
||||
"Loaded": "Yüklendi",
|
||||
"Progress": "Yükleniyor",
|
||||
"Fullscreen": "Tam Ekran",
|
||||
"Non-Fullscreen": "Küçük Ekran",
|
||||
"Mute": "Ses Kapa",
|
||||
"Unmuted": "Ses Aç",
|
||||
"Playback Rate": "Oynatma Hızı",
|
||||
"Subtitles": "Altyazı",
|
||||
"subtitles off": "Altyazı Kapat",
|
||||
"Captions": "Ek Açıklamalar",
|
||||
"captions off": "Ek Açıklamalar Kapalı",
|
||||
"Chapters": "Bölümler",
|
||||
"You aborted the media playback": "Video oynatmayı iptal ettiniz",
|
||||
"A network error caused the media download to fail part-way.": "Video indirilirken bağlantı sorunu oluştu.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video oynatılamadı, ağ ya da sunucu hatası veya belirtilen format desteklenmiyor.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Tarayıcınız desteklemediği için videoda hata oluştu.",
|
||||
"No compatible source was found for this media.": "Video için kaynak bulunamadı."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("uk",{
|
||||
"Play": "Відтворити",
|
||||
"Pause": "Призупинити",
|
||||
"Current Time": "Поточний час",
|
||||
"Duration Time": "Тривалість",
|
||||
"Remaining Time": "Час, що залишився",
|
||||
"Stream Type": "Тип потоку",
|
||||
"LIVE": "НАЖИВО",
|
||||
"Loaded": "Завантаження",
|
||||
"Progress": "Прогрес",
|
||||
"Fullscreen": "Повноекранний режим",
|
||||
"Non-Fullscreen": "Неповноекранний режим",
|
||||
"Mute": "Без звуку",
|
||||
"Unmuted": "Зі звуком",
|
||||
"Playback Rate": "Швидкість відтворення",
|
||||
"Subtitles": "Субтитри",
|
||||
"subtitles off": "Без субтитрів",
|
||||
"Captions": "Підписи",
|
||||
"captions off": "Без підписів",
|
||||
"Chapters": "Розділи",
|
||||
"You aborted the media playback": "Ви припинили відтворення відео",
|
||||
"A network error caused the media download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.",
|
||||
"No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні."
|
||||
});
|
||||
externo
+26
@@ -0,0 +1,26 @@
|
||||
videojs.addLanguage("vi",{
|
||||
"Play": "Phát",
|
||||
"Pause": "Tạm dừng",
|
||||
"Current Time": "Thời gian hiện tại",
|
||||
"Duration Time": "Độ dài",
|
||||
"Remaining Time": "Thời gian còn lại",
|
||||
"Stream Type": "Kiểu Stream",
|
||||
"LIVE": "TRỰC TIẾP",
|
||||
"Loaded": "Đã tải",
|
||||
"Progress": "Tiến trình",
|
||||
"Fullscreen": "Toàn màn hình",
|
||||
"Non-Fullscreen": "Thoát toàn màn hình",
|
||||
"Mute": "Tắt tiếng",
|
||||
"Unmuted": "Bật âm thanh",
|
||||
"Playback Rate": "Tốc độ phát",
|
||||
"Subtitles": "Phụ đề",
|
||||
"subtitles off": "Tắt phụ đề",
|
||||
"Captions": "Chú thích",
|
||||
"captions off": "Tắt chú thích",
|
||||
"Chapters": "Chương",
|
||||
"You aborted the media playback": "Bạn đã hủy việc phát media.",
|
||||
"A network error caused the media download to fail part-way.": "Một lỗi mạng dẫn đến việc tải media bị lỗi.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video không tải được, mạng hay server có lỗi hoặc định dạng không được hỗ trợ.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Phát media đã bị hủy do một sai lỗi hoặc media sử dụng những tính năng trình duyệt không hỗ trợ.",
|
||||
"No compatible source was found for this media.": "Không có nguồn tương thích cho media này."
|
||||
});
|
||||
externo
+27
@@ -0,0 +1,27 @@
|
||||
videojs.addLanguage("zh-CN",{
|
||||
"Play": "播放",
|
||||
"Pause": "暂停",
|
||||
"Current Time": "当前时间",
|
||||
"Duration Time": "时长",
|
||||
"Remaining Time": "剩余时间",
|
||||
"Stream Type": "媒体流类型",
|
||||
"LIVE": "直播",
|
||||
"Loaded": "加载完毕",
|
||||
"Progress": "进度",
|
||||
"Fullscreen": "全屏",
|
||||
"Non-Fullscreen": "退出全屏",
|
||||
"Mute": "静音",
|
||||
"Unmuted": "取消静音",
|
||||
"Playback Rate": "播放码率",
|
||||
"Subtitles": "字幕",
|
||||
"subtitles off": "字幕关闭",
|
||||
"Captions": "内嵌字幕",
|
||||
"captions off": "内嵌字幕关闭",
|
||||
"Chapters": "节目段落",
|
||||
"You aborted the media playback": "视频播放被终止",
|
||||
"A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
|
||||
"No compatible source was found for this media.": "无法找到此视频兼容的源。",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。"
|
||||
});
|
||||
externo
+27
@@ -0,0 +1,27 @@
|
||||
videojs.addLanguage("zh-TW",{
|
||||
"Play": "播放",
|
||||
"Pause": "暫停",
|
||||
"Current Time": "目前時間",
|
||||
"Duration Time": "總共時間",
|
||||
"Remaining Time": "剩餘時間",
|
||||
"Stream Type": "串流類型",
|
||||
"LIVE": "直播",
|
||||
"Loaded": "載入完畢",
|
||||
"Progress": "進度",
|
||||
"Fullscreen": "全螢幕",
|
||||
"Non-Fullscreen": "退出全螢幕",
|
||||
"Mute": "靜音",
|
||||
"Unmuted": "取消靜音",
|
||||
"Playback Rate": " 播放速率",
|
||||
"Subtitles": "字幕",
|
||||
"subtitles off": "關閉字幕",
|
||||
"Captions": "內嵌字幕",
|
||||
"captions off": "關閉內嵌字幕",
|
||||
"Chapters": "章節",
|
||||
"You aborted the media playback": "影片播放已終止",
|
||||
"A network error caused the media download to fail part-way.": "網路錯誤導致影片下載失敗。",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "影片因格式不支援或者伺服器或網路的問題無法載入。",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由於影片檔案損毀或是該影片使用了您的瀏覽器不支援的功能,播放終止。",
|
||||
"No compatible source was found for this media.": "無法找到相容此影片的來源。",
|
||||
"The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。"
|
||||
});
|
||||
externo
BIN
Arquivo binário não exibido.
externo
+1324
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
BIN
Arquivo binário não exibido.
externo
+21100
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+285
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+22
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+1
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -41,4 +41,4 @@ myPlayer.currentTime(120);
|
||||
|
||||
```
|
||||
|
||||
The full list of player API methods and events can be found in the [player API docs](../api/vjs.Player.md).
|
||||
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).
|
||||
|
||||
@@ -11,7 +11,7 @@ Text Tracks are a function of HTML5 video for providing time triggered text to t
|
||||
|
||||
Creating the Text File
|
||||
----------------------
|
||||
Timed text requires a text file in [WebVTT](http://dev.w3.org/html5/webvtt/) format. This format defines a list of "cues" that have a start time, and end time, and text to display.
|
||||
Timed text requires a text file in [WebVTT](http://dev.w3.org/html5/webvtt/) format. This format defines a list of "cues" that have a start time, and end time, and text to display. [Microsoft has a builder](https://dev.modern.ie/testdrive/demos/captionmaker/) that can help you get started on the file.
|
||||
|
||||
When creating captions, there's also additional [caption formatting techniques] (http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_FORMAT.HTML#style) that would be good to use, like brackets around sound effects: [ sound effect ]. If you'd like a more in depth style guide for captioning, you can reference the [Captioning Key](http://www.dcmp.org/captioningkey/), but keep in mind not all features are supported by WebVTT or (more likely) the Video.js WebVTT implementation.
|
||||
|
||||
@@ -20,12 +20,12 @@ Adding to Video.js
|
||||
Once you have your WebVTT file created, you can add it to Video.js using the track tag. Put your track tag after all the source elements, and before any fallback content.
|
||||
|
||||
```html
|
||||
<video id="example_video_1" class="video-js vjs-default-skin"
|
||||
controls preload="auto" width="640" height="264"
|
||||
data-setup='{"example_option":true}'>
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
<video id="example_video_1" class="video-js vjs-default-skin"
|
||||
controls preload="auto" width="640" height="264"
|
||||
data-setup='{"example_option":true}'>
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
|
||||
<track kind="captions" src="http://example.com/path/to/captions.vtt" srclang="en" label="English" default>
|
||||
|
||||
|
||||
+12
-12
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"Play": "Afspelen",
|
||||
"Pause": "Pauze",
|
||||
"Current Time": "Huidige Tijd",
|
||||
"Current Time": "Huidige tijd",
|
||||
"Duration Time": "Looptijd",
|
||||
"Remaining Time": "Resterende Tijd",
|
||||
"Stream Type": "Stream Type",
|
||||
"Remaining Time": "Resterende tijd",
|
||||
"Stream Type": "Streamtype",
|
||||
"LIVE": "LIVE",
|
||||
"Loaded": "Geladen",
|
||||
"Progress": "Status",
|
||||
"Fullscreen": "Volledig scherm",
|
||||
"Non-Fullscreen": "Geen volledig scherm",
|
||||
"Mute": "Geluid Uit",
|
||||
"Unmuted": "Geluid Aan",
|
||||
"Playback Rate": "Weergave Rate",
|
||||
"Mute": "Geluid uit",
|
||||
"Unmuted": "Geluid aan",
|
||||
"Playback Rate": "Weergavesnelheid",
|
||||
"Subtitles": "Ondertiteling",
|
||||
"subtitles off": "Ondertiteling uit",
|
||||
"Captions": "Onderschriften",
|
||||
"captions off": "Onderschriften uit",
|
||||
"Captions": "Ondertiteling",
|
||||
"captions off": "Ondertiteling uit",
|
||||
"Chapters": "Hoofdstukken",
|
||||
"You aborted the media playback": "Je hebt de media weergave afgebroken.",
|
||||
"A network error caused the media download to fail part-way.": "De media download is mislukt door een netwerkfout.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, veroorzaakt door een server of netwerkfout of het formaat word niet ondersteund.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De media weergave is afgebroken omdat deze beschadigd is of de media gebruikt functionaliteit die niet door je browser word ondersteund.",
|
||||
"You aborted the media playback": "U hebt de mediaweergave afgebroken.",
|
||||
"A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
|
||||
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, vanwege een server- of netwerkfout of doordat het formaat niet wordt ondersteund.",
|
||||
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De mediaweergave is afgebroken vanwege beschadigde data of het mediabestand gebruikt functies die niet door uw browser worden ondersteund.",
|
||||
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden."
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "5.0.2",
|
||||
"version": "5.2.0",
|
||||
"copyright": "Copyright Brightcove, Inc. <https://www.brightcove.com/>",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
@@ -28,9 +28,9 @@
|
||||
"object.assign": "^4.0.1",
|
||||
"safe-json-parse": "^4.0.0",
|
||||
"tsml": "1.0.1",
|
||||
"videojs-font": "1.3.0",
|
||||
"videojs-font": "1.4.0",
|
||||
"videojs-ie8": "1.1.0",
|
||||
"videojs-swf": "5.0.0-rc1",
|
||||
"videojs-swf": "5.0.1",
|
||||
"vtt.js": "git+https://github.com/gkatsev/vtt.js.git#vjs-v0.12.1",
|
||||
"xhr": "2.1.0"
|
||||
},
|
||||
@@ -48,14 +48,14 @@
|
||||
"grunt-aws-s3": "^0.12.1",
|
||||
"grunt-banner": "^0.4.0",
|
||||
"grunt-browserify": "3.5.1",
|
||||
"grunt-cli": "~0.1.0",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"grunt-concurrent": "^1.0.0",
|
||||
"grunt-contrib-clean": "~0.4.0a",
|
||||
"grunt-contrib-concat": "^0.5.1",
|
||||
"grunt-contrib-connect": "~0.7.1",
|
||||
"grunt-contrib-copy": "^0.8.0",
|
||||
"grunt-contrib-cssmin": "~0.6.0",
|
||||
"grunt-contrib-jshint": "^0.11.0",
|
||||
"grunt-contrib-jshint": "~0.11.3",
|
||||
"grunt-contrib-less": "~0.6.4",
|
||||
"grunt-contrib-uglify": "^0.8.0",
|
||||
"grunt-contrib-watch": "~0.1.4",
|
||||
@@ -64,7 +64,7 @@
|
||||
"grunt-fastly": "^0.1.3",
|
||||
"grunt-github-releaser": "^0.1.17",
|
||||
"grunt-karma": "^0.8.3",
|
||||
"grunt-sass": "^0.18.1",
|
||||
"grunt-sass": "^1.0.0",
|
||||
"grunt-version": "~0.3.0",
|
||||
"grunt-videojs-languages": "0.0.4",
|
||||
"grunt-zip": "0.10.2",
|
||||
@@ -73,6 +73,7 @@
|
||||
"karma-browserstack-launcher": "^0.1.4",
|
||||
"karma-chrome-launcher": "^0.1.3",
|
||||
"karma-coverage": "^0.4.0",
|
||||
"karma-detect-browsers": "^2.0.2",
|
||||
"karma-firefox-launcher": "^0.1.3",
|
||||
"karma-ie-launcher": "^0.1.5",
|
||||
"karma-opera-launcher": "~0.1.0",
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
};
|
||||
|
||||
// register the plugin
|
||||
vjs.plugin('progressed', progressed);
|
||||
videojs.plugin('progressed', progressed);
|
||||
|
||||
// initialize it
|
||||
vid1 = vjs('vid1');
|
||||
vid1 = videojs('vid1');
|
||||
vid1.progressed();
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
@import "utilities/linear-gradient";
|
||||
|
||||
@mixin background-color-with-alpha($color, $alpha) {
|
||||
background-color: $color;
|
||||
background-color: rgba($color, $alpha);
|
||||
}
|
||||
|
||||
@mixin transform($transform) {
|
||||
-moz-transform: $transform;
|
||||
-ms-transform: $transform;
|
||||
-o-transform: $transform;
|
||||
-webkit-transform: $transform;
|
||||
transform: $transform;
|
||||
}
|
||||
|
||||
@mixin transition($string: $transition--default) {
|
||||
-webkit-transition: $string;
|
||||
-moz-transition: $string;
|
||||
@@ -94,11 +104,15 @@
|
||||
order: $value;
|
||||
}
|
||||
|
||||
%icon-default {
|
||||
%fill-parent {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
%icon-default {
|
||||
@extend %fill-parent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
height: $big-play-button--height;
|
||||
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.
|
||||
display: block;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.video-js .vjs-control.vjs-close-button {
|
||||
@extend .vjs-icon-cancel;
|
||||
cursor: pointer;
|
||||
height: 3em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0.5em;
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -1,48 +1,23 @@
|
||||
.vjs-error-display {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-error .vjs-error-display {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.vjs-error .vjs-error-display .vjs-modal-dialog-content {
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vjs-error .vjs-error-display:before {
|
||||
color: #fff;
|
||||
content: 'X';
|
||||
font-family: $text-font-family;
|
||||
font-size: 4em;
|
||||
color: #fff;
|
||||
/* In order to center the play icon vertically we need to set the line height
|
||||
to the same as the button height */
|
||||
line-height: 1;
|
||||
text-shadow: 0.05em 0.05em 0.1em #000;
|
||||
text-align: center /* Needed for IE8 */;
|
||||
vertical-align: middle;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
|
||||
// In order to center the play icon vertically we need to set the line height
|
||||
// to the same as the button height
|
||||
line-height: 1;
|
||||
margin-top: -0.5em;
|
||||
position: absolute;
|
||||
text-shadow: 0.05em 0.05em 0.1em #000;
|
||||
text-align: center; // Needed for IE8
|
||||
top: 50%;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vjs-error-display div {
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
right: 0;
|
||||
left: 0;
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
padding: 3px;
|
||||
|
||||
@include background-color-with-alpha(#000, 0.5);
|
||||
}
|
||||
|
||||
.vjs-error-display a,
|
||||
.vjs-error-display a:visited {
|
||||
color: #66A8CC;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,15 @@ body.vjs-full-window {
|
||||
/* Hide disabled or unsupported controls. */
|
||||
.vjs-hidden { display: none !important; }
|
||||
|
||||
// Visually hidden offscreen, but accessible to screen readers.
|
||||
.video-js .vjs-offscreen {
|
||||
height: 1px;
|
||||
left: -9999px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.vjs-lock-showing {
|
||||
display: block !important;
|
||||
opacity: 1;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
.video-js .vjs-modal-dialog {
|
||||
@extend %fill-parent;
|
||||
@include linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content {
|
||||
@extend %fill-parent;
|
||||
|
||||
font-size: 1.2em; // 12px
|
||||
line-height: 1.5; // 18px
|
||||
padding: 20px 24px;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* Emulated tracks */
|
||||
.vjs-text-track-display {
|
||||
position: absolute;
|
||||
bottom: 3em;
|
||||
@@ -24,3 +25,13 @@
|
||||
.vjs-subtitles { color: #fff /* Subtitles are white */; }
|
||||
.vjs-captions { color: #fc6 /* Captions are yellow */; }
|
||||
.vjs-tt-cue { display: block; }
|
||||
|
||||
/* Native tracks */
|
||||
video::-webkit-media-text-track-display {
|
||||
@include transform(translateY(-3em));
|
||||
}
|
||||
|
||||
/* Move captions down when controls aren't being shown */
|
||||
.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display {
|
||||
@include transform(translateY(-1.5em));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// These functions and mixins taken from:
|
||||
//
|
||||
// "Building a linear-gradient Mixin in Sass" by Hugo Giraudel
|
||||
// http://www.sitepoint.com/building-linear-gradient-mixin-sass/
|
||||
// http://sassmeister.com/gist/b58f6e2cc3160007c880
|
||||
//
|
||||
|
||||
/// Convert angle
|
||||
/// @author Chris Eppstein
|
||||
/// @param {Number} $value - Value to convert
|
||||
/// @param {String} $unit - Unit to convert to
|
||||
/// @return {Number} Converted angle
|
||||
@function convert-angle($value, $unit) {
|
||||
$convertable-units: deg grad turn rad;
|
||||
$conversion-factors: 1 (10grad/9deg) (1turn/360deg) (3.1415926rad/180deg);
|
||||
@if index($convertable-units, unit($value)) and index($convertable-units, $unit) {
|
||||
@return $value
|
||||
/ nth($conversion-factors, index($convertable-units, unit($value)))
|
||||
* nth($conversion-factors, index($convertable-units, $unit));
|
||||
}
|
||||
|
||||
@warn "Cannot convert `#{unit($value)}` to `#{$unit}`.";
|
||||
}
|
||||
|
||||
/// Test if `$value` is an angle
|
||||
/// @param {*} $value - Value to test
|
||||
/// @return {Bool}
|
||||
@function is-direction($value) {
|
||||
$is-direction: index((
|
||||
'to top',
|
||||
'to top right',
|
||||
'to right top',
|
||||
'to right',
|
||||
'to bottom right',
|
||||
'to right bottom',
|
||||
'to bottom',
|
||||
'to bottom left',
|
||||
'to left bottom',
|
||||
'to left',
|
||||
'to left top',
|
||||
'to top left'
|
||||
), $value);
|
||||
$is-angle: type-of($value) == 'number' and index('deg' 'grad' 'turn' 'rad', unit($value));
|
||||
|
||||
@return $is-direction or $is-angle;
|
||||
}
|
||||
|
||||
/// Convert a direction to legacy syntax
|
||||
/// @param {Keyword | Angle} $value - Value to convert
|
||||
/// @require {function} is-direction
|
||||
/// @require {function} convert-angle
|
||||
@function legacy-direction($value) {
|
||||
@if is-direction($value) == false {
|
||||
@warn "Cannot convert `#{$value}` to legacy syntax because it doesn't seem to be an angle or a direction";
|
||||
}
|
||||
|
||||
$conversion-map: (
|
||||
'to top' : 'bottom',
|
||||
'to top right' : 'bottom left',
|
||||
'to right top' : 'left bottom',
|
||||
'to right' : 'left',
|
||||
'to bottom right' : 'top left',
|
||||
'to right bottom' : 'left top',
|
||||
'to bottom' : 'top',
|
||||
'to bottom left' : 'top right',
|
||||
'to left bottom' : 'right top',
|
||||
'to left' : 'right',
|
||||
'to left top' : 'right bottom',
|
||||
'to top left' : 'bottom right'
|
||||
);
|
||||
|
||||
@if map-has-key($conversion-map, $value) {
|
||||
@return map-get($conversion-map, $value);
|
||||
}
|
||||
|
||||
@return 90deg - convert-angle($value, 'deg');
|
||||
}
|
||||
|
||||
/// Mixin printing a linear-gradient
|
||||
/// as well as a plain color fallback
|
||||
/// and the `-webkit-` prefixed declaration
|
||||
/// @access public
|
||||
/// @param {String | List | Angle} $direction - Linear gradient direction
|
||||
/// @param {Arglist} $color-stops - List of color-stops composing the gradient
|
||||
@mixin linear-gradient($direction, $color-stops...) {
|
||||
@if is-direction($direction) == false {
|
||||
$color-stops: ($direction, $color-stops);
|
||||
$direction: 180deg;
|
||||
}
|
||||
|
||||
background: nth(nth($color-stops, 1), 1);
|
||||
background: -webkit-linear-gradient(legacy-direction($direction), $color-stops);
|
||||
background: linear-gradient($direction, $color-stops);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
@import "components/layout";
|
||||
@import "components/big-play";
|
||||
@import "components/button";
|
||||
@import "components/close-button";
|
||||
|
||||
@import "components/menu/menu";
|
||||
@import "components/menu/menu-popup";
|
||||
@@ -35,3 +36,4 @@
|
||||
@import "components/subtitles";
|
||||
@import "components/adaptive";
|
||||
@import "components/captions-settings";
|
||||
@import "components/modal-dialog";
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import Button from './button';
|
||||
import Component from './component';
|
||||
|
||||
/**
|
||||
* The `CloseButton` component is a button which fires a "close" event
|
||||
* when it is activated.
|
||||
*
|
||||
* @extends Button
|
||||
* @class CloseButton
|
||||
*/
|
||||
class CloseButton extends Button {
|
||||
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.controlText(options && options.controlText || this.localize('Close'));
|
||||
}
|
||||
|
||||
buildCSSClass() {
|
||||
return `vjs-close-button ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.trigger({type: 'close', bubbles: false});
|
||||
}
|
||||
}
|
||||
|
||||
Component.registerComponent('CloseButton', CloseButton);
|
||||
export default CloseButton;
|
||||
+111
-23
@@ -369,6 +369,10 @@ class Component {
|
||||
// If there's no .player_, this is a player
|
||||
let ComponentClass = Component.getComponent(componentClassName);
|
||||
|
||||
if (!ComponentClass) {
|
||||
throw new Error(`Component ${componentClassName} does not exist`);
|
||||
}
|
||||
|
||||
component = new ComponentClass(this.player_ || this, options);
|
||||
|
||||
// child is a component instance
|
||||
@@ -493,7 +497,10 @@ class Component {
|
||||
// `this` is `parent`
|
||||
let parentOptions = this.options_;
|
||||
|
||||
let handleAdd = (name, opts) => {
|
||||
let handleAdd = (child) => {
|
||||
let name = child.name;
|
||||
let opts = child.opts;
|
||||
|
||||
// Allow options for children to be set at the parent options
|
||||
// e.g. videojs(id, { controlBar: false });
|
||||
// instead of videojs(id, { children: { controlBar: false });
|
||||
@@ -521,33 +528,57 @@ class Component {
|
||||
// Add a direct reference to the child by name on the parent instance.
|
||||
// If two of the same component are used, different names should be supplied
|
||||
// for each
|
||||
this[name] = this.addChild(name, opts);
|
||||
let newChild = this.addChild(name, opts);
|
||||
if (newChild) {
|
||||
this[name] = newChild;
|
||||
}
|
||||
};
|
||||
|
||||
// Allow for an array of children details to passed in the options
|
||||
let workingChildren;
|
||||
let Tech = Component.getComponent('Tech');
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
let name;
|
||||
let opts;
|
||||
|
||||
if (typeof child === 'string') {
|
||||
// ['myComponent']
|
||||
name = child;
|
||||
opts = {};
|
||||
} else {
|
||||
// [{ name: 'myComponent', otherOption: true }]
|
||||
name = child.name;
|
||||
opts = child;
|
||||
}
|
||||
|
||||
handleAdd(name, opts);
|
||||
}
|
||||
workingChildren = children;
|
||||
} else {
|
||||
Object.getOwnPropertyNames(children).forEach(function(name){
|
||||
handleAdd(name, children[name]);
|
||||
});
|
||||
workingChildren = Object.keys(children);
|
||||
}
|
||||
|
||||
workingChildren
|
||||
// children that are in this.options_ but also in workingChildren would
|
||||
// give us extra children we do not want. So, we want to filter them out.
|
||||
.concat(Object.keys(this.options_)
|
||||
.filter(function(child) {
|
||||
return !workingChildren.some(function(wchild) {
|
||||
if (typeof wchild === 'string') {
|
||||
return child === wchild;
|
||||
} else {
|
||||
return child === wchild.name;
|
||||
}
|
||||
});
|
||||
}))
|
||||
.map((child) => {
|
||||
let name, opts;
|
||||
|
||||
if (typeof child === 'string') {
|
||||
name = child;
|
||||
opts = children[name] || this.options_[name] || {};
|
||||
} else {
|
||||
name = child.name;
|
||||
opts = child;
|
||||
}
|
||||
|
||||
return {name, opts};
|
||||
})
|
||||
.filter((child) => {
|
||||
// we have to make sure that child.name isn't in the techOrder since
|
||||
// techs are registerd as Components but can't aren't compatible
|
||||
// See https://github.com/videojs/video.js/issues/2772
|
||||
let c = Component.getComponent(child.opts.componentClass ||
|
||||
toTitleCase(child.name));
|
||||
return c && !Tech.isTech(c);
|
||||
})
|
||||
.forEach(handleAdd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,6 +830,46 @@ class Component {
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single DOM element matching `selector` within the component's
|
||||
* `contentEl` or another custom context.
|
||||
*
|
||||
* @method $
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelector`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {Element|null}
|
||||
*/
|
||||
$(selector, context) {
|
||||
return Dom.$(selector, context || this.contentEl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a all DOM elements matching `selector` within the component's
|
||||
* `contentEl` or another custom context.
|
||||
*
|
||||
* @method $$
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelectorAll`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {NodeList}
|
||||
*/
|
||||
$$(selector, context) {
|
||||
return Dom.$$(selector, context || this.contentEl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a component's element has a CSS class name
|
||||
*
|
||||
@@ -823,7 +894,7 @@ class Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return a CSS class name from the component's element
|
||||
* Remove a CSS class name from the component's element
|
||||
*
|
||||
* @param {String} classToRemove Classname to remove
|
||||
* @return {Component}
|
||||
@@ -834,6 +905,23 @@ class Component {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove a CSS class name from the component's element
|
||||
*
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*
|
||||
* @return {Component}
|
||||
* @method toggleClass
|
||||
*/
|
||||
toggleClass(classToToggle, predicate) {
|
||||
Dom.toggleElClass(this.el_, classToToggle, predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the component element if hidden
|
||||
*
|
||||
|
||||
+32
-27
@@ -2,53 +2,58 @@
|
||||
* @file error-display.js
|
||||
*/
|
||||
import Component from './component';
|
||||
import * as Dom from './utils/dom.js';
|
||||
import ModalDialog from './modal-dialog';
|
||||
|
||||
import * as Dom from './utils/dom';
|
||||
import mergeOptions from './utils/merge-options';
|
||||
|
||||
/**
|
||||
* Display that an error has occurred making the video unplayable
|
||||
* Display that an error has occurred making the video unplayable.
|
||||
*
|
||||
* @param {Object} player Main Player
|
||||
* @param {Object=} options Object of option names and values
|
||||
* @extends Component
|
||||
* @extends ModalDialog
|
||||
* @class ErrorDisplay
|
||||
*/
|
||||
class ErrorDisplay extends Component {
|
||||
class ErrorDisplay extends ModalDialog {
|
||||
|
||||
/**
|
||||
* Constructor for error display modal.
|
||||
*
|
||||
* @param {Player} player
|
||||
* @param {Object} [options]
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.update();
|
||||
this.on(player, 'error', this.update);
|
||||
this.on(player, 'error', this.open);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the component's DOM element
|
||||
* Include the old class for backward-compatibility.
|
||||
*
|
||||
* @return {Element}
|
||||
* @method createEl
|
||||
* This can be removed in 6.0.
|
||||
*
|
||||
* @method buildCSSClass
|
||||
* @deprecated
|
||||
* @return {String}
|
||||
*/
|
||||
createEl() {
|
||||
var el = super.createEl('div', {
|
||||
className: 'vjs-error-display'
|
||||
});
|
||||
|
||||
this.contentEl_ = Dom.createEl('div');
|
||||
el.appendChild(this.contentEl_);
|
||||
|
||||
return el;
|
||||
buildCSSClass() {
|
||||
return `vjs-error-display ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the error message in localized language
|
||||
* Generates the modal content based on the player error.
|
||||
*
|
||||
* @method update
|
||||
* @return {String|Null}
|
||||
*/
|
||||
update() {
|
||||
if (this.player().error()) {
|
||||
this.contentEl_.innerHTML = this.localize(this.player().error().message);
|
||||
}
|
||||
content() {
|
||||
let error = this.player().error();
|
||||
return error ? this.localize(error.message) : '';
|
||||
}
|
||||
}
|
||||
|
||||
ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
|
||||
fillAlways: true,
|
||||
uncloseable: true
|
||||
});
|
||||
|
||||
Component.registerComponent('ErrorDisplay', ErrorDisplay);
|
||||
export default ErrorDisplay;
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @file modal-dialog.js
|
||||
*/
|
||||
import document from 'global/document';
|
||||
|
||||
import * as Dom from './utils/dom';
|
||||
import * as Fn from './utils/fn';
|
||||
import log from './utils/log';
|
||||
|
||||
import Component from './component';
|
||||
import CloseButton from './close-button';
|
||||
|
||||
const MODAL_CLASS_NAME = 'vjs-modal-dialog';
|
||||
const ESC = 27;
|
||||
|
||||
/**
|
||||
* The `ModalDialog` displays over the video and its controls, which blocks
|
||||
* interaction with the player until it is closed.
|
||||
*
|
||||
* Modal dialogs include a "Close" button and will close when that button
|
||||
* is activated - or when ESC is pressed anywhere.
|
||||
*
|
||||
* @extends Component
|
||||
* @class ModalDialog
|
||||
*/
|
||||
class ModalDialog extends Component {
|
||||
|
||||
/**
|
||||
* Constructor for modals.
|
||||
*
|
||||
* @param {Player} player
|
||||
* @param {Object} [options]
|
||||
* @param {Mixed} [options.content=undefined]
|
||||
* Provide customized content for this modal.
|
||||
*
|
||||
* @param {String} [options.description]
|
||||
* A text description for the modal, primarily for accessibility.
|
||||
*
|
||||
* @param {Boolean} [options.fillAlways=false]
|
||||
* Normally, modals are automatically filled only the first time
|
||||
* they open. This tells the modal to refresh its content
|
||||
* every time it opens.
|
||||
*
|
||||
* @param {String} [options.label]
|
||||
* A text label for the modal, primarily for accessibility.
|
||||
*
|
||||
* @param {Boolean} [options.temporary=true]
|
||||
* If `true`, the modal can only be opened once; it will be
|
||||
* disposed as soon as it's closed.
|
||||
*
|
||||
* @param {Boolean} [options.uncloseable=false]
|
||||
* If `true`, the user will not be able to close the modal
|
||||
* through the UI in the normal ways. Programmatic closing is
|
||||
* still possible.
|
||||
*
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = false;
|
||||
|
||||
this.closeable(!this.options_.uncloseable);
|
||||
this.content(this.options_.content);
|
||||
|
||||
// Make sure the contentEl is defined AFTER any children are initialized
|
||||
// because we only want the contents of the modal in the contentEl
|
||||
// (not the UI elements like the close button).
|
||||
this.contentEl_ = Dom.createEl('div', {
|
||||
className: `${MODAL_CLASS_NAME}-content`
|
||||
}, {
|
||||
role: 'document'
|
||||
});
|
||||
|
||||
this.descEl_ = Dom.createEl('p', {
|
||||
className: `${MODAL_CLASS_NAME}-description vjs-offscreen`,
|
||||
id: this.el().getAttribute('aria-describedby')
|
||||
});
|
||||
|
||||
Dom.textContent(this.descEl_, this.description());
|
||||
this.el_.appendChild(this.descEl_);
|
||||
this.el_.appendChild(this.contentEl_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the modal's DOM element
|
||||
*
|
||||
* @method createEl
|
||||
* @return {Element}
|
||||
*/
|
||||
createEl() {
|
||||
return super.createEl('div', {
|
||||
className: this.buildCSSClass(),
|
||||
tabIndex: -1
|
||||
}, {
|
||||
'aria-describedby': `${this.id()}_description`,
|
||||
'aria-hidden': 'true',
|
||||
'aria-label': this.label(),
|
||||
role: 'dialog'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the modal's CSS class.
|
||||
*
|
||||
* @method buildCSSClass
|
||||
* @return {String}
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return `${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles key presses on the document, looking for ESC, which closes
|
||||
* the modal.
|
||||
*
|
||||
* @method handleKeyPress
|
||||
* @param {Event} e
|
||||
*/
|
||||
handleKeyPress(e) {
|
||||
if (e.which === ESC && this.closeable()) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label string for this modal. Primarily used for accessibility.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
label() {
|
||||
return this.options_.label || this.localize('Modal Window');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description string for this modal. Primarily used for
|
||||
* accessibility.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
description() {
|
||||
let desc = this.options_.description || this.localize('This is a modal window.');
|
||||
|
||||
// Append a universal closeability message if the modal is closeable.
|
||||
if (this.closeable()) {
|
||||
desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the modal.
|
||||
*
|
||||
* @method open
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
open() {
|
||||
if (!this.opened_) {
|
||||
let player = this.player();
|
||||
|
||||
this.trigger('beforemodalopen');
|
||||
this.opened_ = true;
|
||||
|
||||
// Fill content if the modal has never opened before and
|
||||
// never been filled.
|
||||
if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
|
||||
this.fill();
|
||||
}
|
||||
|
||||
// If the player was playing, pause it and take note of its previously
|
||||
// playing state.
|
||||
this.wasPlaying_ = !player.paused();
|
||||
|
||||
if (this.wasPlaying_) {
|
||||
player.pause();
|
||||
}
|
||||
|
||||
if (this.closeable()) {
|
||||
this.on(document, 'keydown', Fn.bind(this, this.handleKeyPress));
|
||||
}
|
||||
|
||||
player.controls(false);
|
||||
this.show();
|
||||
this.el().setAttribute('aria-hidden', 'false');
|
||||
this.trigger('modalopen');
|
||||
this.hasBeenOpened_ = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the modal is opened currently.
|
||||
*
|
||||
* @method opened
|
||||
* @param {Boolean} [value]
|
||||
* If given, it will open (`true`) or close (`false`) the modal.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
opened(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
this[value ? 'open' : 'close']();
|
||||
}
|
||||
return this.opened_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the modal.
|
||||
*
|
||||
* @method close
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
close() {
|
||||
if (this.opened_) {
|
||||
let player = this.player();
|
||||
|
||||
this.trigger('beforemodalclose');
|
||||
this.opened_ = false;
|
||||
|
||||
if (this.wasPlaying_) {
|
||||
player.play();
|
||||
}
|
||||
|
||||
if (this.closeable()) {
|
||||
this.off(document, 'keydown', Fn.bind(this, this.handleKeyPress));
|
||||
}
|
||||
|
||||
player.controls(true);
|
||||
this.hide();
|
||||
this.el().setAttribute('aria-hidden', 'true');
|
||||
this.trigger('modalclose');
|
||||
|
||||
if (this.options_.temporary) {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the modal is closeable via the UI.
|
||||
*
|
||||
* @method closeable
|
||||
* @param {Boolean} [value]
|
||||
* If given as a Boolean, it will set the `closeable` option.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
closeable(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
let closeable = this.closeable_ = !!value;
|
||||
let close = this.getChild('closeButton');
|
||||
|
||||
// If this is being made closeable and has no close button, add one.
|
||||
if (closeable && !close) {
|
||||
|
||||
// The close button should be a child of the modal - not its
|
||||
// content element, so temporarily change the content element.
|
||||
let temp = this.contentEl_;
|
||||
this.contentEl_ = this.el_;
|
||||
close = this.addChild('closeButton');
|
||||
this.contentEl_ = temp;
|
||||
this.on(close, 'close', this.close);
|
||||
}
|
||||
|
||||
// If this is being made uncloseable and has a close button, remove it.
|
||||
if (!closeable && close) {
|
||||
this.off(close, 'close', this.close);
|
||||
this.removeChild(close);
|
||||
close.dispose();
|
||||
}
|
||||
}
|
||||
return this.closeable_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the modal's content element with the modal's "content" option.
|
||||
*
|
||||
* The content element will be emptied before this change takes place.
|
||||
*
|
||||
* @method fill
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
fill() {
|
||||
return this.fillWith(this.content());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the modal's content element with arbitrary content.
|
||||
*
|
||||
* The content element will be emptied before this change takes place.
|
||||
*
|
||||
* @method fillWith
|
||||
* @param {Mixed} [content]
|
||||
* The same rules apply to this as apply to the `content` option.
|
||||
*
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
fillWith(content) {
|
||||
let contentEl = this.contentEl();
|
||||
let parentEl = contentEl.parentNode;
|
||||
let nextSiblingEl = contentEl.nextSibling;
|
||||
|
||||
this.trigger('beforemodalfill');
|
||||
this.hasBeenFilled_ = true;
|
||||
|
||||
// Detach the content element from the DOM before performing
|
||||
// manipulation to avoid modifying the live DOM multiple times.
|
||||
parentEl.removeChild(contentEl);
|
||||
this.empty();
|
||||
Dom.insertContent(contentEl, content);
|
||||
this.trigger('modalfill');
|
||||
|
||||
// Re-inject the re-filled content element.
|
||||
if (nextSiblingEl) {
|
||||
parentEl.insertBefore(contentEl, nextSiblingEl);
|
||||
} else {
|
||||
parentEl.appendChild(contentEl);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the content element.
|
||||
*
|
||||
* This happens automatically anytime the modal is filled.
|
||||
*
|
||||
* @method empty
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
empty() {
|
||||
this.trigger('beforemodalempty');
|
||||
Dom.emptyEl(this.contentEl());
|
||||
this.trigger('modalempty');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or sets the modal content, which gets normalized before being
|
||||
* rendered into the DOM.
|
||||
*
|
||||
* This does not update the DOM or fill the modal, but it is called during
|
||||
* that process.
|
||||
*
|
||||
* @method content
|
||||
* @param {Mixed} [value]
|
||||
* If defined, sets the internal content value to be used on the
|
||||
* next call(s) to `fill`. This value is normalized before being
|
||||
* inserted. To "clear" the internal content value, pass `null`.
|
||||
*
|
||||
* @return {Mixed}
|
||||
*/
|
||||
content(value) {
|
||||
if (typeof value !== 'undefined') {
|
||||
this.content_ = value;
|
||||
}
|
||||
return this.content_;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Modal dialog default options.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
ModalDialog.prototype.options_ = {
|
||||
temporary: true
|
||||
};
|
||||
|
||||
Component.registerComponent('ModalDialog', ModalDialog);
|
||||
export default ModalDialog;
|
||||
+96
-8
@@ -32,8 +32,10 @@ import BigPlayButton from './big-play-button.js';
|
||||
import ControlBar from './control-bar/control-bar.js';
|
||||
import ErrorDisplay from './error-display.js';
|
||||
import TextTrackSettings from './tracks/text-track-settings.js';
|
||||
import ModalDialog from './modal-dialog';
|
||||
|
||||
// Require html5 tech, at least for disposing the original video tag
|
||||
import Tech from './tech/tech.js';
|
||||
import Html5 from './tech/html5.js';
|
||||
|
||||
/**
|
||||
@@ -279,8 +281,8 @@ class Player extends Component {
|
||||
// of the player in a way that's still overrideable by CSS, just like the
|
||||
// video element
|
||||
this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
|
||||
let defaultsStyleEl = document.querySelector('.vjs-styles-defaults');
|
||||
let head = document.querySelector('head');
|
||||
let defaultsStyleEl = Dom.$('.vjs-styles-defaults');
|
||||
let head = Dom.$('head');
|
||||
head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
|
||||
|
||||
// Pass in the width/height/aspectRatio options which will update the style el
|
||||
@@ -485,7 +487,7 @@ class Player extends Component {
|
||||
|
||||
// get rid of the HTML5 video tag as soon as we are using another tech
|
||||
if (techName !== 'Html5' && this.tag) {
|
||||
Component.getComponent('Html5').disposeMediaElement(this.tag);
|
||||
Tech.getTech('Html5').disposeMediaElement(this.tag);
|
||||
this.tag.player = null;
|
||||
this.tag = null;
|
||||
}
|
||||
@@ -525,7 +527,12 @@ class Player extends Component {
|
||||
}
|
||||
|
||||
// Initialize tech instance
|
||||
let techComponent = Component.getComponent(techName);
|
||||
let techComponent = Tech.getTech(techName);
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!techComponent) {
|
||||
techComponent = Component.getComponent(techName);
|
||||
}
|
||||
this.tech_ = new techComponent(techOptions);
|
||||
|
||||
// player.triggerReady is always async, so don't need this to be async
|
||||
@@ -590,7 +597,7 @@ class Player extends Component {
|
||||
unloadTech_() {
|
||||
// Save the current text tracks so that we can reuse the same text tracks with the next tech
|
||||
this.textTracks_ = this.textTracks();
|
||||
this.textTracksJson_ = textTrackConverter.textTracksToJson(this);
|
||||
this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
|
||||
|
||||
this.isReady_ = false;
|
||||
|
||||
@@ -1639,6 +1646,46 @@ class Player extends Component {
|
||||
this.trigger('exitFullWindow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the player can play a given mimetype
|
||||
*
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
* @method canPlayType
|
||||
*/
|
||||
canPlayType(type) {
|
||||
let can;
|
||||
|
||||
// Loop through each playback technology in the options order
|
||||
for (let i = 0, j = this.options_.techOrder; i < j.length; i++) {
|
||||
let techName = toTitleCase(j[i]);
|
||||
let tech = Tech.getTech(techName);
|
||||
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!tech) {
|
||||
tech = Component.getComponent(techName);
|
||||
}
|
||||
|
||||
// Check if the current tech is defined before continuing
|
||||
if (!tech) {
|
||||
log.error(`The "${techName}" tech is undefined. Skipped browser support check for that tech.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the browser supports this technology
|
||||
if (tech.isSupported()) {
|
||||
can = tech.canPlayType(type);
|
||||
|
||||
if (can) {
|
||||
return can;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Select source based on tech order
|
||||
*
|
||||
@@ -1650,8 +1697,12 @@ class Player extends Component {
|
||||
// Loop through each playback technology in the options order
|
||||
for (var i=0,j=this.options_.techOrder;i<j.length;i++) {
|
||||
let techName = toTitleCase(j[i]);
|
||||
let tech = Component.getComponent(techName);
|
||||
|
||||
let tech = Tech.getTech(techName);
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!tech) {
|
||||
tech = Component.getComponent(techName);
|
||||
}
|
||||
// Check if the current tech is defined before continuing
|
||||
if (!tech) {
|
||||
log.error(`The "${techName}" tech is undefined. Skipped browser support check for that tech.`);
|
||||
@@ -1712,7 +1763,12 @@ class Player extends Component {
|
||||
return this.techGet_('src');
|
||||
}
|
||||
|
||||
let currentTech = Component.getComponent(this.techName_);
|
||||
let currentTech = Tech.getTech(this.techName_);
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!currentTech) {
|
||||
currentTech = Component.getComponent(this.techName_);
|
||||
}
|
||||
|
||||
// case: Array of source objects to choose from and pick the best to play
|
||||
if (Array.isArray(source)) {
|
||||
@@ -2479,6 +2535,38 @@ class Player extends Component {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple modal dialog (an instance of the `ModalDialog`
|
||||
* component) that immediately overlays the player with arbitrary
|
||||
* content and removes itself when closed.
|
||||
*
|
||||
* @param {String|Function|Element|Array|Null} content
|
||||
* Same as `ModalDialog#content`'s param of the same name.
|
||||
*
|
||||
* The most straight-forward usage is to provide a string or DOM
|
||||
* element.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* Extra options which will be passed on to the `ModalDialog`.
|
||||
*
|
||||
* @return {ModalDialog}
|
||||
*/
|
||||
createModal(content, options) {
|
||||
let player = this;
|
||||
|
||||
options = options || {};
|
||||
options.content = content || '';
|
||||
|
||||
let modal = new ModalDialog(player, options);
|
||||
|
||||
player.addChild(modal);
|
||||
modal.on('dispose', function() {
|
||||
player.removeChild(modal);
|
||||
});
|
||||
|
||||
return modal.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tag settings
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ function FlashRtmpDecorator(Flash) {
|
||||
// Look for the normal URL separator we expect, '&'.
|
||||
// If found, we split the URL into two pieces around the
|
||||
// first '&'.
|
||||
let connEnd = src.indexOf('&');
|
||||
let connEnd = src.search(/&(?!\w+=)/);
|
||||
let streamBegin;
|
||||
if (connEnd !== -1) {
|
||||
streamBegin = connEnd + 1;
|
||||
@@ -60,12 +60,31 @@ function FlashRtmpDecorator(Flash) {
|
||||
Flash.rtmpSourceHandler = {};
|
||||
|
||||
/**
|
||||
* Check Flash can handle the source natively
|
||||
* Check if Flash can play the given videotype
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
Flash.rtmpSourceHandler.canPlayType = function(type){
|
||||
if (Flash.isStreamingType(type)) {
|
||||
return 'maybe';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if Flash can handle the source natively
|
||||
* @param {Object} source The source object
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
Flash.rtmpSourceHandler.canHandleSource = function(source){
|
||||
if (Flash.isStreamingType(source.type) || Flash.isStreamingSrc(source.src)) {
|
||||
let can = Flash.rtmpSourceHandler.canPlayType(source.type);
|
||||
|
||||
if (can) {
|
||||
return can;
|
||||
}
|
||||
|
||||
if (Flash.isStreamingSrc(source.src)) {
|
||||
return 'maybe';
|
||||
}
|
||||
|
||||
|
||||
+15
-5
@@ -352,6 +352,19 @@ Tech.withSourceHandlers(Flash);
|
||||
*/
|
||||
Flash.nativeSourceHandler = {};
|
||||
|
||||
/**
|
||||
* Check if Flash can play the given videotype
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
Flash.nativeSourceHandler.canPlayType = function(type){
|
||||
if (type in Flash.formats) {
|
||||
return 'maybe';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
/*
|
||||
* Check Flash can handle the source natively
|
||||
*
|
||||
@@ -376,11 +389,7 @@ Flash.nativeSourceHandler.canHandleSource = function(source){
|
||||
type = source.type.replace(/;.*/, '').toLowerCase();
|
||||
}
|
||||
|
||||
if (type in Flash.formats) {
|
||||
return 'maybe';
|
||||
}
|
||||
|
||||
return '';
|
||||
return Flash.nativeSourceHandler.canPlayType(type);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -539,4 +548,5 @@ Flash.getEmbedCode = function(swf, flashVars, params, attributes){
|
||||
FlashRtmpDecorator(Flash);
|
||||
|
||||
Component.registerComponent('Flash', Flash);
|
||||
Tech.registerTech('Flash', Flash);
|
||||
export default Flash;
|
||||
|
||||
+20
-13
@@ -771,7 +771,7 @@ class Html5 extends Tech {
|
||||
|
||||
this.remoteTextTracks().removeTrack_(track);
|
||||
|
||||
tracks = this.el().querySelectorAll('track');
|
||||
tracks = this.$$('track');
|
||||
|
||||
i = tracks.length;
|
||||
while (i--) {
|
||||
@@ -828,6 +828,22 @@ Tech.withSourceHandlers(Html5);
|
||||
*/
|
||||
Html5.nativeSourceHandler = {};
|
||||
|
||||
/*
|
||||
* Check if the video element can play the given videotype
|
||||
*
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
Html5.nativeSourceHandler.canPlayType = function(type){
|
||||
// IE9 on Windows 7 without MediaPlayer throws an error here
|
||||
// https://github.com/videojs/video.js/issues/519
|
||||
try {
|
||||
return Html5.TEST_VID.canPlayType(type);
|
||||
} catch(e) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if the video element can handle the source natively
|
||||
*
|
||||
@@ -837,24 +853,14 @@ Html5.nativeSourceHandler = {};
|
||||
Html5.nativeSourceHandler.canHandleSource = function(source){
|
||||
var match, ext;
|
||||
|
||||
function canPlayType(type){
|
||||
// IE9 on Windows 7 without MediaPlayer throws an error here
|
||||
// https://github.com/videojs/video.js/issues/519
|
||||
try {
|
||||
return Html5.TEST_VID.canPlayType(type);
|
||||
} catch(e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// If a type was provided we should rely on that
|
||||
if (source.type) {
|
||||
return canPlayType(source.type);
|
||||
return Html5.nativeSourceHandler.canPlayType(source.type);
|
||||
} else if (source.src) {
|
||||
// If no type, fall back to checking 'video/[EXTENSION]'
|
||||
ext = Url.getFileExtension(source.src);
|
||||
|
||||
return canPlayType(`video/${ext}`);
|
||||
return Html5.nativeSourceHandler.canPlayType(`video/${ext}`);
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -1081,4 +1087,5 @@ Html5.disposeMediaElement = function(el){
|
||||
};
|
||||
|
||||
Component.registerComponent('Html5', Html5);
|
||||
Tech.registerTech('Html5', Html5);
|
||||
export default Html5;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @file loader.js
|
||||
*/
|
||||
import Component from '../component';
|
||||
import Component from '../component.js';
|
||||
import Tech from './tech.js';
|
||||
import window from 'global/window';
|
||||
import toTitleCase from '../utils/to-title-case.js';
|
||||
|
||||
@@ -26,7 +27,12 @@ class MediaLoader extends Component {
|
||||
if (!options.playerOptions['sources'] || options.playerOptions['sources'].length === 0) {
|
||||
for (let i=0, j=options.playerOptions['techOrder']; i<j.length; i++) {
|
||||
let techName = toTitleCase(j[i]);
|
||||
let tech = Component.getComponent(techName);
|
||||
let tech = Tech.getTech(techName);
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!techName) {
|
||||
tech = Component.getComponent(techName);
|
||||
}
|
||||
|
||||
// Check if the browser supports this technology
|
||||
if (tech && tech.isSupported()) {
|
||||
|
||||
+112
-13
@@ -309,6 +309,11 @@ class Tech extends Component {
|
||||
* @method emulateTextTracks
|
||||
*/
|
||||
emulateTextTracks() {
|
||||
let tracks = this.textTracks();
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window['WebVTT'] && this.el().parentNode != null) {
|
||||
let script = document.createElement('script');
|
||||
script.src = this.options_['vtt.js'] || '../node_modules/vtt.js/dist/vtt.js';
|
||||
@@ -316,11 +321,6 @@ class Tech extends Component {
|
||||
window['WebVTT'] = true;
|
||||
}
|
||||
|
||||
let tracks = this.textTracks();
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
let textTracksChanges = Fn.bind(this, function() {
|
||||
let updateDisplay = () => this.trigger('texttrackchange');
|
||||
|
||||
@@ -424,6 +424,71 @@ class Tech extends Component {
|
||||
*/
|
||||
setPoster() {}
|
||||
|
||||
/*
|
||||
* Check if the tech can support the given type
|
||||
*
|
||||
* The base tech does not support any type, but source handlers might
|
||||
* overwrite this.
|
||||
*
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
canPlayType() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the argument is a Tech or not.
|
||||
* Can be passed either a Class like `Html5` or a instance like `player.tech_`
|
||||
*
|
||||
* @param {Object} component An item to check
|
||||
* @return {Boolean} Whether it is a tech or not
|
||||
*/
|
||||
static isTech(component) {
|
||||
return component.prototype instanceof Tech ||
|
||||
component instanceof Tech ||
|
||||
component === Tech;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Tech
|
||||
*
|
||||
* @param {String} name Name of the Tech to register
|
||||
* @param {Object} tech The tech to register
|
||||
* @static
|
||||
* @method registerComponent
|
||||
*/
|
||||
static registerTech(name, tech) {
|
||||
if (!Tech.techs_) {
|
||||
Tech.techs_ = {};
|
||||
}
|
||||
|
||||
if (!Tech.isTech(tech)) {
|
||||
throw new Error(`Tech ${name} must be a Tech`);
|
||||
}
|
||||
|
||||
Tech.techs_[name] = tech;
|
||||
return tech;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a component by name
|
||||
*
|
||||
* @param {String} name Name of the component to get
|
||||
* @return {Component}
|
||||
* @static
|
||||
* @method getComponent
|
||||
*/
|
||||
static getTech(name) {
|
||||
if (Tech.techs_ && Tech.techs_[name]) {
|
||||
return Tech.techs_[name];
|
||||
}
|
||||
|
||||
if (window && window.videojs && window.videojs[name]) {
|
||||
log.warn(`The ${name} tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)`);
|
||||
return window.videojs[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -498,6 +563,26 @@ Tech.withSourceHandlers = function(_Tech){
|
||||
handlers.splice(index, 0, handler);
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if the tech can support the given type
|
||||
* @param {String} type The mimetype to check
|
||||
* @return {String} 'probably', 'maybe', or '' (empty string)
|
||||
*/
|
||||
_Tech.canPlayType = function(type){
|
||||
let handlers = _Tech.sourceHandlers || [];
|
||||
let can;
|
||||
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
can = handlers[i].canPlayType(type);
|
||||
|
||||
if (can) {
|
||||
return can;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the first source handler that supports the source
|
||||
* TODO: Answer question: should 'probably' be prioritized over 'maybe'
|
||||
@@ -535,16 +620,29 @@ Tech.withSourceHandlers = function(_Tech){
|
||||
return '';
|
||||
};
|
||||
|
||||
let originalSeekable = _Tech.prototype.seekable;
|
||||
/*
|
||||
* When using a source handler, prefer its implementation of
|
||||
* any function normally provided by the tech.
|
||||
*/
|
||||
let deferrable = [
|
||||
'seekable',
|
||||
'duration'
|
||||
];
|
||||
|
||||
// when a source handler is registered, prefer its implementation of
|
||||
// seekable when present.
|
||||
_Tech.prototype.seekable = function() {
|
||||
if (this.sourceHandler_ && this.sourceHandler_.seekable) {
|
||||
return this.sourceHandler_.seekable();
|
||||
deferrable.forEach(function (fnName) {
|
||||
let originalFn = this[fnName];
|
||||
|
||||
if (typeof originalFn !== 'function') {
|
||||
return;
|
||||
}
|
||||
return originalSeekable.call(this);
|
||||
};
|
||||
|
||||
this[fnName] = function() {
|
||||
if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
|
||||
return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
|
||||
}
|
||||
return originalFn.apply(this, arguments);
|
||||
};
|
||||
}, _Tech.prototype);
|
||||
|
||||
/*
|
||||
* Create a function for setting the source using a source object
|
||||
@@ -591,4 +689,5 @@ Tech.withSourceHandlers = function(_Tech){
|
||||
Component.registerComponent('Tech', Tech);
|
||||
// Old name for Tech
|
||||
Component.registerComponent('MediaTechController', Tech);
|
||||
Tech.registerTech('Tech', Tech);
|
||||
export default Tech;
|
||||
|
||||
@@ -21,7 +21,9 @@ let TextTrackCueList = function(cues) {
|
||||
list = document.createElement('custom');
|
||||
|
||||
for (let prop in TextTrackCueList.prototype) {
|
||||
list[prop] = TextTrackCueList.prototype[prop];
|
||||
if (prop !== 'constructor') {
|
||||
list[prop] = TextTrackCueList.prototype[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,15 @@
|
||||
* @private
|
||||
*/
|
||||
let trackToJson_ = function(track) {
|
||||
return {
|
||||
kind: track.kind,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
id: track.id,
|
||||
inBandMetadataTrackDispatchType: track.inBandMetadataTrackDispatchType,
|
||||
mode: track.mode,
|
||||
let ret = ['kind', 'label', 'language', 'id',
|
||||
'inBandMetadataTrackDispatchType',
|
||||
'mode', 'src'].reduce((acc, prop, i) => {
|
||||
if (track[prop]) {
|
||||
acc[prop] = track[prop];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {
|
||||
cues: track.cues && Array.prototype.map.call(track.cues, function(cue) {
|
||||
return {
|
||||
startTime: cue.startTime,
|
||||
@@ -27,9 +29,10 @@ let trackToJson_ = function(track) {
|
||||
text: cue.text,
|
||||
id: cue.id
|
||||
};
|
||||
}),
|
||||
src: track.src
|
||||
};
|
||||
})
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -41,12 +44,15 @@ let trackToJson_ = function(track) {
|
||||
* @function textTracksToJson
|
||||
*/
|
||||
let textTracksToJson = function(tech) {
|
||||
let trackEls = tech.el().querySelectorAll('track');
|
||||
|
||||
let trackEls = tech.$$('track');
|
||||
|
||||
let trackObjs = Array.prototype.map.call(trackEls, (t) => t.track);
|
||||
let tracks = Array.prototype.map.call(trackEls, function(trackEl) {
|
||||
let json = trackToJson_(trackEl.track);
|
||||
json.src = trackEl.src;
|
||||
if (trackEl.src) {
|
||||
json.src = trackEl.src;
|
||||
}
|
||||
return json;
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ let TextTrackList = function(tracks) {
|
||||
list = document.createElement('custom');
|
||||
|
||||
for (let prop in TextTrackList.prototype) {
|
||||
list[prop] = TextTrackList.prototype[prop];
|
||||
if (prop !== 'constructor') {
|
||||
list[prop] = TextTrackList.prototype[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,33 +27,33 @@ class TextTrackSettings extends Component {
|
||||
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
|
||||
}
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-done-button'), 'click', Fn.bind(this, function() {
|
||||
Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function() {
|
||||
this.saveSettings();
|
||||
this.hide();
|
||||
}));
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-default-button'), 'click', Fn.bind(this, function() {
|
||||
this.el().querySelector('.vjs-fg-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-bg-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.window-color > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-text-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-bg-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-window-opacity > select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-edge-style select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-font-family select').selectedIndex = 0;
|
||||
this.el().querySelector('.vjs-font-percent select').selectedIndex = 2;
|
||||
Events.on(this.$('.vjs-default-button'), 'click', Fn.bind(this, function() {
|
||||
this.$('.vjs-fg-color > select').selectedIndex = 0;
|
||||
this.$('.vjs-bg-color > select').selectedIndex = 0;
|
||||
this.$('.window-color > select').selectedIndex = 0;
|
||||
this.$('.vjs-text-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-bg-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-window-opacity > select').selectedIndex = 0;
|
||||
this.$('.vjs-edge-style select').selectedIndex = 0;
|
||||
this.$('.vjs-font-family select').selectedIndex = 0;
|
||||
this.$('.vjs-font-percent select').selectedIndex = 2;
|
||||
this.updateDisplay();
|
||||
}));
|
||||
|
||||
Events.on(this.el().querySelector('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.el().querySelector('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
Events.on(this.$('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
|
||||
|
||||
if (this.options_.persistTextTrackSettings) {
|
||||
this.restoreSettings();
|
||||
@@ -74,7 +74,7 @@ class TextTrackSettings extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get texttrack settings
|
||||
* Get texttrack settings
|
||||
* Settings are
|
||||
* .vjs-edge-style
|
||||
* .vjs-font-family
|
||||
@@ -83,23 +83,21 @@ class TextTrackSettings extends Component {
|
||||
* .vjs-bg-color
|
||||
* .vjs-bg-opacity
|
||||
* .window-color
|
||||
* .vjs-window-opacity
|
||||
* .vjs-window-opacity
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {Object}
|
||||
* @method getValues
|
||||
*/
|
||||
getValues() {
|
||||
const el = this.el();
|
||||
|
||||
const textEdge = getSelectedOptionValue(el.querySelector('.vjs-edge-style select'));
|
||||
const fontFamily = getSelectedOptionValue(el.querySelector('.vjs-font-family select'));
|
||||
const fgColor = getSelectedOptionValue(el.querySelector('.vjs-fg-color > select'));
|
||||
const textOpacity = getSelectedOptionValue(el.querySelector('.vjs-text-opacity > select'));
|
||||
const bgColor = getSelectedOptionValue(el.querySelector('.vjs-bg-color > select'));
|
||||
const bgOpacity = getSelectedOptionValue(el.querySelector('.vjs-bg-opacity > select'));
|
||||
const windowColor = getSelectedOptionValue(el.querySelector('.window-color > select'));
|
||||
const windowOpacity = getSelectedOptionValue(el.querySelector('.vjs-window-opacity > select'));
|
||||
const fontPercent = window['parseFloat'](getSelectedOptionValue(el.querySelector('.vjs-font-percent > select')));
|
||||
const textEdge = getSelectedOptionValue(this.$('.vjs-edge-style select'));
|
||||
const fontFamily = getSelectedOptionValue(this.$('.vjs-font-family select'));
|
||||
const fgColor = getSelectedOptionValue(this.$('.vjs-fg-color > select'));
|
||||
const textOpacity = getSelectedOptionValue(this.$('.vjs-text-opacity > select'));
|
||||
const bgColor = getSelectedOptionValue(this.$('.vjs-bg-color > select'));
|
||||
const bgOpacity = getSelectedOptionValue(this.$('.vjs-bg-opacity > select'));
|
||||
const windowColor = getSelectedOptionValue(this.$('.window-color > select'));
|
||||
const windowOpacity = getSelectedOptionValue(this.$('.vjs-window-opacity > select'));
|
||||
const fontPercent = window['parseFloat'](getSelectedOptionValue(this.$('.vjs-font-percent > select')));
|
||||
|
||||
let result = {
|
||||
'backgroundOpacity': bgOpacity,
|
||||
@@ -121,7 +119,7 @@ class TextTrackSettings extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set texttrack settings
|
||||
* Set texttrack settings
|
||||
* Settings are
|
||||
* .vjs-edge-style
|
||||
* .vjs-font-family
|
||||
@@ -130,22 +128,20 @@ class TextTrackSettings extends Component {
|
||||
* .vjs-bg-color
|
||||
* .vjs-bg-opacity
|
||||
* .window-color
|
||||
* .vjs-window-opacity
|
||||
* .vjs-window-opacity
|
||||
*
|
||||
* @param {Object} values Object with texttrack setting values
|
||||
* @method setValues
|
||||
*/
|
||||
setValues(values) {
|
||||
const el = this.el();
|
||||
|
||||
setSelectedOption(el.querySelector('.vjs-edge-style select'), values.edgeStyle);
|
||||
setSelectedOption(el.querySelector('.vjs-font-family select'), values.fontFamily);
|
||||
setSelectedOption(el.querySelector('.vjs-fg-color > select'), values.color);
|
||||
setSelectedOption(el.querySelector('.vjs-text-opacity > select'), values.textOpacity);
|
||||
setSelectedOption(el.querySelector('.vjs-bg-color > select'), values.backgroundColor);
|
||||
setSelectedOption(el.querySelector('.vjs-bg-opacity > select'), values.backgroundOpacity);
|
||||
setSelectedOption(el.querySelector('.window-color > select'), values.windowColor);
|
||||
setSelectedOption(el.querySelector('.vjs-window-opacity > select'), values.windowOpacity);
|
||||
setSelectedOption(this.$('.vjs-edge-style select'), values.edgeStyle);
|
||||
setSelectedOption(this.$('.vjs-font-family select'), values.fontFamily);
|
||||
setSelectedOption(this.$('.vjs-fg-color > select'), values.color);
|
||||
setSelectedOption(this.$('.vjs-text-opacity > select'), values.textOpacity);
|
||||
setSelectedOption(this.$('.vjs-bg-color > select'), values.backgroundColor);
|
||||
setSelectedOption(this.$('.vjs-bg-opacity > select'), values.backgroundOpacity);
|
||||
setSelectedOption(this.$('.window-color > select'), values.windowColor);
|
||||
setSelectedOption(this.$('.vjs-window-opacity > select'), values.windowOpacity);
|
||||
|
||||
let fontPercent = values.fontPercent;
|
||||
|
||||
@@ -153,11 +149,11 @@ class TextTrackSettings extends Component {
|
||||
fontPercent = fontPercent.toFixed(2);
|
||||
}
|
||||
|
||||
setSelectedOption(el.querySelector('.vjs-font-percent > select'), fontPercent);
|
||||
setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore texttrack settings
|
||||
* Restore texttrack settings
|
||||
*
|
||||
* @method restoreSettings
|
||||
*/
|
||||
@@ -174,7 +170,7 @@ class TextTrackSettings extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save texttrack settings to local storage
|
||||
* Save texttrack settings to local storage
|
||||
*
|
||||
* @method saveSettings
|
||||
*/
|
||||
@@ -194,7 +190,7 @@ class TextTrackSettings extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update display of texttrack settings
|
||||
* Update display of texttrack settings
|
||||
*
|
||||
* @method updateDisplay
|
||||
*/
|
||||
|
||||
+298
-22
@@ -7,6 +7,59 @@ import * as Guid from './guid.js';
|
||||
import log from './log.js';
|
||||
import tsml from 'tsml';
|
||||
|
||||
/**
|
||||
* Detect if a value is a string with any non-whitespace characters.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNonBlankString(str) {
|
||||
return typeof str === 'string' && /\S/.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error if the passed string has whitespace. This is used by
|
||||
* class methods to be relatively consistent with the classList API.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function throwIfWhitespace(str) {
|
||||
if (/\s/.test(str)) {
|
||||
throw new Error('class has illegal whitespace characters');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a regular expression for matching a class name.
|
||||
*
|
||||
* @param {String} className
|
||||
* @return {RegExp}
|
||||
*/
|
||||
function classRegExp(className) {
|
||||
return new RegExp('(^|\\s)' + className + '($|\\s)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates functions to query the DOM using a given method.
|
||||
*
|
||||
* @function createQuerier
|
||||
* @private
|
||||
* @param {String} method
|
||||
* @return {Function}
|
||||
*/
|
||||
function createQuerier(method) {
|
||||
return function (selector, context) {
|
||||
if (!isNonBlankString(selector)) {
|
||||
return document[method](null);
|
||||
}
|
||||
if (isNonBlankString(context)) {
|
||||
context = document.querySelector(context);
|
||||
}
|
||||
return (isEl(context) ? context : document)[method](selector);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for document.getElementById()
|
||||
* Also allows for CSS (jQuery) ID syntax. But nothing other than IDs.
|
||||
@@ -58,6 +111,22 @@ export function createEl(tagName='div', properties={}, attributes={}){
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects text into an element, replacing any existing contents entirely.
|
||||
*
|
||||
* @param {Element} el
|
||||
* @param {String} text
|
||||
* @return {Element}
|
||||
* @function textContent
|
||||
*/
|
||||
export function textContent(el, text) {
|
||||
if (typeof el.textContent === 'undefined') {
|
||||
el.innerText = text;
|
||||
} else {
|
||||
el.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an element as the first child node of another
|
||||
*
|
||||
@@ -165,47 +234,99 @@ export function removeElData(el) {
|
||||
/**
|
||||
* Check if an element has a CSS class
|
||||
*
|
||||
* @function hasElClass
|
||||
* @param {Element} element Element to check
|
||||
* @param {String} classToCheck Classname to check
|
||||
* @function hasElClass
|
||||
*/
|
||||
export function hasElClass(element, classToCheck) {
|
||||
return ((' ' + element.className + ' ').indexOf(' ' + classToCheck + ' ') !== -1);
|
||||
if (element.classList) {
|
||||
return element.classList.contains(classToCheck);
|
||||
} else {
|
||||
throwIfWhitespace(classToCheck);
|
||||
return classRegExp(classToCheck).test(element.className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS class name to an element
|
||||
*
|
||||
* @function addElClass
|
||||
* @param {Element} element Element to add class name to
|
||||
* @param {String} classToAdd Classname to add
|
||||
* @function addElClass
|
||||
*/
|
||||
export function addElClass(element, classToAdd) {
|
||||
if (!hasElClass(element, classToAdd)) {
|
||||
element.className = element.className === '' ? classToAdd : element.className + ' ' + classToAdd;
|
||||
if (element.classList) {
|
||||
element.classList.add(classToAdd);
|
||||
|
||||
// Don't need to `throwIfWhitespace` here because `hasElClass` will do it
|
||||
// in the case of classList not being supported.
|
||||
} else if (!hasElClass(element, classToAdd)) {
|
||||
element.className = (element.className + ' ' + classToAdd).trim();
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a CSS class name from an element
|
||||
*
|
||||
* @function removeElClass
|
||||
* @param {Element} element Element to remove from class name
|
||||
* @param {String} classToRemove Classname to remove
|
||||
* @function removeElClass
|
||||
*/
|
||||
export function removeElClass(element, classToRemove) {
|
||||
if (!hasElClass(element, classToRemove)) {return;}
|
||||
|
||||
let classNames = element.className.split(' ');
|
||||
|
||||
// no arr.indexOf in ie8, and we don't want to add a big shim
|
||||
for (let i = classNames.length - 1; i >= 0; i--) {
|
||||
if (classNames[i] === classToRemove) {
|
||||
classNames.splice(i,1);
|
||||
}
|
||||
if (element.classList) {
|
||||
element.classList.remove(classToRemove);
|
||||
} else {
|
||||
throwIfWhitespace(classToRemove);
|
||||
element.className = element.className.split(/\s+/).filter(function(c) {
|
||||
return c !== classToRemove;
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
element.className = classNames.join(' ');
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a CSS class name on an element depending on an optional
|
||||
* condition or the presence/absence of the class name.
|
||||
*
|
||||
* @function toggleElClass
|
||||
* @param {Element} element
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*/
|
||||
export function toggleElClass(element, classToToggle, predicate) {
|
||||
|
||||
// This CANNOT use `classList` internally because IE does not support the
|
||||
// second parameter to the `classList.toggle()` method! Which is fine because
|
||||
// `classList` will be used by the add/remove functions.
|
||||
let has = hasElClass(element, classToToggle);
|
||||
|
||||
if (typeof predicate === 'function') {
|
||||
predicate = predicate(element, classToToggle);
|
||||
}
|
||||
|
||||
if (typeof predicate !== 'boolean') {
|
||||
predicate = !has;
|
||||
}
|
||||
|
||||
// If the necessary class operation matches the current state of the
|
||||
// element, no action is required.
|
||||
if (predicate === has) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
addElClass(element, classToToggle);
|
||||
} else {
|
||||
removeElClass(element, classToToggle);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +397,7 @@ export function getElAttributes(tag) {
|
||||
* Attempt to block the ability to select text while dragging controls
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method blockTextSelection
|
||||
* @function blockTextSelection
|
||||
*/
|
||||
export function blockTextSelection() {
|
||||
document.body.focus();
|
||||
@@ -289,7 +410,7 @@ export function blockTextSelection() {
|
||||
* Turn off text selection blocking
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method unblockTextSelection
|
||||
* @function unblockTextSelection
|
||||
*/
|
||||
export function unblockTextSelection() {
|
||||
document.onselectstart = function() {
|
||||
@@ -302,9 +423,9 @@ export function unblockTextSelection() {
|
||||
* getBoundingClientRect technique from
|
||||
* John Resig http://ejohn.org/blog/getboundingclientrect-is-awesome/
|
||||
*
|
||||
* @function findElPosition
|
||||
* @param {Element} el Element from which to get offset
|
||||
* @return {Object=}
|
||||
* @method findElPosition
|
||||
* @return {Object}
|
||||
*/
|
||||
export function findElPosition(el) {
|
||||
let box;
|
||||
@@ -343,10 +464,10 @@ export function findElPosition(el) {
|
||||
* Returns an object with x and y coordinates.
|
||||
* The base on the coordinates are the bottom left of the element.
|
||||
*
|
||||
* @function getPointerPosition
|
||||
* @param {Element} el Element on which to get the pointer position on
|
||||
* @param {Event} event Event object
|
||||
* @return {Object=} position This object will have x and y coordinates corresponding to the mouse position
|
||||
* @metho getPointerPosition
|
||||
* @return {Object} This object will have x and y coordinates corresponding to the mouse position
|
||||
*/
|
||||
export function getPointerPosition(el, event) {
|
||||
let position = {};
|
||||
@@ -369,3 +490,158 @@ export function getPointerPosition(el, event) {
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a DOM element.
|
||||
*
|
||||
* @function isEl
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export function isEl(value) {
|
||||
return !!value && typeof value === 'object' && value.nodeType === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a text node.
|
||||
*
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export function isTextNode(value) {
|
||||
return !!value && typeof value === 'object' && value.nodeType === 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the contents of an element.
|
||||
*
|
||||
* @function emptyEl
|
||||
* @param {Element} el
|
||||
* @return {Element}
|
||||
*/
|
||||
export function emptyEl(el) {
|
||||
while (el.firstChild) {
|
||||
el.removeChild(el.firstChild);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes content for eventual insertion into the DOM.
|
||||
*
|
||||
* This allows a wide range of content definition methods, but protects
|
||||
* from falling into the trap of simply writing to `innerHTML`, which is
|
||||
* an XSS concern.
|
||||
*
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @function normalizeContent
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Array}
|
||||
*/
|
||||
export function normalizeContent(content) {
|
||||
|
||||
// First, invoke content if it is a function. If it produces an array,
|
||||
// that needs to happen before normalization.
|
||||
if (typeof content === 'function') {
|
||||
content = content();
|
||||
}
|
||||
|
||||
// Next up, normalize to an array, so one or many items can be normalized,
|
||||
// filtered, and returned.
|
||||
return (Array.isArray(content) ? content : [content]).map(value => {
|
||||
|
||||
// First, invoke value if it is a function to produce a new value,
|
||||
// which will be subsequently normalized to a Node of some kind.
|
||||
if (typeof value === 'function') {
|
||||
value = value();
|
||||
}
|
||||
|
||||
if (isEl(value) || isTextNode(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && /\S/.test(value)) {
|
||||
return document.createTextNode(value);
|
||||
}
|
||||
}).filter(value => value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes and appends content to an element.
|
||||
*
|
||||
* @function appendContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* See: `normalizeContent`
|
||||
* @return {Element}
|
||||
*/
|
||||
export function appendContent(el, content) {
|
||||
normalizeContent(content).forEach(node => el.appendChild(node));
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes and inserts content into an element; this is identical to
|
||||
* `appendContent()`, except it empties the element first.
|
||||
*
|
||||
* @function insertContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* See: `normalizeContent`
|
||||
* @return {Element}
|
||||
*/
|
||||
export function insertContent(el, content) {
|
||||
return appendContent(emptyEl(el), content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single DOM element matching `selector` within the optional
|
||||
* `context` of another DOM element (defaulting to `document`).
|
||||
*
|
||||
* @function $
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelector`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {Element|null}
|
||||
*/
|
||||
export const $ = createQuerier('querySelector');
|
||||
|
||||
/**
|
||||
* Finds a all DOM elements matching `selector` within the optional
|
||||
* `context` of another DOM element (defaulting to `document`).
|
||||
*
|
||||
* @function $$
|
||||
* @param {String} selector
|
||||
* A valid CSS selector, which will be passed to `querySelectorAll`.
|
||||
*
|
||||
* @param {Element|String} [context=document]
|
||||
* A DOM element within which to query. Can also be a selector
|
||||
* string in which case the first matching element will be used
|
||||
* as context. If missing (or no element matches selector), falls
|
||||
* back to `document`.
|
||||
*
|
||||
* @return {NodeList}
|
||||
*/
|
||||
export const $$ = createQuerier('querySelectorAll');
|
||||
|
||||
@@ -105,8 +105,8 @@ export const getFileExtension = function(path) {
|
||||
* @method isCrossOrigin
|
||||
*/
|
||||
export const isCrossOrigin = function(url) {
|
||||
let urlInfo = parseUrl(url);
|
||||
let winLoc = window.location;
|
||||
let urlInfo = parseUrl(url);
|
||||
|
||||
// IE8 protocol relative urls will return ':' for protocol
|
||||
let srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
|
||||
|
||||
+195
-24
@@ -26,6 +26,7 @@ import createDeprecationProxy from './utils/create-deprecation-proxy.js';
|
||||
import xhr from 'xhr';
|
||||
|
||||
// Include the built-in techs
|
||||
import Tech from './tech/tech.js';
|
||||
import Html5 from './tech/html5.js';
|
||||
import Flash from './tech/flash.js';
|
||||
|
||||
@@ -98,10 +99,10 @@ var videojs = function(id, options, ready){
|
||||
};
|
||||
|
||||
// Add default styles
|
||||
let style = document.querySelector('.vjs-styles-defaults');
|
||||
let style = Dom.$('.vjs-styles-defaults');
|
||||
if (!style) {
|
||||
style = stylesheet.createStyleElement('vjs-styles-defaults');
|
||||
let head = document.querySelector('head');
|
||||
let head = Dom.$('head');
|
||||
head.insertBefore(style, head.firstChild);
|
||||
stylesheet.setTextContent(style, `
|
||||
.video-js {
|
||||
@@ -202,7 +203,50 @@ videojs.getComponent = Component.getComponent;
|
||||
* @mixes videojs
|
||||
* @method registerComponent
|
||||
*/
|
||||
videojs.registerComponent = Component.registerComponent;
|
||||
videojs.registerComponent = (name, comp) => {
|
||||
if (Tech.isTech(comp)) {
|
||||
log.warn(`The ${name} tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)`);
|
||||
}
|
||||
|
||||
Component.registerComponent.call(Component, name, comp);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a Tech class object by name
|
||||
* ```js
|
||||
* var Html5 = videojs.getTech('Html5');
|
||||
* // Create a new instance of the component
|
||||
* var html5 = new Html5(options);
|
||||
* ```
|
||||
*
|
||||
* @return {Tech} Tech identified by name
|
||||
* @mixes videojs
|
||||
* @method getComponent
|
||||
*/
|
||||
videojs.getTech = Tech.getTech;
|
||||
|
||||
/**
|
||||
* Register a Tech so it can referred to by name.
|
||||
* This is used in the tech order for the player.
|
||||
*
|
||||
* ```js
|
||||
* // get the Html5 Tech
|
||||
* var Html5 = videojs.getTech('Html5');
|
||||
* var MyTech = videojs.extend(Html5, {});
|
||||
* // Register the new Tech
|
||||
* VjsButton.registerTech('Tech', MyTech);
|
||||
* var player = videojs('myplayer', {
|
||||
* techOrder: ['myTech', 'html5']
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param {String} The class name of the tech
|
||||
* @param {Tech} The tech class
|
||||
* @return {Tech} The newly registered Tech
|
||||
* @mixes videojs
|
||||
* @method registerTech
|
||||
*/
|
||||
videojs.registerTech = Component.registerTech;
|
||||
|
||||
/**
|
||||
* A suite of browser and device tests
|
||||
@@ -282,9 +326,9 @@ videojs.extend = extendFn;
|
||||
* // result.bar.b = [4,5,6];
|
||||
* ```
|
||||
*
|
||||
* @param {Object} The options object whose values will be overriden
|
||||
* @param {Object} The options object with values to override the first
|
||||
* @param {Object} Any number of additional options objects
|
||||
* @param {Object} defaults The options object whose values will be overriden
|
||||
* @param {Object} overrides The options object with values to override the first
|
||||
* @param {Object} etc Any number of additional options objects
|
||||
*
|
||||
* @return {Object} a new object with the merged values
|
||||
* @mixes videojs
|
||||
@@ -349,8 +393,8 @@ videojs.bind = Fn.bind;
|
||||
* // --> Should alert 'Plugin added later!'
|
||||
* ```
|
||||
*
|
||||
* @param {String} The plugin name
|
||||
* @param {Function} The plugin function that will be called with options
|
||||
* @param {String} name The plugin name
|
||||
* @param {Function} fn The plugin function that will be called with options
|
||||
* @mixes videojs
|
||||
* @method plugin
|
||||
*/
|
||||
@@ -501,22 +545,149 @@ videojs.xhr = xhr;
|
||||
*/
|
||||
videojs.TextTrack = TextTrack;
|
||||
|
||||
// REMOVING: We probably should add this to the migration plugin
|
||||
// // Expose but deprecate the window[componentName] method for accessing components
|
||||
// Object.getOwnPropertyNames(Component.components).forEach(function(name){
|
||||
// let component = Component.components[name];
|
||||
//
|
||||
// // A deprecation warning as the constuctor
|
||||
// module.exports[name] = function(player, options, ready){
|
||||
// log.warn('Using videojs.'+name+' to access the '+name+' component has been deprecated. Please use videojs.getComponent("componentName")');
|
||||
//
|
||||
// return new Component(player, options, ready);
|
||||
// };
|
||||
//
|
||||
// // Allow the prototype and class methods to be accessible still this way
|
||||
// // Though anything that attempts to override class methods will no longer work
|
||||
// assign(module.exports[name], component);
|
||||
// });
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a DOM element.
|
||||
*
|
||||
* @method isEl
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
videojs.isEl = Dom.isEl;
|
||||
|
||||
/**
|
||||
* Determines, via duck typing, whether or not a value is a text node.
|
||||
*
|
||||
* @method isTextNode
|
||||
* @param {Mixed} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
videojs.isTextNode = Dom.isTextNode;
|
||||
|
||||
/**
|
||||
* Check if an element has a CSS class
|
||||
*
|
||||
* @method hasClass
|
||||
* @param {Element} element Element to check
|
||||
* @param {String} classToCheck Classname to check
|
||||
*/
|
||||
videojs.hasClass = Dom.hasElClass;
|
||||
|
||||
/**
|
||||
* Add a CSS class name to an element
|
||||
*
|
||||
* @method addClass
|
||||
* @param {Element} element Element to add class name to
|
||||
* @param {String} classToAdd Classname to add
|
||||
*/
|
||||
videojs.addClass = Dom.addElClass;
|
||||
|
||||
/**
|
||||
* Remove a CSS class name from an element
|
||||
*
|
||||
* @method removeClass
|
||||
* @param {Element} element Element to remove from class name
|
||||
* @param {String} classToRemove Classname to remove
|
||||
*/
|
||||
videojs.removeClass = Dom.removeElClass;
|
||||
|
||||
/**
|
||||
* Adds or removes a CSS class name on an element depending on an optional
|
||||
* condition or the presence/absence of the class name.
|
||||
*
|
||||
* @method toggleElClass
|
||||
* @param {Element} element
|
||||
* @param {String} classToToggle
|
||||
* @param {Boolean|Function} [predicate]
|
||||
* Can be a function that returns a Boolean. If `true`, the class
|
||||
* will be added; if `false`, the class will be removed. If not
|
||||
* given, the class will be added if not present and vice versa.
|
||||
*/
|
||||
videojs.toggleClass = Dom.toggleElClass;
|
||||
|
||||
/**
|
||||
* Apply attributes to an HTML element.
|
||||
*
|
||||
* @method setAttributes
|
||||
* @param {Element} el Target element.
|
||||
* @param {Object=} attributes Element attributes to be applied.
|
||||
*/
|
||||
videojs.setAttributes = Dom.setElAttributes;
|
||||
|
||||
/**
|
||||
* Get an element's attribute values, as defined on the HTML tag
|
||||
* Attributes are not the same as properties. They're defined on the tag
|
||||
* or with setAttribute (which shouldn't be used with HTML)
|
||||
* This will return true or false for boolean attributes.
|
||||
*
|
||||
* @method getAttributes
|
||||
* @param {Element} tag Element from which to get tag attributes
|
||||
* @return {Object}
|
||||
*/
|
||||
videojs.getAttributes = Dom.getElAttributes;
|
||||
|
||||
/**
|
||||
* Empties the contents of an element.
|
||||
*
|
||||
* @method emptyEl
|
||||
* @param {Element} el
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.emptyEl = Dom.emptyEl;
|
||||
|
||||
/**
|
||||
* Normalizes and appends content to an element.
|
||||
*
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @method appendContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.appendContent = Dom.appendContent;
|
||||
|
||||
/**
|
||||
* Normalizes and inserts content into an element; this is identical to
|
||||
* `appendContent()`, except it empties the element first.
|
||||
*
|
||||
* The content for an element can be passed in multiple types and
|
||||
* combinations, whose behavior is as follows:
|
||||
*
|
||||
* - String
|
||||
* Normalized into a text node.
|
||||
*
|
||||
* - Element, TextNode
|
||||
* Passed through.
|
||||
*
|
||||
* - Array
|
||||
* A one-dimensional array of strings, elements, nodes, or functions (which
|
||||
* return single strings, elements, or nodes).
|
||||
*
|
||||
* - Function
|
||||
* If the sole argument, is expected to produce a string, element,
|
||||
* node, or array.
|
||||
*
|
||||
* @method insertContent
|
||||
* @param {Element} el
|
||||
* @param {String|Element|TextNode|Array|Function} content
|
||||
* @return {Element}
|
||||
*/
|
||||
videojs.insertContent = Dom.insertContent;
|
||||
|
||||
/*
|
||||
* Custom Universal Module Definition (UMD)
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
<div id="qunit"></div>
|
||||
<script src="../node_modules/qunitjs/qunit/qunit.js"></script>
|
||||
|
||||
<script src="../build/temp/ie8/videojs-ie8.min.js"></script>
|
||||
<script src="../build/temp/ie8/videojs-ie8.js"></script>
|
||||
|
||||
<!-- Execute the bundled tests first -->
|
||||
<script src="../build/temp/tests.js"></script>
|
||||
|
||||
+9
-18
@@ -3,10 +3,9 @@ module.exports = function(config) {
|
||||
var settings = {
|
||||
basePath: '',
|
||||
|
||||
frameworks: ['browserify', 'qunit'],
|
||||
frameworks: ['browserify', 'qunit', 'detectBrowsers'],
|
||||
autoWatch: false,
|
||||
singleRun: true,
|
||||
browsers: ['Chrome'],
|
||||
|
||||
// Compling tests here
|
||||
files: [
|
||||
@@ -46,9 +45,15 @@ module.exports = function(config) {
|
||||
'karma-safari-launcher',
|
||||
'karma-browserstack-launcher',
|
||||
'karma-browserify',
|
||||
'karma-coverage'
|
||||
'karma-coverage',
|
||||
'karma-detect-browsers',
|
||||
],
|
||||
|
||||
detectBrowsers: {
|
||||
enabled: false,
|
||||
usePhantomJS: false
|
||||
},
|
||||
|
||||
reporters: ['dots'],
|
||||
|
||||
// web server port
|
||||
@@ -98,9 +103,7 @@ module.exports = function(config) {
|
||||
'ie11_bs',
|
||||
'ie10_bs',
|
||||
'ie9_bs',
|
||||
'ie8_bs',
|
||||
'android_bs',
|
||||
'ios_bs'
|
||||
'ie8_bs'
|
||||
];
|
||||
} else {
|
||||
settings.browsers = ['Firefox'];
|
||||
@@ -163,18 +166,6 @@ function getCustomLaunchers(){
|
||||
browser_version: '8',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
|
||||
android_bs: {
|
||||
base: 'BrowserStack',
|
||||
os: 'android',
|
||||
os_version: '4.4'
|
||||
},
|
||||
|
||||
ios_bs: {
|
||||
base: 'BrowserStack',
|
||||
os: 'ios',
|
||||
os_version: '8.3'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import CloseButton from '../../src/js/close-button';
|
||||
import TestHelpers from './test-helpers';
|
||||
|
||||
q.module('CloseButton', {
|
||||
|
||||
beforeEach: function() {
|
||||
this.player = TestHelpers.makePlayer();
|
||||
this.btn = new CloseButton(this.player);
|
||||
},
|
||||
|
||||
afterEach: function() {
|
||||
this.player.dispose();
|
||||
this.btn.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
q.test('should create the expected element', function(assert) {
|
||||
let elAssertions = TestHelpers.assertEl(assert, this.btn.el(), {
|
||||
tagName: 'button',
|
||||
classes: [
|
||||
'vjs-button',
|
||||
'vjs-close-button',
|
||||
'vjs-control'
|
||||
]
|
||||
});
|
||||
|
||||
assert.expect(elAssertions.count + 1);
|
||||
elAssertions();
|
||||
assert.strictEqual(this.btn.el().querySelector('.vjs-control-text').innerHTML, 'Close');
|
||||
});
|
||||
|
||||
q.test('should allow setting the controlText_ property as an option', function(assert) {
|
||||
var text = 'OK!';
|
||||
var btn = new CloseButton(this.player, {controlText: text});
|
||||
|
||||
assert.expect(1);
|
||||
assert.strictEqual(btn.controlText_, text, 'set the controlText_ property');
|
||||
});
|
||||
|
||||
q.test('should trigger an event on activation', function(assert) {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.btn.on('close', spy);
|
||||
this.btn.trigger('click');
|
||||
assert.expect(1);
|
||||
assert.strictEqual(spy.callCount, 1, 'the "close" event was triggered');
|
||||
});
|
||||
@@ -5,6 +5,21 @@ import * as browser from '../../src/js/utils/browser.js';
|
||||
import document from 'global/document';
|
||||
import TestHelpers from './test-helpers.js';
|
||||
|
||||
class TestComponent1 extends Component {}
|
||||
class TestComponent2 extends Component {}
|
||||
class TestComponent3 extends Component {}
|
||||
class TestComponent4 extends Component {}
|
||||
TestComponent1.prototype.options_ = {
|
||||
children: [
|
||||
'testComponent2',
|
||||
'testComponent3'
|
||||
]
|
||||
};
|
||||
Component.registerComponent('TestComponent1', TestComponent1);
|
||||
Component.registerComponent('TestComponent2', TestComponent2);
|
||||
Component.registerComponent('TestComponent3', TestComponent3);
|
||||
Component.registerComponent('TestComponent4', TestComponent4);
|
||||
|
||||
q.module('Component', {
|
||||
'setup': function() {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
@@ -40,6 +55,14 @@ test('should add a child component', function(){
|
||||
ok(comp.getChildById(child.id()) === child);
|
||||
});
|
||||
|
||||
test('addChild should throw if the child does not exist', function() {
|
||||
var comp = new Component(getFakePlayer());
|
||||
|
||||
throws(function() {
|
||||
comp.addChild('non-existent-child');
|
||||
}, new Error('Component Non-existent-child does not exist'), 'addChild threw');
|
||||
});
|
||||
|
||||
test('should init child components from options', function(){
|
||||
var comp = new Component(getFakePlayer(), {
|
||||
children: {
|
||||
@@ -111,6 +134,16 @@ test('should do a deep merge of child options', function(){
|
||||
Component.prototype.options_ = null;
|
||||
});
|
||||
|
||||
test('should init child components from component options', function(){
|
||||
let testComp = new TestComponent1(TestHelpers.makePlayer(), {
|
||||
testComponent2: false,
|
||||
testComponent4: {}
|
||||
});
|
||||
|
||||
ok(!testComp.childNameIndex_.testComponent2, 'we do not have testComponent2');
|
||||
ok(testComp.childNameIndex_.testComponent4, 'we have a testComponent4');
|
||||
});
|
||||
|
||||
test('should allows setting child options at the parent options level', function(){
|
||||
var parent, options;
|
||||
|
||||
@@ -133,6 +166,7 @@ test('should allows setting child options at the parent options level', function
|
||||
ok(false, 'Child with `false` option was initialized');
|
||||
}
|
||||
equal(parent.children()[0].options_['foo'], true, 'child options set when children array is used');
|
||||
equal(parent.children().length, 1, 'we should only have one child');
|
||||
|
||||
// using children object
|
||||
options = {
|
||||
@@ -155,6 +189,7 @@ test('should allows setting child options at the parent options level', function
|
||||
ok(false, 'Child with `false` option was initialized');
|
||||
}
|
||||
equal(parent.children()[0].options_['foo'], true, 'child options set when children object is used');
|
||||
equal(parent.children().length, 1, 'we should only have one child');
|
||||
});
|
||||
|
||||
test('should dispose of component and children', function(){
|
||||
@@ -436,6 +471,10 @@ test('should add and remove a CSS class', function(){
|
||||
ok(comp.el().className.indexOf('test-class') !== -1);
|
||||
comp.removeClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') === -1);
|
||||
comp.toggleClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') !== -1);
|
||||
comp.toggleClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') === -1);
|
||||
});
|
||||
|
||||
test('should show and hide an element', function(){
|
||||
@@ -660,3 +699,18 @@ test('should provide interval methods that automatically get cleared on componen
|
||||
|
||||
ok(intervalsFired === 5, 'Interval was cleared when component was disposed');
|
||||
});
|
||||
|
||||
test('$ and $$ functions', function() {
|
||||
var comp = new Component(getFakePlayer());
|
||||
var contentEl = document.createElement('div');
|
||||
var children = [
|
||||
document.createElement('div'),
|
||||
document.createElement('div')
|
||||
];
|
||||
|
||||
comp.contentEl_ = contentEl;
|
||||
children.forEach(child => contentEl.appendChild(child));
|
||||
|
||||
strictEqual(comp.$('div'), children[0], '$ defaults to contentEl as scope');
|
||||
strictEqual(comp.$$('div').length, children.length, '$$ defaults to contentEl as scope');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
import CloseButton from '../../src/js/close-button';
|
||||
import ModalDialog from '../../src/js/modal-dialog';
|
||||
import * as Dom from '../../src/js/utils/dom';
|
||||
import * as Fn from '../../src/js/utils/fn';
|
||||
import TestHelpers from './test-helpers';
|
||||
|
||||
var ESC = 27;
|
||||
|
||||
q.module('ModalDialog', {
|
||||
|
||||
beforeEach: function() {
|
||||
this.player = TestHelpers.makePlayer();
|
||||
this.modal = new ModalDialog(this.player, {temporary: false});
|
||||
this.el = this.modal.el();
|
||||
},
|
||||
|
||||
afterEach: function() {
|
||||
this.player.dispose();
|
||||
this.modal.dispose();
|
||||
this.el = null;
|
||||
}
|
||||
});
|
||||
|
||||
q.test('should create the expected element', function(assert) {
|
||||
let elAssertions = TestHelpers.assertEl(assert, this.el, {
|
||||
tagName: 'div',
|
||||
classes: [
|
||||
'vjs-modal-dialog',
|
||||
'vjs-hidden'
|
||||
],
|
||||
attrs: {
|
||||
'aria-describedby': this.modal.descEl_.id,
|
||||
'aria-hidden': 'true',
|
||||
'aria-label': this.modal.label(),
|
||||
'role': 'dialog'
|
||||
},
|
||||
props: {
|
||||
tabIndex: -1
|
||||
}
|
||||
});
|
||||
|
||||
assert.expect(elAssertions.count);
|
||||
elAssertions();
|
||||
});
|
||||
|
||||
q.test('should create the expected description element', function(assert) {
|
||||
let elAssertions = TestHelpers.assertEl(assert, this.modal.descEl_, {
|
||||
tagName: 'p',
|
||||
innerHTML: this.modal.description(),
|
||||
classes: [
|
||||
'vjs-modal-dialog-description',
|
||||
'vjs-offscreen'
|
||||
],
|
||||
attrs: {
|
||||
id: this.el.getAttribute('aria-describedby')
|
||||
}
|
||||
});
|
||||
|
||||
assert.expect(elAssertions.count);
|
||||
elAssertions();
|
||||
});
|
||||
|
||||
q.test('should create the expected contentEl', function(assert) {
|
||||
let elAssertions = TestHelpers.assertEl(assert, this.modal.contentEl(), {
|
||||
tagName: 'div',
|
||||
classes: [
|
||||
'vjs-modal-dialog-content'
|
||||
],
|
||||
props: {
|
||||
parentNode: this.el
|
||||
}
|
||||
});
|
||||
|
||||
assert.expect(elAssertions.count);
|
||||
elAssertions();
|
||||
});
|
||||
|
||||
q.test('should create a close button by default', function(assert) {
|
||||
var btn = this.modal.getChild('closeButton');
|
||||
|
||||
// We only check the aspects of the button that relate to the modal. Other
|
||||
// aspects of the button (classes, etc) are tested in their appropriate test
|
||||
// module.
|
||||
assert.expect(2);
|
||||
assert.ok(btn instanceof CloseButton, 'close button is a CloseButton');
|
||||
assert.strictEqual(btn.el().parentNode, this.el, 'close button is a child of el');
|
||||
});
|
||||
|
||||
q.test('returns `this` for expected methods', function(assert) {
|
||||
var methods = ['close', 'empty', 'fill', 'fillWith', 'open'];
|
||||
|
||||
assert.expect(methods.length);
|
||||
methods.forEach(function(method) {
|
||||
assert.strictEqual(this[method](), this, '`' + method + '()` returns `this`');
|
||||
}, this.modal);
|
||||
});
|
||||
|
||||
q.test('open() triggers events', function(assert) {
|
||||
var modal = this.modal;
|
||||
var beforeModalOpenSpy = sinon.spy(function() {
|
||||
assert.notOk(modal.opened(), 'modal is not opened before opening event');
|
||||
});
|
||||
|
||||
var modalOpenSpy = sinon.spy(function() {
|
||||
assert.ok(modal.opened(), 'modal is opened on opening event');
|
||||
});
|
||||
|
||||
assert.expect(4);
|
||||
|
||||
modal.
|
||||
on('beforemodalopen', beforeModalOpenSpy).
|
||||
on('modalopen', modalOpenSpy).
|
||||
open();
|
||||
|
||||
assert.strictEqual(beforeModalOpenSpy.callCount, 1, 'beforemodalopen spy was called');
|
||||
assert.strictEqual(modalOpenSpy.callCount, 1, 'modalopen spy was called');
|
||||
});
|
||||
|
||||
q.test('open() removes "vjs-hidden" class', function(assert) {
|
||||
assert.expect(2);
|
||||
assert.ok(this.modal.hasClass('vjs-hidden'), 'modal starts hidden');
|
||||
this.modal.open();
|
||||
assert.notOk(this.modal.hasClass('vjs-hidden'), 'modal is not hidden after opening');
|
||||
});
|
||||
|
||||
q.test('open() cannot be called on an opened modal', function(assert) {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.modal.on('modalopen', spy).open().open();
|
||||
|
||||
assert.expect(1);
|
||||
assert.strictEqual(spy.callCount, 1, 'modal was only opened once');
|
||||
});
|
||||
|
||||
q.test('close() triggers events', function(assert) {
|
||||
var modal = this.modal;
|
||||
var beforeModalCloseSpy = sinon.spy(function() {
|
||||
assert.ok(modal.opened(), 'modal is not closed before closing event');
|
||||
});
|
||||
|
||||
var modalCloseSpy = sinon.spy(function() {
|
||||
assert.notOk(modal.opened(), 'modal is closed on closing event');
|
||||
});
|
||||
|
||||
assert.expect(4);
|
||||
|
||||
modal.
|
||||
on('beforemodalclose', beforeModalCloseSpy).
|
||||
on('modalclose', modalCloseSpy).
|
||||
open().
|
||||
close();
|
||||
|
||||
assert.strictEqual(beforeModalCloseSpy.callCount, 1, 'beforemodalclose spy was called');
|
||||
assert.strictEqual(modalCloseSpy.callCount, 1, 'modalclose spy was called');
|
||||
});
|
||||
|
||||
q.test('close() adds the "vjs-hidden" class', function(assert) {
|
||||
assert.expect(1);
|
||||
this.modal.open().close();
|
||||
assert.ok(this.modal.hasClass('vjs-hidden'), 'modal is hidden upon close');
|
||||
});
|
||||
|
||||
q.test('pressing ESC triggers close(), but only when the modal is opened', function(assert) {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.modal.on('modalclose', spy).handleKeyPress({which: ESC});
|
||||
assert.expect(2);
|
||||
assert.strictEqual(spy.callCount, 0, 'ESC did not close the closed modal');
|
||||
|
||||
this.modal.open().handleKeyPress({which: ESC});
|
||||
assert.strictEqual(spy.callCount, 1, 'ESC closed the now-opened modal');
|
||||
});
|
||||
|
||||
q.test('close() cannot be called on a closed modal', function(assert) {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.modal.on('modalclose', spy);
|
||||
this.modal.open().close().close();
|
||||
|
||||
assert.expect(1);
|
||||
assert.strictEqual(spy.callCount, 1, 'modal was only closed once');
|
||||
});
|
||||
|
||||
q.test('open() pauses playback, close() resumes', function(assert) {
|
||||
var playSpy = sinon.spy();
|
||||
var pauseSpy = sinon.spy();
|
||||
|
||||
// Quick and dirty; make it looks like the player is playing.
|
||||
this.player.paused = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
this.player.play = function() {
|
||||
playSpy();
|
||||
};
|
||||
|
||||
this.player.pause = function() {
|
||||
pauseSpy();
|
||||
};
|
||||
|
||||
this.modal.open();
|
||||
|
||||
assert.expect(2);
|
||||
assert.strictEqual(pauseSpy.callCount, 1, 'player is paused when the modal opens');
|
||||
|
||||
this.modal.close();
|
||||
assert.strictEqual(playSpy.callCount, 1, 'player is resumed when the modal closes');
|
||||
});
|
||||
|
||||
q.test('open() hides controls, close() shows controls', function(assert) {
|
||||
this.modal.open();
|
||||
|
||||
assert.expect(2);
|
||||
assert.notOk(this.player.controls_, 'controls are hidden');
|
||||
|
||||
this.modal.close();
|
||||
assert.ok(this.player.controls_, 'controls are no longer hidden');
|
||||
});
|
||||
|
||||
q.test('opened()', function(assert) {
|
||||
var openSpy = sinon.spy();
|
||||
var closeSpy = sinon.spy();
|
||||
|
||||
assert.expect(4);
|
||||
assert.strictEqual(this.modal.opened(), false, 'the modal is closed');
|
||||
this.modal.open();
|
||||
assert.strictEqual(this.modal.opened(), true, 'the modal is open');
|
||||
|
||||
this.modal.
|
||||
close().
|
||||
on('modalopen', openSpy).
|
||||
on('modalclose', closeSpy).
|
||||
opened(true);
|
||||
|
||||
this.modal.opened(true);
|
||||
this.modal.opened(false);
|
||||
assert.strictEqual(openSpy.callCount, 1, 'modal was opened only once');
|
||||
assert.strictEqual(closeSpy.callCount, 1, 'modal was closed only once');
|
||||
});
|
||||
|
||||
q.test('content()', function(assert) {
|
||||
var content;
|
||||
|
||||
assert.expect(3);
|
||||
assert.strictEqual(typeof this.modal.content(), 'undefined', 'no content by default');
|
||||
|
||||
content = this.modal.content(Dom.createEl());
|
||||
assert.ok(Dom.isEl(content), 'content was set from a single DOM element');
|
||||
|
||||
assert.strictEqual(this.modal.content(null), null, 'content was nullified');
|
||||
});
|
||||
|
||||
q.test('fillWith()', function(assert) {
|
||||
var contentEl = this.modal.contentEl();
|
||||
var children = [Dom.createEl(), Dom.createEl(), Dom.createEl()];
|
||||
var beforeFillSpy = sinon.spy();
|
||||
var fillSpy = sinon.spy();
|
||||
|
||||
children.forEach(function(el) {
|
||||
contentEl.appendChild(el);
|
||||
});
|
||||
|
||||
this.modal.
|
||||
on('beforemodalfill', beforeFillSpy).
|
||||
on('modalfill', fillSpy).
|
||||
fillWith(children);
|
||||
|
||||
assert.expect(5 + children.length);
|
||||
assert.strictEqual(contentEl.children.length, children.length, 'has the right number of children');
|
||||
|
||||
children.forEach(function(el) {
|
||||
assert.strictEqual(el.parentNode, contentEl, 'new child appended');
|
||||
});
|
||||
|
||||
assert.strictEqual(beforeFillSpy.callCount, 1, 'the "beforemodalfill" callback was called');
|
||||
assert.strictEqual(beforeFillSpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
|
||||
assert.strictEqual(fillSpy.callCount, 1, 'the "modalfill" callback was called');
|
||||
assert.strictEqual(fillSpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
|
||||
});
|
||||
|
||||
q.test('empty()', function(assert) {
|
||||
var beforeEmptySpy = sinon.spy();
|
||||
var emptySpy = sinon.spy();
|
||||
|
||||
this.modal.
|
||||
fillWith([Dom.createEl(), Dom.createEl()]).
|
||||
on('beforemodalempty', beforeEmptySpy).
|
||||
on('modalempty', emptySpy).
|
||||
empty();
|
||||
|
||||
assert.expect(5);
|
||||
assert.strictEqual(this.modal.contentEl().children.length, 0, 'removed all `contentEl()` children');
|
||||
assert.strictEqual(beforeEmptySpy.callCount, 1, 'the "beforemodalempty" callback was called');
|
||||
assert.strictEqual(beforeEmptySpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
|
||||
assert.strictEqual(emptySpy.callCount, 1, 'the "modalempty" callback was called');
|
||||
assert.strictEqual(emptySpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
|
||||
});
|
||||
|
||||
q.test('closeable()', function(assert) {
|
||||
let initialCloseButton = this.modal.getChild('closeButton');
|
||||
|
||||
assert.expect(8);
|
||||
assert.strictEqual(this.modal.closeable(), true, 'the modal is closed');
|
||||
|
||||
this.modal.open().closeable(false);
|
||||
assert.notOk(this.modal.getChild('closeButton'), 'the close button is no longer a child of the modal');
|
||||
assert.notOk(initialCloseButton.el(), 'the initial close button was disposed');
|
||||
|
||||
this.modal.handleKeyPress({which: ESC});
|
||||
assert.ok(this.modal.opened(), 'the modal was not closed by the ESC key');
|
||||
|
||||
this.modal.close();
|
||||
assert.notOk(this.modal.opened(), 'the modal was closed programmatically');
|
||||
|
||||
this.modal.open().closeable(true);
|
||||
assert.ok(this.modal.getChild('closeButton'), 'a new close button was created');
|
||||
|
||||
this.modal.getChild('closeButton').trigger('click');
|
||||
assert.notOk(this.modal.opened(), 'the modal was closed by the new close button');
|
||||
|
||||
this.modal.open().handleKeyPress({which: ESC});
|
||||
assert.notOk(this.modal.opened(), 'the modal was closed by the ESC key');
|
||||
});
|
||||
|
||||
q.test('"content" option (fills on first open() invocation)', function(assert) {
|
||||
var modal = new ModalDialog(this.player, {
|
||||
content: Dom.createEl(),
|
||||
temporary: false
|
||||
});
|
||||
|
||||
var spy = sinon.spy();
|
||||
|
||||
modal.on('modalfill', spy);
|
||||
modal.open().close().open();
|
||||
|
||||
assert.expect(3);
|
||||
assert.strictEqual(modal.content(), modal.options_.content, 'has the expected content');
|
||||
assert.strictEqual(spy.callCount, 1, 'auto-fills only once');
|
||||
assert.strictEqual(modal.contentEl().firstChild, modal.options_.content, 'has the expected content in the DOM');
|
||||
});
|
||||
|
||||
q.test('"temporary" option', function(assert) {
|
||||
var temp = new ModalDialog(this.player, {temporary: true});
|
||||
var tempSpy = sinon.spy();
|
||||
var perm = new ModalDialog(this.player, {temporary: false});
|
||||
var permSpy = sinon.spy();
|
||||
|
||||
temp.on('dispose', tempSpy);
|
||||
perm.on('dispose', permSpy);
|
||||
temp.open().close();
|
||||
perm.open().close();
|
||||
|
||||
assert.expect(2);
|
||||
assert.strictEqual(tempSpy.callCount, 1, 'temporary modals are disposed');
|
||||
assert.strictEqual(permSpy.callCount, 0, 'permanent modals are not disposed');
|
||||
});
|
||||
|
||||
q.test('"fillAlways" option', function(assert) {
|
||||
var modal = new ModalDialog(this.player, {
|
||||
content: 'foo',
|
||||
fillAlways: true,
|
||||
temporary: false
|
||||
});
|
||||
|
||||
var spy = sinon.spy();
|
||||
|
||||
modal.on('modalfill', spy);
|
||||
modal.open().close().open();
|
||||
|
||||
assert.expect(1);
|
||||
assert.strictEqual(spy.callCount, 2, 'the modal was filled on each open call');
|
||||
});
|
||||
|
||||
q.test('"label" option', function(assert) {
|
||||
var label = 'foo';
|
||||
var modal = new ModalDialog(this.player, {label: label});
|
||||
|
||||
assert.expect(1);
|
||||
assert.strictEqual(modal.el().getAttribute('aria-label'), label, 'uses the label as the aria-label');
|
||||
});
|
||||
|
||||
q.test('"uncloseable" option', function(assert) {
|
||||
var modal = new ModalDialog(this.player, {
|
||||
temporary: false,
|
||||
uncloseable: true
|
||||
});
|
||||
|
||||
var spy = sinon.spy();
|
||||
|
||||
modal.on('modalclose', spy);
|
||||
|
||||
assert.expect(3);
|
||||
assert.strictEqual(modal.closeable(), false, 'the modal is uncloseable');
|
||||
assert.notOk(modal.getChild('closeButton'), 'the close button is not present');
|
||||
|
||||
modal.open().handleKeyPress({which: ESC});
|
||||
assert.strictEqual(spy.callCount, 0, 'ESC did not close the modal');
|
||||
});
|
||||
@@ -824,3 +824,38 @@ expect(3);
|
||||
player.language('en-GB');
|
||||
strictEqual(player.localize('Good'), 'Brilliant', 'Ignored case');
|
||||
});
|
||||
|
||||
test('should return correct values for canPlayType', function(){
|
||||
var player = TestHelpers.makePlayer();
|
||||
|
||||
equal(player.canPlayType('video/mp4'), 'maybe', 'player can play mp4 files');
|
||||
equal(player.canPlayType('video/unsupported-format'), '', 'player can not play unsupported files');
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
test('createModal()', function() {
|
||||
var player = TestHelpers.makePlayer();
|
||||
var modal = player.createModal('foo');
|
||||
var spy = sinon.spy();
|
||||
|
||||
modal.on('dispose', spy);
|
||||
|
||||
expect(5);
|
||||
strictEqual(modal.el().parentNode, player.el(), 'the modal is injected into the player');
|
||||
strictEqual(modal.content(), 'foo', 'content is set properly');
|
||||
ok(modal.opened(), 'modal is opened by default');
|
||||
modal.close();
|
||||
ok(spy.called, 'modal was disposed when closed');
|
||||
strictEqual(player.children().indexOf(modal), -1, 'modal was removed from player\'s children');
|
||||
});
|
||||
|
||||
test('createModal() options object', function() {
|
||||
var player = TestHelpers.makePlayer();
|
||||
var modal = player.createModal('foo', {content: 'bar', label: 'boo'});
|
||||
|
||||
expect(2);
|
||||
strictEqual(modal.content(), 'foo', 'content argument takes precedence');
|
||||
strictEqual(modal.options_.label, 'boo', 'modal options are set properly');
|
||||
modal.close();
|
||||
});
|
||||
|
||||
@@ -22,6 +22,10 @@ test('test streamToParts', function() {
|
||||
ok(parts.connection === 'http://myurl.com/');
|
||||
ok(parts.stream === 'streaming&/is/fun');
|
||||
|
||||
parts = Flash.streamToParts('http://myurl.com/really?streaming=fun&really=fun');
|
||||
ok(parts.connection === 'http://myurl.com/');
|
||||
ok(parts.stream === 'really?streaming=fun&really=fun');
|
||||
|
||||
parts = Flash.streamToParts('http://myurl.com/streaming/is/fun');
|
||||
ok(parts.connection === 'http://myurl.com/streaming/is/');
|
||||
ok(parts.stream === 'fun');
|
||||
|
||||
@@ -135,6 +135,16 @@ test('should have the source handler interface', function() {
|
||||
ok(Flash.registerSourceHandler, 'has the registerSourceHandler function');
|
||||
});
|
||||
|
||||
test('canPlayType should select the correct types to play', function () {
|
||||
let canPlayType = Flash.nativeSourceHandler.canPlayType;
|
||||
|
||||
equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files');
|
||||
equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files');
|
||||
equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files');
|
||||
equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files');
|
||||
equal(canPlayType('video/ogg'), '', 'should return empty string if it can not play the video');
|
||||
});
|
||||
|
||||
test('canHandleSource should be able to work with src objects without a type', function () {
|
||||
let canHandleSource = Flash.nativeSourceHandler.canHandleSource;
|
||||
|
||||
|
||||
@@ -155,6 +155,27 @@ test('should have the source handler interface', function() {
|
||||
ok(Html5.registerSourceHandler, 'has the registerSourceHandler function');
|
||||
});
|
||||
|
||||
test('native source handler canPlayType', function(){
|
||||
var result;
|
||||
|
||||
// Stub the test video canPlayType (used in canPlayType) to control results
|
||||
var origCPT = Html5.TEST_VID.canPlayType;
|
||||
Html5.TEST_VID.canPlayType = function(type){
|
||||
if (type === 'video/mp4') {
|
||||
return 'maybe';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
var canPlayType = Html5.nativeSourceHandler.canPlayType;
|
||||
|
||||
equal(canPlayType('video/mp4'), 'maybe', 'Native source handler reported type support');
|
||||
equal(canPlayType('foo'), '', 'Native source handler handled bad type');
|
||||
|
||||
// Reset test video canPlayType
|
||||
Html5.TEST_VID.canPlayType = origCPT;
|
||||
});
|
||||
|
||||
test('native source handler canHandleSource', function(){
|
||||
var result;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// can run without HTML5 or Flash, of which PhantomJS supports neither.
|
||||
|
||||
import Tech from '../../../src/js/tech/tech.js';
|
||||
import Component from '../../../src/js/component.js';
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
@@ -50,8 +49,9 @@ class TechFaker extends Tech {
|
||||
|
||||
// Support everything except for "video/unsupported-format"
|
||||
static isSupported() { return true; }
|
||||
static canPlayType(type) { return (type !== 'video/unsupported-format' ? 'maybe' : ''); }
|
||||
static canPlaySource(srcObj) { return srcObj.type !== 'video/unsupported-format'; }
|
||||
}
|
||||
|
||||
Component.registerComponent('TechFaker', TechFaker);
|
||||
Tech.registerTech('TechFaker', TechFaker);
|
||||
export default TechFaker;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
var noop = function() {}, clock, oldTextTracks;
|
||||
|
||||
import Tech from '../../../src/js/tech/tech.js';
|
||||
import Html5 from '../../../src/js/tech/html5.js';
|
||||
import Flash from '../../../src/js/tech/flash.js';
|
||||
import Button from '../../../src/js/button.js';
|
||||
import { createTimeRange } from '../../../src/js/utils/time-ranges.js';
|
||||
import extendFn from '../../../src/js/extend.js';
|
||||
import MediaError from '../../../src/js/media-error.js';
|
||||
@@ -132,6 +135,12 @@ test('should add the source handler interface to a tech', function(){
|
||||
|
||||
// Create source handlers
|
||||
var handlerOne = {
|
||||
canPlayType: function(type){
|
||||
if (type !=='no-support') {
|
||||
return 'probably';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
canHandleSource: function(source){
|
||||
if (source.type !=='no-support') {
|
||||
return 'probably';
|
||||
@@ -146,6 +155,9 @@ test('should add the source handler interface to a tech', function(){
|
||||
};
|
||||
|
||||
var handlerTwo = {
|
||||
canPlayType: function(type){
|
||||
return ''; // no support
|
||||
},
|
||||
canHandleSource: function(source){
|
||||
return ''; // no support
|
||||
},
|
||||
@@ -164,6 +176,10 @@ test('should add the source handler interface to a tech', function(){
|
||||
strictEqual(MyTech.selectSourceHandler(sourceA), handlerOne, 'handlerOne was selected to handle the valid source');
|
||||
strictEqual(MyTech.selectSourceHandler(sourceB), null, 'no handler was selected to handle the invalid source');
|
||||
|
||||
// Test canPlayType return values
|
||||
strictEqual(MyTech.canPlayType(sourceA.type), 'probably', 'the Tech returned probably for the valid source');
|
||||
strictEqual(MyTech.canPlayType(sourceB.type), '', 'the Tech returned an empty string for the invalid source');
|
||||
|
||||
// Test canPlaySource return values
|
||||
strictEqual(MyTech.canPlaySource(sourceA), 'probably', 'the Tech returned probably for the valid source');
|
||||
strictEqual(MyTech.canPlaySource(sourceB), '', 'the Tech returned an empty string for the invalid source');
|
||||
@@ -239,6 +255,9 @@ test('delegates seekable to the source handler', function(){
|
||||
};
|
||||
|
||||
MyTech.registerSourceHandler({
|
||||
canPlayType: function() {
|
||||
return true;
|
||||
},
|
||||
canHandleSource: function() {
|
||||
return true;
|
||||
},
|
||||
@@ -255,3 +274,17 @@ test('delegates seekable to the source handler', function(){
|
||||
tech.seekable();
|
||||
equal(seekableCount, 1, 'called the source handler');
|
||||
});
|
||||
|
||||
test('Tech.isTech returns correct answers for techs and components', function() {
|
||||
let isTech = Tech.isTech;
|
||||
|
||||
ok(isTech(Tech), 'Tech is a Tech');
|
||||
ok(isTech(Html5), 'Html5 is a Tech');
|
||||
ok(isTech(new Html5({}, {})), 'An html5 instance is a Tech');
|
||||
ok(isTech(Flash), 'Flash is a Tech');
|
||||
ok(!isTech(5), 'A number is not a Tech');
|
||||
ok(!isTech('this is a tech'), 'A string is not a Tech');
|
||||
ok(!isTech(Button), 'A Button is not a Tech');
|
||||
ok(!isTech(new Button({}, {})), 'A Button instance is not a Tech');
|
||||
ok(!isTech(isTech), 'A function is not a Tech');
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as Dom from '../../src/js/utils/dom';
|
||||
import Player from '../../src/js/player.js';
|
||||
import TechFaker from './tech/tech-faker.js';
|
||||
import window from 'global/window';
|
||||
@@ -40,6 +41,90 @@ var TestHelpers = {
|
||||
return el.currentStyle[rule];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs a range of assertions on a DOM element.
|
||||
*
|
||||
* @param {QUnit.Assert} assert
|
||||
* @param {Element} el
|
||||
* @param {Object} spec
|
||||
* An object from which assertions are generated.
|
||||
*
|
||||
* @param {Object} [spec.attrs]
|
||||
* An object mapping attribute names (keys) to strict values.
|
||||
*
|
||||
* @param {Array} [spec.classes]
|
||||
* An array of classes that are expected on the element.
|
||||
*
|
||||
* @param {String} [spec.innerHTML]
|
||||
* A string of text/html that is expected as the content of element.
|
||||
* Both values will be trimmed, but remains case-sensitive.
|
||||
*
|
||||
* @param {Object} [spec.props]
|
||||
* An object mapping property names (keys) to strict values.
|
||||
*
|
||||
* @param {String} [spec.tagName]
|
||||
* A string (case-insensitive) representing that element's tagName.
|
||||
*
|
||||
* @return {Function}
|
||||
* Invoke the returned function to run the assertions. This
|
||||
* function has a `count` property which can be used to
|
||||
* reference how many assertions will be run (e.g. for use
|
||||
* with `assert.expect()`).
|
||||
*/
|
||||
assertEl: function(assert, el, spec) {
|
||||
let attrs = spec.attrs ? Object.keys(spec.attrs) : [];
|
||||
let classes = spec.classes || [];
|
||||
let innerHTML = spec.innerHTML ? spec.innerHTML.trim() : '';
|
||||
let props = spec.props ? Object.keys(spec.props) : [];
|
||||
let tagName = spec.tagName ? spec.tagName.toLowerCase() : '';
|
||||
|
||||
// Return value is a function, which runs through all the combined
|
||||
// assertions. This is done so that the count can be attached dynamically
|
||||
// and run whenever desired.
|
||||
let run = () => {
|
||||
if (tagName) {
|
||||
let elTagName = el.tagName.toLowerCase();
|
||||
let msg = `el should have been a <${tagName}> and was a <${elTagName}>`;
|
||||
assert.strictEqual(elTagName, tagName, msg);
|
||||
}
|
||||
|
||||
if (innerHTML) {
|
||||
let elInnerHTML = el.innerHTML.trim();
|
||||
let msg = `el should have expected HTML content`;
|
||||
assert.strictEqual(elInnerHTML, innerHTML, msg);
|
||||
}
|
||||
|
||||
attrs.forEach(a => {
|
||||
let actual = el.getAttribute(a);
|
||||
let expected = spec.attrs[a];
|
||||
let msg = `el should have the "${a}" attribute with the value "${expected}" and it was "${actual}"`;
|
||||
assert.strictEqual(actual, expected, msg);
|
||||
});
|
||||
|
||||
classes.forEach(c => {
|
||||
let msg = `el should have the "${c}" class in its className, which is "${el.className}"`;
|
||||
assert.ok(Dom.hasElClass(el, c), msg);
|
||||
});
|
||||
|
||||
props.forEach(p => {
|
||||
let actual = el[p];
|
||||
let expected = spec.props[p];
|
||||
let msg = `el should have the "${p}" property with the value "${expected}" and it was "${actual}"`;
|
||||
assert.strictEqual(actual, expected, msg);
|
||||
});
|
||||
};
|
||||
|
||||
// Include the number of assertions to run, so it can be used to set
|
||||
// expectations (via `assert.expect()`).
|
||||
run.count = Number(!!tagName) +
|
||||
Number(!!innerHTML) +
|
||||
classes.length +
|
||||
attrs.length +
|
||||
props.length;
|
||||
|
||||
return run;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,13 +26,12 @@ let cleanup = (item) => {
|
||||
if (Html5.supportsNativeTextTracks()) {
|
||||
q.test('trackToJson_ produces correct representation for native track object', function(a) {
|
||||
let track = document.createElement('track');
|
||||
track.src = 'http://example.com/english.vtt';
|
||||
track.src = 'example.com/english.vtt';
|
||||
track.kind = 'captions';
|
||||
track.srclang = 'en';
|
||||
track.label = 'English';
|
||||
|
||||
a.deepEqual(cleanup(c.trackToJson_(track.track)), {
|
||||
src: undefined,
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
@@ -45,12 +44,10 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
src: 'http://example.com/english.vtt',
|
||||
tech: {}
|
||||
});
|
||||
|
||||
let nativeTrack = document.createElement('track');
|
||||
nativeTrack.src = 'http://example.com/spanish.vtt';
|
||||
nativeTrack.kind = 'captions';
|
||||
nativeTrack.srclang = 'es';
|
||||
nativeTrack.label = 'Spanish';
|
||||
@@ -60,6 +57,10 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
tt.addTrack_(emulatedTrack);
|
||||
|
||||
let tech = {
|
||||
$$() {
|
||||
return [nativeTrack];
|
||||
},
|
||||
|
||||
el() {
|
||||
return {
|
||||
querySelectorAll() {
|
||||
@@ -73,13 +74,11 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
};
|
||||
|
||||
a.deepEqual(cleanup(c.textTracksToJson(tech)), [{
|
||||
src: 'http://example.com/spanish.vtt',
|
||||
kind: 'captions',
|
||||
label: 'Spanish',
|
||||
language: 'es',
|
||||
mode: 'disabled'
|
||||
}, {
|
||||
src: 'http://example.com/english.vtt',
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
@@ -92,12 +91,12 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
tech: {}
|
||||
});
|
||||
|
||||
let nativeTrack = document.createElement('track');
|
||||
nativeTrack.src = 'http://example.com/spanish.vtt';
|
||||
nativeTrack.src = 'example.com/spanish.vtt';
|
||||
nativeTrack.kind = 'captions';
|
||||
nativeTrack.srclang = 'es';
|
||||
nativeTrack.label = 'Spanish';
|
||||
@@ -108,6 +107,10 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
|
||||
let addRemotes = 0;
|
||||
let tech = {
|
||||
$$() {
|
||||
return [nativeTrack];
|
||||
},
|
||||
|
||||
el() {
|
||||
return {
|
||||
querySelectorAll() {
|
||||
@@ -137,12 +140,12 @@ q.test('trackToJson_ produces correct representation for emulated track object',
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
tech: {}
|
||||
});
|
||||
|
||||
a.deepEqual(cleanup(c.trackToJson_(track)), {
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
@@ -155,12 +158,12 @@ q.test('textTracksToJson produces good json output for emulated only', function(
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
tech: {}
|
||||
});
|
||||
|
||||
let anotherTrack = new TextTrack({
|
||||
src: 'http://example.com/spanish.vtt',
|
||||
src: 'example.com/spanish.vtt',
|
||||
kind: 'captions',
|
||||
srclang: 'es',
|
||||
label: 'Spanish',
|
||||
@@ -172,6 +175,10 @@ q.test('textTracksToJson produces good json output for emulated only', function(
|
||||
tt.addTrack_(emulatedTrack);
|
||||
|
||||
let tech = {
|
||||
$$() {
|
||||
return [];
|
||||
},
|
||||
|
||||
el() {
|
||||
return {
|
||||
querySelectorAll() {
|
||||
@@ -185,13 +192,13 @@ q.test('textTracksToJson produces good json output for emulated only', function(
|
||||
};
|
||||
|
||||
a.deepEqual(cleanup(c.textTracksToJson(tech)), [{
|
||||
src: 'http://example.com/spanish.vtt',
|
||||
src: 'example.com/spanish.vtt',
|
||||
kind: 'captions',
|
||||
label: 'Spanish',
|
||||
language: 'es',
|
||||
mode: 'disabled'
|
||||
}, {
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
@@ -204,12 +211,12 @@ q.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated trac
|
||||
kind: 'captions',
|
||||
label: 'English',
|
||||
language: 'en',
|
||||
src: 'http://example.com/english.vtt',
|
||||
src: 'example.com/english.vtt',
|
||||
tech: {}
|
||||
});
|
||||
|
||||
let anotherTrack = new TextTrack({
|
||||
src: 'http://example.com/spanish.vtt',
|
||||
src: 'example.com/spanish.vtt',
|
||||
kind: 'captions',
|
||||
srclang: 'es',
|
||||
label: 'Spanish',
|
||||
@@ -222,6 +229,10 @@ q.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated trac
|
||||
|
||||
let addRemotes = 0;
|
||||
let tech = {
|
||||
$$() {
|
||||
return [];
|
||||
},
|
||||
|
||||
el() {
|
||||
return {
|
||||
querySelectorAll() {
|
||||
|
||||
@@ -35,17 +35,17 @@ test('should update settings', function() {
|
||||
player.textTrackSettings.setValues(newSettings);
|
||||
deepEqual(player.textTrackSettings.getValues(), newSettings, 'values are updated');
|
||||
|
||||
equal(player.el().querySelector('.vjs-fg-color > select').selectedIndex, 1, 'fg-color is set to new value');
|
||||
equal(player.el().querySelector('.vjs-bg-color > select').selectedIndex, 1, 'bg-color is set to new value');
|
||||
equal(player.el().querySelector('.window-color > select').selectedIndex, 1, 'window-color is set to new value');
|
||||
equal(player.el().querySelector('.vjs-text-opacity > select').selectedIndex, 1, 'text-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-bg-opacity > select').selectedIndex, 1, 'bg-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-window-opacity > select').selectedIndex, 1, 'window-opacity is set to new value');
|
||||
equal(player.el().querySelector('.vjs-edge-style select').selectedIndex, 1, 'edge-style is set to new value');
|
||||
equal(player.el().querySelector('.vjs-font-family select').selectedIndex, 1, 'font-family is set to new value');
|
||||
equal(player.el().querySelector('.vjs-font-percent select').selectedIndex, 3, 'font-percent is set to new value');
|
||||
equal(player.$('.vjs-fg-color > select').selectedIndex, 1, 'fg-color is set to new value');
|
||||
equal(player.$('.vjs-bg-color > select').selectedIndex, 1, 'bg-color is set to new value');
|
||||
equal(player.$('.window-color > select').selectedIndex, 1, 'window-color is set to new value');
|
||||
equal(player.$('.vjs-text-opacity > select').selectedIndex, 1, 'text-opacity is set to new value');
|
||||
equal(player.$('.vjs-bg-opacity > select').selectedIndex, 1, 'bg-opacity is set to new value');
|
||||
equal(player.$('.vjs-window-opacity > select').selectedIndex, 1, 'window-opacity is set to new value');
|
||||
equal(player.$('.vjs-edge-style select').selectedIndex, 1, 'edge-style is set to new value');
|
||||
equal(player.$('.vjs-font-family select').selectedIndex, 1, 'font-family is set to new value');
|
||||
equal(player.$('.vjs-font-percent select').selectedIndex, 3, 'font-percent is set to new value');
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
deepEqual(safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'))[1], newSettings, 'values are saved');
|
||||
|
||||
player.dispose();
|
||||
@@ -57,32 +57,32 @@ test('should restore default settings', function() {
|
||||
persistTextTrackSettings: true
|
||||
});
|
||||
|
||||
player.el().querySelector('.vjs-fg-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-bg-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.window-color > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-text-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-bg-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-window-opacity > select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-edge-style select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-font-family select').selectedIndex = 1;
|
||||
player.el().querySelector('.vjs-font-percent select').selectedIndex = 3;
|
||||
player.$('.vjs-fg-color > select').selectedIndex = 1;
|
||||
player.$('.vjs-bg-color > select').selectedIndex = 1;
|
||||
player.$('.window-color > select').selectedIndex = 1;
|
||||
player.$('.vjs-text-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-bg-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-window-opacity > select').selectedIndex = 1;
|
||||
player.$('.vjs-edge-style select').selectedIndex = 1;
|
||||
player.$('.vjs-font-family select').selectedIndex = 1;
|
||||
player.$('.vjs-font-percent select').selectedIndex = 3;
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-default-button'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-default-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
deepEqual(player.textTrackSettings.getValues(), {}, 'values are defaulted');
|
||||
deepEqual(window.localStorage.getItem('vjs-text-track-settings'), null, 'values are saved');
|
||||
|
||||
equal(player.el().querySelector('.vjs-fg-color > select').selectedIndex, 0, 'fg-color is set to default value');
|
||||
equal(player.el().querySelector('.vjs-bg-color > select').selectedIndex, 0, 'bg-color is set to default value');
|
||||
equal(player.el().querySelector('.window-color > select').selectedIndex, 0, 'window-color is set to default value');
|
||||
equal(player.el().querySelector('.vjs-text-opacity > select').selectedIndex, 0, 'text-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-bg-opacity > select').selectedIndex, 0, 'bg-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-window-opacity > select').selectedIndex, 0, 'window-opacity is set to default value');
|
||||
equal(player.el().querySelector('.vjs-edge-style select').selectedIndex, 0, 'edge-style is set to default value');
|
||||
equal(player.el().querySelector('.vjs-font-family select').selectedIndex, 0, 'font-family is set to default value');
|
||||
equal(player.el().querySelector('.vjs-font-percent select').selectedIndex, 2, 'font-percent is set to default value');
|
||||
equal(player.$('.vjs-fg-color > select').selectedIndex, 0, 'fg-color is set to default value');
|
||||
equal(player.$('.vjs-bg-color > select').selectedIndex, 0, 'bg-color is set to default value');
|
||||
equal(player.$('.window-color > select').selectedIndex, 0, 'window-color is set to default value');
|
||||
equal(player.$('.vjs-text-opacity > select').selectedIndex, 0, 'text-opacity is set to default value');
|
||||
equal(player.$('.vjs-bg-opacity > select').selectedIndex, 0, 'bg-opacity is set to default value');
|
||||
equal(player.$('.vjs-window-opacity > select').selectedIndex, 0, 'window-opacity is set to default value');
|
||||
equal(player.$('.vjs-edge-style select').selectedIndex, 0, 'edge-style is set to default value');
|
||||
equal(player.$('.vjs-font-family select').selectedIndex, 0, 'font-family is set to default value');
|
||||
equal(player.$('.vjs-font-percent select').selectedIndex, 2, 'font-percent is set to default value');
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
@@ -91,7 +91,7 @@ test('should open on click', function() {
|
||||
var player = TestHelpers.makePlayer({
|
||||
tracks: tracks
|
||||
});
|
||||
Events.trigger(player.el().querySelector('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.$('.vjs-texttrack-settings'), 'click');
|
||||
ok(!player.textTrackSettings.hasClass('vjs-hidden'), 'settings open');
|
||||
|
||||
player.dispose();
|
||||
@@ -101,8 +101,8 @@ test('should close on done click', function() {
|
||||
var player = TestHelpers.makePlayer({
|
||||
tracks: tracks
|
||||
});
|
||||
Events.trigger(player.el().querySelector('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-texttrack-settings'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
ok(player.textTrackSettings.hasClass('vjs-hidden'), 'settings closed');
|
||||
|
||||
player.dispose();
|
||||
@@ -141,7 +141,7 @@ test('if persist option is set, save settings when "done"', function() {
|
||||
save++;
|
||||
};
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
equal(save, 1, 'save was called');
|
||||
|
||||
@@ -171,7 +171,7 @@ test('do not try to restore or save settings if persist option is not set', func
|
||||
|
||||
equal(restore, 0, 'restore was not called');
|
||||
|
||||
Events.trigger(player.el().querySelector('.vjs-done-button'), 'click');
|
||||
Events.trigger(player.$('.vjs-done-button'), 'click');
|
||||
|
||||
// saveSettings is called but does nothing
|
||||
equal(save, 1, 'save was not called');
|
||||
|
||||
+319
-11
@@ -62,23 +62,164 @@ test('should get and remove data from an element', function(){
|
||||
ok(!Dom.hasElData(el), 'cached item emptied');
|
||||
});
|
||||
|
||||
test('should add and remove a class name on an element', function(){
|
||||
test('addElClass()', function(){
|
||||
var el = document.createElement('div');
|
||||
|
||||
expect(5);
|
||||
|
||||
Dom.addElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class', 'class added');
|
||||
strictEqual(el.className, 'test-class', 'adds a single class');
|
||||
|
||||
Dom.addElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class', 'same class not duplicated');
|
||||
Dom.addElClass(el, 'test-class2');
|
||||
ok(el.className === 'test-class test-class2', 'added second class');
|
||||
Dom.removeElClass(el, 'test-class');
|
||||
ok(el.className === 'test-class2', 'removed first class');
|
||||
strictEqual(el.className, 'test-class', 'does not duplicate classes');
|
||||
|
||||
throws(function(){
|
||||
Dom.addElClass(el, 'foo foo-bar');
|
||||
}, 'throws when attempting to add a class with whitespace');
|
||||
|
||||
Dom.addElClass(el, 'test2_className');
|
||||
strictEqual(el.className, 'test-class test2_className', 'adds second class');
|
||||
|
||||
Dom.addElClass(el, 'FOO');
|
||||
strictEqual(el.className, 'test-class test2_className FOO', 'adds third class');
|
||||
});
|
||||
|
||||
test('should read class names on an element', function(){
|
||||
test('removeElClass()', function() {
|
||||
var el = document.createElement('div');
|
||||
Dom.addElClass(el, 'test-class1');
|
||||
ok(Dom.hasElClass(el, 'test-class1') === true, 'class detected');
|
||||
ok(Dom.hasElClass(el, 'test-class') === false, 'substring correctly not detected');
|
||||
|
||||
el.className = 'test-class foo foo test2_className FOO bar';
|
||||
|
||||
expect(5);
|
||||
|
||||
Dom.removeElClass(el, 'test-class');
|
||||
strictEqual(el.className, 'foo foo test2_className FOO bar', 'removes one class');
|
||||
|
||||
Dom.removeElClass(el, 'foo');
|
||||
strictEqual(el.className, 'test2_className FOO bar', 'removes all instances of a class');
|
||||
|
||||
throws(function(){
|
||||
Dom.removeElClass(el, 'test2_className bar');
|
||||
}, 'throws when attempting to remove a class with whitespace');
|
||||
|
||||
Dom.removeElClass(el, 'test2_className');
|
||||
strictEqual(el.className, 'FOO bar', 'removes another class');
|
||||
|
||||
Dom.removeElClass(el, 'FOO');
|
||||
strictEqual(el.className, 'bar', 'removes another class');
|
||||
});
|
||||
|
||||
test('hasElClass()', function(){
|
||||
var el = document.createElement('div');
|
||||
|
||||
el.className = 'test-class foo foo test2_className FOO bar';
|
||||
|
||||
strictEqual(Dom.hasElClass(el, 'test-class'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'foo'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'test2_className'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'FOO'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'bar'), true, 'class detected');
|
||||
strictEqual(Dom.hasElClass(el, 'test2'), false, 'valid substring - but not a class - correctly not detected');
|
||||
strictEqual(Dom.hasElClass(el, 'className'), false, 'valid substring - but not a class - correctly not detected');
|
||||
|
||||
throws(function(){
|
||||
Dom.hasElClass(el, 'FOO bar');
|
||||
}, 'throws when attempting to detect a class with whitespace');
|
||||
});
|
||||
|
||||
test('toggleElClass()', function() {
|
||||
let el = Dom.createEl('div', {className: 'foo bar'});
|
||||
|
||||
let predicateToggles = [
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: true,
|
||||
className: 'foo bar',
|
||||
message: 'if predicate `true` matches state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: false,
|
||||
className: 'foo bar',
|
||||
message: 'if predicate `false` matches state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: true,
|
||||
className: 'foo bar baz',
|
||||
message: 'if predicate `true` differs from state of the element, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: false,
|
||||
className: 'bar baz',
|
||||
message: 'if predicate `false` differs from state of the element, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: () => true,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `true`, matching the state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => false,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `false`, matching the state of the element, do nothing'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => true,
|
||||
className: 'bar baz foo',
|
||||
message: 'if a predicate function returns `true`, differing from state of the element, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: () => false,
|
||||
className: 'bar baz',
|
||||
message: 'if a predicate function returns `false`, differing from state of the element, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'foo',
|
||||
predicate: Function.prototype,
|
||||
className: 'bar baz foo',
|
||||
message: 'if a predicate function returns `undefined` and the element does not have the class, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: Function.prototype,
|
||||
className: 'baz foo',
|
||||
message: 'if a predicate function returns `undefined` and the element has the class, remove the class'
|
||||
},
|
||||
{
|
||||
toggle: 'bar',
|
||||
predicate: () => [],
|
||||
className: 'baz foo bar',
|
||||
message: 'if a predicate function returns a defined non-boolean value and the element does not have the class, add the class'
|
||||
},
|
||||
{
|
||||
toggle: 'baz',
|
||||
predicate: () => 'this is incorrect',
|
||||
className: 'foo bar',
|
||||
message: 'if a predicate function returns a defined non-boolean value and the element has the class, remove the class'
|
||||
},
|
||||
];
|
||||
|
||||
expect(3 + predicateToggles.length);
|
||||
|
||||
Dom.toggleElClass(el, 'bar');
|
||||
strictEqual(el.className, 'foo', 'toggles a class off, if present');
|
||||
|
||||
Dom.toggleElClass(el, 'bar');
|
||||
strictEqual(el.className, 'foo bar', 'toggles a class on, if absent');
|
||||
|
||||
throws(function(){
|
||||
Dom.toggleElClass(el, 'foo bar');
|
||||
}, 'throws when attempting to toggle a class with whitespace');
|
||||
|
||||
predicateToggles.forEach(x => {
|
||||
Dom.toggleElClass(el, x.toggle, x.predicate);
|
||||
strictEqual(el.className, x.className, x.message);
|
||||
});
|
||||
});
|
||||
|
||||
test('should set element attributes from object', function(){
|
||||
@@ -160,3 +301,170 @@ test('Dom.findElPosition should find top and left position', function() {
|
||||
position = Dom.findElPosition(d);
|
||||
deepEqual(position, {left: 0, top: 0}, 'If there is no gBCR, we should get zeros');
|
||||
});
|
||||
|
||||
test('Dom.isEl', function(assert) {
|
||||
assert.expect(7);
|
||||
assert.notOk(Dom.isEl(), 'undefined is not an element');
|
||||
assert.notOk(Dom.isEl(true), 'booleans are not elements');
|
||||
assert.notOk(Dom.isEl({}), 'objects are not elements');
|
||||
assert.notOk(Dom.isEl([]), 'arrays are not elements');
|
||||
assert.notOk(Dom.isEl('<h1></h1>'), 'strings are not elements');
|
||||
assert.ok(Dom.isEl(document.createElement('div')), 'elements are elements');
|
||||
assert.ok(Dom.isEl({nodeType: 1}), 'duck typing is imperfect');
|
||||
});
|
||||
|
||||
test('Dom.isTextNode', function(assert) {
|
||||
assert.expect(7);
|
||||
assert.notOk(Dom.isTextNode(), 'undefined is not a text node');
|
||||
assert.notOk(Dom.isTextNode(true), 'booleans are not text nodes');
|
||||
assert.notOk(Dom.isTextNode({}), 'objects are not text nodes');
|
||||
assert.notOk(Dom.isTextNode([]), 'arrays are not text nodes');
|
||||
assert.notOk(Dom.isTextNode('hola mundo'), 'strings are not text nodes');
|
||||
assert.ok(Dom.isTextNode(document.createTextNode('hello, world!')), 'text nodes are text nodes');
|
||||
assert.ok(Dom.isTextNode({nodeType: 3}), 'duck typing is imperfect');
|
||||
});
|
||||
|
||||
test('Dom.emptyEl', function(assert) {
|
||||
let el = Dom.createEl();
|
||||
|
||||
el.appendChild(Dom.createEl('span'));
|
||||
el.appendChild(Dom.createEl('span'));
|
||||
el.appendChild(document.createTextNode('hola mundo'));
|
||||
el.appendChild(Dom.createEl('span'));
|
||||
|
||||
Dom.emptyEl(el);
|
||||
|
||||
assert.expect(1);
|
||||
assert.notOk(el.firstChild, 'the element was emptied');
|
||||
});
|
||||
|
||||
test('Dom.normalizeContent: strings and elements/nodes', function(assert) {
|
||||
assert.expect(8);
|
||||
|
||||
let str = Dom.normalizeContent('hello');
|
||||
assert.strictEqual(str[0].data, 'hello', 'single string becomes a text node');
|
||||
|
||||
let elem = Dom.normalizeContent(Dom.createEl());
|
||||
assert.ok(Dom.isEl(elem[0]), 'an element is passed through');
|
||||
|
||||
let node = Dom.normalizeContent(document.createTextNode('goodbye'));
|
||||
assert.strictEqual(node[0].data, 'goodbye', 'a text node is passed through');
|
||||
|
||||
assert.strictEqual(Dom.normalizeContent(null).length, 0, 'falsy values are ignored');
|
||||
assert.strictEqual(Dom.normalizeContent(false).length, 0, 'falsy values are ignored');
|
||||
assert.strictEqual(Dom.normalizeContent().length, 0, 'falsy values are ignored');
|
||||
assert.strictEqual(Dom.normalizeContent(123).length, 0, 'numbers are ignored');
|
||||
assert.strictEqual(Dom.normalizeContent({}).length, 0, 'objects are ignored');
|
||||
});
|
||||
|
||||
test('Dom.normalizeContent: functions returning strings and elements/nodes', function(assert) {
|
||||
assert.expect(9);
|
||||
|
||||
let str = Dom.normalizeContent(() => 'hello');
|
||||
assert.strictEqual(str[0].data, 'hello', 'a function can return a string, which becomes a text node');
|
||||
|
||||
let elem = Dom.normalizeContent(() => Dom.createEl());
|
||||
assert.ok(Dom.isEl(elem[0]), 'a function can return an element');
|
||||
|
||||
let node = Dom.normalizeContent(() => document.createTextNode('goodbye'));
|
||||
assert.strictEqual(node[0].data, 'goodbye', 'a function can return a text node');
|
||||
|
||||
assert.strictEqual(Dom.normalizeContent(() => null).length, 0, 'a function CANNOT return falsy values');
|
||||
assert.strictEqual(Dom.normalizeContent(() => false).length, 0, 'a function CANNOT return falsy values');
|
||||
assert.strictEqual(Dom.normalizeContent(() => undefined).length, 0, 'a function CANNOT return falsy values');
|
||||
assert.strictEqual(Dom.normalizeContent(() => 123).length, 0, 'a function CANNOT return numbers');
|
||||
assert.strictEqual(Dom.normalizeContent(() => {}).length, 0, 'a function CANNOT return objects');
|
||||
assert.strictEqual(Dom.normalizeContent(() => (() => null)).length, 0, 'a function CANNOT return a function');
|
||||
});
|
||||
|
||||
test('Dom.normalizeContent: arrays of strings and objects', function(assert) {
|
||||
assert.expect(7);
|
||||
|
||||
let source = [
|
||||
'hello',
|
||||
{},
|
||||
Dom.createEl(),
|
||||
['oops'],
|
||||
null,
|
||||
document.createTextNode('goodbye'),
|
||||
() => 'it works'
|
||||
];
|
||||
|
||||
let result = Dom.normalizeContent(source);
|
||||
|
||||
assert.strictEqual(result[0].data, 'hello', 'an array can include a string normalized to a text node');
|
||||
assert.ok(Dom.isEl(result[1]), 'an array can include an element');
|
||||
assert.strictEqual(result[2].data, 'goodbye', 'an array can include a text node');
|
||||
assert.strictEqual(result[3].data, 'it works', 'an array can include a function, which is invoked');
|
||||
assert.strictEqual(result.indexOf(source[1]), -1, 'an array CANNOT include an object');
|
||||
assert.strictEqual(result.indexOf(source[3]), -1, 'an array CANNOT include an array');
|
||||
assert.strictEqual(result.indexOf(source[4]), -1, 'an array CANNOT include falsy values');
|
||||
});
|
||||
|
||||
test('Dom.normalizeContent: functions returning arrays', function(assert) {
|
||||
assert.expect(3);
|
||||
|
||||
let arr = [];
|
||||
let result = Dom.normalizeContent(() => ['hello', Function.prototype, arr]);
|
||||
|
||||
assert.strictEqual(result[0].data, 'hello', 'a function can return an array');
|
||||
assert.strictEqual(result.indexOf(Function.prototype), -1, 'a function can return an array, but it CANNOT include a function');
|
||||
assert.strictEqual(result.indexOf(arr), -1, 'a function can return an array, but it CANNOT include an array');
|
||||
});
|
||||
|
||||
test('Dom.insertContent', function(assert) {
|
||||
let p = Dom.createEl('p');
|
||||
let text = document.createTextNode('hello');
|
||||
let el = Dom.insertContent(Dom.createEl(), [p, text]);
|
||||
|
||||
assert.expect(2);
|
||||
assert.strictEqual(el.firstChild, p, 'the paragraph was inserted first');
|
||||
assert.strictEqual(el.firstChild.nextSibling, text, 'the text node was inserted last');
|
||||
});
|
||||
|
||||
test('Dom.appendContent', function(assert) {
|
||||
let p1 = Dom.createEl('p');
|
||||
let p2 = Dom.createEl('p');
|
||||
let el = Dom.insertContent(Dom.createEl(), [p1]);
|
||||
|
||||
Dom.appendContent(el, p2);
|
||||
|
||||
assert.expect(2);
|
||||
assert.strictEqual(el.firstChild, p1, 'the first paragraph is the first child');
|
||||
assert.strictEqual(el.firstChild.nextSibling, p2, 'the second paragraph was appended');
|
||||
});
|
||||
|
||||
test('$() and $$()', function() {
|
||||
let fixture = document.getElementById('qunit-fixture');
|
||||
let container = document.createElement('div');
|
||||
let children = [
|
||||
document.createElement('div'),
|
||||
document.createElement('div'),
|
||||
document.createElement('div'),
|
||||
];
|
||||
|
||||
children.forEach(child => container.appendChild(child));
|
||||
fixture.appendChild(container);
|
||||
|
||||
let totalDivCount = document.getElementsByTagName('div').length;
|
||||
|
||||
expect(12);
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture'), fixture, 'can find an element in the document context');
|
||||
strictEqual(Dom.$$('div').length, totalDivCount, 'finds elements in the document context');
|
||||
|
||||
strictEqual(Dom.$('div', container), children[0], 'can find an element in a DOM element context');
|
||||
strictEqual(Dom.$$('div', container).length, children.length, 'finds elements in a DOM element context');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', document.querySelector('unknown')), fixture, 'falls back to document given a bad context element');
|
||||
strictEqual(Dom.$$('div', document.querySelector('unknown')).length, totalDivCount, 'falls back to document given a bad context element');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', 'body'), fixture, 'can find an element in a selector context');
|
||||
strictEqual(Dom.$$('div', '#qunit-fixture').length, 1 + children.length, 'finds elements in a selector context');
|
||||
|
||||
strictEqual(Dom.$('#qunit-fixture', 'unknown'), fixture, 'falls back to document given a bad context selector');
|
||||
strictEqual(Dom.$$('div', 'unknown').length, totalDivCount, 'falls back to document given a bad context selector');
|
||||
|
||||
strictEqual(Dom.$('div', children[0]), null, 'returns null for missing elements');
|
||||
strictEqual(Dom.$$('div', children[0]).length, 0, 'returns 0 for missing elements');
|
||||
});
|
||||
|
||||
@@ -93,7 +93,6 @@ test('isCrossOrigin can identify cross origin urls', function() {
|
||||
win.location.protocol = 'https:';
|
||||
win.location.host = 'google.com';
|
||||
ok(Url.isCrossOrigin('http://google.com/example.vtt'), 'http://google.com from https://google.com is cross origin');
|
||||
ok(Url.isCrossOrigin('//google.com/example.vtt'), '//google.com from https://google.com is cross origin');
|
||||
ok(Url.isCrossOrigin('http://example.com/example.vtt'), 'http://example.com from https://google.com is cross origin');
|
||||
ok(Url.isCrossOrigin('https://example.com/example.vtt'), 'https://example.com from https://google.com is cross origin');
|
||||
ok(Url.isCrossOrigin('//example.com/example.vtt'), '//example.com from https://google.com is cross origin');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import videojs from '../../src/js/video.js';
|
||||
import TestHelpers from './test-helpers.js';
|
||||
import Player from '../../src/js/player.js';
|
||||
import * as Dom from '../../src/js/utils/dom.js';
|
||||
import log from '../../src/js/utils/log.js';
|
||||
import document from 'global/document';
|
||||
|
||||
@@ -78,3 +79,29 @@ test('should expose options and players properties for backward-compatibility',
|
||||
ok(typeof videojs.options, 'object', 'options should be an object');
|
||||
ok(typeof videojs.players, 'object', 'players should be an object');
|
||||
});
|
||||
|
||||
test('should expose DOM functions', function() {
|
||||
|
||||
// Keys are videojs methods, values are Dom methods.
|
||||
let methods = {
|
||||
isEl: 'isEl',
|
||||
isTextNode: 'isTextNode',
|
||||
hasClass: 'hasElClass',
|
||||
addClass: 'addElClass',
|
||||
removeClass: 'removeElClass',
|
||||
toggleClass: 'toggleElClass',
|
||||
setAttributes: 'setElAttributes',
|
||||
getAttributes: 'getElAttributes',
|
||||
emptyEl: 'emptyEl',
|
||||
insertContent: 'insertContent',
|
||||
appendContent: 'appendContent'
|
||||
};
|
||||
|
||||
let keys = Object.keys(methods);
|
||||
|
||||
expect(keys.length);
|
||||
keys.forEach(function(vjsName) {
|
||||
let domName = methods[vjsName];
|
||||
strictEqual(videojs[vjsName], Dom[domName], `videojs.${vjsName} is a reference to Dom.${domName}`);
|
||||
});
|
||||
});
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário