mirror of
https://github.com/videojs/video.js.git
synced 2025-01-10 23:30:03 +02:00
Reorgnizing src files.
Moving to grunt for build.
This commit is contained in:
parent
675147645c
commit
e7c146bc1f
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ test/*.map
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
sandbox/*
|
||||
!sandbox/*.example
|
||||
|
@ -1,7 +1,4 @@
|
||||
{
|
||||
// Hopefully people are smart enough not to use eval
|
||||
// But goog.base uses execScript so it throws an error.
|
||||
// When compiled goog.base is stripped out completely.
|
||||
"evil" : true,
|
||||
"validthis": true,
|
||||
"browser" : true,
|
||||
@ -14,7 +11,7 @@
|
||||
"trailing" : true,
|
||||
"undef" : true,
|
||||
"laxbreak" : true,
|
||||
"predef" : [ // Extra globals.
|
||||
"predef" : [
|
||||
"_V_",
|
||||
"videojs",
|
||||
"vjs",
|
||||
|
148
Gruntfile.js
Normal file
148
Gruntfile.js
Normal file
@ -0,0 +1,148 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
concat: {
|
||||
dist: {
|
||||
src: [
|
||||
'src/js/goog.base.js',
|
||||
'src/js/core.js',
|
||||
'src/js/lib.js',
|
||||
'src/js/events.js',
|
||||
'src/js/component.js',
|
||||
'src/js/player.js',
|
||||
'src/js/media.js',
|
||||
'src/js/media.html5.js',
|
||||
'src/js/media.flash.js',
|
||||
'src/js/controls.js',
|
||||
'src/js/tracks.js',
|
||||
'src/js/setup.js',
|
||||
'src/js/json.js',
|
||||
'src/js/exports.js'
|
||||
],
|
||||
dest: 'dist/video.js'
|
||||
},
|
||||
test: {
|
||||
src: [
|
||||
'test/unit/phantom-logging.js',
|
||||
'test/unit/component.js',
|
||||
'test/unit/core.js',
|
||||
'test/unit/events.js',
|
||||
'test/unit/lib.js',
|
||||
'test/unit/media.html5.js',
|
||||
'test/unit/player.js',
|
||||
'test/unit/setup.js',
|
||||
],
|
||||
dest: 'test/unit.js'
|
||||
}
|
||||
},
|
||||
// Current forEach issue: https://github.com/gruntjs/grunt/issues/610
|
||||
// npm install https://github.com/gruntjs/grunt-contrib-jshint/archive/7fd70e86c5a8d489095fa81589d95dccb8eb3a46.tar.gz
|
||||
jshint: {
|
||||
dist: {
|
||||
src: ["dist/video.js"],
|
||||
options: {
|
||||
jshintrc: ".jshintrc"
|
||||
}
|
||||
}
|
||||
},
|
||||
qunit: {
|
||||
all: ['test/unit.html']
|
||||
},
|
||||
watch: {
|
||||
files: [ "src/**/*.js" ],
|
||||
tasks: "dev"
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Default task.
|
||||
// grunt.registerTask('default', 'lint:beforeconcat concat lint:afterconcat');
|
||||
// // Default task(s).
|
||||
// grunt.registerTask('default', ['uglify']);
|
||||
|
||||
grunt.loadNpmTasks("grunt-contrib-concat");
|
||||
grunt.loadNpmTasks("grunt-contrib-jshint");
|
||||
grunt.loadNpmTasks("grunt-contrib-qunit");
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
|
||||
grunt.registerTask( "dev", [ "compile" ] ); // "build:*:*", "jshint"
|
||||
// compiled += grunt.file.read( filepath );
|
||||
|
||||
var exec = require('child_process').exec,
|
||||
fs = require('fs'),
|
||||
gzip = require('zlib').gzip;
|
||||
|
||||
grunt.registerMultiTask('build', 'Building Source', function(){
|
||||
grunt.log.writeln(this.target)
|
||||
if (this.target === 'latest') {
|
||||
var files = this.data.files;
|
||||
var dist = '';
|
||||
|
||||
// for (prop in this.file) {
|
||||
// grunt.log.writeln(prop + ":" + this.file[prop])
|
||||
// }
|
||||
|
||||
files.forEach(function(file){
|
||||
dist += grunt.file.read('src/js/' + file)
|
||||
});
|
||||
|
||||
grunt.file.write('dist/video.js', dist);
|
||||
} else if (this.target === 'test') {
|
||||
grunt.task.run('build:latest');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('compile', 'Minify JS files using Closure Compiler.', function() {
|
||||
var done = this.async();
|
||||
|
||||
var command = 'java -jar build/compiler/compiler.jar';
|
||||
command += ' --compilation_level ADVANCED_OPTIMIZATIONS';
|
||||
|
||||
var files = [
|
||||
'goog.base.js',
|
||||
'core.js',
|
||||
'lib.js',
|
||||
'events.js',
|
||||
'component.js',
|
||||
'player.js',
|
||||
'media.js',
|
||||
'media.html5.js',
|
||||
'media.flash.js',
|
||||
'controls.js',
|
||||
'tracks.js',
|
||||
'setup.js',
|
||||
'json.js',
|
||||
'exports.js'
|
||||
];
|
||||
|
||||
files.forEach(function(file){
|
||||
command += ' --js=src/js/'+file;
|
||||
});
|
||||
|
||||
command += ' --externs src/js/media.flash.externs.js';
|
||||
// command += ' --formatting=pretty_print';
|
||||
command += ' --js_output_file=test/video.compiled.js';
|
||||
command += ' --create_source_map test/video.compiled.js.map --source_map_format=V3';
|
||||
// command += ' --externs test/qunit-externs.js';
|
||||
command += ' --output_wrapper "(function() {%output%})();//@ sourceMappingURL=video.compiled.js.map"';
|
||||
|
||||
exec(command, { maxBuffer: 500*1024 }, function(err, stdout, stderr){
|
||||
|
||||
if (err) {
|
||||
grunt.warn(err);
|
||||
done(false);
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
grunt.log.writeln(stdout);
|
||||
}
|
||||
|
||||
grunt.log.writeln("done!")
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 85 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB |
@ -1,190 +0,0 @@
|
||||
/* DEFAULT SKIN (override in another file)
|
||||
================================================================================
|
||||
Using all CSS to draw the controls. Images could be used if desired.
|
||||
Instead of editing this file, I recommend creating your own skin CSS file to be included after this file,
|
||||
so you can upgrade to newer versions easier. */
|
||||
|
||||
.vjs-original-skin .vjs-controls {
|
||||
position: absolute; margin: 0; opacity: 0.85; color: #fff;
|
||||
display: block; /* Start hidden */
|
||||
left: 0; right: 0; /* 100% width of video-js-box */
|
||||
width: 100%;
|
||||
bottom: 0px; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
|
||||
height: 35px; /* Including any margin you want above or below control items */
|
||||
padding: 0; /* Controls are absolutely position, so no padding necessary */
|
||||
-webkit-transition: opacity 0.5s linear;
|
||||
-moz-transition: opacity 0.5s linear;
|
||||
-o-transition: opacity 0.5s linear;
|
||||
-ms-transition: opacity 0.5s linear;
|
||||
transition: opacity 0.5s linear;
|
||||
}
|
||||
|
||||
.vjs-original-skin .vjs-control {
|
||||
position: absolute; /* Use top, bottom, left, and right to specifically position the control. */
|
||||
text-align: center; margin: 0; padding: 0;
|
||||
height: 25px; /* Default height of individual controls */
|
||||
top: 5px; /* Top margin to put space between video and controls when controls are below */
|
||||
|
||||
/* CSS Background Gradients
|
||||
Using to give the aqua-ish look. */
|
||||
/* Default */ background-color: #0B151A;
|
||||
/* Webkit */ background: #1F3744 -webkit-gradient(linear, left top, left bottom, from(#0B151A), to(#1F3744)) left 12px;
|
||||
/* Firefox */ background: #1F3744 -moz-linear-gradient(top, #0B151A, #1F3744) left 12px;
|
||||
|
||||
/* CSS Curved Corners */
|
||||
-webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px;
|
||||
|
||||
/* CSS Shadows */
|
||||
-webkit-box-shadow: 1px 1px 2px #000; -moz-box-shadow: 1px 1px 2px #000; box-shadow: 1px 1px 2px #000;
|
||||
}
|
||||
|
||||
/* Placement of Control Items
|
||||
- Left side of pogress bar, use left & width
|
||||
- Rigth side of progress bar, use right & width
|
||||
- Expand with the video (like progress bar) use left & right
|
||||
(using div.x to make more specific than vjs-control style)
|
||||
*/
|
||||
.vjs-original-skin div.vjs-play-control { left: 5px; width: 25px; }
|
||||
.vjs-original-skin div.vjs-progress-control { left: 35px; right: 165px; } /* Using left & right so it expands with the width of the video */
|
||||
.vjs-original-skin div.vjs-time-control { width: 75px; right: 90px; } /* Time control and progress bar are combined to look like one */
|
||||
.vjs-original-skin div.vjs-volume-control { width: 50px; right: 35px; }
|
||||
.vjs-original-skin div.vjs-fullscreen-control { width: 25px; right: 5px; }
|
||||
|
||||
/* Removing curved corners on progress control and time control to join them. */
|
||||
.vjs-original-skin div.vjs-progress-control {
|
||||
-webkit-border-top-right-radius: 0; -moz-border-radius-topright: 0; border-top-right-radius: 0;
|
||||
-webkit-border-bottom-right-radius: 0; -moz-border-radius-bottomright: 0; border-bottom-right-radius: 0;
|
||||
}
|
||||
.vjs-original-skin div.vjs-time-control {
|
||||
-webkit-border-top-left-radius: 0; -moz-border-radius-topleft: 0; border-top-left-radius: 0;
|
||||
-webkit-border-bottom-left-radius: 0; -moz-border-radius-bottomleft: 0; border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
/* Play/Pause
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-original-skin .vjs-play-control { cursor: pointer !important; }
|
||||
/* Play Icon */
|
||||
.vjs-original-skin .vjs-play-control span { display: block; font-size: 0; line-height: 0; }
|
||||
.vjs-original-skin.vjs-paused .vjs-play-control span {
|
||||
width: 0; height: 0; margin: 8px 0 0 8px;
|
||||
/* Drawing the play triangle with borders - http://www.infimum.dk/HTML/slantinfo.html */
|
||||
border-left: 10px solid #fff; /* Width & Color of play icon */
|
||||
/* Height of play icon is total top & bottom border widths. Color is transparent. */
|
||||
border-top: 5px solid rgba(0,0,0,0); border-bottom: 5px solid rgba(0,0,0,0);
|
||||
}
|
||||
.vjs-original-skin.vjs-playing .vjs-play-control span {
|
||||
width: 3px; height: 10px; margin: 8px auto 0;
|
||||
/* Drawing the pause bars with borders */
|
||||
border-top: 0px; border-left: 3px solid #fff; border-bottom: 0px; border-right: 3px solid #fff;
|
||||
}
|
||||
|
||||
/* Progress
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-original-skin .vjs-progress-holder { /* Box containing play and load progresses */
|
||||
position: relative; padding: 0; overflow:hidden; cursor: pointer !important;
|
||||
height: 9px; border: 1px solid #777;
|
||||
margin: 7px 1px 0 5px; /* Placement within the progress control item */
|
||||
-webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px;
|
||||
}
|
||||
.vjs-original-skin .vjs-progress-holder div { /* Progress Bars */
|
||||
position: absolute; display: block; width: 0; height: 9px; margin: 0; padding: 0;
|
||||
left: 0; top: 0; /*Needed for IE6*/
|
||||
-webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px;
|
||||
}
|
||||
.vjs-original-skin .vjs-play-progress {
|
||||
/* CSS Gradient */
|
||||
/* Default */ background: #fff;
|
||||
/* Webkit */ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#777));
|
||||
/* Firefox */ background: -moz-linear-gradient(top, #fff, #777);
|
||||
}
|
||||
.vjs-original-skin .vjs-load-progress {
|
||||
opacity: 0.8;
|
||||
/* CSS Gradient */
|
||||
/* Default */ background-color: #555;
|
||||
/* Webkit */ background: -webkit-gradient(linear, left top, left bottom, from(#555), to(#aaa));
|
||||
/* Firefox */ background: -moz-linear-gradient(top, #555, #aaa);
|
||||
}
|
||||
|
||||
/* Time Display
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-original-skin .vjs-time-control { font-size: 10px; line-height: 1; font-weight: normal; font-family: Helvetica, Arial, sans-serif; }
|
||||
.vjs-original-skin .vjs-time-control span { line-height: 25px; /* Centering vertically */ }
|
||||
|
||||
/* Volume
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-original-skin .vjs-volume-control { cursor: pointer !important; }
|
||||
.vjs-original-skin .vjs-volume-control div { display: block; margin: 0 5px 0 5px; padding: 4px 0 0 0; }
|
||||
/* Drawing the volume icon using 6 span elements */
|
||||
.vjs-original-skin .vjs-volume-control div span { /* Individual volume bars */
|
||||
float: left; padding: 0; font-size: 0; line-height: 0;
|
||||
margin: 0 2px 0 0; /* Space between */
|
||||
width: 5px; height: 0px; /* Total height is height + bottom border */
|
||||
border-bottom: 18px solid #555; /* Default (off) color and height of visible portion */
|
||||
}
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-volume-level-on { border-color: #fff; /* Volume on bar color */ }
|
||||
/* Creating differnt bar heights through height (transparent) and bottom border (visible). */
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-1 { border-bottom-width: 2px; height: 16px; }
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-2 { border-bottom-width: 4px; height: 14px; }
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-3 { border-bottom-width: 7px; height: 11px; }
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-4 { border-bottom-width: 10px; height: 8px; }
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-5 { border-bottom-width: 14px; height: 4px; }
|
||||
.vjs-original-skin .vjs-volume-control div span.vjs-vc-6 { margin-right: 0; }
|
||||
|
||||
/* Fullscreen
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-original-skin .vjs-fullscreen-control { cursor: pointer !important; }
|
||||
.vjs-original-skin .vjs-fullscreen-control div {
|
||||
padding: 0; text-align: left; vertical-align: top; cursor: pointer !important;
|
||||
margin: 5px 0 0 5px; /* Placement within the fullscreen control item */
|
||||
width: 20px; height: 20px;
|
||||
}
|
||||
/* Drawing the fullscreen icon using 4 span elements */
|
||||
.vjs-original-skin .vjs-fullscreen-control div span { float: left; margin: 0; padding: 0; font-size: 0; line-height: 0; width: 0; text-align: left; vertical-align: top; }
|
||||
.vjs-original-skin .vjs-fullscreen-control div span.vjs-fc-1 { /* Top-left triangle */
|
||||
margin-right: 3px; /* Space between top-left and top-right */
|
||||
margin-bottom: 3px; /* Space between top-left and bottom-left */
|
||||
border-top: 6px solid #fff; /* Height and color */
|
||||
border-right: 6px solid rgba(0,0,0,0); /* Width */
|
||||
}
|
||||
.vjs-original-skin .vjs-fullscreen-control div span.vjs-fc-2 { border-top: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
|
||||
.vjs-original-skin .vjs-fullscreen-control div span.vjs-fc-3 { clear: both; margin: 0 3px 0 0; border-bottom: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
|
||||
.vjs-original-skin .vjs-fullscreen-control div span.vjs-fc-4 { border-bottom: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
|
||||
/* Icon when video is in fullscreen mode */
|
||||
.vjs-original-skin.vjs-fullscreen .vjs-fullscreen-control div span.vjs-fc-1 { border: none; border-bottom: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
|
||||
.vjs-original-skin.vjs-fullscreen .vjs-fullscreen-control div span.vjs-fc-2 { border: none; border-bottom: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
|
||||
.vjs-original-skin.vjs-fullscreen .vjs-fullscreen-control div span.vjs-fc-3 { border: none; border-top: 6px solid #fff; border-left: 6px solid rgba(0,0,0,0); }
|
||||
.vjs-original-skin.vjs-fullscreen .vjs-fullscreen-control div span.vjs-fc-4 { border: none; border-top: 6px solid #fff; border-right: 6px solid rgba(0,0,0,0); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Big Play Button (at start)
|
||||
---------------------------------------------------------*/
|
||||
.vjs-original-skin .vjs-big-play-button {
|
||||
display: block; /* Start hidden */ z-index: 2;
|
||||
position: absolute; top: 50%; left: 50%; width: 80px; height: 80px; margin: -43px 0 0 -43px; text-align: center; vertical-align: center; cursor: pointer !important;
|
||||
border: 3px solid #fff; opacity: 0.9;
|
||||
-webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px;
|
||||
|
||||
/* CSS Background Gradients */
|
||||
/* Default */ background-color: #0B151A;
|
||||
/* Webkit */ background: #1F3744 -webkit-gradient(linear, left top, left bottom, from(#0B151A), to(#1F3744)) left 40px;
|
||||
/* Firefox */ background: #1F3744 -moz-linear-gradient(top, #0B151A, #1F3744) left 40px;
|
||||
|
||||
/* CSS Shadows */
|
||||
-webkit-box-shadow: 4px 4px 8px #000; -moz-box-shadow: 4px 4px 8px #000; box-shadow: 4px 4px 8px #000;
|
||||
}
|
||||
|
||||
.vjs-original-skin div.vjs-big-play-button:hover {
|
||||
-webkit-box-shadow: 0px 0px 80px #fff; -moz-box-shadow: 0px 0px 80px #fff; box-shadow: 0px 0px 80px #fff;
|
||||
}
|
||||
|
||||
.vjs-original-skin div.vjs-big-play-button span {
|
||||
display: block; font-size: 0; line-height: 0;
|
||||
width: 0; height: 0; margin: 20px 0 0 23px;
|
||||
/* Drawing the play triangle with borders - http://www.infimum.dk/HTML/slantinfo.html */
|
||||
border-left: 40px solid #fff; /* Width & Color of play icon */
|
||||
/* Height of play icon is total top & bottom border widths. Color is transparent. */
|
||||
border-top: 20px solid rgba(0,0,0,0); border-bottom: 20px solid rgba(0,0,0,0);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>HTML5 Video Player</title>
|
||||
|
||||
<link href="design/video-js.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!--[if IE]>
|
||||
<script src="https://getfirebug.com/releases/lite/1.4/firebug-lite.js"></script>
|
||||
<!--<![endif]-->
|
||||
|
||||
<script src='src/goog.base.js'></script>
|
||||
<script src='src/core.js'></script>
|
||||
<script src='src/lib.js'></script>
|
||||
<script src='src/events.js'></script>
|
||||
<script src='src/component.js'></script>
|
||||
<script src='src/player.js'></script>
|
||||
<script src='src/media.js'></script>
|
||||
<script src='src/media.html5.js'></script>
|
||||
<script src='src/media.flash.js'></script>
|
||||
<script src='src/controls.js'></script>
|
||||
<script src='src/tracks.js'></script>
|
||||
<script src='src/setup.js'></script>
|
||||
<script src='src/json.js'></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
// Easy access to test Flash over HTML5. Add ?flash to URL
|
||||
if (window.location.href.indexOf("?flash") !== -1) {
|
||||
videojs.options.techOrder = ["Flash"];
|
||||
videojs.options.flash.swf = "tech/flash/video-js.swf";
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<video id="vid1" class="video-js vjs-default-skin" controls preload="auto" 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'>
|
||||
<p>Video Playback Not Supported</p>
|
||||
</video>
|
||||
|
||||
<script>
|
||||
vid = document.getElementById("vid1");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
49
package.json
49
package.json
@ -1,16 +1,33 @@
|
||||
{ "name": "Video.js"
|
||||
, "description": "An HTML5 and Flash video player with a common API and skin for both."
|
||||
, "version": "3.2.3"
|
||||
, "keywords": ["html5", "flash", "video", "player"]
|
||||
, "homepage": "http://videojs.com"
|
||||
, "author": "Steve Heffernan"
|
||||
, "scripts": { "test": "make test" }
|
||||
, "repository":
|
||||
{ "type": "git"
|
||||
, "url": "https://github.com/zencoder/video-js.git"
|
||||
}
|
||||
, "devDependencies":
|
||||
{ "jshint": "0.6.1"
|
||||
, "connect": "2.1.3"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "video.js",
|
||||
"description": "An HTML5 and Flash video player with a common API and skin for both.",
|
||||
"version": "3.2.3",
|
||||
"keywords": [
|
||||
"html5",
|
||||
"flash",
|
||||
"video",
|
||||
"player"
|
||||
],
|
||||
"homepage": "http://videojs.com",
|
||||
"author": "Steve Heffernan",
|
||||
"scripts": {
|
||||
"test": "grunt qunit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zencoder/video-js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt-cli": "~0.1.0",
|
||||
"grunt": "0.4.0rc4",
|
||||
"grunt-contrib-jshint": "~0.1.0",
|
||||
"grunt-contrib-nodeunit": "~0.1.0",
|
||||
"jshint": "0.6.1",
|
||||
"connect": "2.1.3",
|
||||
"grunt-contrib-uglify": "~0.1.0",
|
||||
"grunt-closure-compiler": "0.0.13",
|
||||
"grunt-contrib-watch": "~0.1.4",
|
||||
"grunt-contrib-concat": "~0.1.1",
|
||||
"grunt-contrib-qunit": "~0.1.0"
|
||||
}
|
||||
}
|
||||
|
53
sandbox/index.html.example
Normal file
53
sandbox/index.html.example
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Video.js Sandbox</title>
|
||||
|
||||
<link href="../src/css/video-js.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!--[if IE]>
|
||||
<script src="https://getfirebug.com/releases/lite/1.4/firebug-lite.js"></script>
|
||||
<!--<![endif]-->
|
||||
|
||||
<script src='../src/js/goog.base.js'></script>
|
||||
<script src='../src/js/core.js'></script>
|
||||
<script src='../src/js/lib.js'></script>
|
||||
<script src='../src/js/events.js'></script>
|
||||
<script src='../src/js/component.js'></script>
|
||||
<script src='../src/js/player.js'></script>
|
||||
<script src='../src/js/media.js'></script>
|
||||
<script src='../src/js/media.html5.js'></script>
|
||||
<script src='../src/js/media.flash.js'></script>
|
||||
<script src='../src/js/controls.js'></script>
|
||||
<script src='../src/js/tracks.js'></script>
|
||||
<script src='../src/js/setup.js'></script>
|
||||
<script src='../src/js/json.js'></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
// Easy access to test Flash over HTML5. Add ?flash to URL
|
||||
if (window.location.href.indexOf("?flash") !== -1) {
|
||||
videojs.options.techOrder = ["Flash"];
|
||||
videojs.options.flash.swf = "../src/swf/video-js.swf";
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example, so please don't edit or add those files. To get started make a copy of index.html.example and rename it to index.html.</p>
|
||||
|
||||
<video id="vid1" class="video-js vjs-default-skin" controls preload="auto" 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'>
|
||||
<p>Video Playback Not Supported</p>
|
||||
</video>
|
||||
|
||||
<script>
|
||||
vid = document.getElementById("vid1");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
src/css/video-js.fw.png
Normal file
BIN
src/css/video-js.fw.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
0
src/controls.js → src/js/controls.js
vendored
0
src/controls.js → src/js/controls.js
vendored
File diff suppressed because one or more lines are too long
@ -1,603 +0,0 @@
|
||||
|
||||
var VimeoState = {
|
||||
UNSTARTED: -1,
|
||||
ENDED: 0,
|
||||
PLAYING: 1,
|
||||
PAUSED: 2,
|
||||
BUFFERING: 3
|
||||
};
|
||||
|
||||
/* VideoJS-Vimeo - Vimeo iFrame Wrapper
|
||||
================================================================================ */
|
||||
_V_.vimeo = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// Extract the Vimeo videoID from the source
|
||||
var videoId = this.getVideoId(source.src);
|
||||
|
||||
// Which element to embed in
|
||||
var parentEl = options.parentEl;
|
||||
|
||||
// Generate ID for iFrame
|
||||
var objId = player.el.id+"_vimeo_api";
|
||||
|
||||
// Create an iFrame for the Vimeo player
|
||||
var iFrm = this.el = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0,
|
||||
webkitAllowFullScreen: "",
|
||||
mozallowfullscreen: "",
|
||||
allowFullScreen: ""
|
||||
});
|
||||
|
||||
var playerOptions = player.options;
|
||||
var optionsParams = options.params || {};
|
||||
|
||||
// Setup player parameters
|
||||
this.player.apiArgs = {
|
||||
api: 1,
|
||||
byline: 0,
|
||||
portrait: 0,
|
||||
show_title: 0,
|
||||
show_byline: 0,
|
||||
show_portrait: 0,
|
||||
fullscreen: 1,
|
||||
player_id: objId + "_iframe",
|
||||
color: optionsParams.color || playerOptions.color || 'ffffff',
|
||||
autoplay: this.toBoolInt(optionsParams.autoplay || playerOptions.autoplay),
|
||||
loop: this.toBoolInt(optionsParams.loop || playerOptions.loop)
|
||||
};
|
||||
|
||||
// Create a dummy object to hold Vimeo state in case any info is requested
|
||||
// before the iFrame loads
|
||||
this.player.vimeoInfo = {};
|
||||
|
||||
// Add iFrame to player div
|
||||
_V_.insertFirst(iFrm, parentEl);
|
||||
|
||||
this.loadPlayer(videoId);
|
||||
},
|
||||
|
||||
loadPlayer: function(videoId){
|
||||
var baseUrl = (document.location.protocol == 'https:') ?
|
||||
'https://secure.vimeo.com/video/' :
|
||||
'http://player.vimeo.com/video/';
|
||||
|
||||
// Wait until iFrame has loaded to initialize the API
|
||||
_V_.addEvent(this.el, "load", _V_.proxy(this, function(){
|
||||
// Initialize the Vimeo Player object
|
||||
this.vimeo = $f(this.el);
|
||||
|
||||
// Create an object to track the state of the Vimeo player, since we can
|
||||
// only fetch information from the iFrame asynchronously
|
||||
this.player.vimeoInfo = {
|
||||
state: VimeoState.UNSTARTED,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
muteVolume: 1,
|
||||
time: 0,
|
||||
duration: 0,
|
||||
buffered: 0,
|
||||
url: baseUrl + videoId,
|
||||
error: null
|
||||
};
|
||||
|
||||
// Register Vimeo event handlers
|
||||
this.vimeo.addEvent('ready', _V_.vimeo.onReady);
|
||||
this.vimeo.addEvent('loadProgress', _V_.vimeo.onLoadProgress);
|
||||
this.vimeo.addEvent('playProgress', _V_.vimeo.onPlayProgress);
|
||||
this.vimeo.addEvent('play', _V_.vimeo.onPlay);
|
||||
this.vimeo.addEvent('pause', _V_.vimeo.onPause);
|
||||
this.vimeo.addEvent('finish', _V_.vimeo.onFinish);
|
||||
this.vimeo.addEvent('seek', _V_.vimeo.onSeek);
|
||||
}));
|
||||
|
||||
// Set the iFrame URL to start loading the video
|
||||
this.el.src = baseUrl + videoId + "?" + this.makeQueryString(this.player.apiArgs);
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.vimeo.api("unload");
|
||||
delete this.vimeo;
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
play: function(){ this.vimeo.api("play"); },
|
||||
pause: function(){ this.vimeo.api("pause"); },
|
||||
paused: function(){
|
||||
var state = this.player.vimeoInfo.state;
|
||||
return state !== VimeoState.PLAYING &&
|
||||
state !== VimeoState.BUFFERING;
|
||||
},
|
||||
|
||||
src: function(src){
|
||||
var videoId = this.getVideoId(src);
|
||||
this.loadPlayer(videoId);
|
||||
},
|
||||
load: function(){ },
|
||||
poster: function(){
|
||||
// We could fetch the poster image using JSONP, but it would need to be
|
||||
// done ahead of time (and probably enabled only through a player param)
|
||||
return null;
|
||||
},
|
||||
|
||||
currentTime: function(){ return this.player.vimeoInfo.time || 0; },
|
||||
setCurrentTime: function(seconds){ this.vimeo.api("seekTo", seconds); },
|
||||
|
||||
duration: function(){ return this.player.vimeoInfo.duration || 0; },
|
||||
buffered: function(){
|
||||
return _V_.createTimeRange(0, this.player.vimeoInfo.buffered || 0);
|
||||
},
|
||||
|
||||
volume: function(){
|
||||
return (this.player.vimeoInfo.muted) ?
|
||||
this.player.vimeoInfo.muteVolume :
|
||||
this.player.vimeoInfo.volume;
|
||||
},
|
||||
setVolume: function(percentAsDecimal){
|
||||
this.vimeo.api("setVolume", percentAsDecimal);
|
||||
this.player.vimeoInfo.volume = percentAsDecimal;
|
||||
this.player.triggerEvent("volumechange");
|
||||
},
|
||||
muted: function(){ return this.player.vimeoInfo.muted || false; },
|
||||
setMuted: function(muted){
|
||||
if (muted) {
|
||||
this.player.vimeoInfo.muteVolume = this.player.vimeoInfo.volume;
|
||||
this.setVolume(0);
|
||||
} else {
|
||||
this.setVolume(this.player.vimeoInfo.muteVolume);
|
||||
}
|
||||
this.player.vimeoInfo.muted = muted;
|
||||
this.player.triggerEvent("volumechange");
|
||||
},
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
currentSrc: function(){ return this.player.vimeoInfo.url; },
|
||||
|
||||
preload: function(){ return false; },
|
||||
setPreload: function(val){ },
|
||||
autoplay: function(){ return !!this.player.apiArgs.autoplay; },
|
||||
setAutoplay: function(val){ },
|
||||
loop: function(){ return !!this.player.apiArgs.loop; },
|
||||
setLoop: function(val){
|
||||
this.player.apiArgs.loop = (val ? 1 : 0);
|
||||
// We handle looping manually
|
||||
//this.vimeo.api("setLoop", val);
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){ return false; },
|
||||
enterFullScreen: function(){ return false; },
|
||||
|
||||
error: function(){ return this.player.vimeoInfo.error; },
|
||||
seeking: function(){ return false; },
|
||||
ended: function(){ return this.player.vimeoInfo.state === VimeoState.ENDED; },
|
||||
videoWidth: function(){ return this.width(); },
|
||||
videoHeight: function(){ return this.height(); },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return false; },
|
||||
|
||||
// Helpers ------------------------------------------------------------------
|
||||
|
||||
makeQueryString: function(args) {
|
||||
var array = [];
|
||||
for (var key in args) {
|
||||
if (args.hasOwnProperty(key))
|
||||
array.push(encodeURIComponent(key) + "=" + encodeURIComponent(args[key]));
|
||||
}
|
||||
return array.join("&");
|
||||
},
|
||||
|
||||
getVideoId: function(url) {
|
||||
return url.match(/vimeo\.com\/(?:.*#|.*\/videos\/)?([0-9]+)/)[1];
|
||||
},
|
||||
|
||||
toBoolInt: function(val) {
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Event callbacks ------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.onReady = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.tech.triggerReady();
|
||||
player.triggerReady();
|
||||
player.triggerEvent("canplay");
|
||||
_V_.vimeo.hideOverlay(player);
|
||||
|
||||
// Hide our playback controls since we have no good way of hiding the Vimeo
|
||||
// controls
|
||||
player.controlBar.hide();
|
||||
};
|
||||
|
||||
_V_.vimeo.onLoadProgress = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
var durationUpdate = !player.vimeoInfo.duration;
|
||||
player.vimeoInfo.duration = data.duration;
|
||||
player.vimeoInfo.buffered = data.percent;
|
||||
player.triggerEvent("progress");
|
||||
if (durationUpdate) player.triggerEvent("durationchange");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPlayProgress = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.time = data.seconds;
|
||||
player.triggerEvent("timeupdate");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPlay = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.PLAYING;
|
||||
player.triggerEvent("play");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPause = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.PAUSED;
|
||||
player.triggerEvent("pause");
|
||||
};
|
||||
|
||||
_V_.vimeo.onFinish = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.ENDED;
|
||||
player.triggerEvent("ended");
|
||||
_V_.vimeo.hideOverlay(player);
|
||||
|
||||
// Vimeo looping doesn't seem to play well with VideoJS, so we need to
|
||||
// implement it manually here
|
||||
if (player.apiArgs.loop) {
|
||||
//player.tech.vimeo.api("seekTo", 0);
|
||||
player.tech.vimeo.api("play");
|
||||
} else {
|
||||
// Reset the video
|
||||
player.tech.vimeo.api("seekTo", 0);
|
||||
player.tech.vimeo.api("play");
|
||||
player.tech.vimeo.api("pause");
|
||||
}
|
||||
};
|
||||
|
||||
_V_.vimeo.onSeek = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.time = data.seconds;
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("seeked");
|
||||
};
|
||||
|
||||
// Helpers --------------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.hideOverlay = function(player) {
|
||||
// Hide the big play button and poster since Vimeo provides these. Using
|
||||
// our own prevents the video from playing on the first click in mobile
|
||||
// devices
|
||||
player.bigPlayButton.hide();
|
||||
player.posterImage.hide();
|
||||
};
|
||||
|
||||
// Support testing ------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.isSupported = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
_V_.vimeo.canPlaySource = function(srcObj){
|
||||
return srcObj.type == "video/vimeo";
|
||||
};
|
||||
|
||||
_V_.vimeo.prototype.support = {
|
||||
formats: {
|
||||
"video/vimeo": "VIM"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvents: true,
|
||||
timeupdateEvents: true,
|
||||
|
||||
//fullscreen: true,
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS(),
|
||||
|
||||
fullscreenResize: true,
|
||||
parentResize: true
|
||||
};
|
||||
|
||||
// Froogaloop API -------------------------------------------------------------
|
||||
|
||||
// From https://github.com/vimeo/player-api/blob/master/javascript/froogaloop.js
|
||||
var Froogaloop = (function(){
|
||||
// Define a local copy of Froogaloop
|
||||
function Froogaloop(iframe) {
|
||||
// The Froogaloop object is actually just the init constructor
|
||||
return new Froogaloop.fn.init(iframe);
|
||||
}
|
||||
|
||||
var eventCallbacks = {},
|
||||
hasWindowEvent = false,
|
||||
isReady = false,
|
||||
slice = Array.prototype.slice,
|
||||
playerDomain = '';
|
||||
|
||||
Froogaloop.fn = Froogaloop.prototype = {
|
||||
element: null,
|
||||
|
||||
init: function(iframe) {
|
||||
if (typeof iframe === "string") {
|
||||
iframe = document.getElementById(iframe);
|
||||
}
|
||||
|
||||
this.element = iframe;
|
||||
|
||||
// Register message event listeners
|
||||
playerDomain = getDomainFromUrl(this.element.getAttribute('src'));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
* Calls a function to act upon the player.
|
||||
*
|
||||
* @param {string} method The name of the Javascript API method to call. Eg: 'play'.
|
||||
* @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
|
||||
* or callback function when the method returns a value.
|
||||
*/
|
||||
api: function(method, valueOrCallback) {
|
||||
if (!this.element || !method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null,
|
||||
params = !isFunction(valueOrCallback) ? valueOrCallback : null,
|
||||
callback = isFunction(valueOrCallback) ? valueOrCallback : null;
|
||||
|
||||
// Store the callback for get functions
|
||||
if (callback) {
|
||||
storeCallback(method, callback, target_id);
|
||||
}
|
||||
|
||||
postMessage(method, params, element);
|
||||
return self;
|
||||
},
|
||||
|
||||
/*
|
||||
* Registers an event listener and a callback function that gets called when the event fires.
|
||||
*
|
||||
* @param eventName (String): Name of the event to listen for.
|
||||
* @param callback (Function): Function that should be called when the event fires.
|
||||
*/
|
||||
addEvent: function(eventName, callback) {
|
||||
if (!this.element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null;
|
||||
|
||||
|
||||
storeCallback(eventName, callback, target_id);
|
||||
|
||||
// The ready event is not registered via postMessage. It fires regardless.
|
||||
if (eventName != 'ready') {
|
||||
postMessage('addEventListener', eventName, element);
|
||||
}
|
||||
else if (eventName == 'ready' && isReady) {
|
||||
callback.call(null, target_id);
|
||||
}
|
||||
|
||||
return self;
|
||||
},
|
||||
|
||||
/*
|
||||
* Unregisters an event listener that gets called when the event fires.
|
||||
*
|
||||
* @param eventName (String): Name of the event to stop listening for.
|
||||
*/
|
||||
removeEvent: function(eventName) {
|
||||
if (!this.element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null,
|
||||
removed = removeCallback(eventName, target_id);
|
||||
|
||||
// The ready event is not registered
|
||||
if (eventName != 'ready' && removed) {
|
||||
postMessage('removeEventListener', eventName, element);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles posting a message to the parent window.
|
||||
*
|
||||
* @param method (String): name of the method to call inside the player. For api calls
|
||||
* this is the name of the api method (api_play or api_pause) while for events this method
|
||||
* is api_addEventListener.
|
||||
* @param params (Object or Array): List of parameters to submit to the method. Can be either
|
||||
* a single param or an array list of parameters.
|
||||
* @param target (HTMLElement): Target iframe to post the message to.
|
||||
*/
|
||||
function postMessage(method, params, target) {
|
||||
if (!target.contentWindow.postMessage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var url = target.getAttribute('src').split('?')[0],
|
||||
data = JSON.stringify({
|
||||
method: method,
|
||||
value: params
|
||||
});
|
||||
|
||||
if (url.substr(0, 2) === '//') {
|
||||
url = window.location.protocol + url;
|
||||
}
|
||||
|
||||
target.contentWindow.postMessage(data, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that fires whenever the window receives a message from its parent
|
||||
* via window.postMessage.
|
||||
*/
|
||||
function onMessageReceived(event) {
|
||||
var data, method;
|
||||
|
||||
try {
|
||||
data = JSON.parse(event.data);
|
||||
method = data.event || data.method;
|
||||
}
|
||||
catch(e) {
|
||||
//fail silently... like a ninja!
|
||||
}
|
||||
|
||||
if (method == 'ready' && !isReady) {
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
// Handles messages from moogaloop only
|
||||
if (event.origin != playerDomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = data.value,
|
||||
eventData = data.data,
|
||||
target_id = target_id === '' ? null : data.player_id,
|
||||
|
||||
callback = getCallback(method, target_id),
|
||||
params = [];
|
||||
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
params.push(value);
|
||||
}
|
||||
|
||||
if (eventData) {
|
||||
params.push(eventData);
|
||||
}
|
||||
|
||||
if (target_id) {
|
||||
params.push(target_id);
|
||||
}
|
||||
|
||||
return params.length > 0 ? callback.apply(null, params) : callback.call();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores submitted callbacks for each iframe being tracked and each
|
||||
* event for that iframe.
|
||||
*
|
||||
* @param eventName (String): Name of the event. Eg. api_onPlay
|
||||
* @param callback (Function): Function that should get executed when the
|
||||
* event is fired.
|
||||
* @param target_id (String) [Optional]: If handling more than one iframe then
|
||||
* it stores the different callbacks for different iframes based on the iframe's
|
||||
* id.
|
||||
*/
|
||||
function storeCallback(eventName, callback, target_id) {
|
||||
if (target_id) {
|
||||
if (!eventCallbacks[target_id]) {
|
||||
eventCallbacks[target_id] = {};
|
||||
}
|
||||
eventCallbacks[target_id][eventName] = callback;
|
||||
}
|
||||
else {
|
||||
eventCallbacks[eventName] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves stored callbacks.
|
||||
*/
|
||||
function getCallback(eventName, target_id) {
|
||||
if (target_id) {
|
||||
return eventCallbacks[target_id][eventName];
|
||||
}
|
||||
else {
|
||||
return eventCallbacks[eventName];
|
||||
}
|
||||
}
|
||||
|
||||
function removeCallback(eventName, target_id) {
|
||||
if (target_id && eventCallbacks[target_id]) {
|
||||
if (!eventCallbacks[target_id][eventName]) {
|
||||
return false;
|
||||
}
|
||||
eventCallbacks[target_id][eventName] = null;
|
||||
}
|
||||
else {
|
||||
if (!eventCallbacks[eventName]) {
|
||||
return false;
|
||||
}
|
||||
eventCallbacks[eventName] = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a domain's root domain.
|
||||
* Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted
|
||||
*
|
||||
* @param url (String): Url to test against.
|
||||
* @return url (String): Root domain of submitted url
|
||||
*/
|
||||
function getDomainFromUrl(url) {
|
||||
if (url.substr(0, 2) === '//') {
|
||||
url = window.location.protocol + url;
|
||||
}
|
||||
|
||||
var url_pieces = url.split('/'),
|
||||
domain_str = '';
|
||||
|
||||
for(var i = 0, length = url_pieces.length; i < length; i++) {
|
||||
if(i<3) {domain_str += url_pieces[i];}
|
||||
else {break;}
|
||||
if(i<2) {domain_str += '/';}
|
||||
}
|
||||
|
||||
return domain_str;
|
||||
}
|
||||
|
||||
function isFunction(obj) {
|
||||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
}
|
||||
|
||||
function isArray(obj) {
|
||||
return toString.call(obj) === '[object Array]';
|
||||
}
|
||||
|
||||
// Give the init function the Froogaloop prototype for later instantiation
|
||||
Froogaloop.fn.init.prototype = Froogaloop.fn;
|
||||
|
||||
// Listens for the message event.
|
||||
// W3C
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('message', onMessageReceived, false);
|
||||
}
|
||||
// IE
|
||||
else {
|
||||
window.attachEvent('onmessage', onMessageReceived);
|
||||
}
|
||||
|
||||
// Expose froogaloop to the global object
|
||||
return (window.Froogaloop = window.$f = Froogaloop);
|
||||
|
||||
})();
|
@ -1,39 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>YouTube Test</title>
|
||||
|
||||
<link href="../../design/video-js.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!--[if IE]>
|
||||
<script src="https://getfirebug.com/releases/lite/1.4/firebug-lite.js"></script>
|
||||
<!--<![endif]-->
|
||||
|
||||
<script src="../../src/core.js"></script>
|
||||
<script src="../../src/lib.js"></script>
|
||||
<script src="../../src/json.js"></script>
|
||||
|
||||
<script src="../../src/component.js"></script>
|
||||
<script src="../../src/player.js"></script>
|
||||
<script src="../../src/tech.js"></script>
|
||||
<script src="../../src/controls.js"></script>
|
||||
<script src="../../src/events.js"></script>
|
||||
<script src="../../src/tracks.js"></script>
|
||||
|
||||
<script src="../../tech/html5/html5.js"></script>
|
||||
<script src="../../tech/flash/flash.js"></script>
|
||||
|
||||
<script src="../../tech/youtube/youtube.js"></script>
|
||||
|
||||
<script src="../../src/setup.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<video id="example_video_1" class="video-js vjs-default-skin" controls width="640" height="264" data-setup='{"techOrder":["youtube","html5"]}'>
|
||||
<source src="http://www.youtube.com/watch?v=9bZkp7q19f0" type='video/youtube'>
|
||||
</video>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,369 +0,0 @@
|
||||
|
||||
/* VideoJS-YouTube - YouTube iFrame Wrapper
|
||||
================================================================================ */
|
||||
_V_.youtube = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// Extract the YouTube videoID from the source
|
||||
var videoId = this.getVideoId(source.src);
|
||||
|
||||
// Which element to embed in
|
||||
var parentEl = options.parentEl;
|
||||
|
||||
// Generate ID for iFrame
|
||||
var objId = player.el.id+"_youtube_api";
|
||||
|
||||
// Create an iFrame for the YouTube player
|
||||
var iFrm = this.el = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0,
|
||||
webkitAllowFullScreen: "",
|
||||
mozallowfullscreen: "",
|
||||
allowFullScreen: ""
|
||||
});
|
||||
|
||||
// Store a global list of currently loading players
|
||||
_V_.youtube.loadingEls = _V_.youtube.loadingEls || [];
|
||||
_V_.youtube.loadingEls.push(parentEl);
|
||||
|
||||
var playerOptions = player.options;
|
||||
var optionsParams = options.params || {};
|
||||
|
||||
// Setup player parameters
|
||||
var params = {
|
||||
disablekb: 1,
|
||||
enablejsapi: 1,
|
||||
iv_load_policy: 3,
|
||||
modestbranding: 1,
|
||||
playerapiid: objId,
|
||||
wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance (Flash only)
|
||||
rel: 0,
|
||||
showinfo: 0,
|
||||
showsearch: 0,
|
||||
controls: this.toBoolInt(optionsParams.ytcontrols || playerOptions.ytcontrols),
|
||||
autoplay: this.toBoolInt(optionsParams.autoplay || playerOptions.autoplay),
|
||||
loop: this.toBoolInt(optionsParams.loop || playerOptions.loop),
|
||||
hd: this.toBoolInt(optionsParams.hd || playerOptions.hd)
|
||||
};
|
||||
|
||||
var p = (document.location.protocol == 'https:') ? 'https:' : 'http:';
|
||||
|
||||
if (document.domain != 'localhost' && document.location.protocol != 'file:')
|
||||
params.origin = p + "//" + document.domain;
|
||||
|
||||
this.player.apiArgs = {
|
||||
videoId: videoId,
|
||||
playerVars: params,
|
||||
events: {
|
||||
"onReady": _V_.youtube.onReady,
|
||||
"onStateChange": _V_.youtube.onStateChange,
|
||||
"onPlaybackQualityChange": _V_.youtube.onPlaybackQualityChange,
|
||||
"onError": _V_.youtube.onError
|
||||
}
|
||||
};
|
||||
|
||||
_V_.addEvent(parentEl, "techready", _V_.proxy(this, function(){
|
||||
// YouTube JS API is ready, load the player
|
||||
iFrm.src = p + "//www.youtube.com/embed/" + videoId + "?" +
|
||||
this.makeQueryString(params);
|
||||
// Initialize the YouTube Player object. Only pass events as arguments,
|
||||
// since all of our other parameters went into the iFrame URL
|
||||
this.youtube = new YT.Player(iFrm, { events: this.player.apiArgs.events });
|
||||
}));
|
||||
|
||||
// Add iFrame to player div
|
||||
_V_.insertFirst(iFrm, parentEl);
|
||||
|
||||
_V_.youtube.updateVideoQuality(this.player, null);
|
||||
|
||||
this.loadApi();
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
this.youtube.destroy();
|
||||
delete this.youtube;
|
||||
},
|
||||
|
||||
play: function(){ this.youtube.playVideo(); },
|
||||
pause: function(){ this.youtube.pauseVideo(); },
|
||||
paused: function(){
|
||||
var state = this.youtube.getPlayerState();
|
||||
return state !== YT.PlayerState.PLAYING &&
|
||||
state !== YT.PlayerState.BUFFERING;
|
||||
},
|
||||
|
||||
src: function(src){
|
||||
delete this.player.error;
|
||||
|
||||
// FIXME: Does this work or do we have to set the iFrame src again?
|
||||
var videoId = this.getVideoId(src);
|
||||
this.youtube.loadVideoById(videoId);
|
||||
},
|
||||
load: function(){ },
|
||||
poster: function(){
|
||||
var videoId = this.getVideoId(this.youtube.getVideoUrl());
|
||||
return "http://img.youtube.com/vi/" + videoId + "/0.jpg";
|
||||
},
|
||||
|
||||
currentTime: function(){ return this.youtube.getCurrentTime() || 0; },
|
||||
setCurrentTime: function(seconds){
|
||||
this.youtube.seekTo(seconds, true);
|
||||
this.player.triggerEvent("timeupdate");
|
||||
},
|
||||
|
||||
duration: function(){ return this.youtube.getDuration() || 0; },
|
||||
buffered: function(){
|
||||
var loadedBytes = this.youtube.getVideoBytesLoaded();
|
||||
var totalBytes = this.youtube.getVideoBytesTotal();
|
||||
if (!loadedBytes || !totalBytes) return 0;
|
||||
|
||||
var duration = this.youtube.getDuration();
|
||||
var secondsBuffered = (loadedBytes / totalBytes) * duration;
|
||||
var secondsOffset = (this.youtube.getVideoStartBytes() / totalBytes) * duration;
|
||||
return _V_.createTimeRange(secondsOffset, secondsOffset + secondsBuffered);
|
||||
},
|
||||
|
||||
volume: function(){
|
||||
if (isNaN(this.youtube.volumeVal))
|
||||
this.youtube.volumeVal = this.youtube.getVolume() / 100.0;
|
||||
return this.youtube.volumeVal;
|
||||
},
|
||||
setVolume: function(percentAsDecimal){
|
||||
if (percentAsDecimal != this.youtube.volumeVal) {
|
||||
this.youtube.volumeVal = percentAsDecimal;
|
||||
this.youtube.setVolume(percentAsDecimal * 100.0);
|
||||
this.player.triggerEvent("volumechange");
|
||||
}
|
||||
},
|
||||
muted: function(){ return this.youtube.isMuted(); },
|
||||
setMuted: function(muted){
|
||||
if (muted)
|
||||
this.youtube.mute();
|
||||
else
|
||||
this.youtube.unMute();
|
||||
|
||||
// Volume changes do not show up in the API immediately, so we need
|
||||
// to wait for a moment
|
||||
var self = this;
|
||||
setTimeout(function() { self.player.triggerEvent("volumechange"); }, 50);
|
||||
},
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
currentSrc: function(){ return this.youtube.getVideoUrl(); },
|
||||
|
||||
preload: function(){ return false; },
|
||||
setPreload: function(val){ },
|
||||
autoplay: function(){ return !!this.player.apiArgs.playerVars.autoplay; },
|
||||
setAutoplay: function(val){ },
|
||||
loop: function(){ return !!this.player.apiArgs.playerVars.loop; },
|
||||
setLoop: function(val){
|
||||
this.player.apiArgs.playerVars.loop = (val ? 1 : 0);
|
||||
// We handle looping manually
|
||||
//this.youtube.setLoop(val);
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){ return false; },
|
||||
enterFullScreen: function(){ return false; },
|
||||
|
||||
error: function(){ return this.player.error; },
|
||||
seeking: function(){ return false; },
|
||||
ended: function(){ return this.youtube.getPlayerState() === YT.PlayerState.ENDED; },
|
||||
videoWidth: function(){ return this.player.videoWidth; },
|
||||
videoHeight: function(){ return this.player.videoHeight; },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return false; },
|
||||
|
||||
// Helpers ------------------------------------------------------------------
|
||||
|
||||
makeQueryString: function(args) {
|
||||
var array = [];
|
||||
for (var key in args) {
|
||||
if (args.hasOwnProperty(key))
|
||||
array.push(encodeURIComponent(key) + "=" + encodeURIComponent(args[key]));
|
||||
}
|
||||
return array.join("&");
|
||||
},
|
||||
|
||||
getVideoId: function(url) {
|
||||
return url.match(/v=([^&]+)/)[1];
|
||||
},
|
||||
|
||||
toBoolInt: function(val) {
|
||||
return val ? 1 : 0;
|
||||
},
|
||||
|
||||
loadApi: function() {
|
||||
// Check if the YouTube JS API has already been loaded
|
||||
var js, id = "youtube-jssdk", ref = document.getElementsByTagName("script")[0];
|
||||
if (_V_.el(id)) {
|
||||
window.onYouTubePlayerAPIReady();
|
||||
return;
|
||||
}
|
||||
|
||||
// Asynchronously load the YouTube JS API
|
||||
var p = (document.location.protocol == "https:") ? "https:" : "http:";
|
||||
js = _V_.createElement("script", { id: id, async: true, src: p + "//www.youtube.com/player_api" });
|
||||
ref.parentNode.insertBefore(js, ref);
|
||||
}
|
||||
});
|
||||
|
||||
// Event callbacks ------------------------------------------------------------
|
||||
|
||||
_V_.youtube.onReady = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
|
||||
player.tech.triggerReady();
|
||||
player.triggerReady();
|
||||
player.triggerEvent("durationchange");
|
||||
|
||||
_V_.youtube.hideOverlay(player);
|
||||
};
|
||||
|
||||
_V_.youtube.onStateChange = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
|
||||
// Suppress any duplicate events from YouTube
|
||||
if (player.lastState === e.data)
|
||||
return;
|
||||
|
||||
switch (e.data) {
|
||||
case -1: // Unstarted
|
||||
player.triggerEvent("durationchange");
|
||||
break;
|
||||
case YT.PlayerState.CUED:
|
||||
break;
|
||||
case YT.PlayerState.ENDED:
|
||||
player.triggerEvent("ended");
|
||||
_V_.youtube.hideOverlay(player);
|
||||
|
||||
// YouTube looping doesn't seem to play well with VideoJS, so we need to
|
||||
// implement it manually here
|
||||
if (player.apiArgs.playerVars.loop) {
|
||||
player.tech.youtube.seekTo(0, true);
|
||||
player.tech.youtube.playVideo();
|
||||
} else {
|
||||
player.tech.youtube.stopVideo();
|
||||
}
|
||||
break;
|
||||
case YT.PlayerState.PLAYING:
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("playing");
|
||||
player.triggerEvent("play");
|
||||
break;
|
||||
case YT.PlayerState.PAUSED:
|
||||
player.triggerEvent("pause");
|
||||
break;
|
||||
case YT.PlayerState.BUFFERING:
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("waiting");
|
||||
// Hide the waiting spinner since YouTube has its own
|
||||
player.loadingSpinner.hide();
|
||||
break;
|
||||
}
|
||||
|
||||
player.lastState = e.data;
|
||||
};
|
||||
|
||||
_V_.youtube.onPlaybackQualityChange = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
_V_.youtube.updateVideoQuality(player, e.data);
|
||||
player.triggerEvent("ratechange");
|
||||
};
|
||||
|
||||
_V_.youtube.onError = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
player.error = e.data;
|
||||
player.triggerEvent("error");
|
||||
};
|
||||
|
||||
// Helpers --------------------------------------------------------------------
|
||||
|
||||
_V_.youtube.hideOverlay = function(player) {
|
||||
// Hide the big play button and poster since YouTube provides these. Using
|
||||
// our own prevents the video from playing on the first click in mobile
|
||||
// devices
|
||||
player.bigPlayButton.hide();
|
||||
player.posterImage.hide();
|
||||
};
|
||||
|
||||
_V_.youtube.updateVideoQuality = function(player, quality) {
|
||||
switch (quality) {
|
||||
case 'medium':
|
||||
player.videoWidth = 480;
|
||||
player.videoHeight = 360;
|
||||
break;
|
||||
case 'large':
|
||||
player.videoWidth = 640;
|
||||
player.videoHeight = 480;
|
||||
break;
|
||||
case 'hd720':
|
||||
player.videoWidth = 960;
|
||||
player.videoHeight = 720;
|
||||
break;
|
||||
case 'hd1080':
|
||||
player.videoWidth = 1440;
|
||||
player.videoHeight = 1080;
|
||||
break;
|
||||
case 'highres':
|
||||
player.videoWidth = 1920;
|
||||
player.videoHeight = 1080;
|
||||
break;
|
||||
case 'small':
|
||||
player.videoWidth = 320;
|
||||
player.videoHeight = 240;
|
||||
break;
|
||||
default:
|
||||
player.videoWidth = 0;
|
||||
player.videoHeight = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Support testing ------------------------------------------------------------
|
||||
|
||||
_V_.youtube.isSupported = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
_V_.youtube.canPlaySource = function(srcObj){
|
||||
return srcObj.type == "video/youtube";
|
||||
};
|
||||
|
||||
_V_.youtube.prototype.support = {
|
||||
formats: {
|
||||
"video/youtube": "YT"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvents: false,
|
||||
timeupdateEvents: false,
|
||||
|
||||
//fullscreen: true,
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS(),
|
||||
|
||||
fullscreenResize: true,
|
||||
parentResize: true
|
||||
};
|
||||
|
||||
// YouTube JS API load callback -----------------------------------------------
|
||||
|
||||
window.onYouTubePlayerAPIReady = function() {
|
||||
// Fire a techready event for each loading player
|
||||
var loadingEl;
|
||||
while ((loadingEl = _V_.youtube.loadingEls.shift())) {
|
||||
loadingEl.player.triggerEvent("techready");
|
||||
}
|
||||
};
|
@ -8,14 +8,15 @@ QUnit.begin = function () {
|
||||
|
||||
QUnit.moduleDone = function (opts) {
|
||||
if (opts.failed === 0) {
|
||||
console.log("\u2714 All tests passed in '" + opts.name + "' module")
|
||||
console.log("\n\u2714 All tests passed in '" + opts.name + "' module")
|
||||
} else {
|
||||
console.log("\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module")
|
||||
console.log("\n\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module")
|
||||
}
|
||||
}
|
||||
|
||||
QUnit.done = function (opts) {
|
||||
QUnit.done(function (opts) {
|
||||
console.log("\n================================================")
|
||||
console.log("Tests completed in " + opts.runtime + " milliseconds")
|
||||
console.log(opts.passed + " tests of " + opts.total + " passed, " + opts.failed + " failed.")
|
||||
}
|
||||
return false;
|
||||
});
|
@ -8,24 +8,24 @@
|
||||
<script src="../test/vendor/qunit/qunit/qunit.js"></script>
|
||||
|
||||
<!-- phantomjs logging script-->
|
||||
<script src="unit/phantom-logging.js"></script>
|
||||
<script src="phantom-logging.js"></script>
|
||||
|
||||
<!-- Video.js CSS -->
|
||||
<link rel="stylesheet" href="../design/video-js.css" type="text/css">
|
||||
<link rel="stylesheet" href="../src/css/video-js.css" type="text/css">
|
||||
|
||||
<!-- Video.js JavaScript -->
|
||||
<script src='../src/goog.base.js'></script>
|
||||
<script src='../src/core.js'></script>
|
||||
<script src='../src/lib.js'></script>
|
||||
<script src='../src/events.js'></script>
|
||||
<script src='../src/component.js'></script>
|
||||
<script src='../src/player.js'></script>
|
||||
<script src='../src/media.js'></script>
|
||||
<script src='../src/media.html5.js'></script>
|
||||
<script src='../src/media.flash.js'></script>
|
||||
<script src='../src/controls.js'></script>
|
||||
<script src='../src/tracks.js'></script>
|
||||
<script src='../src/setup.js'></script>
|
||||
<script src='../src/js/goog.base.js'></script>
|
||||
<script src='../src/js/core.js'></script>
|
||||
<script src='../src/js/lib.js'></script>
|
||||
<script src='../src/js/events.js'></script>
|
||||
<script src='../src/js/component.js'></script>
|
||||
<script src='../src/js/player.js'></script>
|
||||
<script src='../src/js/media.js'></script>
|
||||
<script src='../src/js/media.html5.js'></script>
|
||||
<script src='../src/js/media.flash.js'></script>
|
||||
<script src='../src/js/controls.js'></script>
|
||||
<script src='../src/js/tracks.js'></script>
|
||||
<script src='../src/js/setup.js'></script>
|
||||
|
||||
<!-- Unit Tests -->
|
||||
<script src="unit/lib.js"></script>
|
||||
|
646
test/unit.js
Normal file
646
test/unit.js
Normal file
@ -0,0 +1,646 @@
|
||||
module("Component");
|
||||
|
||||
test('should create an element', function(){
|
||||
var comp = new vjs.Component({}, {});
|
||||
|
||||
ok(comp.el().nodeName);
|
||||
});
|
||||
|
||||
test('should add a child component', function(){
|
||||
var comp = new vjs.Component({});
|
||||
|
||||
var child = comp.addChild("component");
|
||||
|
||||
ok(comp.children().length === 1);
|
||||
ok(comp.children()[0] === child);
|
||||
ok(comp.el().childNodes[0] === child.el());
|
||||
ok(comp.getChild('component') === child);
|
||||
ok(comp.getChildById(child.id()) === child);
|
||||
});
|
||||
|
||||
test('should init child coponents from options', function(){
|
||||
var comp = new vjs.Component({}, {
|
||||
children: {
|
||||
'component': true
|
||||
}
|
||||
});
|
||||
|
||||
ok(comp.children().length === 1);
|
||||
ok(comp.el().childNodes.length === 1);
|
||||
});
|
||||
|
||||
test('should dispose of component and children', function(){
|
||||
var comp = new vjs.Component({});
|
||||
|
||||
// Add a child
|
||||
var child = comp.addChild("Component");
|
||||
ok(comp.children().length === 1);
|
||||
|
||||
// Add a listener
|
||||
comp.on('click', function(){ return true; });
|
||||
var data = vjs.getData(comp.el());
|
||||
var id = comp.el()[vjs.expando];
|
||||
|
||||
comp.dispose();
|
||||
|
||||
ok(!comp.children(), 'component children were deleted');
|
||||
ok(!comp.el(), 'component element was deleted');
|
||||
ok(!child.children(), 'child children were deleted');
|
||||
ok(!child.el(), 'child element was deleted');
|
||||
ok(!vjs.cache[id], 'listener cache nulled')
|
||||
ok(vjs.isEmpty(data), 'original listener cache object was emptied')
|
||||
});
|
||||
|
||||
test('should add and remove event listeners to element', function(){
|
||||
var comp = new vjs.Component({}, {});
|
||||
|
||||
// No need to make this async because we're triggering events inline.
|
||||
// We're going to trigger the event after removing the listener,
|
||||
// So if we get extra asserts that's a problem.
|
||||
expect(2);
|
||||
|
||||
var testListener = function(){
|
||||
ok(true, 'fired event once');
|
||||
ok(this === comp, 'listener has the component as context');
|
||||
};
|
||||
|
||||
comp.on('test-event', testListener);
|
||||
comp.trigger('test-event');
|
||||
comp.off('test-event', testListener);
|
||||
comp.trigger('test-event');
|
||||
});
|
||||
|
||||
test('should trigger a listener once using one()', function(){
|
||||
var comp = new vjs.Component({}, {});
|
||||
|
||||
expect(1);
|
||||
|
||||
var testListener = function(){
|
||||
ok(true, 'fired event once');
|
||||
};
|
||||
|
||||
comp.one('test-event', testListener);
|
||||
comp.trigger('test-event');
|
||||
comp.trigger('test-event');
|
||||
});
|
||||
|
||||
test('should trigger a listener when ready', function(){
|
||||
expect(2);
|
||||
|
||||
var optionsReadyListener = function(){
|
||||
ok(true, 'options listener fired')
|
||||
};
|
||||
var methodReadyListener = function(){
|
||||
ok(true, 'ready method listener fired')
|
||||
};
|
||||
|
||||
var comp = new vjs.Component({}, {}, optionsReadyListener);
|
||||
|
||||
comp.triggerReady();
|
||||
|
||||
comp.ready(methodReadyListener);
|
||||
|
||||
// First two listeners should only be fired once and then removed
|
||||
comp.triggerReady();
|
||||
});
|
||||
|
||||
test('should add and remove a CSS class', function(){
|
||||
var comp = new vjs.Component({}, {});
|
||||
|
||||
comp.addClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') !== -1);
|
||||
comp.removeClass('test-class');
|
||||
ok(comp.el().className.indexOf('test-class') === -1);
|
||||
});
|
||||
|
||||
test('should show and hide an element', function(){
|
||||
var comp = new vjs.Component({}, {});
|
||||
|
||||
comp.hide();
|
||||
ok(comp.el().style.display === 'none');
|
||||
comp.show();
|
||||
ok(comp.el().style.display === 'block');
|
||||
});
|
||||
|
||||
test('should change the width and height of a component', function(){
|
||||
var container = document.createElement('div');
|
||||
var comp = new vjs.Component({}, {});
|
||||
var el = comp.el();
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
|
||||
fixture.appendChild(container);
|
||||
container.appendChild(el);
|
||||
// Container of el needs dimensions or the component won't have dimensions
|
||||
container.style.width = '1000px'
|
||||
container.style.height = '1000px'
|
||||
|
||||
comp.width('50%');
|
||||
comp.height('123px');
|
||||
|
||||
ok(comp.width() === 500, 'percent values working');
|
||||
ok(vjs.getComputedStyleValue(el, 'width') === comp.width() + 'px', 'matches computed style');
|
||||
ok(comp.height() === 123, 'px values working');
|
||||
|
||||
comp.width(321);
|
||||
ok(comp.width() === 321, 'integer values working');
|
||||
});
|
||||
|
||||
module("Core");
|
||||
|
||||
test('should create a video tag and have access children in old IE', function(){
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
|
||||
fixture.innerHTML += "<video id='test_vid_id'><source type='video/mp4'></video>";
|
||||
|
||||
vid = document.getElementById('test_vid_id');
|
||||
|
||||
ok(vid.childNodes.length === 1);
|
||||
ok(vid.childNodes[0].getAttribute('type') === 'video/mp4');
|
||||
});
|
||||
|
||||
test('should return a video player instance', function(){
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
fixture.innerHTML += "<video id='test_vid_id'></video><video id='test_vid_id2'></video>";
|
||||
|
||||
var player = videojs('test_vid_id');
|
||||
ok(player, 'created player from tag');
|
||||
ok(player.id() === 'test_vid_id');
|
||||
ok(videojs.players['test_vid_id'] === player, 'added player to global reference')
|
||||
|
||||
var playerAgain = videojs('test_vid_id');
|
||||
ok(player === playerAgain, 'did not create a second player from same tag');
|
||||
|
||||
var tag2 = document.getElementById('test_vid_id2');
|
||||
var player2 = videojs(tag2);
|
||||
ok(player2.id() === 'test_vid_id2', 'created player from element');
|
||||
});
|
||||
|
||||
module("Events");
|
||||
|
||||
test('should add and remove an event listener to an element', function(){
|
||||
expect(1);
|
||||
|
||||
var el = document.createElement('div');
|
||||
var listener = function(){
|
||||
ok(true, 'Click Triggered');
|
||||
};
|
||||
|
||||
vjs.on(el, 'click', listener);
|
||||
vjs.trigger(el, 'click'); // 1 click
|
||||
vjs.off(el, 'click', listener)
|
||||
vjs.trigger(el, 'click'); // No click should happen.
|
||||
});
|
||||
|
||||
test('should remove all listeners of a type', function(){
|
||||
var el = document.createElement('div');
|
||||
var clicks = 0;
|
||||
var listener = function(){
|
||||
clicks++;
|
||||
};
|
||||
var listener2 = function(){
|
||||
clicks++;
|
||||
};
|
||||
|
||||
vjs.on(el, 'click', listener);
|
||||
vjs.on(el, 'click', listener2);
|
||||
vjs.trigger(el, 'click'); // 2 clicks
|
||||
|
||||
ok(clicks === 2, 'both click listeners fired')
|
||||
|
||||
vjs.off(el, 'click')
|
||||
vjs.trigger(el, 'click'); // No click should happen.
|
||||
|
||||
ok(clicks === 2, 'no click listeners fired')
|
||||
});
|
||||
|
||||
test('should remove all listeners from an element', function(){
|
||||
expect(2);
|
||||
|
||||
var el = document.createElement('div');
|
||||
var listener = function(){
|
||||
ok(true, 'Fake1 Triggered');
|
||||
};
|
||||
var listener2 = function(){
|
||||
ok(true, 'Fake2 Triggered');
|
||||
};
|
||||
|
||||
vjs.on(el, 'fake1', listener);
|
||||
vjs.on(el, 'fake2', listener2);
|
||||
|
||||
vjs.trigger(el, 'fake1');
|
||||
vjs.trigger(el, 'fake2');
|
||||
|
||||
vjs.off(el);
|
||||
|
||||
// No listener should happen.
|
||||
vjs.trigger(el, 'fake1');
|
||||
vjs.trigger(el, 'fake2');
|
||||
});
|
||||
|
||||
test('should listen only once', function(){
|
||||
expect(1);
|
||||
|
||||
var el = document.createElement('div');
|
||||
var listener = function(){
|
||||
ok(true, 'Click Triggered');
|
||||
};
|
||||
|
||||
vjs.one(el, 'click', listener);
|
||||
vjs.trigger(el, 'click'); // 1 click
|
||||
vjs.trigger(el, 'click'); // No click should happen.
|
||||
});
|
||||
|
||||
module("Lib");
|
||||
|
||||
test('should create an element', function(){
|
||||
var div = vjs.createEl();
|
||||
var span = vjs.createEl('span', { "data-test": "asdf", innerHTML:'fdsa' })
|
||||
ok(div.nodeName === 'DIV');
|
||||
ok(span.nodeName === 'SPAN');
|
||||
ok(span['data-test'] === 'asdf');
|
||||
ok(span.innerHTML === "fdsa");
|
||||
});
|
||||
|
||||
test('should make a string start with an uppercase letter', function(){
|
||||
var foo = vjs.capitalize('bar')
|
||||
ok(foo === 'Bar');
|
||||
});
|
||||
|
||||
test('should loop through each property on an object', function(){
|
||||
var asdf = {
|
||||
a: 1,
|
||||
b: 2,
|
||||
'c': 3
|
||||
}
|
||||
|
||||
// Add 3 to each value
|
||||
vjs.eachProp(asdf, function(key, value){
|
||||
asdf[key] = value + 3;
|
||||
});
|
||||
|
||||
deepEqual(asdf,{a:4,b:5,'c':6})
|
||||
});
|
||||
|
||||
test('should add context to a function', function(){
|
||||
var newContext = { test: 'obj'};
|
||||
var asdf = function(){
|
||||
ok(this === newContext);
|
||||
}
|
||||
var fdsa = vjs.bind(newContext, asdf);
|
||||
|
||||
fdsa();
|
||||
});
|
||||
|
||||
test('should add and remove a class name on an element', function(){
|
||||
var el = document.createElement('div');
|
||||
vjs.addClass(el, 'test-class')
|
||||
ok(el.className === 'test-class', 'class added');
|
||||
vjs.addClass(el, 'test-class')
|
||||
ok(el.className === 'test-class', 'same class not duplicated');
|
||||
vjs.addClass(el, 'test-class2')
|
||||
ok(el.className === 'test-class test-class2', 'added second class');
|
||||
vjs.removeClass(el, 'test-class')
|
||||
ok(el.className === 'test-class2', 'removed first class');
|
||||
});
|
||||
|
||||
test('should get and remove data from an element', function(){
|
||||
var el = document.createElement('div');
|
||||
var data = vjs.getData(el);
|
||||
var id = el[vjs.expando];
|
||||
|
||||
ok(typeof data === 'object', 'data object created');
|
||||
|
||||
// Add data
|
||||
var testData = { asdf: 'fdsa' };
|
||||
data.test = testData;
|
||||
ok(vjs.getData(el).test === testData, 'data added');
|
||||
|
||||
// Remove all data
|
||||
vjs.removeData(el);
|
||||
|
||||
ok(!vjs.cache[id], 'cached item nulled')
|
||||
ok(el[vjs.expando] === null || el[vjs.expando] === undefined, 'element data id removed')
|
||||
});
|
||||
|
||||
test('should read tag attributes from elements, including HTML5 in all browsers', function(){
|
||||
var container = document.createElement('div');
|
||||
|
||||
var tags = '<video id="vid1" controls autoplay loop muted preload="none" src="http://google.com" poster="http://www2.videojs.com/img/video-js-html5-video-player.png" data-test="asdf" data-empty-string=""></video>';
|
||||
tags += '<video id="vid2">';
|
||||
// Not putting source and track inside video element because
|
||||
// oldIE needs the HTML5 shim to read tags inside HTML5 tags.
|
||||
// Still may not work in oldIE.
|
||||
tags += '<source id="source" src="http://google.com" type="video/mp4" media="fdsa" title="test" >';
|
||||
tags += '<track id="track" default src="http://google.com" kind="captions" srclang="en" label="testlabel" title="test" >';
|
||||
container.innerHTML += tags;
|
||||
document.getElementById('qunit-fixture').appendChild(container);
|
||||
|
||||
var vid1Vals = vjs.getAttributeValues(document.getElementById('vid1'));
|
||||
var vid2Vals = vjs.getAttributeValues(document.getElementById('vid2'));
|
||||
var sourceVals = vjs.getAttributeValues(document.getElementById('source'));
|
||||
var trackVals = vjs.getAttributeValues(document.getElementById('track'));
|
||||
|
||||
deepEqual(vid1Vals, { 'autoplay': true, 'controls': true, 'data-test': "asdf", 'data-empty-string': "", 'id': "vid1", 'loop': true, 'muted': true, 'poster': "http://www2.videojs.com/img/video-js-html5-video-player.png", 'preload': "none", 'src': "http://google.com" });
|
||||
deepEqual(vid2Vals, { 'id': "vid2" });
|
||||
deepEqual(sourceVals, {'title': "test", 'media': "fdsa", 'type': "video/mp4", 'src': "http://google.com", 'id': "source" });
|
||||
deepEqual(trackVals, { "default": true, /* IE no likey default key */ 'id': "track", 'kind': "captions", 'label': "testlabel", 'src': "http://google.com", 'srclang': "en", 'title': "test" });
|
||||
});
|
||||
|
||||
test('should get the right style values for an element', function(){
|
||||
var el = document.createElement('div');
|
||||
var container = document.createElement('div');
|
||||
var fixture = document.getElementById('qunit-fixture')
|
||||
|
||||
container.appendChild(el);
|
||||
fixture.appendChild(container);
|
||||
|
||||
container.style.width = "1000px";
|
||||
container.style.height = "1000px";
|
||||
|
||||
el.style.height = "100%";
|
||||
el.style.width = "123px";
|
||||
|
||||
ok(vjs.getComputedStyleValue(el, 'height') === '1000px');
|
||||
ok(vjs.getComputedStyleValue(el, 'width') === '123px');
|
||||
});
|
||||
|
||||
test('should insert an element first in another', function(){
|
||||
var el1 = document.createElement('div');
|
||||
var el2 = document.createElement('div');
|
||||
var parent = document.createElement('div');
|
||||
|
||||
vjs.insertFirst(el1, parent)
|
||||
ok(parent.firstChild === el1, 'inserts first into empty parent');
|
||||
|
||||
vjs.insertFirst(el2, parent)
|
||||
ok(parent.firstChild === el2, 'inserts first into parent with child');
|
||||
});
|
||||
|
||||
test('should return the element with the ID', function(){
|
||||
var el1 = document.createElement('div');
|
||||
var el2 = document.createElement('div');
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
|
||||
fixture.appendChild(el1);
|
||||
fixture.appendChild(el2);
|
||||
|
||||
el1.id = 'test_id1';
|
||||
el2.id = 'test_id2';
|
||||
|
||||
ok(vjs.el("test_id1") === el1, 'found element for ID');
|
||||
ok(vjs.el("#test_id2") === el2, 'found element for CSS ID');
|
||||
});
|
||||
|
||||
test('should trim whitespace from a string', function(){
|
||||
ok(vjs.trim(' asdf asdf asdf \t\n\r') === 'asdf asdf asdf');
|
||||
});
|
||||
|
||||
test('should round a number', function(){
|
||||
ok(vjs.round(1.01) === 1);
|
||||
ok(vjs.round(1.5) === 2);
|
||||
ok(vjs.round(1.55, 2) === 1.55);
|
||||
ok(vjs.round(10.551, 2) === 10.55);
|
||||
});
|
||||
|
||||
test('should format time as a string', function(){
|
||||
ok(vjs.formatTime(1) === "0:01");
|
||||
ok(vjs.formatTime(10) === "0:10");
|
||||
ok(vjs.formatTime(60) === "1:00");
|
||||
ok(vjs.formatTime(600) === "10:00");
|
||||
ok(vjs.formatTime(3600) === "1:00:00");
|
||||
ok(vjs.formatTime(36000) === "10:00:00");
|
||||
ok(vjs.formatTime(360000) === "100:00:00");
|
||||
|
||||
// Using guide should provide extra leading zeros
|
||||
ok(vjs.formatTime(1,1) === "0:01");
|
||||
ok(vjs.formatTime(1,10) === "0:01");
|
||||
ok(vjs.formatTime(1,60) === "0:01");
|
||||
ok(vjs.formatTime(1,600) === "00:01");
|
||||
ok(vjs.formatTime(1,3600) === "0:00:01");
|
||||
// Don't do extra leading zeros for hours
|
||||
ok(vjs.formatTime(1,36000) === "0:00:01");
|
||||
ok(vjs.formatTime(1,360000) === "0:00:01");
|
||||
});
|
||||
|
||||
test('should create a fake timerange', function(){
|
||||
var tr = vjs.createTimeRange(0, 10);
|
||||
ok(tr.start() === 0);
|
||||
ok(tr.end() === 10);
|
||||
});
|
||||
|
||||
test('should get an absolute URL', function(){
|
||||
// Errors on compiled tests that don't use unit.html. Need a better solution.
|
||||
// ok(vjs.getAbsoluteURL('unit.html') === window.location.href);
|
||||
ok(vjs.getAbsoluteURL('http://asdf.com') === "http://asdf.com");
|
||||
ok(vjs.getAbsoluteURL('https://asdf.com/index.html') === "https://asdf.com/index.html");
|
||||
});
|
||||
|
||||
module("HTML5");
|
||||
|
||||
module("Player");
|
||||
|
||||
var PlayerTest = {
|
||||
makeTag: function(){
|
||||
var videoTag = document.createElement('video');
|
||||
videoTag.id = 'example_1';
|
||||
videoTag.className = 'video-js vjs-default-skin';
|
||||
return videoTag;
|
||||
},
|
||||
makePlayer: function(playerOptions){
|
||||
var videoTag = PlayerTest.makeTag();
|
||||
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
fixture.appendChild(videoTag);
|
||||
|
||||
return player = new vjs.Player(videoTag, playerOptions);
|
||||
}
|
||||
};
|
||||
|
||||
// Compiler doesn't like using 'this' in setup/teardown.
|
||||
// module("Player", {
|
||||
// /**
|
||||
// * @this {*}
|
||||
// */
|
||||
// setup: function(){
|
||||
// window.player1 = true; // using window works
|
||||
// },
|
||||
|
||||
// /**
|
||||
// * @this {*}
|
||||
// */
|
||||
// teardown: function(){
|
||||
// // if (this.player && this.player.el() !== null) {
|
||||
// // this.player.dispose();
|
||||
// // this.player = null;
|
||||
// // }
|
||||
// }
|
||||
// });
|
||||
|
||||
// Object.size = function(obj) {
|
||||
// var size = 0, key;
|
||||
// for (key in obj) {
|
||||
// console.log('key', key)
|
||||
// if (obj.hasOwnProperty(key)) size++;
|
||||
// }
|
||||
// return size;
|
||||
// };
|
||||
|
||||
|
||||
test('should create player instance that inherits from component and dispose it', function(){
|
||||
var player = PlayerTest.makePlayer();
|
||||
|
||||
ok(player.el().nodeName === 'DIV');
|
||||
ok(player.on, 'component function exists');
|
||||
|
||||
player.dispose();
|
||||
ok(player.el() === null, 'element disposed');
|
||||
});
|
||||
|
||||
test('should accept options from multiple sources and override in correct order', function(){
|
||||
// For closure compiler to work, all reference to the prop have to be the same type
|
||||
// As in options['attr'] or options.attr. Compiler will minimize each separately.
|
||||
// Since we're using setAttribute which requires a string, we have to use the string
|
||||
// version of the key for all version.
|
||||
|
||||
// Set a global option
|
||||
vjs.options['attr'] = 1;
|
||||
|
||||
var tag0 = PlayerTest.makeTag();
|
||||
var player0 = new vjs.Player(tag0);
|
||||
|
||||
ok(player0.options['attr'] === 1, 'global option was set')
|
||||
player0.dispose();
|
||||
|
||||
// Set a tag level option
|
||||
var tag1 = PlayerTest.makeTag();
|
||||
tag1.setAttribute('attr', 'asdf'); // Attributes must be set as strings
|
||||
|
||||
var player1 = new vjs.Player(tag1);
|
||||
ok(player1.options['attr'] === 'asdf', 'Tag options overrode global options');
|
||||
player1.dispose();
|
||||
|
||||
// Set a tag level option
|
||||
var tag2 = PlayerTest.makeTag();
|
||||
tag2.setAttribute('attr', 'asdf');
|
||||
|
||||
var player2 = new vjs.Player(tag2, { 'attr': 'fdsa' });
|
||||
ok(player2.options['attr'] === 'fdsa', 'Init options overrode tag and global options');
|
||||
player2.dispose();
|
||||
});
|
||||
|
||||
test('should get tag, source, and track settings', function(){
|
||||
// Partially tested in lib->getAttributeValues
|
||||
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
|
||||
var html = '<video id="example_1" class="video-js" autoplay preload="metadata">'
|
||||
html += '<source src="http://google.com" type="video/mp4">';
|
||||
html += '<source src="http://google.com" type="video/webm">';
|
||||
html += '<track src="http://google.com" kind="captions" default>';
|
||||
html += '</video>';
|
||||
|
||||
fixture.innerHTML += html;
|
||||
|
||||
var tag = document.getElementById('example_1');
|
||||
var player = new vjs.Player(tag);
|
||||
|
||||
ok(player.options['autoplay'] === true);
|
||||
ok(player.options['preload'] === 'metadata'); // No extern. Use string.
|
||||
ok(player.options['id'] === 'example_1');
|
||||
ok(player.options['sources'].length === 2);
|
||||
ok(player.options['sources'][0].src === 'http://google.com');
|
||||
ok(player.options['sources'][0].type === 'video/mp4');
|
||||
ok(player.options['sources'][1].type === 'video/webm');
|
||||
ok(player.options['tracks'].length === 1);
|
||||
ok(player.options['tracks'][0]['kind'] === 'captions'); // No extern
|
||||
ok(player.options['tracks'][0]['default'] === true);
|
||||
|
||||
ok(player.el().className.indexOf('video-js') !== -1, 'transferred class from tag to player div');
|
||||
ok(player.el().id === 'example_1', 'transferred id from tag to player div');
|
||||
|
||||
ok(tag.player === player, 'player referenceable on original tag');
|
||||
ok(vjs.players[player.id()] === player, 'player referenceable from global list');
|
||||
ok(tag.id !== player.id, 'tag ID no longer is the same as player ID');
|
||||
ok(tag.className !== player.el().className, 'tag classname updated');
|
||||
|
||||
player.dispose();
|
||||
|
||||
ok(tag.player === null, 'tag player ref killed')
|
||||
ok(!vjs.players['example_1'], 'global player ref killed')
|
||||
ok(player.el() === null, 'player el killed')
|
||||
});
|
||||
|
||||
test('should set the width and height of the player', function(){
|
||||
var player = PlayerTest.makePlayer({ width: 123, height: '100%' });
|
||||
|
||||
ok(player.width() === 123)
|
||||
ok(player.el().style.width === '123px')
|
||||
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
var container = document.createElement('div');
|
||||
fixture.appendChild(container);
|
||||
|
||||
// Player container needs to have height in order to have height
|
||||
// Don't want to mess with the fixture itself
|
||||
container.appendChild(player.el());
|
||||
container.style.height = "1000px";
|
||||
ok(player.height() === 1000);
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
test('should accept options from multiple sources and override in correct order', function(){
|
||||
var tag = PlayerTest.makeTag();
|
||||
var container = document.createElement('div');
|
||||
var fixture = document.getElementById('qunit-fixture');
|
||||
|
||||
container.appendChild(tag);
|
||||
fixture.appendChild(container);
|
||||
|
||||
var player = new vjs.Player(tag);
|
||||
var el = player.el();
|
||||
|
||||
ok(el.parentNode === container, 'player placed at same level as tag')
|
||||
// Tag may be placed inside the player element or it may be removed from the DOM
|
||||
ok(tag.parentNode !== container, 'tag removed from original place')
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
test('should load a media controller', function(){
|
||||
var player = PlayerTest.makePlayer({
|
||||
preload: 'none',
|
||||
sources: [
|
||||
{ src: "http://google.com", type: 'video/mp4' },
|
||||
{ src: "http://google.com", type: 'video/webm' }
|
||||
]
|
||||
});
|
||||
|
||||
ok(player.el().children[0].className.indexOf('vjs-tech') !== -1, 'media controller loaded')
|
||||
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
module("Setup");
|
||||
|
||||
// Logging setup for phantom integration
|
||||
// adapted from Modernizr & Bootstrap
|
||||
|
||||
QUnit.begin = function () {
|
||||
console.log("Starting test suite")
|
||||
console.log("================================================\n")
|
||||
}
|
||||
|
||||
QUnit.moduleDone = function (opts) {
|
||||
if (opts.failed === 0) {
|
||||
console.log("\u2714 All tests passed in '" + opts.name + "' module")
|
||||
} else {
|
||||
console.log("\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module")
|
||||
}
|
||||
}
|
||||
|
||||
QUnit.done = function (opts) {
|
||||
console.log("\n================================================")
|
||||
console.log("Tests completed in " + opts.runtime + " milliseconds")
|
||||
console.log(opts.passed + " tests of " + opts.total + " passed, " + opts.failed + " failed.")
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user