1
0
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:
Steve Heffernan 2013-01-11 23:02:20 -08:00
parent 675147645c
commit e7c146bc1f
37 changed files with 999 additions and 5694 deletions

2
.gitignore vendored
View File

@ -7,3 +7,5 @@ test/*.map
node_modules
npm-debug.log
sandbox/*
!sandbox/*.example

View File

@ -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
View 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

View File

@ -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);
}

View File

@ -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>

View File

@ -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"
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

View File

@ -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);
})();

View File

@ -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>

View File

@ -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");
}
};

View File

@ -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;
});

View File

@ -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
View 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