mirror of
https://github.com/videojs/video.js.git
synced 2025-03-25 22:01:11 +02:00
Close GH-425: Backing out goog.inherits and adding back the previous inheritance API. fixes #415.
This commit is contained in:
parent
3a32f44f23
commit
2138d4f2a0
@ -32,12 +32,12 @@ module.exports = function(grunt) {
|
||||
},
|
||||
minify: {
|
||||
source:{
|
||||
src: ['build/files/combined.video.js'],
|
||||
src: ['build/files/combined.video.js', 'build/compiler/goog.base.js', 'src/js/exports.js'],
|
||||
externs: ['src/js/media.flash.externs.js'],
|
||||
dest: 'build/files/minified.video.js'
|
||||
},
|
||||
tests: {
|
||||
src: ['build/files/combined.video.js', 'test/unit/*.js', '!test/unit/api.js'],
|
||||
src: ['build/files/combined.video.js', 'build/compiler/goog.base.js', 'src/js/exports.js', 'test/unit/*.js', '!test/unit/api.js'],
|
||||
externs: ['src/js/media.flash.externs.js', 'test/qunit/qunit-externs.js'],
|
||||
dest: 'build/files/test.minified.video.js'
|
||||
}
|
||||
|
@ -15,21 +15,27 @@
|
||||
|
||||
// ADD NEW SOURCE FILES HERE
|
||||
var sourceFiles = [
|
||||
"src/js/goog.base.js",
|
||||
"src/js/core.js",
|
||||
"src/js/core-object.js",
|
||||
"src/js/events.js",
|
||||
"src/js/lib.js",
|
||||
"src/js/component.js",
|
||||
"src/js/button.js",
|
||||
"src/js/slider.js",
|
||||
"src/js/menu.js",
|
||||
"src/js/player.js",
|
||||
"src/js/controls.js",
|
||||
"src/js/poster.js",
|
||||
"src/js/loading-spinner.js",
|
||||
"src/js/big-play-button.js",
|
||||
"src/js/media.js",
|
||||
"src/js/media.html5.js",
|
||||
"src/js/media.flash.js",
|
||||
"src/js/media.loader.js",
|
||||
"src/js/tracks.js",
|
||||
"src/js/json.js",
|
||||
"src/js/setup.js",
|
||||
"src/js/plugins.js",
|
||||
"src/js/exports.js"
|
||||
"src/js/plugins.js"
|
||||
];
|
||||
|
||||
// Allow overriding the default project root
|
||||
|
34
src/js/big-play-button.js
Normal file
34
src/js/big-play-button.js
Normal file
@ -0,0 +1,34 @@
|
||||
/* Big Play Button
|
||||
================================================================================ */
|
||||
/**
|
||||
* Initial play button. Shows before the video has played.
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.BigPlayButton = vjs.Button.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
player.on('play', vjs.bind(this, this.hide));
|
||||
// player.on('ended', vjs.bind(this, this.show));
|
||||
}
|
||||
});
|
||||
|
||||
vjs.BigPlayButton.prototype.createEl = function(){
|
||||
return vjs.Button.prototype.createEl.call(this, 'div', {
|
||||
className: 'vjs-big-play-button',
|
||||
innerHTML: '<span></span>',
|
||||
'aria-label': 'play video'
|
||||
});
|
||||
};
|
||||
|
||||
vjs.BigPlayButton.prototype.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();
|
||||
};
|
74
src/js/button.js
Normal file
74
src/js/button.js
Normal file
@ -0,0 +1,74 @@
|
||||
/* Button - Base class for all buttons
|
||||
================================================================================ */
|
||||
/**
|
||||
* Base class for all buttons
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Button = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
|
||||
var touchstart = false;
|
||||
this.on('touchstart', function() {
|
||||
touchstart = true;
|
||||
});
|
||||
this.on('touchmove', function() {
|
||||
touchstart = false;
|
||||
});
|
||||
var self = this;
|
||||
this.on('touchend', function(event) {
|
||||
if (touchstart) {
|
||||
self.onClick(event);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
this.on('click', this.onClick);
|
||||
this.on('focus', this.onFocus);
|
||||
this.on('blur', this.onBlur);
|
||||
}
|
||||
});
|
||||
|
||||
vjs.Button.prototype.createEl = function(type, props){
|
||||
// Add standard Aria and Tabindex info
|
||||
props = vjs.obj.merge({
|
||||
className: this.buildCSSClass(),
|
||||
innerHTML: '<div><span class="vjs-control-text">' + (this.buttonText || 'Need Text') + '</span></div>',
|
||||
role: 'button',
|
||||
'aria-live': 'polite', // let the screen reader user know that the text of the button may change
|
||||
tabIndex: 0
|
||||
}, props);
|
||||
|
||||
return vjs.Component.prototype.createEl.call(this, type, props);
|
||||
};
|
||||
|
||||
vjs.Button.prototype.buildCSSClass = function(){
|
||||
// TODO: Change vjs-control to vjs-button?
|
||||
return 'vjs-control ' + vjs.Component.prototype.buildCSSClass.call(this);
|
||||
};
|
||||
|
||||
// Click - Override with specific functionality for button
|
||||
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));
|
||||
};
|
||||
|
||||
// KeyPress (document level) - Trigger click when keys are pressed
|
||||
vjs.Button.prototype.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
|
||||
vjs.Button.prototype.onBlur = function(){
|
||||
vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress));
|
||||
};
|
@ -9,34 +9,37 @@
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Component = function(player, options, ready){
|
||||
this.player_ = player;
|
||||
vjs.Component = vjs.CoreObject.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
this.player_ = player;
|
||||
|
||||
// Make a copy of prototype.options_ to protect against overriding global defaults
|
||||
this.options_ = vjs.obj.copy(this.options_);
|
||||
// Make a copy of prototype.options_ to protect against overriding global defaults
|
||||
this.options_ = vjs.obj.copy(this.options_);
|
||||
|
||||
// Updated options with supplied options
|
||||
options = this.options(options);
|
||||
// Updated options with supplied options
|
||||
options = 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++ );
|
||||
// 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.name_ = options['name'] || null;
|
||||
this.name_ = options['name'] || null;
|
||||
|
||||
// Create element if one wasn't provided in options
|
||||
this.el_ = options['el'] || this.createEl();
|
||||
// Create element if one wasn't provided in options
|
||||
this.el_ = options['el'] || this.createEl();
|
||||
|
||||
this.children_ = [];
|
||||
this.childIndex_ = {};
|
||||
this.childNameIndex_ = {};
|
||||
this.children_ = [];
|
||||
this.childIndex_ = {};
|
||||
this.childNameIndex_ = {};
|
||||
|
||||
// Add any child components in options
|
||||
this.initChildren();
|
||||
// Add any child components in options
|
||||
this.initChildren();
|
||||
|
||||
this.ready(ready);
|
||||
// Don't want to trigger ready here or it will before init is actually
|
||||
// finished for all children that run this constructor
|
||||
};
|
||||
this.ready(ready);
|
||||
// Don't want to trigger ready here or it will before init is actually
|
||||
// finished for all children that run this constructor
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Dispose of the component and all child components.
|
||||
@ -334,7 +337,6 @@ vjs.Component.prototype.initChildren = function(){
|
||||
|
||||
// Loop through components and add them to the player
|
||||
vjs.obj.each(options['children'], function(name, opts){
|
||||
|
||||
// Allow for disabling default components
|
||||
// e.g. vjs.options['children']['posterImage'] = false
|
||||
if (opts === false) return;
|
||||
|
830
src/js/controls.js
vendored
830
src/js/controls.js
vendored
File diff suppressed because it is too large
Load Diff
74
src/js/core-object.js
Normal file
74
src/js/core-object.js
Normal file
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Core Object/Class for objects that use inheritance + contstructors
|
||||
* @constructor
|
||||
*/
|
||||
vjs.CoreObject = vjs['CoreObject'] = function(){};
|
||||
// Manually exporting vjs['CoreObject'] here for Closure Compiler
|
||||
// because of the use of the extend/create class methods
|
||||
// If we didn't do this, those functions would get flattend to something like
|
||||
// `a = ...` and `this.prototype` would refer to the global object instead of
|
||||
// CoreObject
|
||||
|
||||
/**
|
||||
* Create a new object that inherits from this Object
|
||||
* @param {Object} props Functions and properties to be applied to the
|
||||
* new object's prototype
|
||||
* @return {vjs.CoreObject} Returns an object that inherits from CoreObject
|
||||
* @this {*}
|
||||
*/
|
||||
vjs.CoreObject.extend = function(props){
|
||||
var init, subObj;
|
||||
|
||||
props = props || {};
|
||||
// Set up the constructor using the supplied init method
|
||||
// or using the init of the parent object
|
||||
init = props.init || this.prototype.init || function(){};
|
||||
// In Resig's simple class inheritance (previously used) the constructor
|
||||
// is a function that calls `this.init.apply(arguments)`
|
||||
// However that would prevent us from using `ParentObject.call(this);`
|
||||
// in a Child constuctor because the `this` in `this.init`
|
||||
// would still refer to the Child and cause an inifinite loop.
|
||||
// We would instead have to do
|
||||
// `ParentObject.prototype.init.apply(this, argumnents);`
|
||||
// Bleh. We're not creating a _super() function, so it's good to keep
|
||||
// the parent constructor reference simple.
|
||||
subObj = function(){
|
||||
init.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Inherit from this object's prototype
|
||||
subObj.prototype = vjs.obj.create(this.prototype);
|
||||
// Reset the constructor property for subObj otherwise
|
||||
// instances of subObj would have the constructor of the parent Object
|
||||
subObj.prototype.constructor = subObj;
|
||||
|
||||
// Make the class extendable
|
||||
subObj.extend = vjs.CoreObject.extend;
|
||||
// Make a function for creating instances
|
||||
subObj.create = vjs.CoreObject.create;
|
||||
|
||||
// Extend subObj's prototype with functions and other properties from props
|
||||
for (var name in props) {
|
||||
if (props.hasOwnProperty(name)) {
|
||||
subObj.prototype[name] = props[name];
|
||||
}
|
||||
}
|
||||
|
||||
return subObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new instace of this Object class
|
||||
* @return {vjs.CoreObject} Returns an instance of a CoreObject subclass
|
||||
* @this {*}
|
||||
*/
|
||||
vjs.CoreObject.create = function(){
|
||||
// Create a new object that inherits from this object's prototype
|
||||
var inst = vjs.obj.create(this.prototype);
|
||||
|
||||
// Apply this constructor function to the new object
|
||||
this.apply(inst, arguments);
|
||||
|
||||
// Return the new object
|
||||
return inst;
|
||||
};
|
@ -33,6 +33,9 @@ goog.exportSymbol('videojs.options', vjs.options);
|
||||
// Allow external components to use global cache
|
||||
goog.exportSymbol('videojs.cache', vjs.cache);
|
||||
|
||||
// goog.exportSymbol('videojs.CoreObject', vjs.CoreObject);
|
||||
// goog.exportProperty(vjs.CoreObject, 'create', vjs.CoreObject.create);
|
||||
|
||||
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);
|
||||
@ -57,11 +60,8 @@ goog.exportProperty(vjs.Player.prototype, 'dispose', vjs.Player.prototype.dispos
|
||||
goog.exportSymbol('videojs.MediaLoader', vjs.MediaLoader);
|
||||
goog.exportSymbol('videojs.TextTrackDisplay', vjs.TextTrackDisplay);
|
||||
|
||||
goog.exportSymbol('videojs.Control', vjs.Control);
|
||||
goog.exportSymbol('videojs.ControlBar', vjs.ControlBar);
|
||||
goog.exportSymbol('videojs.Button', vjs.Button);
|
||||
goog.exportSymbol('videojs.PlayButton', vjs.PlayButton);
|
||||
goog.exportSymbol('videojs.PauseButton', vjs.PauseButton);
|
||||
goog.exportSymbol('videojs.PlayToggle', vjs.PlayToggle);
|
||||
goog.exportSymbol('videojs.FullscreenToggle', vjs.FullscreenToggle);
|
||||
goog.exportSymbol('videojs.BigPlayButton', vjs.BigPlayButton);
|
||||
|
@ -46,6 +46,23 @@ vjs.capitalize = function(string){
|
||||
*/
|
||||
vjs.obj = {};
|
||||
|
||||
/**
|
||||
* Object.create shim for prototypal inheritance.
|
||||
* https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
|
||||
* @param {Object} obj Object to use as prototype
|
||||
*/
|
||||
vjs.obj.create = Object.create || function(obj){
|
||||
//Create a new function called 'F' which is just an empty object.
|
||||
function F() {}
|
||||
|
||||
//the prototype of the 'F' function should point to the
|
||||
//parameter of the anonymous function.
|
||||
F.prototype = obj;
|
||||
|
||||
//create a new constructor function based off of the 'F' function.
|
||||
return new F();
|
||||
};
|
||||
|
||||
/**
|
||||
* Loop through each property in an object and call a function
|
||||
* whose arguments are (key,value)
|
||||
|
55
src/js/loading-spinner.js
Normal file
55
src/js/loading-spinner.js
Normal file
@ -0,0 +1,55 @@
|
||||
/* Loading Spinner
|
||||
================================================================================ */
|
||||
/**
|
||||
* Loading spinner for waiting events
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.LoadingSpinner = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(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('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));
|
||||
}
|
||||
});
|
||||
|
||||
vjs.LoadingSpinner.prototype.createEl = function(){
|
||||
var classNameSpinner, innerHtmlSpinner;
|
||||
|
||||
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 = '<div class="ball1"></div><div class="ball2"></div><div class="ball3"></div><div class="ball4"></div><div class="ball5"></div><div class="ball6"></div><div class="ball7"></div><div class="ball8"></div>';
|
||||
} else {
|
||||
classNameSpinner = 'vjs-loading-spinner-fallback';
|
||||
innerHtmlSpinner = '';
|
||||
}
|
||||
|
||||
return vjs.Component.prototype.createEl.call(this, 'div', {
|
||||
className: classNameSpinner,
|
||||
innerHTML: innerHtmlSpinner
|
||||
});
|
||||
};
|
@ -11,209 +11,211 @@
|
||||
* @param {Function=} ready
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Flash = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
vjs.Flash = vjs.MediaTechController.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.MediaTechController.call(this, player, options, ready);
|
||||
|
||||
var source = options['source'],
|
||||
var source = options['source'],
|
||||
|
||||
// Which element to embed in
|
||||
parentEl = options['parentEl'],
|
||||
// Which element to embed in
|
||||
parentEl = options['parentEl'],
|
||||
|
||||
// Create a temporary element to be replaced by swf object
|
||||
placeHolder = this.el_ = vjs.createEl('div', { id: player.id() + '_temp_flash' }),
|
||||
// Create a temporary element to be replaced by swf object
|
||||
placeHolder = this.el_ = vjs.createEl('div', { id: player.id() + '_temp_flash' }),
|
||||
|
||||
// Generate ID for swf object
|
||||
objId = player.id()+'_flash_api',
|
||||
// Generate ID for swf object
|
||||
objId = player.id()+'_flash_api',
|
||||
|
||||
// Store player options in local var for optimization
|
||||
// TODO: switch to using player methods instead of options
|
||||
// e.g. player.autoplay();
|
||||
playerOptions = player.options_,
|
||||
// Store player options in local var for optimization
|
||||
// TODO: switch to using player methods instead of options
|
||||
// e.g. player.autoplay();
|
||||
playerOptions = player.options_,
|
||||
|
||||
// Merge default flashvars with ones passed in to init
|
||||
flashVars = vjs.obj.merge({
|
||||
// Merge default flashvars with ones passed in to init
|
||||
flashVars = vjs.obj.merge({
|
||||
|
||||
// SWF Callback Functions
|
||||
'readyFunction': 'videojs.Flash.onReady',
|
||||
'eventProxyFunction': 'videojs.Flash.onEvent',
|
||||
'errorEventProxyFunction': 'videojs.Flash.onError',
|
||||
// SWF Callback Functions
|
||||
'readyFunction': 'videojs.Flash.onReady',
|
||||
'eventProxyFunction': 'videojs.Flash.onEvent',
|
||||
'errorEventProxyFunction': 'videojs.Flash.onError',
|
||||
|
||||
// Player Settings
|
||||
'autoplay': playerOptions.autoplay,
|
||||
'preload': playerOptions.preload,
|
||||
'loop': playerOptions.loop,
|
||||
'muted': playerOptions.muted
|
||||
// Player Settings
|
||||
'autoplay': playerOptions.autoplay,
|
||||
'preload': playerOptions.preload,
|
||||
'loop': playerOptions.loop,
|
||||
'muted': playerOptions.muted
|
||||
|
||||
}, options['flashVars']),
|
||||
}, options['flashVars']),
|
||||
|
||||
// Merge default parames with ones passed in
|
||||
params = vjs.obj.merge({
|
||||
'wmode': 'opaque', // Opaque is needed to overlay controls, but can affect playback performance
|
||||
'bgcolor': '#000000' // Using bgcolor prevents a white flash when the object is loading
|
||||
}, options['params']),
|
||||
// Merge default parames with ones passed in
|
||||
params = vjs.obj.merge({
|
||||
'wmode': 'opaque', // Opaque is needed to overlay controls, but can affect playback performance
|
||||
'bgcolor': '#000000' // Using bgcolor prevents a white flash when the object is loading
|
||||
}, options['params']),
|
||||
|
||||
// Merge default attributes with ones passed in
|
||||
attributes = vjs.obj.merge({
|
||||
'id': objId,
|
||||
'name': objId, // Both ID and Name needed or swf to identifty itself
|
||||
'class': 'vjs-tech'
|
||||
}, options['attributes'])
|
||||
;
|
||||
// Merge default attributes with ones passed in
|
||||
attributes = vjs.obj.merge({
|
||||
'id': objId,
|
||||
'name': objId, // Both ID and Name needed or swf to identifty itself
|
||||
'class': 'vjs-tech'
|
||||
}, options['attributes'])
|
||||
;
|
||||
|
||||
// If source was supplied pass as a flash var.
|
||||
if (source) {
|
||||
flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
|
||||
}
|
||||
// If source was supplied pass as a flash var.
|
||||
if (source) {
|
||||
flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
|
||||
}
|
||||
|
||||
// Add placeholder to player div
|
||||
vjs.insertFirst(placeHolder, parentEl);
|
||||
// Add placeholder to player div
|
||||
vjs.insertFirst(placeHolder, parentEl);
|
||||
|
||||
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
|
||||
// This allows resetting the playhead when we catch the reload
|
||||
if (options['startTime']) {
|
||||
this.ready(function(){
|
||||
this.load();
|
||||
this.play();
|
||||
this.currentTime(options['startTime']);
|
||||
});
|
||||
}
|
||||
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
|
||||
// This allows resetting the playhead when we catch the reload
|
||||
if (options['startTime']) {
|
||||
this.ready(function(){
|
||||
this.load();
|
||||
this.play();
|
||||
this.currentTime(options['startTime']);
|
||||
});
|
||||
}
|
||||
|
||||
// Flash iFrame Mode
|
||||
// In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
|
||||
// - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
|
||||
// - Webkit when hiding the plugin
|
||||
// - Webkit and Firefox when using requestFullScreen on a parent element
|
||||
// Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
|
||||
// Issues that remain include hiding the element and requestFullScreen in Firefox specifically
|
||||
// Flash iFrame Mode
|
||||
// In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
|
||||
// - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
|
||||
// - Webkit when hiding the plugin
|
||||
// - Webkit and Firefox when using requestFullScreen on a parent element
|
||||
// Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
|
||||
// Issues that remain include hiding the element and requestFullScreen in Firefox specifically
|
||||
|
||||
// There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
|
||||
// Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
|
||||
// I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
|
||||
// Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
|
||||
// In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
|
||||
// The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
|
||||
// There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
|
||||
// Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
|
||||
// I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
|
||||
// Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
|
||||
// In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
|
||||
// The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
|
||||
|
||||
// NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
|
||||
// Firefox 9 throws a security error, unleess you call location.href right before doc.write.
|
||||
// Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
|
||||
// Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
|
||||
// NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
|
||||
// Firefox 9 throws a security error, unleess you call location.href right before doc.write.
|
||||
// Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
|
||||
// Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
|
||||
|
||||
if (options['iFrameMode'] === true && !vjs.IS_FIREFOX) {
|
||||
if (options['iFrameMode'] === true && !vjs.IS_FIREFOX) {
|
||||
|
||||
// Create iFrame with vjs-tech class so it's 100% width/height
|
||||
var iFrm = vjs.createEl('iframe', {
|
||||
'id': objId + '_iframe',
|
||||
'name': objId + '_iframe',
|
||||
'className': 'vjs-tech',
|
||||
'scrolling': 'no',
|
||||
'marginWidth': 0,
|
||||
'marginHeight': 0,
|
||||
'frameBorder': 0
|
||||
});
|
||||
|
||||
// Update ready function names in flash vars for iframe window
|
||||
flashVars['readyFunction'] = 'ready';
|
||||
flashVars['eventProxyFunction'] = 'events';
|
||||
flashVars['errorEventProxyFunction'] = 'errors';
|
||||
|
||||
// Tried multiple methods to get this to work in all browsers
|
||||
|
||||
// Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
|
||||
// The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
|
||||
// var newObj = vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
|
||||
// (in onload)
|
||||
// var temp = vjs.createEl('a', { id:'asdf', innerHTML: 'asdf' } );
|
||||
// iDoc.body.appendChild(temp);
|
||||
|
||||
// Tried embedding the flash object through javascript in the iframe source.
|
||||
// This works in webkit but still triggers the firefox security error
|
||||
// iFrm.src = 'javascript: document.write('"+vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes)+"');";
|
||||
|
||||
// Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
|
||||
// We should add an option to host the iframe locally though, because it could help a lot of issues.
|
||||
// iFrm.src = "iframe.html";
|
||||
|
||||
// Wait until iFrame has loaded to write into it.
|
||||
vjs.on(iFrm, 'load', vjs.bind(this, function(){
|
||||
|
||||
var iDoc,
|
||||
iWin = iFrm.contentWindow;
|
||||
|
||||
// The one working method I found was to use the iframe's document.write() to create the swf object
|
||||
// This got around the security issue in all browsers except firefox.
|
||||
// I did find a hack where if I call the iframe's window.location.href='', it would get around the security error
|
||||
// However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
|
||||
// Plus Firefox 3.6 didn't work no matter what I tried.
|
||||
// if (vjs.USER_AGENT.match('Firefox')) {
|
||||
// iWin.location.href = '';
|
||||
// }
|
||||
|
||||
// Get the iFrame's document depending on what the browser supports
|
||||
iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
|
||||
|
||||
// Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
|
||||
// Even tried adding /. that was mentioned in a browser security writeup
|
||||
// document.domain = document.domain+'/.';
|
||||
// iDoc.domain = document.domain+'/.';
|
||||
|
||||
// Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
|
||||
// iDoc.body.innerHTML = swfObjectHTML;
|
||||
|
||||
// Tried appending the object to the iframe doc's body. Security error in all browsers.
|
||||
// iDoc.body.appendChild(swfObject);
|
||||
|
||||
// Using document.write actually got around the security error that browsers were throwing.
|
||||
// Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
|
||||
// Not sure why that's a security issue, but apparently it is.
|
||||
iDoc.write(vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes));
|
||||
|
||||
// Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
|
||||
// So far no issues with swf ready event being called before it's set on the window.
|
||||
iWin['player'] = this.player_;
|
||||
|
||||
// Create swf ready function for iFrame window
|
||||
iWin['ready'] = vjs.bind(this.player_, function(currSwf){
|
||||
var el = iDoc.getElementById(currSwf),
|
||||
player = this,
|
||||
tech = player.tech;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el_ = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
vjs.on(el, 'click', tech.bind(tech.onClick));
|
||||
|
||||
// Make sure swf is actually ready. Sometimes the API isn't actually yet.
|
||||
vjs.Flash.checkReady(tech);
|
||||
// Create iFrame with vjs-tech class so it's 100% width/height
|
||||
var iFrm = vjs.createEl('iframe', {
|
||||
'id': objId + '_iframe',
|
||||
'name': objId + '_iframe',
|
||||
'className': 'vjs-tech',
|
||||
'scrolling': 'no',
|
||||
'marginWidth': 0,
|
||||
'marginHeight': 0,
|
||||
'frameBorder': 0
|
||||
});
|
||||
|
||||
// Create event listener for all swf events
|
||||
iWin['events'] = vjs.bind(this.player_, function(swfID, eventName){
|
||||
var player = this;
|
||||
if (player && player.techName === 'flash') {
|
||||
player.trigger(eventName);
|
||||
}
|
||||
});
|
||||
// Update ready function names in flash vars for iframe window
|
||||
flashVars['readyFunction'] = 'ready';
|
||||
flashVars['eventProxyFunction'] = 'events';
|
||||
flashVars['errorEventProxyFunction'] = 'errors';
|
||||
|
||||
// Create error listener for all swf errors
|
||||
iWin['errors'] = vjs.bind(this.player_, function(swfID, eventName){
|
||||
vjs.log('Flash Error', eventName);
|
||||
});
|
||||
// Tried multiple methods to get this to work in all browsers
|
||||
|
||||
}));
|
||||
// Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
|
||||
// The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
|
||||
// var newObj = vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
|
||||
// (in onload)
|
||||
// var temp = vjs.createEl('a', { id:'asdf', innerHTML: 'asdf' } );
|
||||
// iDoc.body.appendChild(temp);
|
||||
|
||||
// Replace placeholder with iFrame (it will load now)
|
||||
placeHolder.parentNode.replaceChild(iFrm, placeHolder);
|
||||
// Tried embedding the flash object through javascript in the iframe source.
|
||||
// This works in webkit but still triggers the firefox security error
|
||||
// iFrm.src = 'javascript: document.write('"+vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes)+"');";
|
||||
|
||||
// If not using iFrame mode, embed as normal object
|
||||
} else {
|
||||
vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
|
||||
// Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
|
||||
// We should add an option to host the iframe locally though, because it could help a lot of issues.
|
||||
// iFrm.src = "iframe.html";
|
||||
|
||||
// Wait until iFrame has loaded to write into it.
|
||||
vjs.on(iFrm, 'load', vjs.bind(this, function(){
|
||||
|
||||
var iDoc,
|
||||
iWin = iFrm.contentWindow;
|
||||
|
||||
// The one working method I found was to use the iframe's document.write() to create the swf object
|
||||
// This got around the security issue in all browsers except firefox.
|
||||
// I did find a hack where if I call the iframe's window.location.href='', it would get around the security error
|
||||
// However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
|
||||
// Plus Firefox 3.6 didn't work no matter what I tried.
|
||||
// if (vjs.USER_AGENT.match('Firefox')) {
|
||||
// iWin.location.href = '';
|
||||
// }
|
||||
|
||||
// Get the iFrame's document depending on what the browser supports
|
||||
iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
|
||||
|
||||
// Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
|
||||
// Even tried adding /. that was mentioned in a browser security writeup
|
||||
// document.domain = document.domain+'/.';
|
||||
// iDoc.domain = document.domain+'/.';
|
||||
|
||||
// Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
|
||||
// iDoc.body.innerHTML = swfObjectHTML;
|
||||
|
||||
// Tried appending the object to the iframe doc's body. Security error in all browsers.
|
||||
// iDoc.body.appendChild(swfObject);
|
||||
|
||||
// Using document.write actually got around the security error that browsers were throwing.
|
||||
// Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
|
||||
// Not sure why that's a security issue, but apparently it is.
|
||||
iDoc.write(vjs.Flash.getEmbedCode(options['swf'], flashVars, params, attributes));
|
||||
|
||||
// Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
|
||||
// So far no issues with swf ready event being called before it's set on the window.
|
||||
iWin['player'] = this.player_;
|
||||
|
||||
// Create swf ready function for iFrame window
|
||||
iWin['ready'] = vjs.bind(this.player_, function(currSwf){
|
||||
var el = iDoc.getElementById(currSwf),
|
||||
player = this,
|
||||
tech = player.tech;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el_ = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
vjs.on(el, 'click', tech.bind(tech.onClick));
|
||||
|
||||
// Make sure swf is actually ready. Sometimes the API isn't actually yet.
|
||||
vjs.Flash.checkReady(tech);
|
||||
});
|
||||
|
||||
// Create event listener for all swf events
|
||||
iWin['events'] = vjs.bind(this.player_, function(swfID, eventName){
|
||||
var player = this;
|
||||
if (player && player.techName === 'flash') {
|
||||
player.trigger(eventName);
|
||||
}
|
||||
});
|
||||
|
||||
// Create error listener for all swf errors
|
||||
iWin['errors'] = vjs.bind(this.player_, function(swfID, eventName){
|
||||
vjs.log('Flash Error', eventName);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// Replace placeholder with iFrame (it will load now)
|
||||
placeHolder.parentNode.replaceChild(iFrm, placeHolder);
|
||||
|
||||
// If not using iFrame mode, embed as normal object
|
||||
} else {
|
||||
vjs.Flash.embed(options['swf'], placeHolder, flashVars, params, attributes);
|
||||
}
|
||||
}
|
||||
};
|
||||
goog.inherits(vjs.Flash, vjs.MediaTechController);
|
||||
});
|
||||
|
||||
vjs.Flash.prototype.dispose = function(){
|
||||
goog.base(this, 'dispose');
|
||||
vjs.MediaTechController.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
vjs.Flash.prototype.play = function(){
|
||||
|
@ -9,8 +9,10 @@
|
||||
* @param {Function=} ready
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Html5 = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
vjs.Html5 = vjs.MediaTechController.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.MediaTechController.call(this, player, options, ready);
|
||||
|
||||
// volume cannot be changed from 1 on iOS
|
||||
this.features.volumeControl = vjs.Html5.canControlVolume();
|
||||
@ -20,37 +22,37 @@ vjs.Html5 = function(player, options, ready){
|
||||
|
||||
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.trigger('loadstart');
|
||||
// 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');
|
||||
|
||||
// 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();
|
||||
// Otherwise set the source if one was provided.
|
||||
} else if (source) {
|
||||
this.el_.src = source.src;
|
||||
}
|
||||
});
|
||||
|
||||
this.on('click', this.onClick);
|
||||
// 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.on('click', this.onClick);
|
||||
|
||||
this.triggerReady();
|
||||
};
|
||||
goog.inherits(vjs.Html5, vjs.MediaTechController);
|
||||
this.setupTriggers();
|
||||
|
||||
this.triggerReady();
|
||||
}
|
||||
});
|
||||
|
||||
vjs.Html5.prototype.dispose = function(){
|
||||
goog.base(this, 'dispose');
|
||||
vjs.MediaTechController.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
vjs.Html5.prototype.createEl = function(){
|
||||
|
@ -8,15 +8,17 @@
|
||||
* @param {Object=} options Options object
|
||||
* @constructor
|
||||
*/
|
||||
vjs.MediaTechController = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
vjs.MediaTechController = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.Component.call(this, player, options, ready);
|
||||
|
||||
// Make playback element clickable
|
||||
// this.addEvent('click', this.proxy(this.onClick));
|
||||
// Make playback element clickable
|
||||
// this.addEvent('click', this.proxy(this.onClick));
|
||||
|
||||
// player.triggerEvent('techready');
|
||||
};
|
||||
goog.inherits(vjs.MediaTechController, vjs.Component);
|
||||
// player.triggerEvent('techready');
|
||||
}
|
||||
});
|
||||
|
||||
// destroy: function(){},
|
||||
// createElement: function(){},
|
||||
|
30
src/js/media.loader.js
Normal file
30
src/js/media.loader.js
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.MediaLoader = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.Component.call(this, 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) {
|
||||
for (var i=0,j=player.options_['techOrder']; i<j.length; i++) {
|
||||
var techName = vjs.capitalize(j[i]),
|
||||
tech = window['videojs'][techName];
|
||||
|
||||
// Check if the browser supports this technology
|
||||
if (tech && tech.isSupported()) {
|
||||
player.loadTech(techName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// // Loop through playback technologies (HTML5, Flash) and check for support.
|
||||
// // Then load the best source.
|
||||
// // A few assumptions here:
|
||||
// // All playback technologies respect preload false.
|
||||
player.src(player.options_['sources']);
|
||||
}
|
||||
}
|
||||
});
|
68
src/js/menu.js
Normal file
68
src/js/menu.js
Normal file
@ -0,0 +1,68 @@
|
||||
/* Menu
|
||||
================================================================================ */
|
||||
/**
|
||||
* The base for text track and settings menu buttons.
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Menu = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
}
|
||||
});
|
||||
|
||||
vjs.Menu.prototype.addItem = function(component){
|
||||
this.addChild(component);
|
||||
component.on('click', vjs.bind(this, function(){
|
||||
this.unlockShowing();
|
||||
}));
|
||||
};
|
||||
|
||||
vjs.Menu.prototype.createEl = function(){
|
||||
return vjs.Component.prototype.createEl.call(this, 'ul', {
|
||||
className: 'vjs-menu'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Menu item
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.MenuItem = vjs.Button.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
if (options['selected']) {
|
||||
this.addClass('vjs-selected');
|
||||
this.el_.setAttribute('aria-selected',true);
|
||||
} else {
|
||||
this.el_.setAttribute('aria-selected',false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
vjs.MenuItem.prototype.createEl = function(type, props){
|
||||
return vjs.Button.prototype.createEl.call(this, 'li', vjs.obj.merge({
|
||||
className: 'vjs-menu-item',
|
||||
innerHTML: this.options_['label']
|
||||
}, props));
|
||||
};
|
||||
|
||||
vjs.MenuItem.prototype.onClick = function(){
|
||||
this.selected(true);
|
||||
};
|
||||
|
||||
vjs.MenuItem.prototype.selected = function(selected){
|
||||
if (selected) {
|
||||
this.addClass('vjs-selected');
|
||||
this.el_.setAttribute('aria-selected',true);
|
||||
} else {
|
||||
this.removeClass('vjs-selected');
|
||||
this.el_.setAttribute('aria-selected',false);
|
||||
}
|
||||
};
|
133
src/js/player.js
133
src/js/player.js
@ -5,62 +5,64 @@
|
||||
* @param {Function=} ready Ready callback function
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Player = function(tag, options, ready){
|
||||
this.tag = tag; // Store the original tag used to set options
|
||||
vjs.Player = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(tag, options, ready){
|
||||
this.tag = tag; // Store the original tag used to set options
|
||||
|
||||
// Set Options
|
||||
// The options argument overrides options set in the video tag
|
||||
// which overrides globally set options.
|
||||
// This latter part coincides with the load order
|
||||
// (tag must exist before Player)
|
||||
options = vjs.obj.merge(this.getTagSettings(tag), options);
|
||||
// Set Options
|
||||
// The options argument overrides options set in the video tag
|
||||
// which overrides globally set options.
|
||||
// This latter part coincides with the load order
|
||||
// (tag must exist before Player)
|
||||
options = vjs.obj.merge(this.getTagSettings(tag), options);
|
||||
|
||||
// Cache for video property values.
|
||||
this.cache_ = {};
|
||||
// Cache for video property values.
|
||||
this.cache_ = {};
|
||||
|
||||
// Set poster
|
||||
this.poster_ = options['poster'];
|
||||
// Set controls
|
||||
this.controls_ = options['controls'];
|
||||
// Set poster
|
||||
this.poster_ = options['poster'];
|
||||
// Set controls
|
||||
this.controls_ = options['controls'];
|
||||
|
||||
// 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, options, ready);
|
||||
// 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, options, ready);
|
||||
|
||||
// Firstplay event implimentation. Not sold on the event yet.
|
||||
// Could probably just check currentTime==0?
|
||||
this.one('play', function(e){
|
||||
var fpEvent = { type: 'firstplay', target: this.el_ };
|
||||
// Using vjs.trigger so we can check if default was prevented
|
||||
var keepGoing = vjs.trigger(this.el_, fpEvent);
|
||||
// Firstplay event implimentation. Not sold on the event yet.
|
||||
// Could probably just check currentTime==0?
|
||||
this.one('play', function(e){
|
||||
var fpEvent = { type: 'firstplay', target: this.el_ };
|
||||
// Using vjs.trigger so we can check if default was prevented
|
||||
var keepGoing = vjs.trigger(this.el_, fpEvent);
|
||||
|
||||
if (!keepGoing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
if (!keepGoing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('ended', this.onEnded);
|
||||
this.on('play', this.onPlay);
|
||||
this.on('firstplay', this.onFirstPlay);
|
||||
this.on('pause', this.onPause);
|
||||
this.on('progress', this.onProgress);
|
||||
this.on('durationchange', this.onDurationChange);
|
||||
this.on('error', this.onError);
|
||||
this.on('fullscreenchange', this.onFullscreenChange);
|
||||
|
||||
// Make player easily findable by ID
|
||||
vjs.players[this.id_] = this;
|
||||
|
||||
if (options['plugins']) {
|
||||
vjs.obj.each(options['plugins'], function(key, val){
|
||||
this[key](val);
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
this.on('ended', this.onEnded);
|
||||
this.on('play', this.onPlay);
|
||||
this.on('firstplay', this.onFirstPlay);
|
||||
this.on('pause', this.onPause);
|
||||
this.on('progress', this.onProgress);
|
||||
this.on('durationchange', this.onDurationChange);
|
||||
this.on('error', this.onError);
|
||||
this.on('fullscreenchange', this.onFullscreenChange);
|
||||
|
||||
// Make player easily findable by ID
|
||||
vjs.players[this.id_] = this;
|
||||
|
||||
if (options['plugins']) {
|
||||
vjs.obj.each(options['plugins'], function(key, val){
|
||||
this[key](val);
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
goog.inherits(vjs.Player, vjs.Component);
|
||||
});
|
||||
|
||||
/**
|
||||
* Player instance options, surfaced using vjs.options
|
||||
@ -88,7 +90,7 @@ vjs.Player.prototype.dispose = function(){
|
||||
if (this.tech) { this.tech.dispose(); }
|
||||
|
||||
// Component dispose
|
||||
goog.base(this, 'dispose');
|
||||
vjs.Component.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
vjs.Player.prototype.getTagSettings = function(tag){
|
||||
@ -125,7 +127,7 @@ vjs.Player.prototype.getTagSettings = function(tag){
|
||||
};
|
||||
|
||||
vjs.Player.prototype.createEl = function(){
|
||||
var el = this.el_ = goog.base(this, 'createEl', 'div');
|
||||
var el = this.el_ = vjs.Component.prototype.createEl.call(this, 'div');
|
||||
var tag = this.tag;
|
||||
|
||||
// Original tag settings stored in options
|
||||
@ -759,7 +761,7 @@ vjs.Player.prototype.src = function(source){
|
||||
}
|
||||
} else {
|
||||
this.el_.appendChild(vjs.createEl('p', {
|
||||
innerHTML: 'Sorry, no compatible source and playback technology were found for this video. Try using another browser like <a href="http://www.google.com/chrome">Google Chrome</a> or download the latest <a href="http://get.adobe.com/flashplayer/">Adobe Flash Player</a>.'
|
||||
innerHTML: 'Sorry, no compatible source and playback technology were found for this video. Try using another browser like <a href="http://bit.ly/ccMUEC">Chrome</a> or download the latest <a href="http://adobe.ly/mwfN1">Adobe Flash Player</a>.'
|
||||
}));
|
||||
}
|
||||
|
||||
@ -946,33 +948,4 @@ vjs.Player.prototype.ended = function(){ return this.techGet('ended'); };
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.MediaLoader = function(player, options, ready){
|
||||
vjs.Component.call(this, 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) {
|
||||
for (var i=0,j=player.options_['techOrder']; i<j.length; i++) {
|
||||
var techName = vjs.capitalize(j[i]),
|
||||
tech = window['videojs'][techName];
|
||||
|
||||
// Check if the browser supports this technology
|
||||
if (tech && tech.isSupported()) {
|
||||
player.loadTech(techName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// // Loop through playback technologies (HTML5, Flash) and check for support.
|
||||
// // Then load the best source.
|
||||
// // A few assumptions here:
|
||||
// // All playback technologies respect preload false.
|
||||
player.src(player.options_['sources']);
|
||||
}
|
||||
};
|
||||
goog.inherits(vjs.MediaLoader, vjs.Component);
|
||||
|
||||
|
||||
|
44
src/js/poster.js
Normal file
44
src/js/poster.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* Poster Image
|
||||
================================================================================ */
|
||||
/**
|
||||
* Poster image. Shows before the video plays.
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.PosterImage = vjs.Button.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
if (!player.poster()) {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
player.on('play', vjs.bind(this, this.hide));
|
||||
}
|
||||
});
|
||||
|
||||
vjs.PosterImage.prototype.createEl = function(){
|
||||
var el = vjs.createEl('div', {
|
||||
className: 'vjs-poster',
|
||||
|
||||
// Don't want poster to be tabbable.
|
||||
tabIndex: -1
|
||||
}),
|
||||
poster = this.player_.poster();
|
||||
|
||||
if (poster) {
|
||||
if ('backgroundSize' in el.style) {
|
||||
el.style.backgroundImage = 'url("' + poster + '")';
|
||||
} else {
|
||||
el.appendChild(vjs.createEl('img', { src: poster }));
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
vjs.PosterImage.prototype.onClick = function(){
|
||||
this.player_.play();
|
||||
};
|
164
src/js/slider.js
Normal file
164
src/js/slider.js
Normal file
@ -0,0 +1,164 @@
|
||||
/* Slider
|
||||
================================================================================ */
|
||||
/**
|
||||
* Parent for seek bar and volume slider
|
||||
* @param {vjs.Player|Object} player
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.Slider = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(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']);
|
||||
|
||||
player.on(this.playerEvent, vjs.bind(this, this.update));
|
||||
|
||||
this.on('mousedown', this.onMouseDown);
|
||||
this.on('touchstart', 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));
|
||||
|
||||
player.ready(vjs.bind(this, this.update));
|
||||
|
||||
this.boundEvents = {};
|
||||
}
|
||||
});
|
||||
|
||||
vjs.Slider.prototype.createEl = function(type, props) {
|
||||
props = vjs.obj.merge({
|
||||
role: 'slider',
|
||||
'aria-valuenow': 0,
|
||||
'aria-valuemin': 0,
|
||||
'aria-valuemax': 100,
|
||||
tabIndex: 0
|
||||
}, props);
|
||||
|
||||
return vjs.Component.prototype.createEl.call(this, type, props);
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.onMouseDown = function(event){
|
||||
event.preventDefault();
|
||||
vjs.blockTextSelection();
|
||||
|
||||
this.boundEvents.move = vjs.bind(this, this.onMouseMove);
|
||||
this.boundEvents.end = vjs.bind(this, this.onMouseUp);
|
||||
|
||||
vjs.on(document, 'mousemove', this.boundEvents.move);
|
||||
vjs.on(document, 'mouseup', this.boundEvents.end);
|
||||
vjs.on(document, 'touchmove', this.boundEvents.move);
|
||||
vjs.on(document, 'touchend', this.boundEvents.end);
|
||||
|
||||
this.onMouseMove(event);
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.onMouseUp = function() {
|
||||
vjs.unblockTextSelection();
|
||||
vjs.off(document, 'mousemove', this.boundEvents.move, false);
|
||||
vjs.off(document, 'mouseup', this.boundEvents.end, false);
|
||||
vjs.off(document, 'touchmove', this.boundEvents.move, false);
|
||||
vjs.off(document, 'touchend', this.boundEvents.end, false);
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.update = function(){
|
||||
// In VolumeBar init we have a setTimeout for update that pops and update to the end of the
|
||||
// execution stack. The player is destroyed before then update will cause an error
|
||||
if (!this.el_) return;
|
||||
|
||||
// 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) + '%';
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.calculateDistance = function(event){
|
||||
var box = this.el_,
|
||||
boxX = vjs.findPosX(box),
|
||||
boxW = box.offsetWidth,
|
||||
handle = this.handle,
|
||||
pageX = event.pageX;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// This is done because on Android, event.pageX is always 0 and the actual
|
||||
// values live under the changedTouches array.
|
||||
if (pageX === 0 && event.changedTouches) {
|
||||
pageX = event.changedTouches[0].pageX;
|
||||
}
|
||||
|
||||
// Percent that the click is through the adjusted area
|
||||
return Math.max(0, Math.min(1, (pageX - boxX) / boxW));
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.onFocus = function(){
|
||||
vjs.on(document, 'keyup', vjs.bind(this, this.onKeyPress));
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.onKeyPress = function(event){
|
||||
if (event.which == 37) { // Left Arrow
|
||||
event.preventDefault();
|
||||
this.stepBack();
|
||||
} else if (event.which == 39) { // Right Arrow
|
||||
event.preventDefault();
|
||||
this.stepForward();
|
||||
}
|
||||
};
|
||||
|
||||
vjs.Slider.prototype.onBlur = function(){
|
||||
vjs.off(document, 'keyup', vjs.bind(this, this.onKeyPress));
|
||||
};
|
228
src/js/tracks.js
228
src/js/tracks.js
@ -118,28 +118,30 @@ vjs.Player.prototype.showTextTrack = function(id, disableSameKind){
|
||||
* @param {Object=} options
|
||||
* @constructor
|
||||
*/
|
||||
vjs.TextTrack = function(player, options){
|
||||
goog.base(this, player, options);
|
||||
vjs.TextTrack = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
|
||||
// Apply track info to track object
|
||||
// Options will often be a track element
|
||||
// Apply track info to track object
|
||||
// Options will often be a track element
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
this.player_.on('fullscreenchange', vjs.bind(this, this.adjustFontSize));
|
||||
};
|
||||
goog.inherits(vjs.TextTrack, vjs.Component);
|
||||
this.player_.on('fullscreenchange', vjs.bind(this, this.adjustFontSize));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Track kind value. Captions, subtitles, etc.
|
||||
@ -317,7 +319,7 @@ vjs.TextTrack.prototype.adjustFontSize = function(){
|
||||
* @return {Element}
|
||||
*/
|
||||
vjs.TextTrack.prototype.createEl = function(){
|
||||
return goog.base(this, 'createEl', 'div', {
|
||||
return vjs.Component.prototype.createEl.call(this, 'div', {
|
||||
className: 'vjs-' + this.kind_ + ' vjs-text-track'
|
||||
});
|
||||
};
|
||||
@ -338,7 +340,7 @@ vjs.TextTrack.prototype.show = function(){
|
||||
this.mode_ = 2;
|
||||
|
||||
// Show element.
|
||||
goog.base(this, 'show');
|
||||
vjs.Component.prototype.show.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -354,7 +356,7 @@ vjs.TextTrack.prototype.hide = function(){
|
||||
this.mode_ = 1;
|
||||
|
||||
// Hide element.
|
||||
goog.base(this, 'hide');
|
||||
vjs.Component.prototype.hide.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -668,10 +670,7 @@ vjs.TextTrack.prototype.reset = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.CaptionsTrack = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
};
|
||||
goog.inherits(vjs.CaptionsTrack, vjs.TextTrack);
|
||||
vjs.CaptionsTrack = vjs.TextTrack.extend();
|
||||
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']
|
||||
@ -679,19 +678,13 @@ vjs.CaptionsTrack.prototype.kind_ = 'captions';
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.SubtitlesTrack = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
};
|
||||
goog.inherits(vjs.SubtitlesTrack, vjs.TextTrack);
|
||||
vjs.SubtitlesTrack = vjs.TextTrack.extend();
|
||||
vjs.SubtitlesTrack.prototype.kind_ = 'subtitles';
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.ChaptersTrack = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
};
|
||||
goog.inherits(vjs.ChaptersTrack, vjs.TextTrack);
|
||||
vjs.ChaptersTrack = vjs.TextTrack.extend();
|
||||
vjs.ChaptersTrack.prototype.kind_ = 'chapters';
|
||||
|
||||
|
||||
@ -702,21 +695,23 @@ vjs.ChaptersTrack.prototype.kind_ = 'chapters';
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.TextTrackDisplay = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
vjs.TextTrackDisplay = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.Component.call(this, player, options, ready);
|
||||
|
||||
// This used to be called during player init, but was causing an error
|
||||
// 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(player.options_['tracks']);
|
||||
// This used to be called during player init, but was causing an error
|
||||
// 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(player.options_['tracks']);
|
||||
}
|
||||
}
|
||||
};
|
||||
goog.inherits(vjs.TextTrackDisplay, vjs.Component);
|
||||
});
|
||||
|
||||
vjs.TextTrackDisplay.prototype.createEl = function(){
|
||||
return goog.base(this, 'createEl', 'div', {
|
||||
return vjs.Component.prototype.createEl.call(this, 'div', {
|
||||
className: 'vjs-text-track-display'
|
||||
});
|
||||
};
|
||||
@ -727,20 +722,22 @@ vjs.TextTrackDisplay.prototype.createEl = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.TextTrackMenuItem = function(player, options){
|
||||
var track = this.track = options['track'];
|
||||
vjs.TextTrackMenuItem = vjs.MenuItem.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
var track = this.track = options['track'];
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = track.label();
|
||||
options['selected'] = track.dflt();
|
||||
goog.base(this, player, options);
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = track.label();
|
||||
options['selected'] = track.dflt();
|
||||
vjs.MenuItem.call(this, player, options);
|
||||
|
||||
this.player_.on(track.kind() + 'trackchange', vjs.bind(this, this.update));
|
||||
};
|
||||
goog.inherits(vjs.TextTrackMenuItem, vjs.MenuItem);
|
||||
this.player_.on(track.kind() + 'trackchange', vjs.bind(this, this.update));
|
||||
}
|
||||
});
|
||||
|
||||
vjs.TextTrackMenuItem.prototype.onClick = function(){
|
||||
goog.base(this, 'onClick');
|
||||
vjs.MenuItem.prototype.onClick.call(this);
|
||||
this.player_.showTextTrack(this.track.id_, this.track.kind());
|
||||
};
|
||||
|
||||
@ -755,23 +752,25 @@ vjs.TextTrackMenuItem.prototype.update = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.OffTextTrackMenuItem = function(player, options){
|
||||
// Create pseudo track info
|
||||
// Requires options['kind']
|
||||
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);
|
||||
this.selected(true);
|
||||
};
|
||||
goog.inherits(vjs.OffTextTrackMenuItem, vjs.TextTrackMenuItem);
|
||||
vjs.OffTextTrackMenuItem = vjs.TextTrackMenuItem.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
// Create pseudo track info
|
||||
// Requires options['kind']
|
||||
options['track'] = {
|
||||
kind: function() { return options['kind']; },
|
||||
player: player,
|
||||
label: function(){ return 'Off'; },
|
||||
dflt: function(){ return false; },
|
||||
mode: function(){ return false; }
|
||||
};
|
||||
vjs.TextTrackMenuItem.call(this, player, options);
|
||||
this.selected(true);
|
||||
}
|
||||
});
|
||||
|
||||
vjs.OffTextTrackMenuItem.prototype.onClick = function(){
|
||||
goog.base(this, 'onClick');
|
||||
vjs.TextTrackMenuItem.prototype.onClick(this);
|
||||
this.player_.showTextTrack(this.track.id_, this.track.kind());
|
||||
};
|
||||
|
||||
@ -799,19 +798,21 @@ vjs.OffTextTrackMenuItem.prototype.update = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.TextTrackButton = function(player, options){
|
||||
goog.base(this, player, options);
|
||||
vjs.TextTrackButton = vjs.Button.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
this.menu = this.createMenu();
|
||||
this.menu = this.createMenu();
|
||||
|
||||
if (this.items.length === 0) {
|
||||
this.hide();
|
||||
if (this.items.length === 0) {
|
||||
this.hide();
|
||||
}
|
||||
this.on('keyup', this.onKeyPress);
|
||||
this.el_.setAttribute('aria-haspopup',true);
|
||||
this.el_.setAttribute('role','button');
|
||||
}
|
||||
this.on('keyup', this.onKeyPress);
|
||||
this.el_.setAttribute('aria-haspopup',true);
|
||||
this.el_.setAttribute('role','button');
|
||||
};
|
||||
goog.inherits(vjs.TextTrackButton, vjs.Button);
|
||||
});
|
||||
|
||||
vjs.TextTrackButton.prototype.buttonPressed = false;
|
||||
|
||||
@ -858,7 +859,7 @@ vjs.TextTrackButton.prototype.createItems = function(){
|
||||
};
|
||||
|
||||
vjs.TextTrackButton.prototype.buildCSSClass = function(){
|
||||
return this.className + ' vjs-menu-button ' + goog.base(this, 'buildCSSClass');
|
||||
return this.className + ' vjs-menu-button ' + vjs.Button.prototype.buildCSSClass.call(this);
|
||||
};
|
||||
|
||||
// Focus - Add keyboard functionality to element
|
||||
@ -931,11 +932,13 @@ vjs.TextTrackButton.prototype.unpressButton = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.CaptionsButton = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Captions Menu');
|
||||
};
|
||||
goog.inherits(vjs.CaptionsButton, vjs.TextTrackButton);
|
||||
vjs.CaptionsButton = vjs.TextTrackButton.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.TextTrackButton.call(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Captions Menu');
|
||||
}
|
||||
});
|
||||
vjs.CaptionsButton.prototype.kind_ = 'captions';
|
||||
vjs.CaptionsButton.prototype.buttonText = 'Captions';
|
||||
vjs.CaptionsButton.prototype.className = 'vjs-captions-button';
|
||||
@ -943,11 +946,13 @@ vjs.CaptionsButton.prototype.className = 'vjs-captions-button';
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.SubtitlesButton = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Subtitles Menu');
|
||||
};
|
||||
goog.inherits(vjs.SubtitlesButton, vjs.TextTrackButton);
|
||||
vjs.SubtitlesButton = vjs.TextTrackButton.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.TextTrackButton.call(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Subtitles Menu');
|
||||
}
|
||||
});
|
||||
vjs.SubtitlesButton.prototype.kind_ = 'subtitles';
|
||||
vjs.SubtitlesButton.prototype.buttonText = 'Subtitles';
|
||||
vjs.SubtitlesButton.prototype.className = 'vjs-subtitles-button';
|
||||
@ -957,11 +962,13 @@ vjs.SubtitlesButton.prototype.className = 'vjs-subtitles-button';
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.ChaptersButton = function(player, options, ready){
|
||||
goog.base(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Chapters Menu');
|
||||
};
|
||||
goog.inherits(vjs.ChaptersButton, vjs.TextTrackButton);
|
||||
vjs.ChaptersButton = vjs.TextTrackButton.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options, ready){
|
||||
vjs.TextTrackButton.call(this, player, options, ready);
|
||||
this.el_.setAttribute('aria-label','Chapters Menu');
|
||||
}
|
||||
});
|
||||
vjs.ChaptersButton.prototype.kind_ = 'chapters';
|
||||
vjs.ChaptersButton.prototype.buttonText = 'Chapters';
|
||||
vjs.ChaptersButton.prototype.className = 'vjs-chapters-button';
|
||||
@ -1044,22 +1051,24 @@ vjs.ChaptersButton.prototype.createMenu = function(){
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.ChaptersTrackMenuItem = function(player, options){
|
||||
var track = this.track = options['track'],
|
||||
cue = this.cue = options['cue'],
|
||||
currentTime = player.currentTime();
|
||||
vjs.ChaptersTrackMenuItem = vjs.MenuItem.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
var track = this.track = options['track'],
|
||||
cue = this.cue = options['cue'],
|
||||
currentTime = player.currentTime();
|
||||
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = cue.text;
|
||||
options['selected'] = (cue.startTime <= currentTime && currentTime < cue.endTime);
|
||||
goog.base(this, player, options);
|
||||
// Modify options for parent MenuItem class's init.
|
||||
options['label'] = cue.text;
|
||||
options['selected'] = (cue.startTime <= currentTime && currentTime < cue.endTime);
|
||||
vjs.MenuItem.call(this, player, options);
|
||||
|
||||
track.on('cuechange', vjs.bind(this, this.update));
|
||||
};
|
||||
goog.inherits(vjs.ChaptersTrackMenuItem, vjs.MenuItem);
|
||||
track.on('cuechange', vjs.bind(this, this.update));
|
||||
}
|
||||
});
|
||||
|
||||
vjs.ChaptersTrackMenuItem.prototype.onClick = function(){
|
||||
goog.base(this, 'onClick');
|
||||
vjs.MenuItem.prototype.onClick.call(this);
|
||||
this.player_.currentTime(this.cue.startTime);
|
||||
this.update(this.cue.startTime);
|
||||
};
|
||||
@ -1084,7 +1093,8 @@ vjs.obj.merge(vjs.ControlBar.prototype.options_['children'], {
|
||||
});
|
||||
|
||||
// vjs.Cue = vjs.Component.extend({
|
||||
// /** @constructor */
|
||||
// init: function(player, options){
|
||||
// goog.base(this, player, options);
|
||||
// vjs.Component.call(this, player, options);
|
||||
// }
|
||||
// });
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
// ADD NEW TEST FILES HERE
|
||||
var tests = [
|
||||
'test/unit/core-object.js',
|
||||
'test/unit/lib.js',
|
||||
'test/unit/events.js',
|
||||
'test/unit/component.js',
|
||||
|
58
test/unit/core-object.js
Normal file
58
test/unit/core-object.js
Normal file
@ -0,0 +1,58 @@
|
||||
module('Core Object');
|
||||
|
||||
test('should verify CoreObject extension', function(){
|
||||
var TestObject = vjs.CoreObject.extend({
|
||||
init: function(initOptions){
|
||||
this['a'] = initOptions['a'];
|
||||
},
|
||||
testFn: function(){
|
||||
return true;
|
||||
}
|
||||
});
|
||||
var instance = new TestObject({ 'a': true });
|
||||
|
||||
ok(instance instanceof TestObject, 'New instance is instance of TestObject');
|
||||
ok(instance instanceof vjs.CoreObject, 'New instance is instance of CoreObject');
|
||||
ok(instance['a'], 'Init options are passed to init');
|
||||
ok(instance.testFn(), 'Additional methods are applied to TestObject prototype');
|
||||
|
||||
// Two levels of inheritance
|
||||
var TestChild = TestObject.extend({
|
||||
init: function(initOptions){
|
||||
TestObject.call(this, initOptions);
|
||||
// TestObject.prototype.init.call(this, initOptions);
|
||||
this['b'] = initOptions['b'];
|
||||
},
|
||||
testFn: function(){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
var childInstance = new TestChild({ 'a': true, 'b': true });
|
||||
|
||||
ok(childInstance instanceof TestChild, 'New instance is instance of TestChild');
|
||||
ok(childInstance instanceof TestObject, 'New instance is instance of TestObject');
|
||||
ok(childInstance instanceof vjs.CoreObject, 'New instance is instance of CoreObject');
|
||||
ok(childInstance['b'], 'Init options are passed to init');
|
||||
ok(childInstance['a'], 'Init options are passed to super init');
|
||||
ok(childInstance.testFn() === false, 'Methods can be overridden by extend');
|
||||
ok(TestObject.prototype.testFn() === true, 'Prototype of parent not overridden');
|
||||
});
|
||||
|
||||
test('should verify CoreObject create function', function(){
|
||||
var TestObject = vjs.CoreObject.extend({
|
||||
init: function(initOptions){
|
||||
this['a'] = initOptions['a'];
|
||||
},
|
||||
testFn: function(){
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var instance = TestObject.create({ 'a': true });
|
||||
|
||||
ok(instance instanceof TestObject, 'New instance is instance of TestObject');
|
||||
ok(instance instanceof vjs.CoreObject, 'New instance is instance of CoreObject');
|
||||
ok(instance['a'], 'Init options are passed to init');
|
||||
ok(instance.testFn(), 'Additional methods are applied to TestObject prototype');
|
||||
});
|
@ -4,19 +4,20 @@
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
vjs.MediaFaker = function(player, options, onReady){
|
||||
goog.base(this, player, options, onReady);
|
||||
vjs.MediaFaker = vjs.MediaTechController.extend({
|
||||
init: function(player, options, onReady){
|
||||
vjs.MediaTechController.call(this, player, options, onReady);
|
||||
|
||||
this.triggerReady();
|
||||
};
|
||||
goog.inherits(vjs.MediaFaker, vjs.MediaTechController);
|
||||
this.triggerReady();
|
||||
}
|
||||
});
|
||||
|
||||
// Support everything
|
||||
vjs.MediaFaker.isSupported = function(){ return true; };
|
||||
vjs.MediaFaker.canPlaySource = function(srcObj){ return true; };
|
||||
|
||||
vjs.MediaFaker.prototype.createEl = function(){
|
||||
var el = goog.base(this, 'createEl', 'div', {
|
||||
var el = vjs.MediaTechController.prototype.createEl.call(this, 'div', {
|
||||
className: 'vjs-tech'
|
||||
});
|
||||
if (this.player().poster()) {
|
||||
@ -32,6 +33,7 @@ vjs.MediaFaker.prototype.createEl = function(){
|
||||
vjs.MediaFaker.prototype.currentTime = function(){ return 0; };
|
||||
vjs.MediaFaker.prototype.volume = function(){ return 0; };
|
||||
|
||||
goog.exportSymbol('videojs.MediaFaker', vjs.MediaFaker);
|
||||
goog.exportProperty(vjs.MediaFaker, 'isSupported', vjs.MediaFaker.isSupported);
|
||||
goog.exportProperty(vjs.MediaFaker, 'canPlaySource', vjs.MediaFaker.canPlaySource);
|
||||
// Export vars for Closure Compiler
|
||||
vjs['MediaFaker'] = vjs.MediaFaker;
|
||||
vjs['MediaFaker']['isSupported'] = vjs.MediaFaker.isSupported;
|
||||
vjs['MediaFaker']['canPlaySource'] = vjs.MediaFaker.canPlaySource;
|
||||
|
Loading…
x
Reference in New Issue
Block a user