diff --git a/.gitignore b/.gitignore index bb4fa1325..e6d6908fc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ projects node_modules npm-debug.log -closure-test diff --git a/.jshintrc b/.jshintrc index bfb9ebe82..5acda60d6 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,16 +1,23 @@ { + // 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, "debug" : true, "boss" : true, "expr" : true, "eqnull" : true, - "quotmark" : "double", + "quotmark" : "single", "sub" : true, "trailing" : true, "undef" : true, + "laxbreak" : true, "predef" : [ // Extra globals. "_V_", - "VideoJS" + "videojs", + "vjs", + "goog" ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 18a946e9e..fa4bbd866 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ -[Video.js - HTML5 Video Player](http://videojs.com) [![Build Status](https://travis-ci.org/zencoder/video-js.png?branch=master)](https://travis-ci.org/zencoder/video-js) +[Video.js - HTML5 and Flash Video Player](http://videojs.com) [![Build Status](https://travis-ci.org/zencoder/video-js.png?branch=master)](https://travis-ci.org/zencoder/video-js) ================================================== Visit the main site at [videojs.com](http://videojs.com) for download options and instructions. +Video.js was built to provide a fast and easy way to embed and work with video in a web page. It was built from the ground up with the assumption that HTML5 is the future of web video, however it supports Flash equally well for older browsers and for advanced features not yet supported in HTML5. + +Some of the focuses of Video.js are: +- Universal browser and device support +- Easily skinned (themed/chromed) using just CSS +- A JavaScript API for controlling the video that works consistently across video platforms (HTML5, Flash, and others) as well as devices +- A common skin and API between HTML5, Flash, and potentially other players like YouTube + To build video-js from the latest version of the source, clone the source repository and run: sh build.sh in the video-js directory using the commnand-line/terminal of a unix-based system. diff --git a/Rakefile b/Rakefile index dfb36bd64..0d1cf2cbf 100644 --- a/Rakefile +++ b/Rakefile @@ -6,12 +6,12 @@ namespace :build do desc "Compile" task :compiled do - Rake::Shell['java -jar build/compiler/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js=src/goog.base.js --js=src/core.js --js=src/lib.js --js=src/events.js --js=src/component.js --js=src/player.js --js=src/media.js --js=src/media.html5.js --js=src/media.flash.js --js=src/controls.js --js=src/tracks.js --js=src/setup.js --js=src/json.js --js=src/exports.js --formatting=pretty_print --js_output_file=closure-test/video.compiled.js --create_source_map closure-test/video.compiled.js.map --source_map_format=V3 --externs src/media.flash.externs.js --externs closure-test/qunit-externs.js --output_wrapper "(function() {%output%})();//@ sourceMappingURL=video.compiled.js.map"'] + Rake::Shell['java -jar build/compiler/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js=src/goog.base.js --js=src/core.js --js=src/lib.js --js=src/events.js --js=src/component.js --js=src/player.js --js=src/media.js --js=src/media.html5.js --js=src/media.flash.js --js=src/controls.js --js=src/tracks.js --js=src/setup.js --js=src/json.js --js=src/exports.js --formatting=pretty_print --js_output_file=test/video.compiled.js --create_source_map test/video.compiled.js.map --source_map_format=V3 --externs src/media.flash.externs.js --externs test/qunit-externs.js --output_wrapper "(function() {%output%})();//@ sourceMappingURL=video.compiled.js.map"'] end desc "Compile with test" task :compiled_tests do - Rake::Shell['java -jar build/compiler/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js=src/goog.base.js --js=src/core.js --js=src/lib.js --js=src/events.js --js=src/component.js --js=src/player.js --js=src/media.js --js=src/media.html5.js --js=src/media.flash.js --js=src/controls.js --js=src/tracks.js --js=src/setup.js --js=src/json.js --js=src/exports.js --js=closure-test/unit/component.js --js=closure-test/unit/lib.js --js=closure-test/unit/events.js --js=closure-test/unit/player.js --formatting=pretty_print --js_output_file=closure-test/video.test.compiled.js --create_source_map closure-test/video.test.compiled.js.map --source_map_format=V3 --externs src/media.flash.externs.js --externs closure-test/qunit-externs.js --output_wrapper "(function() {%output%})();//@ sourceMappingURL=video.test.compiled.js.map"'] + Rake::Shell['java -jar build/compiler/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js=src/goog.base.js --js=src/core.js --js=src/lib.js --js=src/events.js --js=src/component.js --js=src/player.js --js=src/media.js --js=src/media.html5.js --js=src/media.flash.js --js=src/controls.js --js=src/tracks.js --js=src/setup.js --js=src/json.js --js=src/exports.js --js=test/unit/component.js --js=test/unit/lib.js --js=test/unit/events.js --js=test/unit/player.js --js=test/unit/setup.js --js=test/unit/core.js --formatting=pretty_print --js_output_file=test/video.test.compiled.js --create_source_map test/video.test.compiled.js.map --source_map_format=V3 --externs src/media.flash.externs.js --externs test/qunit-externs.js --output_wrapper "(function() {%output%})();//@ sourceMappingURL=video.test.compiled.js.map"'] end desc "Build version for current '/c/' CDN copy and locked in version" diff --git a/dev.html.example b/dev.html.example index 710419ff7..1d78f163b 100644 --- a/dev.html.example +++ b/dev.html.example @@ -10,27 +10,25 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/docs/components.md b/docs/components.md index ffed577df..8bcd771b8 100644 --- a/docs/components.md +++ b/docs/components.md @@ -76,16 +76,16 @@ myComponent.addChild(''); myPlayer.addChild('BigPlayButton'); myPlayer.removeChild('BigPlayButton'); myPlayer.getChild('BiPlayButton'); -myPlayer.getChildren(); +myPlayer.children(); myPlayer.getChildById('biPlayButton'); myPlayer.removeChildById('my-player-big-play-button'); -getEl(); +el(); getContentEl(); getChildren(); getParent(); -#home_player_big-play-button \ No newline at end of file +#home_player_big-play-button diff --git a/docs/setup.md b/docs/setup.md index b33242b88..af3885e8f 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -22,7 +22,7 @@ With the self hosted option you'll also want to update the location of the video ``` diff --git a/src/component.js b/src/component.js index 1a662a4ed..e983a29d5 100644 --- a/src/component.js +++ b/src/component.js @@ -1,11 +1,8 @@ /** - * Player Component - Base class for all UI objects + * @fileoverview Player Component - Base class for all UI objects + * */ -goog.provide('vjs.Component'); - -goog.require('vjs'); - /** * Base UI Component class * @param {Object} player Main Player @@ -19,7 +16,7 @@ vjs.Component = function(player, options, ready){ options = this.options = vjs.merge(this.options || {}, options); // Get ID from options, element, or create using player ID and unique ID - this.id_ = options.id || ((options.el && options.el.id) ? options.el.id : player.id + "_component_" + vjs.guid++); + this.id_ = options.id || ((options.el && options.el.id) ? options.el.id : player.id + '_component_' + vjs.guid++ ); this.name_ = options.name || null; @@ -46,7 +43,7 @@ vjs.Component.prototype.dispose = function(){ if (this.children_) { for (var i = this.children_.length - 1; i >= 0; i--) { this.children_[i].dispose(); - }; + } } // Delete child references @@ -94,7 +91,7 @@ vjs.Component.prototype.createEl = function(tagName, attributes){ * Return the component's DOM element. * @return {Element} */ -vjs.Component.prototype.getEl = function(){ +vjs.Component.prototype.el = function(){ return this.el_; }; @@ -109,7 +106,7 @@ vjs.Component.prototype.id_; * Return the component's ID. * @return {String} */ -vjs.Component.prototype.getId = function(){ +vjs.Component.prototype.id = function(){ return this.id_; }; @@ -124,7 +121,7 @@ vjs.Component.prototype.name_; * Return the component's ID. * @return {String} */ -vjs.Component.prototype.getName = function(){ +vjs.Component.prototype.name = function(){ return this.name_; }; @@ -139,9 +136,9 @@ vjs.Component.prototype.children_; * Returns array of all child components. * @return {Array} */ -vjs.Component.prototype.getChildren = function(){ +vjs.Component.prototype.children = function(){ return this.children_; -} +}; /** * Object of child components by ID @@ -156,7 +153,7 @@ vjs.Component.prototype.childIndex_; */ vjs.Component.prototype.getChildById = function(id){ return this.childIndex_[id]; -} +}; /** * Object of child components by Name @@ -171,7 +168,7 @@ vjs.Component.prototype.childNameIndex_; */ vjs.Component.prototype.getChild = function(name){ return this.childNameIndex_[name]; -} +}; /** * Adds a child component inside this component. @@ -185,7 +182,7 @@ vjs.Component.prototype.addChild = function(child, options){ var component, componentClass, componentName, componentId; // If string, create new component with options - if (typeof child === "string") { + if (typeof child === 'string') { componentName = child; @@ -209,8 +206,8 @@ vjs.Component.prototype.addChild = function(child, options){ component = child; } - componentName = component.getName(); - componentId = component.getId(); + componentName = component.name(); + componentId = component.id(); this.children_.push(component); @@ -223,7 +220,7 @@ vjs.Component.prototype.addChild = function(child, options){ } // Add the UI object's element to the container div (box) - this.el_.appendChild(component.getEl()); + this.el_.appendChild(component.el()); // Return so it can stored on parent object if desired. return component; @@ -243,16 +240,16 @@ vjs.Component.prototype.removeChild = function(component){ this.children_.splice(i,1); break; } - }; + } if (!childFound) return; this.childIndex_[component.id] = null; this.childNameIndex_[component.name] = null; - var compEl = component.getEl(); + var compEl = component.el(); if (compEl && compEl.parentNode === this.el_) { - this.el_.removeChild(component.getEl()); + this.el_.removeChild(component.el()); } }; @@ -289,8 +286,8 @@ vjs.Component.prototype.initChildren = function(){ vjs.Component.prototype.buildCSSClass = function(){ // Child classes can include a function that does: - // return "CLASS NAME" + this._super(); - return ""; + // return 'CLASS NAME' + this._super(); + return ''; }; /* Events @@ -399,14 +396,14 @@ vjs.Component.prototype.triggerReady = function(){ if (readyQueue && readyQueue.length > 0) { for (var i = 0, j = readyQueue.length; i < j; i++) { - readyQueue[i].call(this) - }; + readyQueue[i].call(this); + } // Reset Ready Queue this.readyQueue_ = []; // Allow for using event listeners also, in case you want to do something everytime a source is ready. - this.trigger("ready"); + this.trigger('ready'); } }; @@ -438,7 +435,7 @@ vjs.Component.prototype.removeClass = function(classToRemove){ * @return {vjs.Component} */ vjs.Component.prototype.show = function(){ - this.el_.style.display = "block"; + this.el_.style.display = 'block'; return this; }; @@ -447,7 +444,7 @@ vjs.Component.prototype.show = function(){ * @return {vjs.Component} */ vjs.Component.prototype.hide = function(){ - this.el_.style.display = "none"; + this.el_.style.display = 'none'; return this; }; @@ -456,8 +453,8 @@ vjs.Component.prototype.hide = function(){ * @return {vjs.Component} */ vjs.Component.prototype.fadeIn = function(){ - this.removeClass("vjs-fade-out"); - this.addClass("vjs-fade-in"); + this.removeClass('vjs-fade-out'); + this.addClass('vjs-fade-in'); return this; }; @@ -466,8 +463,8 @@ vjs.Component.prototype.fadeIn = function(){ * @return {vjs.Component} */ vjs.Component.prototype.fadeOut = function(){ - this.removeClass("vjs-fade-in"); - this.addClass("vjs-fade-out"); + this.removeClass('vjs-fade-in'); + this.addClass('vjs-fade-out'); return this; }; @@ -477,9 +474,9 @@ vjs.Component.prototype.fadeOut = function(){ */ vjs.Component.prototype.lockShowing = function(){ var style = this.el_.style; - style.display = "block"; + style.display = 'block'; style.opacity = 1; - style.visiblity = "visible"; + style.visiblity = 'visible'; return this; }; @@ -488,10 +485,10 @@ vjs.Component.prototype.lockShowing = function(){ * @return {vjs.Component} */ vjs.Component.prototype.unlockShowing = function(){ - var style = this.el.style; - style.display = ""; - style.opacity = ""; - style.visiblity = ""; + var style = this.el_.style; + style.display = ''; + style.opacity = ''; + style.visiblity = ''; return this; }; @@ -507,7 +504,7 @@ vjs.Component.prototype.unlockShowing = function(){ * Otherwise it returns the dimension. */ vjs.Component.prototype.width = function(num, skipListeners){ - return this.dimension("width", num, skipListeners); + return this.dimension('width', num, skipListeners); }; /** @@ -517,7 +514,7 @@ vjs.Component.prototype.width = function(num, skipListeners){ * @return {vjs.Component|Number|String} The player, or the dimension */ vjs.Component.prototype.height = function(num, skipListeners){ - return this.dimension("height", num, skipListeners); + return this.dimension('height', num, skipListeners); }; /** @@ -549,14 +546,14 @@ vjs.Component.prototype.dimension = function(widthOrHeight, num, skipListeners){ if (num !== undefined) { // Check if using css width/height (% or px) and adjust - if ((""+num).indexOf("%") !== -1 || (""+num).indexOf("px") !== -1) { + if ((''+num).indexOf('%') !== -1 || (''+num).indexOf('px') !== -1) { this.el_.style[widthOrHeight] = num; } else { - this.el_.style[widthOrHeight] = num+"px"; + this.el_.style[widthOrHeight] = num+'px'; } // skipListeners allows us to avoid triggering the resize event when setting both width and height - if (!skipListeners) { this.trigger("resize"); } + if (!skipListeners) { this.trigger('resize'); } // Return component return this; @@ -568,7 +565,7 @@ vjs.Component.prototype.dimension = function(widthOrHeight, num, skipListeners){ // Get dimension value from style var val = this.el_.style[widthOrHeight]; - var pxIndex = val.indexOf("px"); + var pxIndex = val.indexOf('px'); if (pxIndex !== -1) { // Return the pixel value with no 'px' return parseInt(val.slice(0,pxIndex), 10); @@ -585,7 +582,7 @@ vjs.Component.prototype.dimension = function(widthOrHeight, num, skipListeners){ // the percent value (e.g. '100%'') // instead of zero like offsetWidth returns. // var val = vjs.getComputedStyleValue(this.el_, widthOrHeight); - // var pxIndex = val.indexOf("px"); + // var pxIndex = val.indexOf('px'); // if (pxIndex !== -1) { // return val.slice(0, pxIndex); @@ -594,14 +591,3 @@ vjs.Component.prototype.dimension = function(widthOrHeight, num, skipListeners){ // } } }; - -// /* Utility -// ================================================================================ */ -// vjs.Component.prototype.each = function(arr, fn){ vjs.each.call(this, arr, fn); }; - -// vjs.Component.prototype.eachProp = function(obj, fn){ vjs.eachProp.call(this, obj, fn); }; - -// vjs.Component.prototype.extend = function(obj){ vjs.merge(this, obj) }; - -// // More easily attach 'this' to functions -// vjs.Component.prototype.proxy = function(fn, uid){ return vjs.proxy(this, fn, uid); }; diff --git a/src/controls.js b/src/controls.js index 5b34c8993..c4ebc20b6 100644 --- a/src/controls.js +++ b/src/controls.js @@ -1,35 +1,7 @@ -goog.provide('vjs.Control'); -goog.provide('vjs.ControlBar'); -goog.provide('vjs.Button'); -goog.provide('vjs.PlayButton'); -goog.provide('vjs.PauseButton'); -goog.provide('vjs.PlayToggle'); -goog.provide('vjs.FullscreenToggle'); -goog.provide('vjs.BigPlayButton'); -goog.provide('vjs.LoadingSpinner'); -goog.provide('vjs.CurrentTimeDisplay'); -goog.provide('vjs.DurationDisplay'); -goog.provide('vjs.TimeDivider'); -goog.provide('vjs.RemainingTimeDisplay'); -goog.provide('vjs.Slider'); -goog.provide('vjs.ProgressControl'); -goog.provide('vjs.SeekBar'); -goog.provide('vjs.LoadProgressBar'); -goog.provide('vjs.PlayProgressBar'); -goog.provide('vjs.SeekHandle'); -goog.provide('vjs.VolumeControl'); -goog.provide('vjs.VolumeBar'); -goog.provide('vjs.VolumeLevel'); -goog.provide('vjs.VolumeHandle'); -goog.provide('vjs.MuteToggle'); -goog.provide('vjs.PosterImage'); -goog.provide('vjs.Menu'); -goog.provide('vjs.MenuItem'); +/** + * @fileoverview Controls classes for Video.js buttons, sliders, etc. + */ -goog.require('vjs.Component'); - -/* Control - Base class for all control elements -================================================================================ */ /** * Base class for all control elements * @param {vjs.Player|Object} player @@ -42,7 +14,7 @@ vjs.Control = function(player, options){ goog.inherits(vjs.Control, vjs.Component); vjs.Control.prototype.buildCSSClass = function(){ - return "vjs-control " + goog.base(this, 'buildCSSClass'); + return 'vjs-control ' + goog.base(this, 'buildCSSClass'); }; /* Control Bar @@ -56,47 +28,47 @@ vjs.Control.prototype.buildCSSClass = function(){ vjs.ControlBar = function(player, options){ goog.base(this, player, options); - player.one("play", vjs.bind(this, function(){ + player.one('play', vjs.bind(this, function(){ this.fadeIn(); - this.player.on("mouseover", vjs.bind(this, this.fadeIn)); - this.player.on("mouseout", vjs.bind(this, this.fadeOut)); + this.player.on('mouseover', vjs.bind(this, this.fadeIn)); + this.player.on('mouseout', vjs.bind(this, this.fadeOut)); })); }; goog.inherits(vjs.ControlBar, vjs.Component); vjs.ControlBar.prototype.options = { - loadEvent: "play", + loadEvent: 'play', children: { - "playToggle": {}, - "fullscreenToggle": {}, - "currentTimeDisplay": {}, - "timeDivider": {}, - "durationDisplay": {}, - "remainingTimeDisplay": {}, - "progressControl": {}, - "volumeControl": {}, - "muteToggle": {} + 'playToggle': {}, + 'fullscreenToggle': {}, + 'currentTimeDisplay': {}, + 'timeDivider': {}, + 'durationDisplay': {}, + 'remainingTimeDisplay': {}, + 'progressControl': {}, + 'volumeControl': {}, + 'muteToggle': {} } }; vjs.ControlBar.prototype.createEl = function(){ - return vjs.createEl("div", { - className: "vjs-control-bar" + return vjs.createEl('div', { + className: 'vjs-control-bar' }); }; vjs.ControlBar.prototype.fadeIn = function(){ goog.base(this, 'fadeIn'); - this.player.trigger("controlsvisible"); + this.player.trigger('controlsvisible'); }; vjs.ControlBar.prototype.fadeOut = function(){ goog.base(this, 'fadeOut'); - this.player.trigger("controlshidden"); + this.player.trigger('controlshidden'); }; vjs.ControlBar.prototype.lockShowing = function(){ - this.el_.style.opacity = "1"; + this.el_.style.opacity = '1'; }; /* Button - Base class for all buttons @@ -110,9 +82,9 @@ vjs.ControlBar.prototype.lockShowing = function(){ vjs.Button = function(player, options){ goog.base(this, player, options); - this.on("click", this.onClick); - this.on("focus", this.onFocus); - this.on("blur", this.onBlur); + this.on('click', this.onClick); + this.on('focus', this.onFocus); + this.on('blur', this.onBlur); }; goog.inherits(vjs.Button, vjs.Control); @@ -120,8 +92,8 @@ vjs.Button.prototype.createEl = function(type, attrs){ // Add standard Aria and Tabindex info attrs = vjs.merge({ className: this.buildCSSClass(), - innerHTML: '
' + (this.buttonText || "Need Text") + '
', - role: "button", + innerHTML: '
' + (this.buttonText || 'Need Text') + '
', + role: 'button', tabIndex: 0 }, attrs); @@ -133,7 +105,7 @@ vjs.Button.prototype.onClick = function(){}; // Focus - Add keyboard functionality to element vjs.Button.prototype.onFocus = function(){ - vjs.on(document, "keyup", vjs.bind(this, this.onKeyPress)); + vjs.on(document, 'keyup', vjs.bind(this, this.onKeyPress)); }; // KeyPress (document level) - Trigger click when keys are pressed @@ -147,8 +119,8 @@ vjs.Button.prototype.onKeyPress = function(event){ // Blur - Remove keyboard triggers vjs.Button.prototype.onBlur = function(){ - vjs.off(document, "keyup", vjs.bind(this, this.onKeyPress)); -} + vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress)); +}; /* Play Button ================================================================================ */ @@ -163,15 +135,15 @@ vjs.PlayButton = function(player, options){ }; goog.inherits(vjs.PlayButton, vjs.Button); -vjs.PlayButton.prototype.buttonText = "Play"; +vjs.PlayButton.prototype.buttonText = 'Play'; vjs.PlayButton.prototype.buildCSSClass = function(){ - return "vjs-play-button " + goog.base(this, 'buildCSSClass'); + return 'vjs-play-button ' + goog.base(this, 'buildCSSClass'); }; vjs.PlayButton.prototype.onClick = function(){ this.player.play(); -} +}; /* Pause Button ================================================================================ */ @@ -186,10 +158,10 @@ vjs.PauseButton = function(player, options){ }; goog.inherits(vjs.PauseButton, vjs.Button); -vjs.PauseButton.prototype.buttonText = "Play"; +vjs.PauseButton.prototype.buttonText = 'Play'; vjs.PauseButton.prototype.buildCSSClass = function(){ - return "vjs-pause-button " + goog.base(this, 'buildCSSClass'); + return 'vjs-pause-button ' + goog.base(this, 'buildCSSClass'); }; vjs.PauseButton.prototype.onClick = function(){ @@ -207,15 +179,15 @@ vjs.PauseButton.prototype.onClick = function(){ vjs.PlayToggle = function(player, options){ goog.base(this, player, options); - player.on("play", vjs.bind(this, this.onPlay)); - player.on("pause", vjs.bind(this, this.onPause)); + player.on('play', vjs.bind(this, this.onPlay)); + player.on('pause', vjs.bind(this, this.onPause)); }; goog.inherits(vjs.PlayToggle, vjs.Button); -vjs.PlayToggle.prototype.buttonText = "Play"; +vjs.PlayToggle.prototype.buttonText = 'Play'; vjs.PlayToggle.prototype.buildCSSClass = function(){ - return "vjs-play-control " + goog.base(this, 'buildCSSClass'); + return 'vjs-play-control ' + goog.base(this, 'buildCSSClass'); }; // OnClick - Toggle between play and pause @@ -229,15 +201,15 @@ vjs.PlayToggle.prototype.onClick = function(){ // OnPlay - Add the vjs-playing class to the element so it can change appearance vjs.PlayToggle.prototype.onPlay = function(){ - vjs.removeClass(this.el_, "vjs-paused"); - vjs.addClass(this.el_, "vjs-playing"); + vjs.removeClass(this.el_, 'vjs-paused'); + vjs.addClass(this.el_, 'vjs-playing'); }; // OnPause - Add the vjs-paused class to the element so it can change appearance vjs.PlayToggle.prototype.onPause = function(){ - vjs.removeClass(this.el_, "vjs-playing"); - vjs.addClass(this.el_, "vjs-paused"); -} + vjs.removeClass(this.el_, 'vjs-playing'); + vjs.addClass(this.el_, 'vjs-paused'); +}; /* Fullscreen Toggle Behaviors @@ -253,10 +225,10 @@ vjs.FullscreenToggle = function(player, options){ }; goog.inherits(vjs.FullscreenToggle, vjs.Button); -vjs.FullscreenToggle.prototype.buttonText = "Fullscreen"; +vjs.FullscreenToggle.prototype.buttonText = 'Fullscreen'; vjs.FullscreenToggle.prototype.buildCSSClass = function(){ - return "vjs-fullscreen-control " + goog.base(this, 'buildCSSClass'); + return 'vjs-fullscreen-control ' + goog.base(this, 'buildCSSClass'); }; vjs.FullscreenToggle.prototype.onClick = function(){ @@ -279,15 +251,15 @@ vjs.FullscreenToggle.prototype.onClick = function(){ vjs.BigPlayButton = function(player, options){ goog.base(this, player, options); - player.on("play", vjs.bind(this, this.hide)); - player.on("ended", vjs.bind(this, this.show)); + player.on('play', vjs.bind(this, this.hide)); + player.on('ended', vjs.bind(this, this.show)); }; goog.inherits(vjs.BigPlayButton, vjs.Button); vjs.BigPlayButton.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-big-play-button", - innerHTML: "" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-big-play-button', + innerHTML: '' }); }; @@ -311,44 +283,44 @@ vjs.BigPlayButton.prototype.onClick = function(){ vjs.LoadingSpinner = function(player, options){ goog.base(this, player, options); - player.on("canplay", vjs.bind(this, this.hide)); - player.on("canplaythrough", vjs.bind(this, this.hide)); - player.on("playing", vjs.bind(this, this.hide)); - player.on("seeked", vjs.bind(this, this.hide)); + player.on('canplay', vjs.bind(this, this.hide)); + player.on('canplaythrough', vjs.bind(this, this.hide)); + player.on('playing', vjs.bind(this, this.hide)); + player.on('seeked', vjs.bind(this, this.hide)); - player.on("seeking", vjs.bind(this, this.show)); + player.on('seeking', vjs.bind(this, this.show)); // in some browsers seeking does not trigger the 'playing' event, // so we also need to trap 'seeked' if we are going to set a // 'seeking' event - player.on("seeked", vjs.bind(this, this.hide)); + player.on('seeked', vjs.bind(this, this.hide)); - player.on("error", vjs.bind(this, this.show)); + player.on('error', vjs.bind(this, this.show)); // Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner. // Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing - // player.on("stalled", vjs.bind(this, this.show)); + // player.on('stalled', vjs.bind(this, this.show)); - player.on("waiting", vjs.bind(this, this.show)); + player.on('waiting', vjs.bind(this, this.show)); }; goog.inherits(vjs.LoadingSpinner, vjs.Component); vjs.LoadingSpinner.prototype.createEl = function(){ var classNameSpinner, innerHtmlSpinner; - if ( typeof this.player.getEl().style.WebkitBorderRadius == "string" - || typeof this.player.getEl().style.MozBorderRadius == "string" - || typeof this.player.getEl().style.KhtmlBorderRadius == "string" - || typeof this.player.getEl().style.borderRadius == "string") + if ( typeof this.player.el().style.WebkitBorderRadius == 'string' + || typeof this.player.el().style.MozBorderRadius == 'string' + || typeof this.player.el().style.KhtmlBorderRadius == 'string' + || typeof this.player.el().style.borderRadius == 'string') { - classNameSpinner = "vjs-loading-spinner"; - innerHtmlSpinner = "
"; + classNameSpinner = 'vjs-loading-spinner'; + innerHtmlSpinner = '
'; } else { - classNameSpinner = "vjs-loading-spinner-fallback"; - innerHtmlSpinner = ""; + classNameSpinner = 'vjs-loading-spinner-fallback'; + innerHtmlSpinner = ''; } - return goog.base(this, 'createEl', "div", { + return goog.base(this, 'createEl', 'div', { className: classNameSpinner, innerHTML: innerHtmlSpinner }); @@ -366,21 +338,21 @@ vjs.LoadingSpinner.prototype.createEl = function(){ vjs.CurrentTimeDisplay = function(player, options){ goog.base(this, player, options); - player.on("timeupdate", vjs.bind(this, this.updateContent)); + player.on('timeupdate', vjs.bind(this, this.updateContent)); }; goog.inherits(vjs.CurrentTimeDisplay, vjs.Component); vjs.CurrentTimeDisplay.prototype.createEl = function(){ - var el = goog.base(this, 'createEl', "div", { - className: "vjs-current-time vjs-time-controls vjs-control" + var el = goog.base(this, 'createEl', 'div', { + className: 'vjs-current-time vjs-time-controls vjs-control' }); - this.content = vjs.createEl("div", { - className: "vjs-current-time-display", + this.content = vjs.createEl('div', { + className: 'vjs-current-time-display', innerHTML: '0:00' }); - el.appendChild(vjs.createEl("div").appendChild(this.content)); + el.appendChild(vjs.createEl('div').appendChild(this.content)); return el; }; @@ -399,21 +371,21 @@ vjs.CurrentTimeDisplay.prototype.updateContent = function(){ vjs.DurationDisplay = function(player, options){ goog.base(this, player, options); - player.on("timeupdate", vjs.bind(this, this.updateContent)); + player.on('timeupdate', vjs.bind(this, this.updateContent)); }; goog.inherits(vjs.DurationDisplay, vjs.Component); vjs.DurationDisplay.prototype.createEl = function(){ - var el = goog.base(this, 'createEl', "div", { - className: "vjs-duration vjs-time-controls vjs-control" + var el = goog.base(this, 'createEl', 'div', { + className: 'vjs-duration vjs-time-controls vjs-control' }); - this.content = vjs.createEl("div", { - className: "vjs-duration-display", + this.content = vjs.createEl('div', { + className: 'vjs-duration-display', innerHTML: '0:00' }); - el.appendChild(vjs.createEl("div").appendChild(this.content)); + el.appendChild(vjs.createEl('div').appendChild(this.content)); return el; }; @@ -433,8 +405,8 @@ vjs.TimeDivider = function(player, options){ goog.inherits(vjs.TimeDivider, vjs.Component); vjs.TimeDivider.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-time-divider", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-time-divider', innerHTML: '
/
' }); }; @@ -448,27 +420,27 @@ vjs.TimeDivider.prototype.createEl = function(){ vjs.RemainingTimeDisplay = function(player, options){ goog.base(this, player, options); - player.on("timeupdate", vjs.bind(this, this.updateContent)); + player.on('timeupdate', vjs.bind(this, this.updateContent)); }; goog.inherits(vjs.RemainingTimeDisplay, vjs.Component); vjs.RemainingTimeDisplay.prototype.createEl = function(){ - var el = goog.base(this, 'createEl', "div", { - className: "vjs-remaining-time vjs-time-controls vjs-control" + var el = goog.base(this, 'createEl', 'div', { + className: 'vjs-remaining-time vjs-time-controls vjs-control' }); - this.content = vjs.createEl("div", { - className: "vjs-remaining-time-display", + this.content = vjs.createEl('div', { + className: 'vjs-remaining-time-display', innerHTML: '-0:00' }); - el.appendChild(vjs.createEl("div").appendChild(this.content)); + el.appendChild(vjs.createEl('div').appendChild(this.content)); return el; }; vjs.RemainingTimeDisplay.prototype.updateContent = function(){ - if (this.player.duration()) { this.content.innerHTML = "-"+vjs.formatTime(this.player.remainingTime()); } + if (this.player.duration()) { this.content.innerHTML = '-'+vjs.formatTime(this.player.remainingTime()); } // Allows for smooth scrubbing, when player can't keep up. // var time = (this.player.scrubbing) ? this.player.getCache().currentTime : this.player.currentTime(); @@ -487,21 +459,21 @@ vjs.Slider = function(player, options){ goog.base(this, player, options); // Set property names to bar and handle to match with the child Slider class is looking for - this.bar = this.getChild(this.options["barName"]); - this.handle = this.getChild(this.options["handleName"]); + this.bar = this.getChild(this.options['barName']); + this.handle = this.getChild(this.options['handleName']); // console.log('asdf', this.bar, this.childNameIndex_, this.options) player.on(this.playerEvent, vjs.bind(this, this.update)); - this.on("mousedown", this.onMouseDown); - this.on("focus", this.onFocus); - this.on("blur", this.onBlur); + this.on('mousedown', this.onMouseDown); + this.on('focus', this.onFocus); + this.on('blur', this.onBlur); - this.player.on("controlsvisible", vjs.bind(this, this.update)); + this.player.on('controlsvisible', vjs.bind(this, this.update)); // This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520 - // this.player.one("timeupdate", vjs.bind(this, this.update)); + // this.player.one('timeupdate', vjs.bind(this, this.update)); player.ready(vjs.bind(this, this.update)); }; @@ -509,10 +481,10 @@ goog.inherits(vjs.Slider, vjs.Component); vjs.Slider.prototype.createEl = function(type, attrs) { attrs = vjs.merge({ - role: "slider", - "aria-valuenow": 0, - "aria-valuemin": 0, - "aria-valuemax": 100, + role: 'slider', + 'aria-valuenow': 0, + 'aria-valuemin': 0, + 'aria-valuemax': 100, tabIndex: 0 }, attrs); @@ -523,16 +495,16 @@ vjs.Slider.prototype.onMouseDown = function(event){ event.preventDefault(); vjs.blockTextSelection(); - vjs.on(document, "mousemove", vjs.bind(this, this.onMouseMove)); - vjs.on(document, "mouseup", vjs.bind(this, this.onMouseUp)); + vjs.on(document, 'mousemove', vjs.bind(this, this.onMouseMove)); + vjs.on(document, 'mouseup', vjs.bind(this, this.onMouseUp)); this.onMouseMove(event); }; -vjs.Slider.prototype.onMouseUp = function(event) { +vjs.Slider.prototype.onMouseUp = function() { vjs.unblockTextSelection(); - vjs.off(document, "mousemove", this.onMouseMove, false); - vjs.off(document, "mouseup", this.onMouseUp, false); + vjs.off(document, 'mousemove', this.onMouseMove, false); + vjs.off(document, 'mouseup', this.onMouseUp, false); this.update(); }; @@ -543,7 +515,7 @@ vjs.Slider.prototype.update = function(){ // var progress = (this.player.scrubbing) ? this.player.getCache().currentTime / this.player.duration() : this.player.currentTime() / this.player.duration(); var barProgress, - progress = this.getPercent(); + progress = this.getPercent(), handle = this.handle, bar = this.bar; @@ -559,7 +531,7 @@ vjs.Slider.prototype.update = function(){ var box = this.el_, boxWidth = box.offsetWidth, - handleWidth = handle.getEl().offsetWidth, + handleWidth = handle.el().offsetWidth, // The width of the handle in percent of the containing box // In IE, widths may not be ready yet causing NaN @@ -567,20 +539,20 @@ vjs.Slider.prototype.update = function(){ // Get the adjusted size of the box, considering that the handle's center never touches the left or right side. // There is a margin of half the handle's width on both sides. - boxAdjustedPercent = 1 - handlePercent; + boxAdjustedPercent = 1 - handlePercent, // Adjust the progress that we'll use to set widths to the new adjusted box width - adjustedProgress = progress * boxAdjustedPercent, + adjustedProgress = progress * boxAdjustedPercent; - // The bar does reach the left side, so we need to account for this in the bar's width - barProgress = adjustedProgress + (handlePercent / 2); + // The bar does reach the left side, so we need to account for this in the bar's width + barProgress = adjustedProgress + (handlePercent / 2); // Move the handle from the left based on the adjected progress - handle.getEl().style.left = vjs.round(adjustedProgress * 100, 2) + "%"; + handle.el().style.left = vjs.round(adjustedProgress * 100, 2) + '%'; } // Set the new bar width - bar.getEl().style.width = vjs.round(barProgress * 100, 2) + "%"; + bar.el().style.width = vjs.round(barProgress * 100, 2) + '%'; }; vjs.Slider.prototype.calculateDistance = function(event){ @@ -590,7 +562,7 @@ vjs.Slider.prototype.calculateDistance = function(event){ handle = this.handle; if (handle) { - var handleW = handle.getEl().offsetWidth; + var handleW = handle.el().offsetWidth; // Adjusted X and Width, so handle doesn't go outside the bar boxX = boxX + (handleW / 2); @@ -601,8 +573,8 @@ vjs.Slider.prototype.calculateDistance = function(event){ return Math.max(0, Math.min(1, (event.pageX - boxX) / boxW)); }; -vjs.Slider.prototype.onFocus = function(event){ - vjs.on(document, "keyup", vjs.bind(this, this.onKeyPress)); +vjs.Slider.prototype.onFocus = function(){ + vjs.on(document, 'keyup', vjs.bind(this, this.onKeyPress)); }; vjs.Slider.prototype.onKeyPress = function(event){ @@ -615,8 +587,8 @@ vjs.Slider.prototype.onKeyPress = function(event){ } }; -vjs.Slider.prototype.onBlur = function(event){ - vjs.off(document, "keyup", vjs.bind(this, this.onKeyPress)); +vjs.Slider.prototype.onBlur = function(){ + vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress)); }; @@ -636,13 +608,13 @@ goog.inherits(vjs.ProgressControl, vjs.Component); vjs.ProgressControl.prototype.options = { children: { - "seekBar": {} + 'seekBar': {} } }; vjs.ProgressControl.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-progress-control vjs-control" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-progress-control vjs-control' }); }; @@ -659,19 +631,19 @@ goog.inherits(vjs.SeekBar, vjs.Slider); vjs.SeekBar.prototype.options = { children: { - "loadProgressBar": {}, - "playProgressBar": {}, - "seekHandle": {} + 'loadProgressBar': {}, + 'playProgressBar': {}, + 'seekHandle': {} }, - "barName": "playProgressBar", - "handleName": "seekHandle" + 'barName': 'playProgressBar', + 'handleName': 'seekHandle' }; -vjs.SeekBar.prototype.playerEvent = "timeupdate"; +vjs.SeekBar.prototype.playerEvent = 'timeupdate'; vjs.SeekBar.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-progress-holder" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-progress-holder' }); }; @@ -724,19 +696,19 @@ vjs.SeekBar.prototype.stepBack = function(){ */ vjs.LoadProgressBar = function(player, options){ goog.base(this, player, options); - player.on("progress", vjs.bind(this, this.update)); + player.on('progress', vjs.bind(this, this.update)); }; goog.inherits(vjs.LoadProgressBar, vjs.Component); vjs.LoadProgressBar.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-load-progress", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-load-progress', innerHTML: 'Loaded: 0%' }); }; vjs.LoadProgressBar.prototype.update = function(){ - if (this.el_.style) { this.el_.style.width = vjs.round(this.player.bufferedPercent() * 100, 2) + "%"; } + if (this.el_.style) { this.el_.style.width = vjs.round(this.player.bufferedPercent() * 100, 2) + '%'; } }; @@ -752,8 +724,8 @@ vjs.PlayProgressBar = function(player, options){ goog.inherits(vjs.PlayProgressBar, vjs.Component); vjs.PlayProgressBar.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-play-progress", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-play-progress', innerHTML: 'Progress: 0%' }); }; @@ -771,8 +743,8 @@ vjs.SeekHandle = function(player, options){ goog.inherits(vjs.SeekHandle, vjs.Component); vjs.SeekHandle.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-seek-handle", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-seek-handle', innerHTML: '00:00' }); }; @@ -790,13 +762,13 @@ goog.inherits(vjs.VolumeControl, vjs.Component); vjs.VolumeControl.prototype.options = { children: { - "volumeBar": {} + 'volumeBar': {} } }; vjs.VolumeControl.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-volume-control vjs-control" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-volume-control vjs-control' }); }; @@ -813,18 +785,18 @@ goog.inherits(vjs.VolumeBar, vjs.Slider); vjs.VolumeBar.prototype.options = { children: { - "volumeLevel": {}, - "volumeHandle": {} + 'volumeLevel': {}, + 'volumeHandle': {} }, - "barName": "volumeLevel", - "handleName": "volumeHandle" + 'barName': 'volumeLevel', + 'handleName': 'volumeHandle' }; -vjs.VolumeBar.prototype.playerEvent = "volumechange"; +vjs.VolumeBar.prototype.playerEvent = 'volumechange'; vjs.VolumeBar.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-volume-bar" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-volume-bar' }); }; @@ -856,8 +828,8 @@ vjs.VolumeLevel = function(player, options){ goog.inherits(vjs.VolumeLevel, vjs.Component); vjs.VolumeLevel.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-volume-level", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-volume-level', innerHTML: '' }); }; @@ -874,11 +846,11 @@ vjs.VolumeHandle = function(player, options){ goog.inherits(vjs.VolumeHandle, vjs.Component); vjs.VolumeHandle.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-volume-handle", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-volume-handle', innerHTML: '' // tabindex: 0, - // role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100 + // role: 'slider', 'aria-valuenow': 0, 'aria-valuemin': 0, 'aria-valuemax': 100 }); }; @@ -891,26 +863,26 @@ vjs.VolumeHandle.prototype.createEl = function(){ vjs.MuteToggle = function(player, options){ goog.base(this, player, options); - player.on("volumechange", vjs.bind(this, this.update)); + player.on('volumechange', vjs.bind(this, this.update)); }; goog.inherits(vjs.MuteToggle, vjs.Button); vjs.MuteToggle.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-mute-control vjs-control", + return goog.base(this, 'createEl', 'div', { + className: 'vjs-mute-control vjs-control', innerHTML: '
Mute
' }); }; -vjs.MuteToggle.prototype.onClick = function(event){ +vjs.MuteToggle.prototype.onClick = function(){ this.player.muted( this.player.muted() ? false : true ); }; -vjs.MuteToggle.prototype.update = function(event){ +vjs.MuteToggle.prototype.update = function(){ var vol = this.player.volume(), level = 3; - if (vol == 0 || this.player.muted()) { + if (vol === 0 || this.player.muted()) { level = 0; } else if (vol < 0.33) { level = 1; @@ -920,10 +892,10 @@ vjs.MuteToggle.prototype.update = function(event){ /* TODO improve muted icon classes */ for (var i = 0; i < 4; i++) { - vjs.removeClass(this.el_, "vjs-vol-"+i); - }; - vjs.addClass(this.el_, "vjs-vol-"+level); -} + vjs.removeClass(this.el_, 'vjs-vol-'+i); + } + vjs.addClass(this.el_, 'vjs-vol-'+level); +}; /* Poster Image ================================================================================ */ @@ -940,13 +912,13 @@ vjs.PosterImage = function(player, options){ this.hide(); } - player.on("play", vjs.bind(this, this.hide)); + player.on('play', vjs.bind(this, this.hide)); }; goog.inherits(vjs.PosterImage, vjs.Button); vjs.PosterImage.prototype.createEl = function(){ - var el = vjs.createEl("img", { - className: "vjs-poster", + var el = vjs.createEl('img', { + className: 'vjs-poster', // Don't want poster to be tabbable. tabIndex: -1 @@ -961,7 +933,7 @@ vjs.PosterImage.prototype.createEl = function(){ vjs.PosterImage.prototype.onClick = function(){ this.player.play(); -} +}; /* Menu ================================================================================ */ @@ -978,14 +950,14 @@ goog.inherits(vjs.Menu, vjs.Component); vjs.Menu.prototype.addItem = function(component){ this.addChild(component); - component.on("click", vjs.bind(this, function(){ + component.on('click', vjs.bind(this, function(){ this.unlockShowing(); })); }; vjs.Menu.prototype.createEl = function(){ - return goog.base(this, 'createEl', "ul", { - className: "vjs-menu" + return goog.base(this, 'createEl', 'ul', { + className: 'vjs-menu' }); }; @@ -999,14 +971,14 @@ vjs.MenuItem = function(player, options){ goog.base(this, player, options); if (options.selected) { - this.addClass("vjs-selected"); + this.addClass('vjs-selected'); } }; goog.inherits(vjs.MenuItem, vjs.Button); vjs.MenuItem.prototype.createEl = function(type, attrs){ - return goog.base(this, 'createEl', "li", vjs.merge({ - className: "vjs-menu-item", + return goog.base(this, 'createEl', 'li', vjs.merge({ + className: 'vjs-menu-item', innerHTML: this.options.label }, attrs)); }; @@ -1017,863 +989,8 @@ vjs.MenuItem.prototype.onClick = function(){ vjs.MenuItem.prototype.selected = function(selected){ if (selected) { - this.addClass("vjs-selected"); + this.addClass('vjs-selected'); } else { - this.removeClass("vjs-selected") + this.removeClass('vjs-selected'); } }; - - -// /* Control - Base class for all control elements -// ================================================================================ */ -// vjs.Control = vjs.Component.extend({ - -// buildCSSClass: function(){ -// return "vjs-control " + this._super(); -// } - -// }); - -// /* Control Bar -// ================================================================================ */ -// vjs.ControlBar = vjs.Component.extend({ - -// options: { -// loadEvent: "play", -// children: { -// "playToggle": {}, -// "fullscreenToggle": {}, -// "currentTimeDisplay": {}, -// "timeDivider": {}, -// "durationDisplay": {}, -// "remainingTimeDisplay": {}, -// "progressControl": {}, -// "volumeControl": {}, -// "muteToggle": {} -// } -// }, - -// init: function(player, options){ -// this._super(player, options); - -// player.one("play", vjs.bind(this, function(){ -// this.fadeIn(); -// this.player.on("mouseover", vjs.bind(this, this.fadeIn)); -// this.player.on("mouseout", vjs.bind(this, this.fadeOut)); -// })); - -// }, - -// createEl: function(){ -// return vjs.createEl("div", { -// className: "vjs-controls" -// }); -// }, - -// fadeIn: function(){ -// this._super(); -// this.player.trigger("controlsvisible"); -// }, - -// fadeOut: function(){ -// this._super(); -// this.player.trigger("controlshidden"); -// }, - -// lockShowing: function(){ -// this.el_.style.opacity = "1"; -// } - -// }); - -// /* Button - Base class for all buttons -// ================================================================================ */ -// vjs.Button = vjs.Control.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// this.on("click", this.onClick); -// this.on("focus", this.onFocus); -// this.on("blur", this.onBlur); -// }, - -// createEl: function(type, attrs){ -// // Add standard Aria and Tabindex info -// attrs = vjs.merge({ -// className: this.buildCSSClass(), -// innerHTML: '
' + (this.buttonText || "Need Text") + '
', -// role: "button", -// tabIndex: 0 -// }, attrs); - -// return this._super(type, attrs); -// }, - -// // Click - Override with specific functionality for button -// onClick: function(){}, - -// // Focus - Add keyboard functionality to element -// onFocus: function(){ -// vjs.on(document, "keyup", vjs.bind(this, this.onKeyPress)); -// }, - -// // KeyPress (document level) - Trigger click when keys are pressed -// onKeyPress: function(event){ -// // Check for space bar (32) or enter (13) keys -// if (event.which == 32 || event.which == 13) { -// event.preventDefault(); -// this.onClick(); -// } -// }, - -// // Blur - Remove keyboard triggers -// onBlur: function(){ -// vjs.off(document, "keyup", vjs.bind(this, this.onKeyPress)); -// } - -// }); - -// /* Play Button -// ================================================================================ */ -// vjs.PlayButton = vjs.Button.extend({ - -// buttonText: "Play", - -// buildCSSClass: function(){ -// return "vjs-play-button " + this._super(); -// }, - -// onClick: function(){ -// this.player.play(); -// } - -// }); - -// /* Pause Button -// ================================================================================ */ -// vjs.PauseButton = vjs.Button.extend({ - -// buttonText: "Pause", - -// buildCSSClass: function(){ -// return "vjs-pause-button " + this._super(); -// }, - -// onClick: function(){ -// this.player.pause(); -// } - -// }); - -// /* Play Toggle - Play or Pause Media -// ================================================================================ */ -// vjs.PlayToggle = vjs.Button.extend({ - -// buttonText: "Play", - -// init: function(player, options){ -// this._super(player, options); - -// player.on("play", vjs.bind(this, this.onPlay)); -// player.on("pause", vjs.bind(this, this.onPause)); -// }, - -// buildCSSClass: function(){ -// return "vjs-play-control " + this._super(); -// }, - -// // OnClick - Toggle between play and pause -// onClick: function(){ -// if (this.player.paused()) { -// this.player.play(); -// } else { -// this.player.pause(); -// } -// }, - -// // OnPlay - Add the vjs-playing class to the element so it can change appearance -// onPlay: function(){ -// vjs.removeClass(this.el_, "vjs-paused"); -// vjs.addClass(this.el_, "vjs-playing"); -// }, - -// // OnPause - Add the vjs-paused class to the element so it can change appearance -// onPause: function(){ -// vjs.removeClass(this.el_, "vjs-playing"); -// vjs.addClass(this.el_, "vjs-paused"); -// } - -// }); - - -// /* Fullscreen Toggle Behaviors -// ================================================================================ */ -// vjs.FullscreenToggle = vjs.Button.extend({ - -// buttonText: "Fullscreen", - -// buildCSSClass: function(){ -// return "vjs-fullscreen-control " + this._super(); -// }, - -// onClick: function(){ -// if (!this.player.isFullScreen) { -// this.player.requestFullScreen(); -// } else { -// this.player.cancelFullScreen(); -// } -// } - -// }); - -// /* Big Play Button -// ================================================================================ */ -// vjs.BigPlayButton = vjs.Button.extend({ -// init: function(player, options){ -// this._super(player, options); - -// player.on("play", vjs.bind(this, this.hide)); -// player.on("ended", vjs.bind(this, this.show)); -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-big-play-button", -// innerHTML: "" -// }); -// }, - -// onClick: function(){ -// // Go back to the beginning if big play button is showing at the end. -// // Have to check for current time otherwise it might throw a 'not ready' error. -// if(this.player.currentTime()) { -// this.player.currentTime(0); -// } -// this.player.play(); -// } -// }); - -// /* Loading Spinner -// ================================================================================ */ -// vjs.LoadingSpinner = vjs.Component.extend({ -// init: function(player, options){ -// this._super(player, options); - -// player.on("canplay", vjs.bind(this, this.hide)); -// player.on("canplaythrough", vjs.bind(this, this.hide)); -// player.on("playing", vjs.bind(this, this.hide)); -// player.on("seeked", vjs.bind(this, this.hide)); - -// player.on("seeking", vjs.bind(this, this.show)); - -// // in some browsers seeking does not trigger the 'playing' event, -// // so we also need to trap 'seeked' if we are going to set a -// // 'seeking' event -// player.on("seeked", vjs.bind(this, this.hide)); - -// player.on("error", vjs.bind(this, this.show)); - -// // Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner. -// // Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing -// // player.on("stalled", vjs.bind(this, this.show)); - -// player.on("waiting", vjs.bind(this, this.show)); -// }, - -// createEl: function(){ - -// var classNameSpinner, innerHtmlSpinner; - -// if ( typeof this.player.getEl().style.WebkitBorderRadius == "string" -// || typeof this.player.getEl().style.MozBorderRadius == "string" -// || typeof this.player.getEl().style.KhtmlBorderRadius == "string" -// || typeof this.player.getEl().style.borderRadius == "string") -// { -// classNameSpinner = "vjs-loading-spinner"; -// innerHtmlSpinner = "
"; -// } else { -// classNameSpinner = "vjs-loading-spinner-fallback"; -// innerHtmlSpinner = ""; -// } - -// return this._super("div", { -// className: classNameSpinner, -// innerHTML: innerHtmlSpinner -// }); -// } -// }); - -// /* Time -// ================================================================================ */ -// vjs.CurrentTimeDisplay = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// player.on("timeupdate", vjs.bind(this, this.updateContent)); -// }, - -// createEl: function(){ -// var el = this._super("div", { -// className: "vjs-current-time vjs-time-controls vjs-control" -// }); - -// this.content = vjs.createEl("div", { -// className: "vjs-current-time-display", -// innerHTML: '0:00' -// }); - -// el.appendChild(vjs.createEl("div").appendChild(this.content)); -// return el; -// }, - -// updateContent: function(){ -// // Allows for smooth scrubbing, when player can't keep up. -// var time = (this.player.scrubbing) ? this.player.getCache().currentTime : this.player.currentTime(); -// this.content.innerHTML = vjs.formatTime(time, this.player.duration()); -// } - -// }); - -// vjs.DurationDisplay = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// player.on("timeupdate", vjs.bind(this, this.updateContent)); -// }, - -// createEl: function(){ -// var el = this._super("div", { -// className: "vjs-duration vjs-time-controls vjs-control" -// }); - -// this.content = vjs.createEl("div", { -// className: "vjs-duration-display", -// innerHTML: '0:00' -// }); - -// el.appendChild(vjs.createEl("div").appendChild(this.content)); -// return el; -// }, - -// updateContent: function(){ -// if (this.player.duration()) { this.content.innerHTML = vjs.formatTime(this.player.duration()); } -// } - -// }); - -// // Time Separator (Not used in main skin, but still available, and could be used as a 'spare element') -// vjs.TimeDivider = vjs.Component.extend({ - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-time-divider", -// innerHTML: '
/
' -// }); -// } - -// }); - -// vjs.RemainingTimeDisplay = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// player.on("timeupdate", vjs.bind(this, this.updateContent)); -// }, - -// createEl: function(){ -// var el = this._super("div", { -// className: "vjs-remaining-time vjs-time-controls vjs-control" -// }); - -// this.content = vjs.createEl("div", { -// className: "vjs-remaining-time-display", -// innerHTML: '-0:00' -// }); - -// el.appendChild(vjs.createEl("div").appendChild(this.content)); -// return el; -// }, - -// updateContent: function(){ -// if (this.player.duration()) { this.content.innerHTML = "-"+vjs.formatTime(this.player.remainingTime()); } - -// // Allows for smooth scrubbing, when player can't keep up. -// // var time = (this.player.scrubbing) ? this.player.getCache().currentTime : this.player.currentTime(); -// // this.content.innerHTML = vjs.formatTime(time, this.player.duration()); -// } - -// }); - -// /* Slider - Parent for seek bar and volume slider -// ================================================================================ */ -// vjs.Slider = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// player.on(this.playerEvent, vjs.bind(this, this.update)); - -// this.on("mousedown", this.onMouseDown); -// this.on("focus", this.onFocus); -// this.on("blur", this.onBlur); - -// this.player.on("controlsvisible", vjs.bind(this, this.update)); - -// // This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520 -// // this.player.one("timeupdate", vjs.bind(this, this.update)); - -// this.update(); -// }, - -// createEl: function(type, attrs) { -// attrs = vjs.merge({ -// role: "slider", -// "aria-valuenow": 0, -// "aria-valuemin": 0, -// "aria-valuemax": 100, -// tabIndex: 0 -// }, attrs); - -// return this._super(type, attrs); -// }, - -// onMouseDown: function(event){ -// event.preventDefault(); -// vjs.blockTextSelection(); - -// vjs.on(document, "mousemove", vjs.bind(this, this.onMouseMove)); -// vjs.on(document, "mouseup", vjs.bind(this, this.onMouseUp)); - -// this.onMouseMove(event); -// }, - -// onMouseUp: function(event) { -// vjs.unblockTextSelection(); -// vjs.off(document, "mousemove", this.onMouseMove, false); -// vjs.off(document, "mouseup", this.onMouseUp, false); - -// this.update(); -// }, - -// update: function(){ -// // If scrubbing, we could use a cached value to make the handle keep up with the user's mouse. -// // On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later. -// // var progress = (this.player.scrubbing) ? this.player.getCache().currentTime / this.player.duration() : this.player.currentTime() / this.player.duration(); - -// var barProgress, -// progress = this.getPercent(); -// handle = this.handle, -// bar = this.bar; - -// // Protect against no duration and other division issues -// if (isNaN(progress)) { progress = 0; } - -// barProgress = progress; - -// // If there is a handle, we need to account for the handle in our calculation for progress bar -// // so that it doesn't fall short of or extend past the handle. -// if (handle) { - -// var box = this.el_, -// boxWidth = box.offsetWidth, - -// handleWidth = handle.el.offsetWidth, - -// // The width of the handle in percent of the containing box -// // In IE, widths may not be ready yet causing NaN -// handlePercent = (handleWidth) ? handleWidth / boxWidth : 0, - -// // Get the adjusted size of the box, considering that the handle's center never touches the left or right side. -// // There is a margin of half the handle's width on both sides. -// boxAdjustedPercent = 1 - handlePercent; - -// // Adjust the progress that we'll use to set widths to the new adjusted box width -// adjustedProgress = progress * boxAdjustedPercent, - -// // The bar does reach the left side, so we need to account for this in the bar's width -// barProgress = adjustedProgress + (handlePercent / 2); - -// // Move the handle from the left based on the adjected progress -// handle.el.style.left = vjs.round(adjustedProgress * 100, 2) + "%"; -// } - -// // Set the new bar width -// bar.el.style.width = vjs.round(barProgress * 100, 2) + "%"; -// }, - -// calculateDistance: function(event){ -// var box = this.el_, -// boxX = vjs.findPosX(box), -// boxW = box.offsetWidth, -// handle = this.handle; - -// if (handle) { -// var handleW = handle.el.offsetWidth; - -// // Adjusted X and Width, so handle doesn't go outside the bar -// boxX = boxX + (handleW / 2); -// boxW = boxW - handleW; -// } - -// // Percent that the click is through the adjusted area -// return Math.max(0, Math.min(1, (event.pageX - boxX) / boxW)); -// }, - -// onFocus: function(event){ -// vjs.on(document, "keyup", vjs.bind(this, this.onKeyPress)); -// }, - -// onKeyPress: function(event){ -// if (event.which == 37) { // Left Arrow -// event.preventDefault(); -// this.stepBack(); -// } else if (event.which == 39) { // Right Arrow -// event.preventDefault(); -// this.stepForward(); -// } -// }, - -// onBlur: function(event){ -// vjs.off(document, "keyup", vjs.bind(this, this.onKeyPress)); -// } -// }); - - -// /* Progress -// ================================================================================ */ - -// // Progress Control: Seek, Load Progress, and Play Progress -// vjs.ProgressControl = vjs.Component.extend({ - -// options: { -// children: { -// "seekBar": {} -// } -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-progress-control vjs-control" -// }); -// } - -// }); - -// // Seek Bar and holder for the progress bars -// vjs.SeekBar = vjs.Slider.extend({ - -// options: { -// children: { -// "loadProgressBar": {}, - -// // Set property names to bar and handle to match with the parent Slider class is looking for -// "bar": { componentClass: "PlayProgressBar" }, -// "handle": { componentClass: "SeekHandle" } -// } -// }, - -// playerEvent: "timeupdate", - -// init: function(player, options){ -// this._super(player, options); -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-progress-holder" -// }); -// }, - -// getPercent: function(){ -// return this.player.currentTime() / this.player.duration(); -// }, - -// onMouseDown: function(event){ -// this._super(event); - -// this.player.scrubbing = true; - -// this.videoWasPlaying = !this.player.paused(); -// this.player.pause(); -// }, - -// onMouseMove: function(event){ -// var newTime = this.calculateDistance(event) * this.player.duration(); - -// // Don't let video end while scrubbing. -// if (newTime == this.player.duration()) { newTime = newTime - 0.1; } - -// // Set new time (tell player to seek to new time) -// this.player.currentTime(newTime); -// }, - -// onMouseUp: function(event){ -// this._super(event); - -// this.player.scrubbing = false; -// if (this.videoWasPlaying) { -// this.player.play(); -// } -// }, - -// stepForward: function(){ -// this.player.currentTime(this.player.currentTime() + 1); -// }, - -// stepBack: function(){ -// this.player.currentTime(this.player.currentTime() - 1); -// } - -// }); - -// // Load Progress Bar -// vjs.LoadProgressBar = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); -// player.on("progress", vjs.bind(this, this.update)); -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-load-progress", -// innerHTML: 'Loaded: 0%' -// }); -// }, - -// update: function(){ -// if (this.el_.style) { this.el_.style.width = vjs.round(this.player.bufferedPercent() * 100, 2) + "%"; } -// } - -// }); - -// // Play Progress Bar -// vjs.PlayProgressBar = vjs.Component.extend({ - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-play-progress", -// innerHTML: 'Progress: 0%' -// }); -// } - -// }); - -// // Seek Handle -// // SeekBar Behavior includes play progress bar, and seek handle -// // Needed so it can determine seek position based on handle position/size -// vjs.SeekHandle = vjs.Component.extend({ - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-seek-handle", -// innerHTML: '00:00' -// }); -// } - -// }); - -// /* Volume Scrubber -// ================================================================================ */ -// // Progress Control: Seek, Load Progress, and Play Progress -// vjs.VolumeControl = vjs.Component.extend({ - -// options: { -// children: { -// "volumeBar": {} -// } -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-volume-control vjs-control" -// }); -// } - -// }); - -// vjs.VolumeBar = vjs.Slider.extend({ - -// options: { -// children: { -// "bar": { componentClass: "VolumeLevel" }, -// "handle": { componentClass: "VolumeHandle" } -// } -// }, - -// playerEvent: "volumechange", - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-volume-bar" -// }); -// }, - -// onMouseMove: function(event) { -// this.player.volume(this.calculateDistance(event)); -// }, - -// getPercent: function(){ -// return this.player.volume(); -// }, - -// stepForward: function(){ -// this.player.volume(this.player.volume() + 0.1); -// }, - -// stepBack: function(){ -// this.player.volume(this.player.volume() - 0.1); -// } -// }); - -// vjs.VolumeLevel = vjs.Component.extend({ - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-volume-level", -// innerHTML: '' -// }); -// } - -// }); - -// vjs.VolumeHandle = vjs.Component.extend({ - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-volume-handle", -// innerHTML: '' -// // tabindex: 0, -// // role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100 -// }); -// } - -// }); - -// vjs.MuteToggle = vjs.Button.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// player.on("volumechange", vjs.bind(this, this.update)); -// }, - -// createEl: function(){ -// return this._super("div", { -// className: "vjs-mute-control vjs-control", -// innerHTML: '
Mute
' -// }); -// }, - -// onClick: function(event){ -// this.player.muted( this.player.muted() ? false : true ); -// }, - -// update: function(event){ -// var vol = this.player.volume(), -// level = 3; - -// if (vol == 0 || this.player.muted()) { -// level = 0; -// } else if (vol < 0.33) { -// level = 1; -// } else if (vol < 0.67) { -// level = 2; -// } - -// /* TODO improve muted icon classes */ -// vjs.each.call(this, [0,1,2,3], function(i){ -// vjs.removeClass(this.el, "vjs-vol-"+i); -// }); -// vjs.addClass(this.el, "vjs-vol-"+level); -// } - -// }); - - -// /* Poster Image -// ================================================================================ */ -// vjs.PosterImage = vjs.Button.extend({ -// init: function(player, options){ -// this._super(player, options); - -// if (!this.player.options.poster) { -// this.hide(); -// } - -// player.on("play", vjs.bind(this, this.hide)); -// }, - -// createEl: function(){ -// return vjs.createEl("img", { -// className: "vjs-poster", -// src: this.player.options.poster, - -// // Don't want poster to be tabbable. -// tabIndex: -1 -// }); -// }, - -// onClick: function(){ -// this.player.play(); -// } -// }); - -// /* Menu -// ================================================================================ */ -// // The base for text track and settings menu buttons. -// vjs.Menu = vjs.Component.extend({ - -// init: function(player, options){ -// this._super(player, options); -// }, - -// addItem: function(component){ -// this.addChild(component); -// component.on("click", vjs.bind(this, function(){ -// this.unlockShowing(); -// })); -// }, - -// createEl: function(){ -// return this._super("ul", { -// className: "vjs-menu" -// }); -// } - -// }); - -// vjs.MenuItem = vjs.Button.extend({ - -// init: function(player, options){ -// this._super(player, options); - -// if (options.selected) { -// this.addClass("vjs-selected"); -// } -// }, - -// createEl: function(type, attrs){ -// return this._super("li", vjs.merge({ -// className: "vjs-menu-item", -// innerHTML: this.options.label -// }, attrs)); -// }, - -// onClick: function(){ -// this.selected(true); -// }, - -// selected: function(selected){ -// if (selected) { -// this.addClass("vjs-selected"); -// } else { -// this.removeClass("vjs-selected") -// } -// } - -// }); diff --git a/src/core.js b/src/core.js index ec22c395a..034bcaa04 100644 --- a/src/core.js +++ b/src/core.js @@ -1,7 +1,9 @@ -// HTML5 Shiv. Must be in to support older browsers. -document.createElement("video");document.createElement("audio"); +/** + * @fileoverview Main function src. First file after goog.base. + */ -goog.provide('vjs'); +// HTML5 Shiv. Must be in to support older browsers. +document.createElement('video');document.createElement('audio'); /** * Doubles as the main function for users to create a player instance and also @@ -17,10 +19,10 @@ vjs = function(id, options, ready){ // Allow for element or ID to be passed in // String ID - if (typeof id == "string") { + if (typeof id === 'string') { // Adjust for jQuery ID syntax - if (id.indexOf("#") === 0) { + if (id.indexOf('#') === 0) { id = id.slice(1); } @@ -30,7 +32,7 @@ vjs = function(id, options, ready){ // Otherwise get element for ID } else { - tag = vjs.el(id) + tag = vjs.el(id); } // ID is a media element @@ -40,7 +42,7 @@ vjs = function(id, options, ready){ // Check for a useable element if (!tag || !tag.nodeName) { // re: nodeName, could be a box div also - throw new TypeError("The element or ID supplied is not valid. (videojs)"); // Returns + throw new TypeError('The element or ID supplied is not valid. (videojs)'); // Returns } // Element may have a player attr referring to an already created player instance. @@ -48,12 +50,11 @@ vjs = function(id, options, ready){ return tag.player || new vjs.Player(tag, options, ready); }; -// Shortcut +// Extended name, also available externally, window.videojs var videojs = vjs; -// videojs = vjs; // CDN Version. Used to target right flash swf. -CDN_VERSION = "GENERATED_CDN_VSN"; +var CDN_VERSION = 'GENERATED_CDN_VSN'; /** * Global Player instance options @@ -63,11 +64,11 @@ CDN_VERSION = "GENERATED_CDN_VSN"; vjs.options = { // Default order of fallback technology - 'techOrder': ["html5","flash"], - // techOrder: ["flash","html5"], + 'techOrder': ['html5','flash'], + // techOrder: ['flash','html5'], 'html5': {}, - 'flash': { swf: "http://vjs.zencdn.net/c/video-js.swf" }, + 'flash': { swf: 'http://vjs.zencdn.net/c/video-js.swf' }, // Default of web browser is 300x150. Should rely on source width/height. 'width': 300, @@ -77,14 +78,13 @@ vjs.options = { 'defaultVolume': 0.00, // The freakin seaguls are driving me crazy! // Included control sets - // TODO: just use uppercase Class name 'children': { - "mediaLoader": {}, - "posterImage": {}, - // "textTrackDisplay": {}, - "loadingSpinner": {}, - "bigPlayButton": {}, - "controlBar": {} + 'mediaLoader': {}, + 'posterImage': {}, + 'textTrackDisplay': {}, + 'loadingSpinner': {}, + 'bigPlayButton': {}, + 'controlBar': {} } }; @@ -96,6 +96,6 @@ vjs.players = {}; // Set CDN Version of swf -if (CDN_VERSION != "GENERATED_CDN_VSN") { - vjs.options.Flash.swf = "http://vjs.zencdn.net/"+CDN_VERSION+"/video-js.swf" +if (CDN_VERSION != 'GENERATED_CDN_VSN') { + videojs.options['flash']['swf'] = 'http://vjs.zencdn.net/'+CDN_VERSION+'/video-js.swf'; } diff --git a/src/events.js b/src/events.js index d6bcc1f4a..bc1bf47da 100644 --- a/src/events.js +++ b/src/events.js @@ -56,7 +56,7 @@ vjs.on = function(elem, type, fn){ if (document.addEventListener) { elem.addEventListener(type, data.dispatcher, false); } else if (document.attachEvent) { - elem.attachEvent("on" + type, data.dispatcher); + elem.attachEvent('on' + type, data.dispatcher); } } }; @@ -129,7 +129,7 @@ vjs.cleanUpEvents = function(elem, type) { if (document.removeEventListener) { elem.removeEventListener(type, data.dispatcher, false); } else if (document.detachEvent) { - elem.detachEvent("on" + type, data.dispatcher); + elem.detachEvent('on' + type, data.dispatcher); } } @@ -250,7 +250,7 @@ vjs.trigger = function(elem, event) { // handler; // If an event name was passed as a string, creates an event out of it - if (typeof event === "string") { + if (typeof event === 'string') { event = { type:event, target:elem }; } // Normalizes the event properties. @@ -286,7 +286,7 @@ vjs.trigger = function(elem, event) { * for now just in case. */ // // Added in attion to book. Book code was broke. - // event = typeof event === "object" ? + // event = typeof event === 'object' ? // event[vjs.expando] ? // event : // new vjs.Event(type, event) : @@ -311,7 +311,7 @@ vjs.trigger = function(elem, event) { */ vjs.one = function(elem, type, fn) { vjs.on(elem, type, function(){ - vjs.off(elem, type, arguments.callee) + vjs.off(elem, type, arguments.callee); fn.apply(this, arguments); }); -} +}; diff --git a/src/exports.js b/src/exports.js index de63a77af..6bb73eb0f 100644 --- a/src/exports.js +++ b/src/exports.js @@ -34,20 +34,20 @@ goog.exportSymbol('videojs.options', vjs.options); goog.exportSymbol('videojs.cache', vjs.cache); goog.exportSymbol('videojs.Component', vjs.Component); -goog.exportProperty(vjs.Component.prototype, "dispose", vjs.Component.prototype.dispose); -goog.exportProperty(vjs.Component.prototype, "createEl", vjs.Component.prototype.createEl); -goog.exportProperty(vjs.Component.prototype, "getEl", vjs.Component.prototype.getEl); -goog.exportProperty(vjs.Component.prototype, "addChild", vjs.Component.prototype.addChild); -goog.exportProperty(vjs.Component.prototype, "getChildren", vjs.Component.prototype.getChildren); -goog.exportProperty(vjs.Component.prototype, "on", vjs.Component.prototype.on); -goog.exportProperty(vjs.Component.prototype, "off", vjs.Component.prototype.off); -goog.exportProperty(vjs.Component.prototype, "one", vjs.Component.prototype.one); -goog.exportProperty(vjs.Component.prototype, "trigger", vjs.Component.prototype.trigger); -goog.exportProperty(vjs.Component.prototype, "show", vjs.Component.prototype.show); -goog.exportProperty(vjs.Component.prototype, "hide", vjs.Component.prototype.hide); -goog.exportProperty(vjs.Component.prototype, "width", vjs.Component.prototype.width); -goog.exportProperty(vjs.Component.prototype, "height", vjs.Component.prototype.height); -goog.exportProperty(vjs.Component.prototype, "dimensions", vjs.Component.prototype.dimensions); +goog.exportProperty(vjs.Component.prototype, 'dispose', vjs.Component.prototype.dispose); +goog.exportProperty(vjs.Component.prototype, 'createEl', vjs.Component.prototype.createEl); +goog.exportProperty(vjs.Component.prototype, 'getEl', vjs.Component.prototype.getEl); +goog.exportProperty(vjs.Component.prototype, 'addChild', vjs.Component.prototype.addChild); +goog.exportProperty(vjs.Component.prototype, 'getChildren', vjs.Component.prototype.getChildren); +goog.exportProperty(vjs.Component.prototype, 'on', vjs.Component.prototype.on); +goog.exportProperty(vjs.Component.prototype, 'off', vjs.Component.prototype.off); +goog.exportProperty(vjs.Component.prototype, 'one', vjs.Component.prototype.one); +goog.exportProperty(vjs.Component.prototype, 'trigger', vjs.Component.prototype.trigger); +goog.exportProperty(vjs.Component.prototype, 'show', vjs.Component.prototype.show); +goog.exportProperty(vjs.Component.prototype, 'hide', vjs.Component.prototype.hide); +goog.exportProperty(vjs.Component.prototype, 'width', vjs.Component.prototype.width); +goog.exportProperty(vjs.Component.prototype, 'height', vjs.Component.prototype.height); +goog.exportProperty(vjs.Component.prototype, 'dimensions', vjs.Component.prototype.dimensions); goog.exportSymbol('videojs.Player', vjs.Player); @@ -93,24 +93,27 @@ goog.exportSymbol('videojs.ChaptersButton', vjs.ChaptersButton); goog.exportSymbol('videojs.MediaTechController', vjs.MediaTechController); goog.exportSymbol('videojs.Html5', vjs.Html5); -goog.exportProperty(vjs.Html5, "Events", vjs.Html5.Events); -goog.exportProperty(vjs.Html5, "isSupported", vjs.Html5.isSupported); -goog.exportProperty(vjs.Html5, "canPlaySource", vjs.Html5.canPlaySource); +goog.exportProperty(vjs.Html5, 'Events', vjs.Html5.Events); +goog.exportProperty(vjs.Html5, 'isSupported', vjs.Html5.isSupported); +goog.exportProperty(vjs.Html5, 'canPlaySource', vjs.Html5.canPlaySource); // Export non-standard HTML5 video API methods. // Standard method names already protected by default externs. -goog.exportProperty(vjs.Html5.prototype, "setCurrentTime", vjs.Html5.prototype.setCurrentTime); -goog.exportProperty(vjs.Html5.prototype, "setVolume", vjs.Html5.prototype.setVolume); -goog.exportProperty(vjs.Html5.prototype, "setMuted", vjs.Html5.prototype.setMuted); -goog.exportProperty(vjs.Html5.prototype, "setPreload", vjs.Html5.prototype.setPreload); -goog.exportProperty(vjs.Html5.prototype, "setAutoplay", vjs.Html5.prototype.setAutoplay); -goog.exportProperty(vjs.Html5.prototype, "setLoop", vjs.Html5.prototype.setLoop); +goog.exportProperty(vjs.Html5.prototype, 'setCurrentTime', vjs.Html5.prototype.setCurrentTime); +goog.exportProperty(vjs.Html5.prototype, 'setVolume', vjs.Html5.prototype.setVolume); +goog.exportProperty(vjs.Html5.prototype, 'setMuted', vjs.Html5.prototype.setMuted); +goog.exportProperty(vjs.Html5.prototype, 'setPreload', vjs.Html5.prototype.setPreload); +goog.exportProperty(vjs.Html5.prototype, 'setAutoplay', vjs.Html5.prototype.setAutoplay); +goog.exportProperty(vjs.Html5.prototype, 'setLoop', vjs.Html5.prototype.setLoop); goog.exportSymbol('videojs.Flash', vjs.Flash); -goog.exportProperty(vjs.Flash, "Events", vjs.Flash.Events); -goog.exportProperty(vjs.Flash, "isSupported", vjs.Flash.isSupported); -goog.exportProperty(vjs.Flash, "canPlaySource", vjs.Flash.canPlaySource); -goog.exportProperty(vjs.Flash, "onReady", vjs.Flash['onReady']); +goog.exportProperty(vjs.Flash, 'Events', vjs.Flash.Events); +goog.exportProperty(vjs.Flash, 'isSupported', vjs.Flash.isSupported); +goog.exportProperty(vjs.Flash, 'canPlaySource', vjs.Flash.canPlaySource); +goog.exportProperty(vjs.Flash, 'onReady', vjs.Flash['onReady']); + +goog.exportSymbol('videojs.TextTrack', vjs.TextTrack); +goog.exportProperty(vjs.TextTrack.prototype, 'label', vjs.TextTrack.prototype.label); goog.exportSymbol('videojs.CaptionsTrack', vjs.CaptionsTrack); goog.exportSymbol('videojs.SubtitlesTrack', vjs.SubtitlesTrack); diff --git a/src/goog.base.js b/src/goog.base.js index f564e7bbc..28b281335 100644 --- a/src/goog.base.js +++ b/src/goog.base.js @@ -426,9 +426,10 @@ goog.nullFunction = function() {}; * without type. * @deprecated Use goog.functions.identity instead. */ -goog.identityFunction = function(opt_returnValue, var_args) { - return opt_returnValue; -}; +// Currently not used in Video.js and throwing jshint errors +// goog.identityFunction = function(opt_returnValue, var_args) { +// return opt_returnValue; +// }; /** @@ -458,18 +459,19 @@ goog.abstractMethod = function() { * @param {!Function} ctor The constructor for the class to add the static * method to. */ -goog.addSingletonGetter = function(ctor) { - ctor.getInstance = function() { - if (ctor.instance_) { - return ctor.instance_; - } - if (goog.DEBUG) { - // NOTE: JSCompiler can't optimize away Array#push. - goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; - } - return ctor.instance_ = new ctor; - }; -}; +// Currently not used in Video.js and throwing jshint errors +// goog.addSingletonGetter = function(ctor) { +// ctor.getInstance = function() { +// if (ctor.instance_) { +// return ctor.instance_; +// } +// if (goog.DEBUG) { +// // NOTE: JSCompiler can't optimize away Array#push. +// goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; +// } +// return ctor.instance_ = new ctor; +// }; +// }; /** @@ -1043,9 +1045,10 @@ goog.cloneObject = function(obj) { * is deprecated because some people have declared a pure-JS version. * Only the pure-JS version is truly deprecated. */ -goog.bindNative_ = function(fn, selfObj, var_args) { - return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); -}; +// Currently not used in Video.js and throwing errors +// goog.bindNative_ = function(fn, selfObj, var_args) { +// return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); +// }; /** @@ -1059,26 +1062,26 @@ goog.bindNative_ = function(fn, selfObj, var_args) { * invoked as a method of. * @private */ -goog.bindJs_ = function(fn, selfObj, var_args) { - if (!fn) { - throw new Error(); - } +// goog.bindJs_ = function(fn, selfObj, var_args) { +// if (!fn) { +// throw new Error(); +// } - if (arguments.length > 2) { - var boundArgs = Array.prototype.slice.call(arguments, 2); - return function() { - // Prepend the bound arguments to the current arguments. - var newArgs = Array.prototype.slice.call(arguments); - Array.prototype.unshift.apply(newArgs, boundArgs); - return fn.apply(selfObj, newArgs); - }; +// if (arguments.length > 2) { +// var boundArgs = Array.prototype.slice.call(arguments, 2); +// return function() { +// // Prepend the bound arguments to the current arguments. +// var newArgs = Array.prototype.slice.call(arguments); +// Array.prototype.unshift.apply(newArgs, boundArgs); +// return fn.apply(selfObj, newArgs); +// }; - } else { - return function() { - return fn.apply(selfObj, arguments); - }; - } -}; +// } else { +// return function() { +// return fn.apply(selfObj, arguments); +// }; +// } +// }; /** @@ -1104,23 +1107,23 @@ goog.bindJs_ = function(fn, selfObj, var_args) { * invoked as a method of. * @suppress {deprecated} See above. */ -goog.bind = function(fn, selfObj, var_args) { - // TODO(nicksantos): narrow the type signature. - if (Function.prototype.bind && - // NOTE(nicksantos): Somebody pulled base.js into the default - // Chrome extension environment. This means that for Chrome extensions, - // they get the implementation of Function.prototype.bind that - // calls goog.bind instead of the native one. Even worse, we don't want - // to introduce a circular dependency between goog.bind and - // Function.prototype.bind, so we have to hack this to make sure it - // works correctly. - Function.prototype.bind.toString().indexOf('native code') != -1) { - goog.bind = goog.bindNative_; - } else { - goog.bind = goog.bindJs_; - } - return goog.bind.apply(null, arguments); -}; +// goog.bind = function(fn, selfObj, var_args) { +// // TODO(nicksantos): narrow the type signature. +// if (Function.prototype.bind && +// // NOTE(nicksantos): Somebody pulled base.js into the default +// // Chrome extension environment. This means that for Chrome extensions, +// // they get the implementation of Function.prototype.bind that +// // calls goog.bind instead of the native one. Even worse, we don't want +// // to introduce a circular dependency between goog.bind and +// // Function.prototype.bind, so we have to hack this to make sure it +// // works correctly. +// Function.prototype.bind.toString().indexOf('native code') != -1) { +// goog.bind = goog.bindNative_; +// } else { +// goog.bind = goog.bindJs_; +// } +// return goog.bind.apply(null, arguments); +// }; /** @@ -1137,15 +1140,15 @@ goog.bind = function(fn, selfObj, var_args) { * @return {!Function} A partially-applied form of the function bind() was * invoked as a method of. */ -goog.partial = function(fn, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - return function() { - // Prepend the bound arguments to the current arguments. - var newArgs = Array.prototype.slice.call(arguments); - newArgs.unshift.apply(newArgs, args); - return fn.apply(this, newArgs); - }; -}; +// goog.partial = function(fn, var_args) { +// var args = Array.prototype.slice.call(arguments, 1); +// return function() { +// // Prepend the bound arguments to the current arguments. +// var newArgs = Array.prototype.slice.call(arguments); +// newArgs.unshift.apply(newArgs, args); +// return fn.apply(this, newArgs); +// }; +// }; /** @@ -1186,38 +1189,39 @@ goog.now = Date.now || (function() { * Throws an exception if neither execScript or eval is defined. * @param {string} script JavaScript string. */ -goog.globalEval = function(script) { - if (goog.global.execScript) { - goog.global.execScript(script, 'JavaScript'); - } else if (goog.global.eval) { - // Test to see if eval works - if (goog.evalWorksForGlobals_ == null) { - goog.global.eval('var _et_ = 1;'); - if (typeof goog.global['_et_'] != 'undefined') { - delete goog.global['_et_']; - goog.evalWorksForGlobals_ = true; - } else { - goog.evalWorksForGlobals_ = false; - } - } +// Currently not used in Video.js and throwing jshint errors +// goog.globalEval = function(script) { +// if (goog.global.execScript) { +// goog.global.execScript(script, 'JavaScript'); +// } else if (goog.global.eval) { +// // Test to see if eval works +// if (goog.evalWorksForGlobals_ == null) { +// goog.global.eval('var _et_ = 1;'); +// if (typeof goog.global['_et_'] != 'undefined') { +// delete goog.global['_et_']; +// goog.evalWorksForGlobals_ = true; +// } else { +// goog.evalWorksForGlobals_ = false; +// } +// } - if (goog.evalWorksForGlobals_) { - goog.global.eval(script); - } else { - var doc = goog.global.document; - var scriptElt = doc.createElement('script'); - scriptElt.type = 'text/javascript'; - scriptElt.defer = false; - // Note(user): can't use .innerHTML since "t('')" will fail and - // .text doesn't work in Safari 2. Therefore we append a text node. - scriptElt.appendChild(doc.createTextNode(script)); - doc.body.appendChild(scriptElt); - doc.body.removeChild(scriptElt); - } - } else { - throw Error('goog.globalEval not available'); - } -}; +// if (goog.evalWorksForGlobals_) { +// goog.global.eval(script); +// } else { +// var doc = goog.global.document; +// var scriptElt = doc.createElement('script'); +// scriptElt.type = 'text/javascript'; +// scriptElt.defer = false; +// // Note(user): can't use .innerHTML since "t('')" will fail and +// // .text doesn't work in Safari 2. Therefore we append a text node. +// scriptElt.appendChild(doc.createTextNode(script)); +// doc.body.appendChild(scriptElt); +// doc.body.removeChild(scriptElt); +// } +// } else { +// throw Error('goog.globalEval not available'); +// } +// }; /** @@ -1407,9 +1411,9 @@ goog.getMsg = function(str, opt_values) { * @param {string} b The fallback message. * @return {string} The best translated message. */ -goog.getMsgWithFallback = function(a, b) { - return a; -}; +// goog.getMsgWithFallback = function(a, b) { +// return a; +// }; /** @@ -1486,7 +1490,7 @@ goog.exportProperty = function(object, publicName, symbol) { */ goog.inherits = function(childCtor, parentCtor) { /** @constructor */ - function tempCtor() {}; + function tempCtor() {} tempCtor.prototype = parentCtor.prototype; childCtor.superClass_ = parentCtor.prototype; childCtor.prototype = new tempCtor(); @@ -1521,6 +1525,7 @@ goog.inherits = function(childCtor, parentCtor) { * @return {*} The return value of the superclass method. */ goog.base = function(me, opt_methodName, var_args) { + var_args = var_args; // Hiding JSHint unused var warning without killing check var caller = arguments.callee.caller; if (caller.superClass_) { // This is a constructor. Call the superclass constructor. diff --git a/src/json.js b/src/json.js index 8002617d4..2d13df095 100644 --- a/src/json.js +++ b/src/json.js @@ -1,9 +1,9 @@ -// Javascript JSON implementation -// (Parse Method Only) -// https://github.com/douglascrockford/JSON-js/blob/master/json2.js - /** - * JSON shim. Only using for parse method when parsing data-setup attribute JSON. + * Javascript JSON implementation + * (Parse Method Only) + * https://github.com/douglascrockford/JSON-js/blob/master/json2.js + * Only using for parse method when parsing data-setup attribute JSON. + * @type {Object} */ vjs.JSON; diff --git a/src/lib.js b/src/lib.js index 316fd3e66..f2f9ea705 100644 --- a/src/lib.js +++ b/src/lib.js @@ -12,7 +12,7 @@ vjs.createEl = function(tagName, properties){ el[propName] = properties[propName]; // Not remembering why we were checking for dash // but using setAttribute means you have to use getAttribute - // if (propName.indexOf("-") !== -1) { + // if (propName.indexOf('-') !== -1) { // el.setAttribute(propName, properties[propName]); // } else { // el[propName] = properties[propName]; @@ -80,43 +80,19 @@ vjs.bind = function(context, fn, uid) { // Create the new function that changes the context var ret = function() { return fn.apply(context, arguments); - } + }; // Allow for the ability to individualize this function // Needed in the case where multiple objects might share the same prototype // IF both items add an event listener with the same function, then you try to remove just one // it will remove both because they both have the same guid. // when using this, you need to use the bind method when you remove the listener as well. - ret.guid = (uid) ? uid + "_" + fn.guid : fn.guid; + // currently used in text tracks + ret.guid = (uid) ? uid + '_' + fn.guid : fn.guid; return ret; }; -/** - * FROM CLOSURE LIB - * Like bind(), except that a 'this object' is not required. Useful when the - * target function is already bound. - * - * Usage: - * var g = partial(f, arg1, arg2); - * g(arg3, arg4); - * - * @param {Function} fn A function to partially apply. - * @param {...*} var_args Additional arguments that are partially - * applied to fn. - * @return {!Function} A partially-applied form of the function bind() was - * invoked as a method of. - */ -vjs.partial = function(fn, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - return function() { - // Prepend the bound arguments to the current arguments. - var newArgs = Array.prototype.slice.call(arguments); - newArgs.unshift.apply(newArgs, args); - return fn.apply(this, newArgs); - }; -}; - /** * Element Data Store. Allows for binding data to an element without putting it directly on the element. * Ex. Event listneres are stored here. @@ -136,7 +112,7 @@ vjs.guid = 1; * @type {String} * @constant */ -vjs.expando = "vdata" + (new Date).getTime(); +vjs.expando = 'vdata' + (new Date()).getTime(); /** * Returns the cache object where data for an element is stored @@ -204,8 +180,8 @@ vjs.isEmpty = function(obj) { * @param {String} classToAdd Classname to add */ vjs.addClass = function(element, classToAdd){ - if ((" "+element.className+" ").indexOf(" "+classToAdd+" ") == -1) { - element.className = element.className === "" ? classToAdd : element.className + " " + classToAdd; + if ((' '+element.className+' ').indexOf(' '+classToAdd+' ') == -1) { + element.className = element.className === '' ? classToAdd : element.className + ' ' + classToAdd; } }; @@ -216,9 +192,9 @@ vjs.addClass = function(element, classToAdd){ */ vjs.removeClass = function(element, classToRemove){ if (element.className.indexOf(classToRemove) == -1) { return; } - var classNames = element.className.split(" "); + var classNames = element.className.split(' '); classNames.splice(classNames.indexOf(classToRemove),1); - element.className = classNames.join(" "); + element.className = classNames.join(' '); }; /** @@ -226,7 +202,7 @@ vjs.removeClass = function(element, classToRemove){ * @type {Element} * @constant */ -vjs.TEST_VID = document.createElement("video"); +vjs.TEST_VID = document.createElement('video'); /** * Useragent for browser testing. @@ -259,11 +235,7 @@ vjs.ANDROID_VERSION = (function() { return null; })(); -// http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html -// IE9+ returns false -vjs.IS_IE6to8 = function(){ return !+"\v1"; }; - -vjs.IS_FIREFOX = function(){ return !!vjs.USER_AGENT.match("Firefox") }; +vjs.IS_FIREFOX = function(){ return !!vjs.USER_AGENT.match('Firefox'); }; /** @@ -281,7 +253,7 @@ vjs.getAttributeValues = function(tag){ // We can check for matching boolean properties, but older browsers // won't know about HTML5 boolean attributes that we still read from. // Bookending with commas to allow for an easy string search. - var knownBooleans = ","+"autoplay,controls,loop,muted,default"+","; + var knownBooleans = ','+'autoplay,controls,loop,muted,default'+','; if (tag && tag.attributes && tag.attributes.length > 0) { var attrs = tag.attributes; @@ -293,15 +265,15 @@ vjs.getAttributeValues = function(tag){ // Check for known booleans // The matching element property will return a value for typeof - if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(","+attrName+",") !== -1) { - // The value of an included boolean attribute is typically an empty string ("") + if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(','+attrName+',') !== -1) { + // The value of an included boolean attribute is typically an empty string ('') // which would equal false if we just check for a false value. - // We also don't want support bad code like autoplay="false" + // We also don't want support bad code like autoplay='false' attrVal = (attrVal !== null) ? true : false; } obj[attrName] = attrVal; - }; + } } return obj; @@ -315,9 +287,9 @@ vjs.getAttributeValues = function(tag){ * @return {String} Style value */ vjs.getComputedStyleValue = function(el, strCssRule){ - var strValue = ""; + var strValue = ''; if(document.defaultView && document.defaultView.getComputedStyle){ - strValue = document.defaultView.getComputedStyle(el, "").getPropertyValue(strCssRule); + strValue = document.defaultView.getComputedStyle(el, '').getPropertyValue(strCssRule); } else if(el.currentStyle){ strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ @@ -354,7 +326,7 @@ vjs.support = {}; * @return {Element} Element with supplied ID */ vjs.el = function(id){ - if (id.indexOf("#") === 0) { + if (id.indexOf('#') === 0) { id = id.slice(1); } @@ -378,14 +350,14 @@ vjs.formatTime = function(seconds, guide) { gh = Math.floor(guide / 3600); // Check if we need to show hours - h = (h > 0 || gh > 0) ? h + ":" : ""; + h = (h > 0 || gh > 0) ? h + ':' : ''; // If hours are showing, we may need to add a leading zero. // Always show at least one digit of minutes. - m = (((h || gm >= 10) && m < 10) ? "0" + m : m) + ":"; + m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':'; // Check if leading zero is need for seconds - s = (s < 10) ? "0" + s : s; + s = (s < 10) ? '0' + s : s; return h + m + s; }; @@ -404,7 +376,7 @@ vjs.unblockTextSelection = function(){ document.onselectstart = function () { re * @return {String} Trimmed string */ vjs.trim = function(string){ - return string.toString().replace(/^\s+/, "").replace(/\s+$/, ""); + return string.toString().replace(/^\s+/, '').replace(/\s+$/, ''); }; /** @@ -435,67 +407,52 @@ vjs.createTimeRange = function(start, end){ }; }; -// vjs.extend({ +/** + * Simple http request for retrieving external files (e.g. text tracks) + * @param {String} url URL of resource + * @param {Function=} onSuccess Success callback + * @param {Function=} onError Error callback + */ +vjs.get = function(url, onSuccess, onError){ + var local = (url.indexOf('file:') === 0 || (window.location.href.indexOf('file:') === 0 && url.indexOf('http') === -1)); -// each: function(arr, fn){ -// if (!arr || arr.length === 0) { return; } -// for (var i=0,j=arr.length; i= 10; - // return swfobject.hasFlashPlayerVersion("10"); + // return swfobject.hasFlashPlayerVersion('10'); }; vjs.Flash.canPlaySource = function(srcObj){ - if (srcObj.type in vjs.Flash.prototype.features.formats) { return "maybe"; } + if (srcObj.type in vjs.Flash.prototype.features.formats) { return 'maybe'; } }; vjs.Flash.prototype.features = { formats: { - "video/flv": "FLV", - "video/x-flv": "FLV", - "video/mp4": "MP4", - "video/m4v": "MP4" + 'video/flv': 'FLV', + 'video/x-flv': 'FLV', + 'video/mp4': 'MP4', + 'video/m4v': 'MP4' }, // Optional events that we can manually mimic with timers @@ -326,7 +320,7 @@ vjs.Flash.prototype.features = { fullscreenResize: false, // Resizing plugins in Firefox always reloads the plugin (e.g. full window mode) - parentResize: !(vjs.USER_AGENT.match("Firefox")) + parentResize: !(vjs.USER_AGENT.match('Firefox')) }; vjs.Flash['onReady'] = function(currSwf){ @@ -344,7 +338,7 @@ vjs.Flash['onReady'] = function(currSwf){ tech.el_ = el; // Now that the element is ready, make a click on the swf play the video - tech.on("click", tech.onClick); + tech.on('click', tech.onClick); vjs.Flash.checkReady(tech); }; @@ -354,7 +348,7 @@ vjs.Flash['onReady'] = function(currSwf){ vjs.Flash.checkReady = function(tech){ // Check if API property exists - if (tech.getEl().vjs_getProperty) { + if (tech.el().vjs_getProperty) { // If so, tell tech it's ready tech.triggerReady(); @@ -378,8 +372,8 @@ vjs.Flash['onEvent'] = function(swfID, eventName){ // Log errors from the swf vjs.Flash['onError'] = function(swfID, err){ var player = vjs.el(swfID).player; - player.trigger("error"); - vjs.log("Flash Error", err, swfID); + player.trigger('error'); + vjs.log('Flash Error', err, swfID); }; // Flash Version Check @@ -388,17 +382,17 @@ vjs.Flash.version = function(){ // IE try { - version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; + version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; // other browsers } catch(e) { try { - if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){ - version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1]; + if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin){ + version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; } - } catch(e) {} + } catch(err) {} } - return version.split(","); + return version.split(','); }; // Flash embedding method. Only used in non-iframe mode @@ -406,7 +400,7 @@ vjs.Flash.embed = function(swf, placeHolder, flashVars, params, attributes){ var code = vjs.Flash.getEmbedCode(swf, flashVars, params, attributes), // Get element by embedding code and retrieving created element - obj = vjs.createEl("div", { innerHTML: code }).childNodes[0], + obj = vjs.createEl('div', { innerHTML: code }).childNodes[0], par = placeHolder.parentNode ; @@ -414,13 +408,11 @@ vjs.Flash.embed = function(swf, placeHolder, flashVars, params, attributes){ placeHolder.parentNode.replaceChild(obj, placeHolder); // IE6 seems to have an issue where it won't initialize the swf object after injecting it. - // This is a dumb temporary fix - if (vjs.IS_IE6to8()) { - var newObj = par.childNodes[0]; - setTimeout(function(){ - newObj.style.display = "block"; - }, 1000); - } + // This is a dumb fix + var newObj = par.childNodes[0]; + setTimeout(function(){ + newObj.style.display = 'block'; + }, 1000); return obj; @@ -436,7 +428,7 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){ // Convert flash vars to string if (flashVars) { vjs.eachProp(flashVars, function(key, val){ - flashVarsString += (key + "=" + val + "&"); + flashVarsString += (key + '=' + val + '&'); }); } @@ -444,8 +436,8 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){ params = vjs.merge({ 'movie': swf, 'flashvars': flashVarsString, - 'allowScriptAccess': "always", // Required to talk to swf - 'allowNetworking': "all" // All should be default, but having security issues. + 'allowScriptAccess': 'always', // Required to talk to swf + 'allowNetworking': 'all' // All should be default, but having security issues. }, params); // Create param tags string @@ -458,8 +450,8 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){ 'data': swf, // Default to 100% width/height - 'width': "100%", - 'height': "100%" + 'width': '100%', + 'height': '100%' }, attributes); diff --git a/src/media.html5.js b/src/media.html5.js index ee6444a93..b61f621d7 100644 --- a/src/media.html5.js +++ b/src/media.html5.js @@ -1,13 +1,7 @@ -goog.provide('vjs.Html5'); -goog.provide('vjs.media.html5'); -goog.provide('vjs.Html5.Events'); -goog.provide('vjs.Html5.isSupported'); -goog.provide('vjs.Html5.canPlaySource'); +/** + * @fileoverview HTML5 Media Controller - Wrapper for HTML5 Media API + */ -goog.require('vjs.MediaTechController'); - -/* HTML5 Media Controller - Wrapper for HTML5 Media API -============================================================================= */ /** * HTML5 Media Controller - Wrapper for HTML5 Media API * @param {vjs.Player|Object} player @@ -23,7 +17,7 @@ vjs.Html5 = function(player, options, ready){ // If the element source is already set, we may have missed the loadstart event, and want to trigger it. // We don't want to set the source again and interrupt playback. if (source && this.el_.currentSrc == source.src) { - player.trigger("loadstart"); + player.trigger('loadstart'); // Otherwise set the source if one was provided. } else if (source) { @@ -41,7 +35,7 @@ vjs.Html5 = function(player, options, ready){ } }); - this.on("click", this.onClick); + this.on('click', this.onClick); this.setupTriggers(); @@ -69,12 +63,12 @@ vjs.Html5.prototype.createEl = function(){ // If the original tag is still there, remove it. if (el) { - player.getEl().removeChild(el); + player.el().removeChild(el); } - newEl = vjs.createElement("video", { - id: el.id || player.id + "_html5_api", - className: el.className || "vjs-tech" + newEl = vjs.createElement('video', { + id: el.id || player.id + '_html5_api', + className: el.className || 'vjs-tech' }); el = newEl; @@ -82,13 +76,13 @@ vjs.Html5.prototype.createEl = function(){ } // Update specific tag settings, in case they were overridden - var attrs = ["autoplay","preload","loop","muted"]; + var attrs = ['autoplay','preload','loop','muted']; for (var i = attrs.length - 1; i >= 0; i--) { var attr = attrs[i]; if (player.options[attr] !== null) { el[attr] = player.options[attr]; } - }; + } return el; // jenniisawesome = true; @@ -99,13 +93,13 @@ vjs.Html5.prototype.createEl = function(){ vjs.Html5.prototype.setupTriggers = function(){ for (var i = vjs.Html5.Events.length - 1; i >= 0; i--) { vjs.on(this.el_, vjs.Html5.Events[i], vjs.bind(this.player, this.eventHandler)); - }; + } }; vjs.Html5.prototype.removeTriggers = function(){ for (var i = vjs.Html5.Events.length - 1; i >= 0; i--) { vjs.off(this.el_, vjs.Html5.Events[i], vjs.bind(this.player, this.eventHandler)); - }; - // console.log("removeTriggers", vjs.getData(this.el_)); + } + // console.log('removeTriggers', vjs.getData(this.el_)); }; vjs.Html5.prototype.eventHandler = function(e){ // console.log('eventHandler', e.type, e, this.el_) @@ -123,7 +117,7 @@ vjs.Html5.prototype.setCurrentTime = function(seconds){ try { this.el_.currentTime = seconds; } catch(e) { - vjs.log(e, "Video isn't ready. (Video.js)"); + vjs.log(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady); } }; @@ -143,7 +137,7 @@ vjs.Html5.prototype.supportsFullScreen = function(){ if (typeof this.el_.webkitEnterFullScreen == 'function') { // Seems to be broken in Chromium/Chrome && Safari in Leopard - if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) { + if (!navigator.userAgent.match('Chrome') && !navigator.userAgent.match('Mac OS X 10.5')) { return true; } } @@ -156,7 +150,7 @@ vjs.Html5.prototype.enterFullScreen = function(){ } catch (e) { if (e.code == 11) { // this.warning(VideoJS.warnings.videoNotReady); - vjs.log("Video.js: Video not ready."); + vjs.log('Video.js: Video not ready.'); } } }; @@ -166,7 +160,7 @@ vjs.Html5.prototype.exitFullScreen = function(){ } catch (e) { if (e.code == 11) { // this.warning(VideoJS.warnings.videoNotReady); - vjs.log("Video.js: Video not ready."); + vjs.log('Video.js: Video not ready.'); } } }; @@ -205,18 +199,18 @@ vjs.Html5.prototype.defaultMuted = function(){ return this.el_.defaultMuted; }; /* HTML5 Support Testing ---------------------------------------------------- */ vjs.Html5.isSupported = function(){ - return !!document.createElement("video").canPlayType; + return !!document.createElement('video').canPlayType; }; vjs.Html5.canPlaySource = function(srcObj){ - return !!document.createElement("video").canPlayType(srcObj.type); + return !!document.createElement('video').canPlayType(srcObj.type); // TODO: Check Type // If no Type, check ext // Check Media Type }; // List of all HTML5 events (various uses). -vjs.Html5.Events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(","); +vjs.Html5.Events = 'loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange'.split(','); // HTML5 Feature detection and Device Fixes --------------------------------- // @@ -226,7 +220,7 @@ vjs.Html5.prototype.features = { // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html // Seems to be broken in Chromium/Chrome && Safari in Leopard fullscreen: (vjs.TEST_VID.webkitEnterFullScreen) - ? ((!vjs.USER_AGENT.match("Chrome") && !vjs.USER_AGENT.match("Mac OS X 10.5") + ? ((!vjs.USER_AGENT.match('Chrome') && !vjs.USER_AGENT.match('Mac OS X 10.5') ? true : false)) : false, @@ -241,8 +235,8 @@ if (vjs.IS_ANDROID) { // Override Android 2.2 and less canPlayType method which is broken if (vjs.ANDROID_VERSION < 3) { - document.createElement("video").constructor.prototype.canPlayType = function(type){ - return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : ""; + document.createElement('video').constructor.prototype.canPlayType = function(type){ + return (type && type.toLowerCase().indexOf('video/mp4') != -1) ? 'maybe' : ''; }; } } diff --git a/src/media.js b/src/media.js index 33e6f434a..e3478ae33 100644 --- a/src/media.js +++ b/src/media.js @@ -1,10 +1,6 @@ -goog.provide('vjs.media'); -goog.provide('vjs.MediaTechController'); - -goog.require('vjs.Component'); - -/* Media Technology Controller - Base class for media playback technologies -================================================================================ */ +/** + * @fileoverview Media Technology Controller - Base class for media playback technologies + */ /** * Base class for media (HTML5 Video, Flash) controllers @@ -16,9 +12,9 @@ vjs.MediaTechController = function(player, options, ready){ goog.base(this, player, options, ready); // Make playback element clickable - // this.addEvent("click", this.proxy(this.onClick)); + // this.addEvent('click', this.proxy(this.onClick)); - // player.triggerEvent("techready"); + // player.triggerEvent('techready'); }; goog.inherits(vjs.MediaTechController, vjs.Component); @@ -38,19 +34,22 @@ vjs.MediaTechController.prototype.onClick = function(){ } }; +vjs.media = {}; + /** * List of default API methods for any MediaTechController * @type {String} */ -vjs.media.ApiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(","); - +vjs.media.ApiMethods = 'play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted'.split(','); // Create placeholder methods for each that warn when a method isn't supported by the current playback technology + +function createMethod(methodName){ + return function(){ + throw new Error('The "'+methodName+'" method is not available on the playback technology\'s API'); + }; +} + for (var i = vjs.media.ApiMethods.length - 1; i >= 0; i--) { var methodName = vjs.media.ApiMethods[i]; - - vjs.MediaTechController.prototype[vjs.media.ApiMethods[i]] = (function(methodName){ - return function(){ - throw new Error("The '"+methodName+"' method is not available on the playback technology's API"); - } - })(methodName); -}; + vjs.MediaTechController.prototype[vjs.media.ApiMethods[i]] = createMethod(methodName); +} diff --git a/src/player.js b/src/player.js index d4b86c338..731c2897b 100644 --- a/src/player.js +++ b/src/player.js @@ -1,9 +1,5 @@ -goog.provide('vjs.Player'); - -goog.require('vjs.Component'); - /** - * Main player class. Returned by vjs(id); + * Main player class. A player instance is returned by _V_(id); * @param {Element} tag The original video tag used for configuring options * @param {Object=} options Player options * @param {Function=} ready Ready callback function @@ -25,28 +21,20 @@ vjs.Player = function(tag, options, ready){ // Cache for video property values. this.cache_ = {}; - // Tracks defined in tracks.js - this.textTracks = []; - // this.ready(function() { - // if (opts.tracks && opts.tracks.length > 0) { - // this.addTextTracks(opts.tracks); - // } - // }); - // Run base component initializing with new options. // Builds the element through createEl() // Inits and embeds any child components in opts vjs.Component.call(this, this, opts, ready); - this.on("ended", this.onEnded); - this.on("play", this.onPlay); - this.on("pause", this.onPause); - this.on("progress", this.onProgress); - this.on("durationchange", this.onDurationChange); - this.on("error", this.onError); + this.on('ended', this.onEnded); + this.on('play', this.onPlay); + this.on('pause', this.onPause); + this.on('progress', this.onProgress); + this.on('durationchange', this.onDurationChange); + this.on('error', this.onError); // Make player easily findable by ID - vjs.players[this.id] = this; + vjs.players[this.id_] = this; }; goog.inherits(vjs.Player, vjs.Component); @@ -54,7 +42,7 @@ vjs.Player.prototype.dispose = function(){ // this.isReady_ = false; // Kill reference to this player - vjs.players[this.id] = null; + vjs.players[this.id_] = null; if (this.tag && this.tag.player) { this.tag.player = null; } if (this.el_ && this.el_.player) { this.el_.player = null; } @@ -70,8 +58,8 @@ vjs.Player.prototype.dispose = function(){ vjs.Player.prototype.getTagSettings = function(tag){ var options = { - sources: [], - tracks: [] + 'sources': [], + 'tracks': [] }; vjs.merge(options, vjs.getAttributeValues(tag)); @@ -88,15 +76,16 @@ vjs.Player.prototype.getTagSettings = function(tag){ // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/ childName = child.nodeName.toLowerCase(); - if (childName === "source") { - options.sources.push(vjs.getAttributeValues(child)); + if (childName === 'source') { + options['sources'].push(vjs.getAttributeValues(child)); - } else if (childName === "track") { - options.tracks.push(vjs.getAttributeValues(child)); + } else if (childName === 'track') { + options['tracks'].push(vjs.getAttributeValues(child)); } } } + return options; }; @@ -106,12 +95,12 @@ vjs.Player.prototype.createEl = function(){ // Original tag settings stored in options // now remove immediately so native controls don't flash. - tag.removeAttribute("controls"); + tag.removeAttribute('controls'); // Poster will be handled by a manual - tag.removeAttribute("poster"); + tag.removeAttribute('poster'); // Remove width/height attrs from tag so CSS can make it 100% width/height - tag.removeAttribute("width"); - tag.removeAttribute("height"); + tag.removeAttribute('width'); + tag.removeAttribute('height'); // Empty video tag sources and tracks so the built-in player doesn't use them also. // This may not be fast enough to stop HTML5 browsers from reading the tags // so we'll need to turn off any default tracks if we're manually doing @@ -119,14 +108,14 @@ vjs.Player.prototype.createEl = function(){ if (tag.hasChildNodes()) { var nrOfChildNodes = tag.childNodes.length; for (var i=0,j=tag.childNodes;i 0) { - techOptions.startTime = this.cache_.currentTime; + techOptions['startTime'] = this.cache_.currentTime; } this.cache_.src = source.src; @@ -212,29 +201,29 @@ vjs.Player.prototype.loadTech = function(techName, source){ this.tech.ready(techReady); }; -// vjs.Player.prototype.unloadTech = function(){ -// this.tech.destroy(); +vjs.Player.prototype.unloadTech = function(){ + this.tech.dispose(); -// // Turn off any manual progress or timeupdate tracking -// if (this.manualProgress) { this.manualProgressOff(); } + // Turn off any manual progress or timeupdate tracking + if (this.manualProgress) { this.manualProgressOff(); } -// if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); } + if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); } -// this.tech = false; -// }; + this.tech = false; +}; // There's many issues around changing the size of a Flash (or other plugin) object. // First is a plugin reload issue in Firefox that has been around for 11 years: https://bugzilla.mozilla.org/show_bug.cgi?id=90268 // Then with the new fullscreen API, Mozilla and webkit browsers will reload the flash object after going to fullscreen. // To get around this, we're unloading the tech, caching source and currentTime values, and reloading the tech once the plugin is resized. // reloadTech: function(betweenFn){ -// vjs.log("unloadingTech") +// vjs.log('unloadingTech') // this.unloadTech(); -// vjs.log("unloadedTech") +// vjs.log('unloadedTech') // if (betweenFn) { betweenFn.call(); } -// vjs.log("LoadingTech") +// vjs.log('LoadingTech') // this.loadTech(this.techName, { src: this.cache_.src }) -// vjs.log("loadedTech") +// vjs.log('loadedTech') // }, /* Fallbacks for unsupported event types @@ -247,13 +236,11 @@ vjs.Player.prototype.manualProgressOn = function(){ // Trigger progress watching when a source begins loading this.trackProgress(); - var techName = this.techName; - // Watch for a native progress event call on the tech element // In HTML5, some older versions don't support the progress event // So we're assuming they don't, and turning off manual progress if they do. // As opposed to doing user agent detection - this.tech.one("progress", function(){ + this.tech.one('progress', function(){ // Update known progress support for this playback technology this.features.progressEvents = true; @@ -275,10 +262,10 @@ vjs.Player.prototype.trackProgress = function(){ // log(this.cache_.bufferEnd, this.buffered().end(0), this.duration()) /* TODO: update for multiple buffered regions */ if (this.cache_.bufferEnd < this.buffered().end(0)) { - this.trigger("progress"); + this.trigger('progress'); } else if (this.bufferedPercent() == 1) { this.stopTrackingProgress(); - this.trigger("progress"); // Last update + this.trigger('progress'); // Last update } }), 500); }; @@ -288,12 +275,12 @@ vjs.Player.prototype.stopTrackingProgress = function(){ clearInterval(this.progr vjs.Player.prototype.manualTimeUpdatesOn = function(){ this.manualTimeUpdates = true; - this.on("play", this.trackCurrentTime); - this.on("pause", this.stopTrackingCurrentTime); + this.on('play', this.trackCurrentTime); + this.on('pause', this.stopTrackingCurrentTime); // timeupdate is also called by .currentTime whenever current time is set // Watch for native timeupdate event - this.tech.one("timeupdate", function(){ + this.tech.one('timeupdate', function(){ // Update known progress support for this playback technology this.features.timeupdateEvents = true; // Turn off manual progress tracking @@ -304,14 +291,14 @@ vjs.Player.prototype.manualTimeUpdatesOn = function(){ vjs.Player.prototype.manualTimeUpdatesOff = function(){ this.manualTimeUpdates = false; this.stopTrackingCurrentTime(); - this.off("play", this.trackCurrentTime); - this.off("pause", this.stopTrackingCurrentTime); + this.off('play', this.trackCurrentTime); + this.off('pause', this.stopTrackingCurrentTime); }; vjs.Player.prototype.trackCurrentTime = function(){ if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); } this.currentTimeInterval = setInterval(vjs.bind(this, function(){ - this.trigger("timeupdate"); + this.trigger('timeupdate'); }), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 }; @@ -321,37 +308,37 @@ vjs.Player.prototype.stopTrackingCurrentTime = function(){ clearInterval(this.cu // /* Player event handlers (how the player reacts to certain events) // ================================================================================ */ vjs.Player.prototype.onEnded = function(){ - if (this.options.loop) { + if (this.options['loop']) { this.currentTime(0); this.play(); } }; vjs.Player.prototype.onPlay = function(){ - vjs.removeClass(this.el_, "vjs-paused"); - vjs.addClass(this.el_, "vjs-playing"); + vjs.removeClass(this.el_, 'vjs-paused'); + vjs.addClass(this.el_, 'vjs-playing'); }; vjs.Player.prototype.onPause = function(){ - vjs.removeClass(this.el_, "vjs-playing"); - vjs.addClass(this.el_, "vjs-paused"); + vjs.removeClass(this.el_, 'vjs-playing'); + vjs.addClass(this.el_, 'vjs-paused'); }; vjs.Player.prototype.onProgress = function(){ // Add custom event for when source is finished downloading. if (this.bufferedPercent() == 1) { - this.trigger("loadedalldata"); + this.trigger('loadedalldata'); } }; // Update duration with durationchange event // Allows for cacheing value instead of asking player each time. vjs.Player.prototype.onDurationChange = function(){ - this.duration(this.techGet("duration")); + this.duration(this.techGet('duration')); }; vjs.Player.prototype.onError = function(e) { - vjs.log("Video Error", e); + vjs.log('Video Error', e); }; // /* Player API @@ -400,14 +387,13 @@ vjs.Player.prototype.techGet = function(method){ // When building additional tech libs, an expected method may not be defined yet if (this.tech[method] === undefined) { - vjs.log("Video.js: " + method + " method not defined for "+this.techName+" playback technology.", e); + vjs.log('Video.js: ' + method + ' method not defined for '+this.techName+' playback technology.', e); } else { // When a method isn't available on the object it throws a TypeError - if (e.name == "TypeError") { - window['e'] = e - vjs.log("Video.js: " + method + " unavailable on "+this.techName+" playback technology element.", e); + if (e.name == 'TypeError') { + vjs.log('Video.js: ' + method + ' unavailable on '+this.techName+' playback technology element.', e); this.tech.isReady_ = false; throw e; } else { @@ -422,20 +408,20 @@ vjs.Player.prototype.techGet = function(method){ // http://dev.w3.org/html5/spec/video.html#dom-media-play vjs.Player.prototype.play = function(){ - this.techCall("play"); + this.techCall('play'); return this; }; // http://dev.w3.org/html5/spec/video.html#dom-media-pause vjs.Player.prototype.pause = function(){ - this.techCall("pause"); + this.techCall('pause'); return this; }; // http://dev.w3.org/html5/spec/video.html#dom-media-paused // The initial state of paused should be true (in Safari it's actually false) vjs.Player.prototype.paused = function(){ - return (this.techGet("paused") === false) ? false : true; + return (this.techGet('paused') === false) ? false : true; }; // http://dev.w3.org/html5/spec/video.html#dom-media-currenttime @@ -445,17 +431,17 @@ vjs.Player.prototype.currentTime = function(seconds){ // Cache the last set value for smoother scrubbing. this.cache_.lastSetCurrentTime = seconds; - this.techCall("setCurrentTime", seconds); + this.techCall('setCurrentTime', seconds); // Improve the accuracy of manual timeupdates - if (this.manualTimeUpdates) { this.trigger("timeupdate"); } + if (this.manualTimeUpdates) { this.trigger('timeupdate'); } return this; } // Cache last currentTime and return // Default to 0 seconds - return this.cache_.currentTime = (this.techGet("currentTime") || 0); + return this.cache_.currentTime = (this.techGet('currentTime') || 0); }; // http://dev.w3.org/html5/spec/video.html#dom-media-duration @@ -478,13 +464,14 @@ vjs.Player.prototype.remainingTime = function(){ }; // http://dev.w3.org/html5/spec/video.html#dom-media-buffered -// Buffered returns a timerange object. Kind of like an array of portions of the video that have been downloaded. +// Buffered returns a timerange object. +// Kind of like an array of portions of the video that have been downloaded. // So far no browsers return more than one range (portion) vjs.Player.prototype.buffered = function(){ - var buffered = this.techGet("buffered"), + var buffered = this.techGet('buffered'), start = 0, - end = this.cache_.bufferEnd = this.cache_.bufferEnd || 0, // Default end to 0 and store in values - timeRange; + // Default end to 0 and store in values + end = this.cache_.bufferEnd = this.cache_.bufferEnd || 0; if (buffered && buffered.length > 0 && buffered.end(0) !== end) { end = buffered.end(0); @@ -507,27 +494,27 @@ vjs.Player.prototype.volume = function(percentAsDecimal){ if (percentAsDecimal !== undefined) { vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1 this.cache_.volume = vol; - this.techCall("setVolume", vol); - vjs.setLocalStorage("volume", vol); + this.techCall('setVolume', vol); + vjs.setLocalStorage('volume', vol); return this; } // Default to 1 when returning current volume. - vol = parseFloat(this.techGet("volume")); + vol = parseFloat(this.techGet('volume')); return (isNaN(vol)) ? 1 : vol; }; // http://dev.w3.org/html5/spec/video.html#attr-media-muted vjs.Player.prototype.muted = function(muted){ if (muted !== undefined) { - this.techCall("setMuted", muted); + this.techCall('setMuted', muted); return this; } - return this.techGet("muted") || false; // Default to false + return this.techGet('muted') || false; // Default to false }; // Check if current tech can support native fullscreen (e.g. with built in controls lik iOS, so not our flash swf) -vjs.Player.prototype.supportsFullScreen = function(){ return this.techGet("supportsFullScreen") || false; }; +vjs.Player.prototype.supportsFullScreen = function(){ return this.techGet('supportsFullScreen') || false; }; // Turn on fullscreen (or window) mode vjs.Player.prototype.requestFullScreen = function(){ @@ -543,16 +530,16 @@ vjs.Player.prototype.requestFullScreen = function(){ this.isFullScreen = document[requestFullScreen.isFullScreen]; // If cancelling fullscreen, remove event listener. - if (this.isFullScreen == false) { + if (this.isFullScreen === false) { vjs.off(document, requestFullScreen.eventName, arguments.callee); } - this.trigger("fullscreenchange"); + this.trigger('fullscreenchange'); })); // Flash and other plugins get reloaded when you take their parent to fullscreen. // To fix that we'll remove the tech, and reload it after the resize has finished. - if (this.tech.features.fullscreenResize === false && this.options.Flash.iFrameMode != true) { + if (this.tech.features.fullscreenResize === false && this.options['flash']['iFrameMode'] !== true) { this.pause(); this.unloadTech(); @@ -569,11 +556,11 @@ vjs.Player.prototype.requestFullScreen = function(){ } } else if (this.tech.supportsFullScreen()) { - this.trigger("fullscreenchange"); - this.techCall("enterFullScreen"); + this.trigger('fullscreenchange'); + this.techCall('enterFullScreen'); } else { - this.trigger("fullscreenchange"); + this.trigger('fullscreenchange'); this.enterFullWindow(); } @@ -590,14 +577,14 @@ vjs.Player.prototype.cancelFullScreen = function(){ // Flash and other plugins get reloaded when you take their parent to fullscreen. // To fix that we'll remove the tech, and reload it after the resize has finished. - if (this.tech.features.fullscreenResize === false && this.options.Flash.iFrameMode != true) { + if (this.tech.features.fullscreenResize === false && this.options['flash']['iFrameMode'] !== true) { this.pause(); this.unloadTech(); vjs.on(document, requestFullScreen.eventName, vjs.bind(this, function(){ vjs.off(document, requestFullScreen.eventName, arguments.callee); - this.loadTech(this.techName, { src: this.cache_.src }) + this.loadTech(this.techName, { src: this.cache_.src }); })); document[requestFullScreen.cancelFn](); @@ -607,12 +594,12 @@ vjs.Player.prototype.cancelFullScreen = function(){ } } else if (this.tech.supportsFullScreen()) { - this.techCall("exitFullScreen"); - this.trigger("fullscreenchange"); + this.techCall('exitFullScreen'); + this.trigger('fullscreenchange'); } else { this.exitFullWindow(); - this.trigger("fullscreenchange"); + this.trigger('fullscreenchange'); } return this; @@ -626,20 +613,20 @@ vjs.Player.prototype.enterFullWindow = function(){ this.docOrigOverflow = document.documentElement.style.overflow; // Add listener for esc key to exit fullscreen - vjs.on(document, "keydown", vjs.bind(this, this.fullWindowOnEscKey)); + vjs.on(document, 'keydown', vjs.bind(this, this.fullWindowOnEscKey)); // Hide any scroll bars document.documentElement.style.overflow = 'hidden'; // Apply fullscreen styles - vjs.addClass(document.body, "vjs-full-window"); - vjs.addClass(this.el_, "vjs-fullscreen"); + vjs.addClass(document.body, 'vjs-full-window'); + vjs.addClass(this.el_, 'vjs-fullscreen'); - this.trigger("enterFullWindow"); + this.trigger('enterFullWindow'); }; vjs.Player.prototype.fullWindowOnEscKey = function(event){ - if (event.keyCode == 27) { - if (this.isFullScreen == true) { + if (event.keyCode === 27) { + if (this.isFullScreen === true) { this.cancelFullScreen(); } else { this.exitFullWindow(); @@ -649,18 +636,18 @@ vjs.Player.prototype.fullWindowOnEscKey = function(event){ vjs.Player.prototype.exitFullWindow = function(){ this.isFullWindow = false; - vjs.off(document, "keydown", this.fullWindowOnEscKey); + vjs.off(document, 'keydown', this.fullWindowOnEscKey); // Unhide scroll bars. document.documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles - vjs.removeClass(document.body, "vjs-full-window"); - vjs.removeClass(this.el_, "vjs-fullscreen"); + vjs.removeClass(document.body, 'vjs-full-window'); + vjs.removeClass(this.el_, 'vjs-fullscreen'); // Resize the box, controller, and poster to original sizes // this.positionAll(); - this.trigger("exitFullWindow"); + this.trigger('exitFullWindow'); }; vjs.Player.prototype.selectSource = function(sources){ @@ -697,7 +684,6 @@ vjs.Player.prototype.src = function(source){ if (source instanceof Array) { var sourceTech = this.selectSource(source), - source, techName; if (sourceTech) { @@ -712,10 +698,10 @@ vjs.Player.prototype.src = function(source){ this.loadTech(techName, source); } } else { - vjs.log("No compatible source and media technology were found.") + vjs.log('No compatible source and media technology were found.'); } - // Case: Source object { src: "", type: "" ... } + // Case: Source object { src: '', type: '' ... } } else if (source instanceof Object) { if (window['videojs'][this.techName]['canPlaySource'](source)) { @@ -735,11 +721,11 @@ vjs.Player.prototype.src = function(source){ this.src(source); }); } else { - this.techCall("src", source); - if (this.options.preload == "auto") { + this.techCall('src', source); + if (this.options['preload'] == 'auto') { this.load(); } - if (this.options.autoplay) { + if (this.options['autoplay']) { this.play(); } } @@ -750,63 +736,63 @@ vjs.Player.prototype.src = function(source){ // Begin loading the src data // http://dev.w3.org/html5/spec/video.html#dom-media-load vjs.Player.prototype.load = function(){ - this.techCall("load"); + this.techCall('load'); return this; }; // http://dev.w3.org/html5/spec/video.html#dom-media-currentsrc vjs.Player.prototype.currentSrc = function(){ - return this.techGet("currentSrc") || this.cache_.src || ""; + return this.techGet('currentSrc') || this.cache_.src || ''; }; // Attributes/Options vjs.Player.prototype.preload = function(value){ if (value !== undefined) { - this.techCall("setPreload", value); - this.options.preload = value; + this.techCall('setPreload', value); + this.options['preload'] = value; return this; } - return this.techGet("preload"); + return this.techGet('preload'); }; vjs.Player.prototype.autoplay = function(value){ if (value !== undefined) { - this.techCall("setAutoplay", value); - this.options.autoplay = value; + this.techCall('setAutoplay', value); + this.options['autoplay'] = value; return this; } - return this.techGet("autoplay", value); + return this.techGet('autoplay', value); }; vjs.Player.prototype.loop = function(value){ if (value !== undefined) { - this.techCall("setLoop", value); - this.options.loop = value; + this.techCall('setLoop', value); + this.options['loop'] = value; return this; } - return this.techGet("loop"); + return this.techGet('loop'); }; -vjs.Player.prototype.controls = function(){ return this.options.controls; }; -vjs.Player.prototype.poster = function(){ return this.techGet("poster"); }; -vjs.Player.prototype.error = function(){ return this.techGet("error"); }; -vjs.Player.prototype.ended = function(){ return this.techGet("ended"); }; +vjs.Player.prototype.controls = function(){ return this.options['controls']; }; +vjs.Player.prototype.poster = function(){ return this.techGet('poster'); }; +vjs.Player.prototype.error = function(){ return this.techGet('error'); }; +vjs.Player.prototype.ended = function(){ return this.techGet('ended'); }; // Methods to add support for -// networkState: function(){ return this.techCall("networkState"); }, -// readyState: function(){ return this.techCall("readyState"); }, -// seeking: function(){ return this.techCall("seeking"); }, -// initialTime: function(){ return this.techCall("initialTime"); }, -// startOffsetTime: function(){ return this.techCall("startOffsetTime"); }, -// played: function(){ return this.techCall("played"); }, -// seekable: function(){ return this.techCall("seekable"); }, -// videoTracks: function(){ return this.techCall("videoTracks"); }, -// audioTracks: function(){ return this.techCall("audioTracks"); }, -// videoWidth: function(){ return this.techCall("videoWidth"); }, -// videoHeight: function(){ return this.techCall("videoHeight"); }, -// defaultPlaybackRate: function(){ return this.techCall("defaultPlaybackRate"); }, -// playbackRate: function(){ return this.techCall("playbackRate"); }, -// mediaGroup: function(){ return this.techCall("mediaGroup"); }, -// controller: function(){ return this.techCall("controller"); }, -// defaultMuted: function(){ return this.techCall("defaultMuted"); } +// networkState: function(){ return this.techCall('networkState'); }, +// readyState: function(){ return this.techCall('readyState'); }, +// seeking: function(){ return this.techCall('seeking'); }, +// initialTime: function(){ return this.techCall('initialTime'); }, +// startOffsetTime: function(){ return this.techCall('startOffsetTime'); }, +// played: function(){ return this.techCall('played'); }, +// seekable: function(){ return this.techCall('seekable'); }, +// videoTracks: function(){ return this.techCall('videoTracks'); }, +// audioTracks: function(){ return this.techCall('audioTracks'); }, +// videoWidth: function(){ return this.techCall('videoWidth'); }, +// videoHeight: function(){ return this.techCall('videoHeight'); }, +// defaultPlaybackRate: function(){ return this.techCall('defaultPlaybackRate'); }, +// playbackRate: function(){ return this.techCall('playbackRate'); }, +// mediaGroup: function(){ return this.techCall('mediaGroup'); }, +// controller: function(){ return this.techCall('controller'); }, +// defaultMuted: function(){ return this.techCall('defaultMuted'); } // TODO // currentSrcList: the array of sources including other formats and bitrates @@ -814,20 +800,16 @@ vjs.Player.prototype.ended = function(){ return this.techGet("ended"); }; // RequestFullscreen API (function(){ - var requestFn, - cancelFn, - eventName, - isFullScreen, - playerProto = vjs.Player.prototype; + var requestFn, cancelFn, eventName, isFullScreen; // Current W3C Spec // http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#api // Mozilla Draft: https://wiki.mozilla.org/Gecko:FullScreenAPI#fullscreenchange_event if (document.cancelFullscreen !== undefined) { - requestFn = "requestFullscreen"; - cancelFn = "exitFullscreen"; - eventName = "fullscreenchange"; - isFullScreen = "fullScreen"; + requestFn = 'requestFullscreen'; + cancelFn = 'exitFullscreen'; + eventName = 'fullscreenchange'; + isFullScreen = 'fullScreen'; // Webkit (Chrome/Safari) and Mozilla (Firefox) have working implementaitons // that use prefixes and vary slightly from the new W3C spec. Specifically, using 'exit' instead of 'cancel', @@ -835,24 +817,24 @@ vjs.Player.prototype.ended = function(){ return this.techGet("ended"); }; // Other browsers don't have any hints of which version they might follow yet, so not going to try to predict by loopeing through all prefixes. } else { - var prefixes = ["moz", "webkit"]; + var prefixes = ['moz', 'webkit']; for (var i = prefixes.length - 1; i >= 0; i--) { - var prefix = prefixes[i] + var prefix = prefixes[i]; // https://github.com/zencoder/video-js/pull/128 - if ((prefix != "moz" || document.mozFullScreenEnabled) && document[prefix + "CancelFullScreen"] !== undefined) { - requestFn = prefix + "RequestFullScreen"; - cancelFn = prefix + "CancelFullScreen"; - eventName = prefix + "fullscreenchange"; + if ((prefix != 'moz' || document.mozFullScreenEnabled) && document[prefix + 'CancelFullScreen'] !== undefined) { + requestFn = prefix + 'RequestFullScreen'; + cancelFn = prefix + 'CancelFullScreen'; + eventName = prefix + 'fullscreenchange'; - if (prefix == "webkit") { - isFullScreen = prefix + "IsFullScreen"; + if (prefix == 'webkit') { + isFullScreen = prefix + 'IsFullScreen'; } else { - isFullScreen = prefix + "FullScreen"; + isFullScreen = prefix + 'FullScreen'; } } - }; + } } if (requestFn) { @@ -874,7 +856,7 @@ vjs.MediaLoader = function(player, options, ready){ // If there are no sources when the player is initialized, // load the first supported playback technology. - if (!player.options.sources || player.options.sources.length == 0) { + if (!player.options['sources'] || player.options['sources'].length === 0) { for (var i=0,j=player.options['techOrder']; i 0) { @@ -10,12 +15,12 @@ vjs.autoSetup = function(){ vid = vids[i]; // Check if element exists, has getAttribute func. - // IE seems to consider typeof el.getAttribute == "object" instead of "function" like expected, at least when loading the player immediately. + // IE seems to consider typeof el.getAttribute == 'object' instead of 'function' like expected, at least when loading the player immediately. if (vid && vid.getAttribute) { // Make sure this player hasn't already been set up. if (vid.player === undefined) { - options = vid.getAttribute("data-setup"); + options = vid.getAttribute('data-setup'); // Check if data-setup attr exists. // We only auto-setup if they've added the data-setup attr. @@ -23,7 +28,7 @@ vjs.autoSetup = function(){ // Parse options JSON // If empty string, make it a parsable json object. - options = vjs.JSON.parse(options || "{}"); + options = vjs.JSON.parse(options || '{}'); // Create new video.js instance. player = videojs(vid, options); @@ -48,7 +53,7 @@ vjs.autoSetupTimeout = function(wait){ setTimeout(vjs.autoSetup, wait); }; -vjs.one(window, "load", function(){ +vjs.one(window, 'load', function(){ vjs.windowLoaded = true; }); diff --git a/src/tracks.js b/src/tracks.js index 95508461f..7b35ff090 100644 --- a/src/tracks.js +++ b/src/tracks.js @@ -1,47 +1,89 @@ -// TEXT TRACKS -// Text tracks are tracks of timed text events. -// Captions - text displayed over the video for the hearing impared -// Subtitles - text displayed over the video for those who don't understand langauge in the video -// Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video -// Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device +/** + * @fileoverview Text Tracks + * Text tracks are tracks of timed text events. + * Captions - text displayed over the video for the hearing impared + * Subtitles - text displayed over the video for those who don't understand langauge in the video + * Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video + * Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device + */ -// Player Track Functions - Functions add to the player object for easier access to tracks -// Add an array of text tracks. captions, subtitles, chapters, descriptions -// Track objects will be stored in the player.textTracks array -vjs.Player.prototype.addTextTracks = function(trackObjects){ - var track, Kind, - tracks = this.textTracks = this.textTracks || [], - i = 0, - j = trackObjects.length; +// Player Additions - Functions add to the player object for easier access to tracks - for (;i 0) { + } else if (disableSameKind && track.kind() == disableSameKind && track.mode() > 0) { track.disable(); } } // Get track kind from shown track or disableSameKind - kind = (showTrack) ? showTrack.kind : ((disableSameKind) ? disableSameKind : false); + kind = (showTrack) ? showTrack.kind() : ((disableSameKind) ? disableSameKind : false); // Trigger trackchange event, captionstrackchange, subtitlestrackchange, etc. if (kind) { - this.trigger(kind+"trackchange"); + this.trigger(kind+'trackchange'); } return this; @@ -77,140 +119,277 @@ vjs.Player.prototype.showTextTrack = function(id, disableSameKind){ * @param {Object=} options * @constructor */ -vjs.Track = function(player, options){ +vjs.TextTrack = function(player, options){ goog.base(this, player, options); // Apply track info to track object // Options will often be a track element - vjs.merge(this, { - // Build ID if one doesn't exist - id: options.id || ("vjs_" + options.kind + "_" + options.language + "_" + vjs.guid++), - src: options.src, - - // If default is used, subtitles/captions to start showing - "default": options["default"], // 'default' is reserved-ish - title: options.title, - - // Language - two letter string to represent track language, e.g. "en" for English - // readonly attribute DOMString language; - language: options.srclang, - - // Track label e.g. "English" - // readonly attribute DOMString label; - label: options.label, - - // All cues of the track. Cues have a startTime, endTime, text, and other properties. - // readonly attribute TextTrackCueList cues; - cues: [], - - // ActiveCues is all cues that are currently showing - // readonly attribute TextTrackCueList activeCues; - activeCues: [], - - // ReadyState describes if the text file has been loaded - // const unsigned short NONE = 0; - // const unsigned short LOADING = 1; - // const unsigned short LOADED = 2; - // const unsigned short ERROR = 3; - // readonly attribute unsigned short readyState; - readyState: 0, - - // Mode describes if the track is showing, hidden, or disabled - // const unsigned short OFF = 0; - // const unsigned short HIDDEN = 1; (still triggering cuechange events, but not visible) - // const unsigned short SHOWING = 2; - // attribute unsigned short mode; - mode: 0 - }); + // Build ID if one doesn't exist + this.id_ = options['id'] || ('vjs_' + options['kind'] + '_' + options['language'] + '_' + vjs.guid++); + this.src_ = options['src']; + // 'default' is a reserved keyword in js so we use an abbreviated version + this.dflt_ = options['default'] || options['dflt']; + this.title_ = options['title']; + this.language_ = options['srclang']; + this.label_ = options['label']; + this.cues_ = []; + this.activeCues_ = []; + this.readyState_ = 0; + this.mode_ = 0; }; -goog.inherits(vjs.Track, vjs.Component); +goog.inherits(vjs.TextTrack, vjs.Component); - // Create basic div to hold cue text -vjs.Track.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-" + this.kind + " vjs-text-track" +/** + * Track kind value. Captions, subtitles, etc. + * @private + */ +vjs.TextTrack.prototype.kind_; + +/** + * Get the track kind value + * @return {String} + */ +vjs.TextTrack.prototype.kind = function(){ + return this.kind_; +}; + +/** + * Track src value + * @private + */ +vjs.TextTrack.prototype.src_; + +/** + * Get the track src value + * @return {String} + */ +vjs.TextTrack.prototype.src = function(){ + return this.src_; +}; + +/** + * Track default value + * If default is used, subtitles/captions to start showing + * @private + */ +vjs.TextTrack.prototype.dflt_; + +/** + * Get the track default value + * 'default' is a reserved keyword + * @return {Boolean} + */ +vjs.TextTrack.prototype.dflt = function(){ + return this.dflt_; +}; + +/** + * Track title value + * @private + */ +vjs.TextTrack.prototype.title_; + +/** + * Get the track title value + * @return {String} + */ +vjs.TextTrack.prototype.title = function(){ + return this.title_; +}; + +/** + * Language - two letter string to represent track language, e.g. 'en' for English + * Spec def: readonly attribute DOMString language; + * @private + */ +vjs.TextTrack.prototype.language_; + +/** + * Get the track language value + * @return {String} + */ +vjs.TextTrack.prototype.language = function(){ + return this.language_; +}; + +/** + * Track label e.g. 'English' + * Spec def: readonly attribute DOMString label; + * @private + */ +vjs.TextTrack.prototype.label_; + +/** + * Get the track label value + * @return {String} + */ +vjs.TextTrack.prototype.label = function(){ + return this.label_; +}; + +/** + * All cues of the track. Cues have a startTime, endTime, text, and other properties. + * Spec def: readonly attribute TextTrackCueList cues; + * @private + */ +vjs.TextTrack.prototype.cues_; + +/** + * Get the track cues + * @return {Array} + */ +vjs.TextTrack.prototype.cues = function(){ + return this.cues_; +}; + +/** + * ActiveCues is all cues that are currently showing + * Spec def: readonly attribute TextTrackCueList activeCues; + * @private + */ +vjs.TextTrack.prototype.activeCues_; + +/** + * Get the track active cues + * @return {Array} + */ +vjs.TextTrack.prototype.activeCues = function(){ + return this.activeCues_; +}; + +/** + * ReadyState describes if the text file has been loaded + * const unsigned short NONE = 0; + * const unsigned short LOADING = 1; + * const unsigned short LOADED = 2; + * const unsigned short ERROR = 3; + * readonly attribute unsigned short readyState; + * @private + */ +vjs.TextTrack.prototype.readyState_; + +/** + * Get the track readyState + * @return {Number} + */ +vjs.TextTrack.prototype.readyState = function(){ + return this.readyState_; +}; + +/** + * Mode describes if the track is showing, hidden, or disabled + * const unsigned short OFF = 0; + * const unsigned short HIDDEN = 1; (still triggering cuechange events, but not visible) + * const unsigned short SHOWING = 2; + * attribute unsigned short mode; + * @private + */ +vjs.TextTrack.prototype.mode_; + +/** + * Get the track mode + * @return {Number} + */ +vjs.TextTrack.prototype.mode = function(){ + return this.mode_; +}; + +/** + * Create basic div to hold cue text + * @return {Element} + */ +vjs.TextTrack.prototype.createEl = function(){ + return goog.base(this, 'createEl', 'div', { + className: 'vjs-' + this.kind_ + ' vjs-text-track' }); }; -// Show: Mode Showing (2) -// Indicates that the text track is active. If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily. -// The user agent is maintaining a list of which cues are active, and events are being fired accordingly. -// In addition, for text tracks whose kind is subtitles or captions, the cues are being displayed over the video as appropriate; -// for text tracks whose kind is descriptions, the user agent is making the cues available to the user in a non-visual fashion; -// and for text tracks whose kind is chapters, the user agent is making available to the user a mechanism by which the user can navigate to any point in the media resource by selecting a cue. -// The showing by default state is used in conjunction with the default attribute on track elements to indicate that the text track was enabled due to that attribute. -// This allows the user agent to override the state if a later track is discovered that is more appropriate per the user's preferences. -vjs.Track.prototype.show = function(){ +/** + * Show: Mode Showing (2) + * Indicates that the text track is active. If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily. + * The user agent is maintaining a list of which cues are active, and events are being fired accordingly. + * In addition, for text tracks whose kind is subtitles or captions, the cues are being displayed over the video as appropriate; + * for text tracks whose kind is descriptions, the user agent is making the cues available to the user in a non-visual fashion; + * and for text tracks whose kind is chapters, the user agent is making available to the user a mechanism by which the user can navigate to any point in the media resource by selecting a cue. + * The showing by default state is used in conjunction with the default attribute on track elements to indicate that the text track was enabled due to that attribute. + * This allows the user agent to override the state if a later track is discovered that is more appropriate per the user's preferences. + */ +vjs.TextTrack.prototype.show = function(){ this.activate(); - this.mode = 2; + this.mode_ = 2; // Show element. goog.base(this, 'show'); }; -// Hide: Mode Hidden (1) -// Indicates that the text track is active, but that the user agent is not actively displaying the cues. -// If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily. -// The user agent is maintaining a list of which cues are active, and events are being fired accordingly. -vjs.Track.prototype.hide = function(){ +/** + * Hide: Mode Hidden (1) + * Indicates that the text track is active, but that the user agent is not actively displaying the cues. + * If no attempt has yet been made to obtain the track's cues, the user agent will perform such an attempt momentarily. + * The user agent is maintaining a list of which cues are active, and events are being fired accordingly. + */ +vjs.TextTrack.prototype.hide = function(){ // When hidden, cues are still triggered. Disable to stop triggering. this.activate(); - this.mode = 1; + this.mode_ = 1; // Hide element. goog.base(this, 'hide'); }; -// Disable: Mode Off/Disable (0) -// Indicates that the text track is not active. Other than for the purposes of exposing the track in the DOM, the user agent is ignoring the text track. -// No cues are active, no events are fired, and the user agent will not attempt to obtain the track's cues. -vjs.Track.prototype.disable = function(){ +/** + * Disable: Mode Off/Disable (0) + * Indicates that the text track is not active. Other than for the purposes of exposing the track in the DOM, the user agent is ignoring the text track. + * No cues are active, no events are fired, and the user agent will not attempt to obtain the track's cues. + */ +vjs.TextTrack.prototype.disable = function(){ // If showing, hide. - if (this.mode == 2) { this.hide(); } + if (this.mode_ == 2) { this.hide(); } // Stop triggering cues this.deactivate(); // Switch Mode to Off - this.mode = 0; + this.mode_ = 0; }; -// Turn on cue tracking. Tracks that are showing OR hidden are active. -vjs.Track.prototype.activate = function(){ +/** + * Turn on cue tracking. Tracks that are showing OR hidden are active. + */ +vjs.TextTrack.prototype.activate = function(){ // Load text file if it hasn't been yet. - if (this.readyState == 0) { this.load(); } + if (this.readyState_ === 0) { this.load(); } // Only activate if not already active. - if (this.mode == 0) { + if (this.mode_ === 0) { // Update current cue on timeupdate // Using unique ID for bind function so other tracks don't remove listener - this.player.on("timeupdate", vjs.bind(this, this.update, this.id)); + this.player.on('timeupdate', vjs.bind(this, this.update, this.id_)); // Reset cue time on media end - this.player.on("ended", vjs.bind(this, this.reset, this.id)); + this.player.on('ended', vjs.bind(this, this.reset, this.id_)); // Add to display - if (this.kind == "captions" || this.kind == "subtitles") { - - console.log('adsf', this.player.childNameIndex_); - - this.player.textTrackDisplay.addChild(this); + if (this.kind_ === 'captions' || this.kind_ === 'subtitles') { + this.player.getChild('textTrackDisplay').addChild(this); } } }; -// Turn off cue tracking. -vjs.Track.prototype.deactivate = function(){ +/** + * Turn off cue tracking. + */ +vjs.TextTrack.prototype.deactivate = function(){ // Using unique ID for bind function so other tracks don't remove listener - this.player.off("timeupdate", vjs.bind(this, this.update, this.id)); - this.player.off("ended", vjs.bind(this, this.reset, this.id)); + this.player.off('timeupdate', vjs.bind(this, this.update, this.id_)); + this.player.off('ended', vjs.bind(this, this.reset, this.id_)); this.reset(); // Reset // Remove from display - this.player.textTrackDisplay.removeChild(this); + this.player.getChild('textTrackDisplay').removeChild(this); }; // A readiness state @@ -227,29 +406,28 @@ vjs.Track.prototype.deactivate = function(){ // // Failed to load // Indicates that the text track was enabled, but when the user agent attempted to obtain it, this failed in some way (e.g. URL could not be resolved, network error, unknown text track format). Some or all of the cues are likely missing and will not be obtained. -vjs.Track.prototype.load = function(){ +vjs.TextTrack.prototype.load = function(){ // Only load if not loaded yet. - if (this.readyState == 0) { - this.readyState = 1; - console.log('track get method not supported yet') - // vjs.get(this.src, vjs.bind(this, this.parseCues), vjs.bind(this, this.onError)); + if (this.readyState_ === 0) { + this.readyState_ = 1; + vjs.get(this.src_, vjs.bind(this, this.parseCues), vjs.bind(this, this.onError)); } }; -vjs.Track.prototype.onError = function(err){ +vjs.TextTrack.prototype.onError = function(err){ this.error = err; - this.readyState = 3; - this.trigger("error"); + this.readyState_ = 3; + this.trigger('error'); }; // Parse the WebVTT text format for cue times. // TODO: Separate parser into own class so alternative timed text formats can be used. (TTML, DFXP) -vjs.Track.prototype.parseCues = function(srcContent) { +vjs.TextTrack.prototype.parseCues = function(srcContent) { var cue, time, text, - lines = srcContent.split("\n"), - line = "", id; + lines = srcContent.split('\n'), + line = '', id; for (var i=1, j=lines.length; i") == -1) { + if (line.indexOf('-->') == -1) { id = line; // Advance to next line for timing. line = vjs.trim(lines[++i]); } else { - id = this.cues.length; + id = this.cues_.length; } // First line - Number cue = { id: id, // Cue Number - index: this.cues.length // Position in Array + index: this.cues_.length // Position in Array }; // Timing line - time = line.split(" --> "); + time = line.split(' --> '); cue.startTime = this.parseCueTime(time[0]); cue.endTime = this.parseCueTime(time[1]); @@ -283,7 +461,7 @@ vjs.Track.prototype.parseCues = function(srcContent) { text = []; // Loop until a blank line or end of lines - // Assumeing trim("") returns false for blank lines + // Assumeing trim('') returns false for blank lines while (lines[++i] && (line = vjs.trim(lines[i]))) { text.push(line); } @@ -291,19 +469,19 @@ vjs.Track.prototype.parseCues = function(srcContent) { cue.text = text.join('
'); // Add this cue - this.cues.push(cue); + this.cues_.push(cue); } } - this.readyState = 2; - this.trigger("loaded"); + this.readyState_ = 2; + this.trigger('loaded'); }; -vjs.Track.prototype.parseCueTime = function(timeText) { +vjs.TextTrack.prototype.parseCueTime = function(timeText) { var parts = timeText.split(':'), time = 0, - hours, minutes, other, seconds, ms, flags; + hours, minutes, other, seconds, ms; // Check if optional hours place is included // 00:00:00.000 vs. 00:00.000 @@ -319,7 +497,7 @@ vjs.Track.prototype.parseCueTime = function(timeText) { // Break other (seconds, milliseconds, and flags) by spaces // TODO: Make additional cue layout settings work with flags - other = other.split(/\s+/) + other = other.split(/\s+/); // Remove seconds. Seconds is the first part before any spaces. seconds = other.splice(0,1)[0]; // Could use either . or , for decimal @@ -341,15 +519,15 @@ vjs.Track.prototype.parseCueTime = function(timeText) { }; // Update active cues whenever timeupdate events are triggered on the player. -vjs.Track.prototype.update = function(){ - if (this.cues.length > 0) { +vjs.TextTrack.prototype.update = function(){ + if (this.cues_.length > 0) { // Get curent player time var time = this.player.currentTime(); // Check if the new time is outside the time box created by the the last update. if (this.prevChange === undefined || time < this.prevChange || this.nextChange <= time) { - var cues = this.cues, + var cues = this.cues_, // Create a new time box for this state. newNextChange = this.player.duration(), // Start at beginning of the timeline @@ -360,9 +538,7 @@ vjs.Track.prototype.update = function(){ // Store where in the loop the current active cues are, to provide a smart starting point for the next loop. firstActiveIndex, lastActiveIndex, - - html = "", // Create cue text HTML to add to the display - cue, i, j; // Loop vars + cue, i; // Loop vars // Check if time is going forwards or backwards (scrubbing/rewinding) // If we know the direction we can optimize the starting position and direction of the loop through the cues array. @@ -437,7 +613,7 @@ vjs.Track.prototype.update = function(){ } - this.activeCues = newCues; + this.activeCues_ = newCues; this.nextChange = newNextChange; this.prevChange = newPrevChange; this.firstActiveIndex = firstActiveIndex; @@ -445,26 +621,26 @@ vjs.Track.prototype.update = function(){ this.updateDisplay(); - this.trigger("cuechange"); + this.trigger('cuechange'); } } }; // Add cue HTML to display -vjs.Track.prototype.updateDisplay = function(){ - var cues = this.activeCues, - html = "", +vjs.TextTrack.prototype.updateDisplay = function(){ + var cues = this.activeCues_, + html = '', i=0,j=cues.length; for (;i"; + html += ''+cues[i].text+''; } this.el_.innerHTML = html; }; // Set all loop helper values back -vjs.Track.prototype.reset = function(){ +vjs.TextTrack.prototype.reset = function(){ this.nextChange = 0; this.prevChange = this.player.duration(); this.firstActiveIndex = 0; @@ -478,8 +654,8 @@ vjs.Track.prototype.reset = function(){ vjs.CaptionsTrack = function(player, options, ready){ goog.base(this, player, options, ready); }; -goog.inherits(vjs.CaptionsTrack, vjs.Track); -vjs.CaptionsTrack.prototype.kind = "captions"; +goog.inherits(vjs.CaptionsTrack, vjs.TextTrack); +vjs.CaptionsTrack.prototype.kind_ = 'captions'; // Exporting here because Track creation requires the track kind // to be available on global object. e.g. new window['videojs'][Kind + 'Track'] @@ -489,8 +665,8 @@ vjs.CaptionsTrack.prototype.kind = "captions"; vjs.SubtitlesTrack = function(player, options, ready){ goog.base(this, player, options, ready); }; -goog.inherits(vjs.SubtitlesTrack, vjs.Track); -vjs.SubtitlesTrack.prototype.kind = "subtitles"; +goog.inherits(vjs.SubtitlesTrack, vjs.TextTrack); +vjs.SubtitlesTrack.prototype.kind_ = 'subtitles'; /** * @constructor @@ -498,8 +674,8 @@ vjs.SubtitlesTrack.prototype.kind = "subtitles"; vjs.ChaptersTrack = function(player, options, ready){ goog.base(this, player, options, ready); }; -goog.inherits(vjs.ChaptersTrack, vjs.Track); -vjs.ChaptersTrack.prototype.kind = "chapters"; +goog.inherits(vjs.ChaptersTrack, vjs.TextTrack); +vjs.ChaptersTrack.prototype.kind_ = 'chapters'; /* Text Track Display @@ -516,15 +692,15 @@ vjs.TextTrackDisplay = function(player, options, ready){ // if a track should show by default and the display hadn't loaded yet. // Should probably be moved to an external track loader when we support // tracks that don't need a display. - if (player.options.tracks && player.options.tracks.length > 0) { - this.player.addTextTracks(options.tracks); + if (player.options['tracks'] && player.options['tracks'].length > 0) { + this.player.addTextTracks(player.options['tracks']); } }; goog.inherits(vjs.TextTrackDisplay, vjs.Component); vjs.TextTrackDisplay.prototype.createEl = function(){ - return goog.base(this, 'createEl', "div", { - className: "vjs-text-track-display" + return goog.base(this, 'createEl', 'div', { + className: 'vjs-text-track-display' }); }; @@ -535,24 +711,24 @@ vjs.TextTrackDisplay.prototype.createEl = function(){ * @constructor */ vjs.TextTrackMenuItem = function(player, options){ - var track = this.track = options.track; + var track = this.track = options['track']; // Modify options for parent MenuItem class's init. - options.label = track.label; - options.selected = track["default"]; + options['label'] = track.label(); + options['selected'] = track.dflt(); goog.base(this, player, options); - this.player.on(track.kind + "trackchange", vjs.bind(this, this.update)); + this.player.on(track.kind() + 'trackchange', vjs.bind(this, this.update)); }; goog.inherits(vjs.TextTrackMenuItem, vjs.MenuItem); vjs.TextTrackMenuItem.prototype.onClick = function(){ goog.base(this, 'onClick'); - this.player.showTextTrack(this.track.id, this.track.kind); + this.player.showTextTrack(this.track.id(), this.track.kind()); }; vjs.TextTrackMenuItem.prototype.update = function(){ - if (this.track.mode == 2) { + if (this.track.mode() == 2) { this.selected(true); } else { this.selected(false); @@ -565,24 +741,30 @@ vjs.TextTrackMenuItem.prototype.update = function(){ vjs.OffTextTrackMenuItem = function(player, options){ // Create pseudo track info // Requires options.kind - options.track = { kind: options.kind, player: player, label: "Off" } + options['track'] = { + kind: function() { return options['kind']; }, + player: player, + label: function(){ return 'Off'; }, + dflt: function(){ return false; }, + mode: function(){ return false; } + }; goog.base(this, player, options); }; goog.inherits(vjs.OffTextTrackMenuItem, vjs.TextTrackMenuItem); vjs.OffTextTrackMenuItem.prototype.onClick = function(){ goog.base(this, 'onClick'); - this.player.showTextTrack(this.track.id, this.track.kind); + this.player.showTextTrack(this.track.id(), this.track.kind()); }; vjs.OffTextTrackMenuItem.prototype.update = function(){ - var tracks = this.player.textTracks, + var tracks = this.player.textTracks(), i=0, j=tracks.length, track, off = true; for (;i= 10; - // return swfobject.hasFlashPlayerVersion("10"); -}; - -_V_.flash.canPlaySource = function(srcObj){ - if (srcObj.type in _V_.flash.prototype.support.formats) { return "maybe"; } -}; - -_V_.flash.prototype.support = { - formats: { - "video/flv": "FLV", - "video/x-flv": "FLV", - "video/mp4": "MP4", - "video/m4v": "MP4" - }, - - // Optional events that we can manually mimic with timers - progressEvents: false, - timeupdateEvents: false, - - // Resizing plugins using request fullscreen reloads the plugin - fullscreenResize: false, - - // Resizing plugins in Firefox always reloads the plugin (e.g. full window mode) - parentResize: !(_V_.ua.match("Firefox")) -}; - -_V_.flash.onReady = function(currSwf){ - - var el = _V_.el(currSwf); - - // Get player from box - // On firefox reloads, el might already have a player - var player = el.player || el.parentNode.player, - tech = player.tech; - - // Reference player on tech element - el.player = player; - - // Update reference to playback technology element - tech.el = el; - - // Now that the element is ready, make a click on the swf play the video - tech.addEvent("click", tech.onClick); - - _V_.flash.checkReady(tech); -}; - -// The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object. -// If it's not ready, we set a timeout to check again shortly. -_V_.flash.checkReady = function(tech){ - - // Check if API property exists - if (tech.el.vjs_getProperty) { - - // If so, tell tech it's ready - tech.triggerReady(); - - // Otherwise wait longer. - } else { - - setTimeout(function(){ - _V_.flash.checkReady(tech); - }, 50); - - } -}; - -// Trigger events from the swf on the player -_V_.flash.onEvent = function(swfID, eventName){ - var player = _V_.el(swfID).player; - player.triggerEvent(eventName); -}; - -// Log errors from the swf -_V_.flash.onError = function(swfID, err){ - var player = _V_.el(swfID).player; - player.triggerEvent("error"); - _V_.log("Flash Error", err, swfID); -}; - -// Flash Version Check -_V_.flash.version = function(){ - var version = '0,0,0'; - - // IE - try { - version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; - - // other browsers - } catch(e) { - try { - if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){ - version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1]; - } - } catch(e) {} - } - return version.split(","); -}; - -// Flash embedding method. Only used in non-iframe mode -_V_.flash.embed = function(swf, placeHolder, flashVars, params, attributes){ - var code = _V_.flash.getEmbedCode(swf, flashVars, params, attributes), - - // Get element by embedding code and retrieving created element - obj = _V_.createElement("div", { innerHTML: code }).childNodes[0], - - par = placeHolder.parentNode - ; - - placeHolder.parentNode.replaceChild(obj, placeHolder); - - // IE6 seems to have an issue where it won't initialize the swf object after injecting it. - // This is a dumb temporary fix - if (_V_.IS_IE6to8()) { - var newObj = par.childNodes[0]; - setTimeout(function(){ - newObj.style.display = "block"; - }, 1000); - } - - return obj; - -}; - -_V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){ - - var objTag = ''; - }); - - attributes = _V_.merge({ - // Add swf to attributes (need both for IE and Others to work) - data: swf, - - // Default to 100% width/height - width: "100%", - height: "100%" - - }, attributes); - - // Create Attributes string - _V_.eachProp(attributes, function(key, val){ - attrsString += (key + '="' + val + '" '); - }); - - return objTag + attrsString + '>' + paramsString + ''; -}; diff --git a/tech/html5/html5.js b/tech/html5/html5.js deleted file mode 100644 index 109bedf76..000000000 --- a/tech/html5/html5.js +++ /dev/null @@ -1,228 +0,0 @@ -/* HTML5 Playback Technology - Wrapper for HTML5 Media API -================================================================================ */ -_V_.html5 = _V_.PlaybackTech.extend({ - - init: function(player, options, ready){ - this.player = player; - this.el = this.createElement(); - this.ready(ready); - - this.addEvent("click", this.proxy(this.onClick)); - - var source = options.source; - - // If the element source is already set, we may have missed the loadstart event, and want to trigger it. - // We don't want to set the source again and interrupt playback. - if (source && this.el.currentSrc == source.src) { - player.triggerEvent("loadstart"); - - // Otherwise set the source if one was provided. - } else if (source) { - this.el.src = source.src; - } - - // Chrome and Safari both have issues with autoplay. - // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work. - // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays) - // This fixes both issues. Need to wait for API, so it updates displays correctly - player.ready(function(){ - if (this.options.autoplay && this.paused()) { - this.tag.poster = null; // Chrome Fix. Fixed in Chrome v16. - this.play(); - } - }); - - this.setupTriggers(); - - this.triggerReady(); - }, - - destroy: function(){ - this.player.tag = false; - this.removeTriggers(); - this.el.parentNode.removeChild(this.el); - }, - - createElement: function(){ - var html5 = _V_.html5, - player = this.player, - - // If possible, reuse original tag for HTML5 playback technology element - el = player.tag, - newEl; - - // Check if this browser supports moving the element into the box. - // On the iPhone video will break if you move the element, - // So we have to create a brand new element. - if (!el || this.support.movingElementInDOM === false) { - - // If the original tag is still there, remove it. - if (el) { - player.el.removeChild(el); - } - - newEl = _V_.createElement("video", { - id: el.id || player.el.id + "_html5_api", - className: el.className || "vjs-tech" - }); - - el = newEl; - _V_.insertFirst(el, player.el); - } - - // Update tag settings, in case they were overridden - _V_.each(["autoplay","preload","loop","muted"], function(attr){ // ,"poster" - if (player.options[attr] !== null) { - el[attr] = player.options[attr]; - } - }, this); - - return el; - }, - - // Make video events trigger player events - // May seem verbose here, but makes other APIs possible. - setupTriggers: function(){ - _V_.each.call(this, _V_.html5.events, function(type){ - _V_.addEvent(this.el, type, _V_.proxy(this.player, this.eventHandler)); - }); - }, - removeTriggers: function(){ - _V_.each.call(this, _V_.html5.events, function(type){ - _V_.removeEvent(this.el, type, _V_.proxy(this.player, this.eventHandler)); - }); - }, - eventHandler: function(e){ - e.stopPropagation(); - this.triggerEvent(e); - }, - - play: function(){ this.el.play(); }, - pause: function(){ this.el.pause(); }, - paused: function(){ return this.el.paused; }, - - currentTime: function(){ return this.el.currentTime; }, - setCurrentTime: function(seconds){ - try { - this.el.currentTime = seconds; - } catch(e) { - _V_.log(e, "Video isn't ready. (VideoJS)"); - // this.warning(VideoJS.warnings.videoNotReady); - } - }, - - duration: function(){ return this.el.duration || 0; }, - buffered: function(){ return this.el.buffered; }, - - volume: function(){ return this.el.volume; }, - setVolume: function(percentAsDecimal){ this.el.volume = percentAsDecimal; }, - muted: function(){ return this.el.muted; }, - setMuted: function(muted){ this.el.muted = muted; }, - - width: function(){ return this.el.offsetWidth; }, - height: function(){ return this.el.offsetHeight; }, - - supportsFullScreen: function(){ - if (typeof this.el.webkitEnterFullScreen == 'function') { - - // Seems to be broken in Chromium/Chrome && Safari in Leopard - if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) { - return true; - } - } - return false; - }, - - enterFullScreen: function(){ - try { - this.el.webkitEnterFullScreen(); - } catch (e) { - if (e.code == 11) { - // this.warning(VideoJS.warnings.videoNotReady); - _V_.log("VideoJS: Video not ready."); - } - } - }, - exitFullScreen: function(){ - try { - this.el.webkitExitFullScreen(); - } catch (e) { - if (e.code == 11) { - // this.warning(VideoJS.warnings.videoNotReady); - _V_.log("VideoJS: Video not ready."); - } - } - }, - src: function(src){ this.el.src = src; }, - load: function(){ this.el.load(); }, - currentSrc: function(){ return this.el.currentSrc; }, - - preload: function(){ return this.el.preload; }, - setPreload: function(val){ this.el.preload = val; }, - autoplay: function(){ return this.el.autoplay; }, - setAutoplay: function(val){ this.el.autoplay = val; }, - loop: function(){ return this.el.loop; }, - setLoop: function(val){ this.el.loop = val; }, - - error: function(){ return this.el.error; }, - // networkState: function(){ return this.el.networkState; }, - // readyState: function(){ return this.el.readyState; }, - seeking: function(){ return this.el.seeking; }, - // initialTime: function(){ return this.el.initialTime; }, - // startOffsetTime: function(){ return this.el.startOffsetTime; }, - // played: function(){ return this.el.played; }, - // seekable: function(){ return this.el.seekable; }, - ended: function(){ return this.el.ended; }, - // videoTracks: function(){ return this.el.videoTracks; }, - // audioTracks: function(){ return this.el.audioTracks; }, - // videoWidth: function(){ return this.el.videoWidth; }, - // videoHeight: function(){ return this.el.videoHeight; }, - // textTracks: function(){ return this.el.textTracks; }, - // defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; }, - // playbackRate: function(){ return this.el.playbackRate; }, - // mediaGroup: function(){ return this.el.mediaGroup; }, - // controller: function(){ return this.el.controller; }, - controls: function(){ return this.player.options.controls; }, - defaultMuted: function(){ return this.el.defaultMuted; } -}); - -/* HTML5 Support Testing -------------------------------------------------------- */ - -_V_.html5.isSupported = function(){ - return !!document.createElement("video").canPlayType; -}; - -_V_.html5.canPlaySource = function(srcObj){ - return !!document.createElement("video").canPlayType(srcObj.type); - // TODO: Check Type - // If no Type, check ext - // Check Media Type -}; - -// List of all HTML5 events (various uses). -_V_.html5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(","); - -/* HTML5 Device Fixes ---------------------------------------------------------- */ - -_V_.html5.prototype.support = { - - // Support for tech specific full screen. (webkitEnterFullScreen, not requestFullscreen) - // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html - // Seems to be broken in Chromium/Chrome && Safari in Leopard - fullscreen: (typeof _V_.testVid.webkitEnterFullScreen !== undefined) ? (!_V_.ua.match("Chrome") && !_V_.ua.match("Mac OS X 10.5") ? true : false) : false, - - // In iOS, if you move a video element in the DOM, it breaks video playback. - movingElementInDOM: !_V_.isIOS() - -}; - -// Android -if (_V_.isAndroid()) { - - // Override Android 2.2 and less canPlayType method which is broken - if (_V_.androidVersion() < 3) { - document.createElement("video").constructor.prototype.canPlayType = function(type){ - return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : ""; - }; - } -} diff --git a/test/dev-compiled.html b/test/dev-compiled.html new file mode 100644 index 000000000..c5ed61946 --- /dev/null +++ b/test/dev-compiled.html @@ -0,0 +1,90 @@ + + + + + HTML5 Video Player + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/qunit-externs.js b/test/qunit-externs.js new file mode 100644 index 000000000..a8209449e --- /dev/null +++ b/test/qunit-externs.js @@ -0,0 +1,84 @@ +// https://github.com/lukeasrodgers/qunit-js-externs/blob/master/qunit-externs.js + +/** + * @param {string} name + * @param {Object=} lifecycle + */ +function module(name, lifecycle) {} + +/** + * @param {string} title + * @param {number|Function} expected + * @param {Function=} test_func + */ +function test(title, expected, test_func){} + +/** + * @param {string} name + * @param {number|Function} expected + * @param {Function=} test_func + */ +function asyncTest(name, expected, test_func){} + +/** + * @param {number} amount + */ +function expect(amount){} + +/** + * @param {*} state + * @param {string=} message + */ +function ok(state, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function equal(actual, expected, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function notEqual(actual, expected, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function deepEqual(actual, expected, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function notDeepEqual(actual, expected, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function strictEqual(actual, expected, message){} + +/** + * @param {*} actual + * @param {*} expected + * @param {string=} message + */ +function notStrictEqual(actual, expected, message){} + +/** + * @param {number=} increment + */ +function start(increment){} + +/** + * @param {number=} increment + */ +function stop(increment){} \ No newline at end of file diff --git a/test/src b/test/src new file mode 120000 index 000000000..5cd551cf2 --- /dev/null +++ b/test/src @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/test/unit-compiled.html b/test/unit-compiled.html new file mode 100644 index 000000000..8521ee700 --- /dev/null +++ b/test/unit-compiled.html @@ -0,0 +1,29 @@ + + + + Video.js Test Suite + + + + + + + + + + + + + + + + +
+

Bootstrap Plugin Test Suite

+

+

+
    +
    +
    + + diff --git a/test/unit.html b/test/unit.html index 231ad6f5e..e4c55f9e5 100644 --- a/test/unit.html +++ b/test/unit.html @@ -4,8 +4,8 @@ Video.js Test Suite - - + + @@ -14,27 +14,28 @@ + - - - + - + + + + - - - - - - - + + + + + +
    @@ -45,4 +46,4 @@
    - \ No newline at end of file + diff --git a/test/unit/component.js b/test/unit/component.js index 7e14a9523..2529d62aa 100644 --- a/test/unit/component.js +++ b/test/unit/component.js @@ -1,61 +1,67 @@ module("Component"); test('should create an element', function(){ - var comp = new _V_.Component({}, {}); + var comp = new vjs.Component({}, {}); - ok(comp.getEl().nodeName); + 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 _V_.Component({}, { - components: { + var comp = new vjs.Component({}, { + children: { 'component': true } }); - ok(comp.el.childNodes.length === 1); + ok(comp.children().length === 1); + ok(comp.el().childNodes.length === 1); }); -test('should add a child component', function(){ - var comp = new _V_.Component({}); +test('should dispose of component and children', function(){ + var comp = new vjs.Component({}); - comp.addChild("Component"); + // Add a child + var child = comp.addChild("Component"); + ok(comp.children().length === 1); - ok(comp.el.childNodes.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(); - - - -test('should show and hide an element', function(){ - var comp = new _V_.Component({}, {}); - - comp.hide(); - ok(comp.el.style.display === 'none'); - comp.show(); - ok(comp.el.style.display === 'block'); -}); - -test('should add and remove a CSS class', function(){ - var comp = new _V_.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); + 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 _V_.Component({}, {}); + 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(1); + expect(2); var testListener = function(){ ok(true, 'fired event once'); + ok(this === comp, 'listener has the component as context'); }; comp.on('test-event', testListener); @@ -65,7 +71,7 @@ test('should add and remove event listeners to element', function(){ }); test('should trigger a listener once using one()', function(){ - var comp = new _V_.Component({}, {}); + var comp = new vjs.Component({}, {}); expect(1); @@ -77,3 +83,64 @@ test('should trigger a listener once using one()', function(){ 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'); +}); diff --git a/test/unit/controls.js b/test/unit/controls.js deleted file mode 100644 index ca2694ed5..000000000 --- a/test/unit/controls.js +++ /dev/null @@ -1 +0,0 @@ -module("Controls"); \ No newline at end of file diff --git a/test/unit/core.js b/test/unit/core.js index cbe914c0b..4f0ae7246 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1 +1,29 @@ -module("Core"); \ No newline at end of file +module("Core"); + +test('should create a video tag and have access children in old IE', function(){ + var fixture = document.getElementById('qunit-fixture'); + + fixture.innerHTML += ""; + + 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 += ""; + + 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'); +}); diff --git a/test/unit/events.js b/test/unit/events.js index 2bf79e400..71bd048ab 100644 --- a/test/unit/events.js +++ b/test/unit/events.js @@ -1 +1,74 @@ -module("Events"); \ No newline at end of file +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. +}); diff --git a/test/unit/lib.js b/test/unit/lib.js index 85a9757f0..910d130c1 100644 --- a/test/unit/lib.js +++ b/test/unit/lib.js @@ -1,69 +1,184 @@ module("Lib"); -test('should merge two objects', function(){ - var obj1 = { a:1, b:2 }; - var obj2 = { b:3, c:4 }; - - _V_.merge(obj1, obj2); - - deepEqual(obj1, {a:1,b:3,c:4} ); +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 create an element with attributes', function(){ - var el = _V_.createElement('div', { className: 'test-class', 'data-test': 'asdf' }) - ok(el.className === 'test-class'); - ok(el.getAttribute('data-test') === 'asdf' ); +test('should make a string start with an uppercase letter', function(){ + var foo = vjs.capitalize('bar') + ok(foo === 'Bar'); }); -test('should insert an element first', function(){ +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 = ''; + tags += '