mirror of
https://github.com/videojs/video.js.git
synced 2025-03-31 22:22:09 +02:00
Make components listen to touch events themselves. Components can have a "listenToTouchMove" property that would report user activity on touch moves. Currently, the only problem left is that the MediaTechController emits tap events to show/hide the controlbar but that causes the control bar to not be hidden via a tap.
886 lines
23 KiB
JavaScript
886 lines
23 KiB
JavaScript
/**
|
|
* @fileoverview Player Component - Base class for all UI objects
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Base UI Component class
|
|
*
|
|
* Components are embeddable UI objects that are represented by both a
|
|
* javascript object and an element in the DOM. They can be children of other
|
|
* components, and can have many children themselves.
|
|
*
|
|
* // adding a button to the player
|
|
* var button = player.addChild('button');
|
|
* button.el(); // -> button element
|
|
*
|
|
* <div class="video-js">
|
|
* <div class="vjs-button">Button</div>
|
|
* </div>
|
|
*
|
|
* Components are also event emitters.
|
|
*
|
|
* button.on('click', function(){
|
|
* console.log('Button Clicked!');
|
|
* });
|
|
*
|
|
* button.trigger('customevent');
|
|
*
|
|
* @param {Object} player Main Player
|
|
* @param {Object=} options
|
|
* @class
|
|
* @constructor
|
|
* @extends vjs.CoreObject
|
|
*/
|
|
vjs.Component = vjs.CoreObject.extend({
|
|
/**
|
|
* the constructor function for the class
|
|
*
|
|
* @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_);
|
|
|
|
// 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++ );
|
|
|
|
this.name_ = options['name'] || null;
|
|
|
|
// Create element if one wasn't provided in options
|
|
this.el_ = options['el'] || this.createEl();
|
|
|
|
this.children_ = [];
|
|
this.childIndex_ = {};
|
|
this.childNameIndex_ = {};
|
|
|
|
// 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
|
|
|
|
|
|
var touchmove = false;
|
|
this.on('touchstart', function() {
|
|
touchmove = false;
|
|
});
|
|
this.on('touchmove', vjs.bind(this, function() {
|
|
if (this.listenToTouchMove) {
|
|
this.player_.reportUserActivity();
|
|
}
|
|
touchmove = true;
|
|
}));
|
|
this.on('touchend', vjs.bind(this, function(event) {
|
|
if (!touchmove && !didSomething) {
|
|
this.player_.reportUserActivity();
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Dispose of the component and all child components
|
|
*/
|
|
vjs.Component.prototype.dispose = function(){
|
|
this.trigger('dispose');
|
|
|
|
// Dispose all children.
|
|
if (this.children_) {
|
|
for (var i = this.children_.length - 1; i >= 0; i--) {
|
|
if (this.children_[i].dispose) {
|
|
this.children_[i].dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete child references
|
|
this.children_ = null;
|
|
this.childIndex_ = null;
|
|
this.childNameIndex_ = null;
|
|
|
|
// Remove all event listeners.
|
|
this.off();
|
|
|
|
// Remove element from DOM
|
|
if (this.el_.parentNode) {
|
|
this.el_.parentNode.removeChild(this.el_);
|
|
}
|
|
|
|
vjs.removeData(this.el_);
|
|
this.el_ = null;
|
|
};
|
|
|
|
/**
|
|
* Reference to main player instance
|
|
*
|
|
* @type {vjs.Player}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.player_ = true;
|
|
|
|
/**
|
|
* Return the component's player
|
|
*
|
|
* @return {vjs.Player}
|
|
*/
|
|
vjs.Component.prototype.player = function(){
|
|
return this.player_;
|
|
};
|
|
|
|
/**
|
|
* The component's options object
|
|
*
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.options_;
|
|
|
|
/**
|
|
* Deep merge of options objects
|
|
*
|
|
* Whenever a property is an object on both options objects
|
|
* the two properties will be merged using vjs.obj.deepMerge.
|
|
*
|
|
* This is used for merging options for child components. We
|
|
* want it to be easy to override individual options on a child
|
|
* component without having to rewrite all the other default options.
|
|
*
|
|
* Parent.prototype.options_ = {
|
|
* children: {
|
|
* 'childOne': { 'foo': 'bar', 'asdf': 'fdsa' },
|
|
* 'childTwo': {},
|
|
* 'childThree': {}
|
|
* }
|
|
* }
|
|
* newOptions = {
|
|
* children: {
|
|
* 'childOne': { 'foo': 'baz', 'abc': '123' }
|
|
* 'childTwo': null,
|
|
* 'childFour': {}
|
|
* }
|
|
* }
|
|
*
|
|
* this.options(newOptions);
|
|
*
|
|
* RESULT
|
|
*
|
|
* {
|
|
* children: {
|
|
* 'childOne': { 'foo': 'baz', 'asdf': 'fdsa', 'abc': '123' },
|
|
* 'childTwo': null, // Disabled. Won't be initialized.
|
|
* 'childThree': {},
|
|
* 'childFour': {}
|
|
* }
|
|
* }
|
|
*
|
|
* @param {Object} obj Object of new option values
|
|
* @return {Object} A NEW object of this.options_ and obj merged
|
|
*/
|
|
vjs.Component.prototype.options = function(obj){
|
|
if (obj === undefined) return this.options_;
|
|
|
|
return this.options_ = vjs.util.mergeOptions(this.options_, obj);
|
|
};
|
|
|
|
/**
|
|
* The DOM element for the component
|
|
*
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.el_;
|
|
|
|
/**
|
|
* Create the component's DOM element
|
|
*
|
|
* @param {String=} tagName Element's node type. e.g. 'div'
|
|
* @param {Object=} attributes An object of element attributes that should be set on the element
|
|
* @return {Element}
|
|
*/
|
|
vjs.Component.prototype.createEl = function(tagName, attributes){
|
|
return vjs.createEl(tagName, attributes);
|
|
};
|
|
|
|
/**
|
|
* Get the component's DOM element
|
|
*
|
|
* var domEl = myComponent.el();
|
|
*
|
|
* @return {Element}
|
|
*/
|
|
vjs.Component.prototype.el = function(){
|
|
return this.el_;
|
|
};
|
|
|
|
/**
|
|
* An optional element where, if defined, children will be inserted instead of
|
|
* directly in `el_`
|
|
*
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.contentEl_;
|
|
|
|
/**
|
|
* Return the component's DOM element for embedding content.
|
|
* Will either be el_ or a new element defined in createEl.
|
|
*
|
|
* @return {Element}
|
|
*/
|
|
vjs.Component.prototype.contentEl = function(){
|
|
return this.contentEl_ || this.el_;
|
|
};
|
|
|
|
/**
|
|
* The ID for the component
|
|
*
|
|
* @type {String}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.id_;
|
|
|
|
/**
|
|
* Get the component's ID
|
|
*
|
|
* var id = myComponent.id();
|
|
*
|
|
* @return {String}
|
|
*/
|
|
vjs.Component.prototype.id = function(){
|
|
return this.id_;
|
|
};
|
|
|
|
/**
|
|
* The name for the component. Often used to reference the component.
|
|
*
|
|
* @type {String}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.name_;
|
|
|
|
/**
|
|
* Get the component's name. The name is often used to reference the component.
|
|
*
|
|
* var name = myComponent.name();
|
|
*
|
|
* @return {String}
|
|
*/
|
|
vjs.Component.prototype.name = function(){
|
|
return this.name_;
|
|
};
|
|
|
|
/**
|
|
* Array of child components
|
|
*
|
|
* @type {Array}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.children_;
|
|
|
|
/**
|
|
* Get an array of all child components
|
|
*
|
|
* var kids = myComponent.children();
|
|
*
|
|
* @return {Array} The children
|
|
*/
|
|
vjs.Component.prototype.children = function(){
|
|
return this.children_;
|
|
};
|
|
|
|
/**
|
|
* Object of child components by ID
|
|
*
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.childIndex_;
|
|
|
|
/**
|
|
* Returns a child component with the provided ID
|
|
*
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.getChildById = function(id){
|
|
return this.childIndex_[id];
|
|
};
|
|
|
|
/**
|
|
* Object of child components by name
|
|
*
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.childNameIndex_;
|
|
|
|
/**
|
|
* Returns a child component with the provided name
|
|
*
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.getChild = function(name){
|
|
return this.childNameIndex_[name];
|
|
};
|
|
|
|
/**
|
|
* Adds a child component inside this component
|
|
*
|
|
* myComponent.el();
|
|
* // -> <div class='my-component'></div>
|
|
* myComonent.children();
|
|
* // [empty array]
|
|
*
|
|
* var myButton = myComponent.addChild('MyButton');
|
|
* // -> <div class='my-component'><div class="my-button">myButton<div></div>
|
|
* // -> myButton === myComonent.children()[0];
|
|
*
|
|
* Pass in options for child constructors and options for children of the child
|
|
*
|
|
* var myButton = myComponent.addChild('MyButton', {
|
|
* text: 'Press Me',
|
|
* children: {
|
|
* buttonChildExample: {
|
|
* buttonChildOption: true
|
|
* }
|
|
* }
|
|
* });
|
|
*
|
|
* @param {String|vjs.Component} child The class name or instance of a child to add
|
|
* @param {Object=} options Options, including options to be passed to children of the child.
|
|
* @return {vjs.Component} The child component (created by this process if a string was used)
|
|
* @suppress {accessControls|checkRegExp|checkTypes|checkVars|const|constantProperty|deprecated|duplicate|es5Strict|fileoverviewTags|globalThis|invalidCasts|missingProperties|nonStandardJsDocs|strictModuleDepCheck|undefinedNames|undefinedVars|unknownDefines|uselessCode|visibility}
|
|
*/
|
|
vjs.Component.prototype.addChild = function(child, options){
|
|
var component, componentClass, componentName, componentId;
|
|
|
|
// If string, create new component with options
|
|
if (typeof child === 'string') {
|
|
|
|
componentName = child;
|
|
|
|
// Make sure options is at least an empty object to protect against errors
|
|
options = options || {};
|
|
|
|
// Assume name of set is a lowercased name of the UI Class (PlayButton, etc.)
|
|
componentClass = options['componentClass'] || vjs.capitalize(componentName);
|
|
|
|
// Set name through options
|
|
options['name'] = componentName;
|
|
|
|
// Create a new object & element for this controls set
|
|
// If there's no .player_, this is a player
|
|
// Closure Compiler throws an 'incomplete alias' warning if we use the vjs variable directly.
|
|
// Every class should be exported, so this should never be a problem here.
|
|
component = new window['videojs'][componentClass](this.player_ || this, options);
|
|
|
|
// child is a component instance
|
|
} else {
|
|
component = child;
|
|
}
|
|
|
|
this.children_.push(component);
|
|
|
|
if (typeof component.id === 'function') {
|
|
this.childIndex_[component.id()] = component;
|
|
}
|
|
|
|
// If a name wasn't used to create the component, check if we can use the
|
|
// name function of the component
|
|
componentName = componentName || (component.name && component.name());
|
|
|
|
if (componentName) {
|
|
this.childNameIndex_[componentName] = component;
|
|
}
|
|
|
|
// Add the UI object's element to the container div (box)
|
|
// Having an element is not required
|
|
if (typeof component['el'] === 'function' && component['el']()) {
|
|
this.contentEl().appendChild(component['el']());
|
|
}
|
|
|
|
// Return so it can stored on parent object if desired.
|
|
return component;
|
|
};
|
|
|
|
/**
|
|
* Remove a child component from this component's list of children, and the
|
|
* child component's element from this component's element
|
|
*
|
|
* @param {vjs.Component} component Component to remove
|
|
*/
|
|
vjs.Component.prototype.removeChild = function(component){
|
|
if (typeof component === 'string') {
|
|
component = this.getChild(component);
|
|
}
|
|
|
|
if (!component || !this.children_) return;
|
|
|
|
var childFound = false;
|
|
for (var i = this.children_.length - 1; i >= 0; i--) {
|
|
if (this.children_[i] === component) {
|
|
childFound = true;
|
|
this.children_.splice(i,1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!childFound) return;
|
|
|
|
this.childIndex_[component.id] = null;
|
|
this.childNameIndex_[component.name] = null;
|
|
|
|
var compEl = component.el();
|
|
if (compEl && compEl.parentNode === this.contentEl()) {
|
|
this.contentEl().removeChild(component.el());
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add and initialize default child components from options
|
|
*
|
|
* // when an instance of MyComponent is created, all children in options
|
|
* // will be added to the instance by their name strings and options
|
|
* MyComponent.prototype.options_.children = {
|
|
* myChildComponent: {
|
|
* myChildOption: true
|
|
* }
|
|
* }
|
|
*/
|
|
vjs.Component.prototype.initChildren = function(){
|
|
var options = this.options_;
|
|
|
|
if (options && options['children']) {
|
|
var self = this;
|
|
|
|
// 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;
|
|
|
|
// Allow waiting to add components until a specific event is called
|
|
var tempAdd = function(){
|
|
// Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy.
|
|
self[name] = self.addChild(name, opts);
|
|
};
|
|
|
|
if (opts['loadEvent']) {
|
|
// this.one(opts.loadEvent, tempAdd)
|
|
} else {
|
|
tempAdd();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Allows sub components to stack CSS class names
|
|
*
|
|
* @return {String} The constructed class name
|
|
*/
|
|
vjs.Component.prototype.buildCSSClass = function(){
|
|
// Child classes can include a function that does:
|
|
// return 'CLASS NAME' + this._super();
|
|
return '';
|
|
};
|
|
|
|
/* Events
|
|
============================================================================= */
|
|
|
|
/**
|
|
* Add an event listener to this component's element
|
|
*
|
|
* var myFunc = function(){
|
|
* var myPlayer = this;
|
|
* // Do something when the event is fired
|
|
* };
|
|
*
|
|
* myPlayer.on("eventName", myFunc);
|
|
*
|
|
* The context will be the component.
|
|
*
|
|
* @param {String} type The event type e.g. 'click'
|
|
* @param {Function} fn The event listener
|
|
* @return {vjs.Component} self
|
|
*/
|
|
vjs.Component.prototype.on = function(type, fn){
|
|
vjs.on(this.el_, type, vjs.bind(this, fn));
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Remove an event listener from the component's element
|
|
*
|
|
* myComponent.off("eventName", myFunc);
|
|
*
|
|
* @param {String=} type Event type. Without type it will remove all listeners.
|
|
* @param {Function=} fn Event listener. Without fn it will remove all listeners for a type.
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.off = function(type, fn){
|
|
vjs.off(this.el_, type, fn);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Add an event listener to be triggered only once and then removed
|
|
*
|
|
* @param {String} type Event type
|
|
* @param {Function} fn Event listener
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.one = function(type, fn) {
|
|
vjs.one(this.el_, type, vjs.bind(this, fn));
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Trigger an event on an element
|
|
*
|
|
* myComponent.trigger('eventName');
|
|
*
|
|
* @param {String} type The event type to trigger, e.g. 'click'
|
|
* @param {Event|Object} event The event object to be passed to the listener
|
|
* @return {vjs.Component} self
|
|
*/
|
|
vjs.Component.prototype.trigger = function(type, event){
|
|
vjs.trigger(this.el_, type, event);
|
|
return this;
|
|
};
|
|
|
|
/* Ready
|
|
================================================================================ */
|
|
/**
|
|
* Is the component loaded
|
|
* This can mean different things depending on the component.
|
|
*
|
|
* @private
|
|
* @type {Boolean}
|
|
*/
|
|
vjs.Component.prototype.isReady_;
|
|
|
|
/**
|
|
* Trigger ready as soon as initialization is finished
|
|
*
|
|
* Allows for delaying ready. Override on a sub class prototype.
|
|
* If you set this.isReadyOnInitFinish_ it will affect all components.
|
|
* Specially used when waiting for the Flash player to asynchrnously load.
|
|
*
|
|
* @type {Boolean}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.isReadyOnInitFinish_ = true;
|
|
|
|
/**
|
|
* List of ready listeners
|
|
*
|
|
* @type {Array}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.readyQueue_;
|
|
|
|
/**
|
|
* Bind a listener to the component's ready state
|
|
*
|
|
* Different from event listeners in that if the ready event has already happend
|
|
* it will trigger the function immediately.
|
|
*
|
|
* @param {Function} fn Ready listener
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.ready = function(fn){
|
|
if (fn) {
|
|
if (this.isReady_) {
|
|
fn.call(this);
|
|
} else {
|
|
if (this.readyQueue_ === undefined) {
|
|
this.readyQueue_ = [];
|
|
}
|
|
this.readyQueue_.push(fn);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Trigger the ready listeners
|
|
*
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.triggerReady = function(){
|
|
this.isReady_ = true;
|
|
|
|
var readyQueue = this.readyQueue_;
|
|
|
|
if (readyQueue && readyQueue.length > 0) {
|
|
|
|
for (var i = 0, j = readyQueue.length; i < j; i++) {
|
|
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');
|
|
}
|
|
};
|
|
|
|
/* Display
|
|
============================================================================= */
|
|
|
|
/**
|
|
* Add a CSS class name to the component's element
|
|
*
|
|
* @param {String} classToAdd Classname to add
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.addClass = function(classToAdd){
|
|
vjs.addClass(this.el_, classToAdd);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Remove a CSS class name from the component's element
|
|
*
|
|
* @param {String} classToRemove Classname to remove
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.removeClass = function(classToRemove){
|
|
vjs.removeClass(this.el_, classToRemove);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Show the component element if hidden
|
|
*
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.show = function(){
|
|
this.el_.style.display = 'block';
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Hide the component element if currently showing
|
|
*
|
|
* @return {vjs.Component}
|
|
*/
|
|
vjs.Component.prototype.hide = function(){
|
|
this.el_.style.display = 'none';
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Lock an item in its visible state
|
|
* To be used with fadeIn/fadeOut.
|
|
*
|
|
* @return {vjs.Component}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.lockShowing = function(){
|
|
this.addClass('vjs-lock-showing');
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Unlock an item to be hidden
|
|
* To be used with fadeIn/fadeOut.
|
|
*
|
|
* @return {vjs.Component}
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.unlockShowing = function(){
|
|
this.removeClass('vjs-lock-showing');
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Disable component by making it unshowable
|
|
*
|
|
* Currently private because we're movign towards more css-based states.
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.disable = function(){
|
|
this.hide();
|
|
this.show = function(){};
|
|
};
|
|
|
|
/**
|
|
* Set or get the width of the component (CSS values)
|
|
*
|
|
* Setting the video tag dimension values only works with values in pixels.
|
|
* Percent values will not work.
|
|
* Some percents can be used, but width()/height() will return the number + %,
|
|
* not the actual computed width/height.
|
|
*
|
|
* @param {Number|String=} num Optional width number
|
|
* @param {Boolean} skipListeners Skip the 'resize' event trigger
|
|
* @return {vjs.Component} This component, when setting the width
|
|
* @return {Number|String} The width, when getting
|
|
*/
|
|
vjs.Component.prototype.width = function(num, skipListeners){
|
|
return this.dimension('width', num, skipListeners);
|
|
};
|
|
|
|
/**
|
|
* Get or set the height of the component (CSS values)
|
|
*
|
|
* Setting the video tag dimension values only works with values in pixels.
|
|
* Percent values will not work.
|
|
* Some percents can be used, but width()/height() will return the number + %,
|
|
* not the actual computed width/height.
|
|
*
|
|
* @param {Number|String=} num New component height
|
|
* @param {Boolean=} skipListeners Skip the resize event trigger
|
|
* @return {vjs.Component} This component, when setting the height
|
|
* @return {Number|String} The height, when getting
|
|
*/
|
|
vjs.Component.prototype.height = function(num, skipListeners){
|
|
return this.dimension('height', num, skipListeners);
|
|
};
|
|
|
|
/**
|
|
* Set both width and height at the same time
|
|
*
|
|
* @param {Number|String} width
|
|
* @param {Number|String} height
|
|
* @return {vjs.Component} The component
|
|
*/
|
|
vjs.Component.prototype.dimensions = function(width, height){
|
|
// Skip resize listeners on width for optimization
|
|
return this.width(width, true).height(height);
|
|
};
|
|
|
|
/**
|
|
* Get or set width or height
|
|
*
|
|
* This is the shared code for the width() and height() methods.
|
|
* All for an integer, integer + 'px' or integer + '%';
|
|
*
|
|
* Known issue: Hidden elements officially have a width of 0. We're defaulting
|
|
* to the style.width value and falling back to computedStyle which has the
|
|
* hidden element issue. Info, but probably not an efficient fix:
|
|
* http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/
|
|
*
|
|
* @param {String} widthOrHeight 'width' or 'height'
|
|
* @param {Number|String=} num New dimension
|
|
* @param {Boolean=} skipListeners Skip resize event trigger
|
|
* @return {vjs.Component} The component if a dimension was set
|
|
* @return {Number|String} The dimension if nothing was set
|
|
* @private
|
|
*/
|
|
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) {
|
|
this.el_.style[widthOrHeight] = num;
|
|
} else if (num === 'auto') {
|
|
this.el_.style[widthOrHeight] = '';
|
|
} else {
|
|
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'); }
|
|
|
|
// Return component
|
|
return this;
|
|
}
|
|
|
|
// Not setting a value, so getting it
|
|
// Make sure element exists
|
|
if (!this.el_) return 0;
|
|
|
|
// Get dimension value from style
|
|
var val = this.el_.style[widthOrHeight];
|
|
var pxIndex = val.indexOf('px');
|
|
if (pxIndex !== -1) {
|
|
// Return the pixel value with no 'px'
|
|
return parseInt(val.slice(0,pxIndex), 10);
|
|
|
|
// No px so using % or no style was set, so falling back to offsetWidth/height
|
|
// If component has display:none, offset will return 0
|
|
// TODO: handle display:none and no dimension style using px
|
|
} else {
|
|
|
|
return parseInt(this.el_['offset'+vjs.capitalize(widthOrHeight)], 10);
|
|
|
|
// ComputedStyle version.
|
|
// Only difference is if the element is hidden it will return
|
|
// the percent value (e.g. '100%'')
|
|
// instead of zero like offsetWidth returns.
|
|
// var val = vjs.getComputedStyleValue(this.el_, widthOrHeight);
|
|
// var pxIndex = val.indexOf('px');
|
|
|
|
// if (pxIndex !== -1) {
|
|
// return val.slice(0, pxIndex);
|
|
// } else {
|
|
// return val;
|
|
// }
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fired when the width and/or height of the component changes
|
|
* @event resize
|
|
*/
|
|
vjs.Component.prototype.onResize;
|
|
|
|
/**
|
|
* Emit 'tap' events when touch events are supported
|
|
*
|
|
* This is used to support toggling the controls through a tap on the video.
|
|
*
|
|
* We're requireing them to be enabled because otherwise every component would
|
|
* have this extra overhead unnecessarily, on mobile devices where extra
|
|
* overhead is especially bad.
|
|
* @private
|
|
*/
|
|
vjs.Component.prototype.emitTapEvents = function(){
|
|
var touchStart, touchTime, couldBeTap, noTap;
|
|
|
|
// Track the start time so we can determine how long the touch lasted
|
|
touchStart = 0;
|
|
|
|
this.on('touchstart', function(event) {
|
|
// Record start time so we can detect a tap vs. "touch and hold"
|
|
touchStart = new Date().getTime();
|
|
// Reset couldBeTap tracking
|
|
couldBeTap = true;
|
|
});
|
|
|
|
noTap = function(){
|
|
couldBeTap = false;
|
|
};
|
|
// TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
|
|
this.on('touchmove', noTap);
|
|
this.on('touchleave', noTap);
|
|
this.on('touchcancel', noTap);
|
|
|
|
// When the touch ends, measure how long it took and trigger the appropriate
|
|
// event
|
|
this.on('touchend', function() {
|
|
// Proceed only if the touchmove/leave/cancel event didn't happen
|
|
if (couldBeTap === true) {
|
|
// Measure how long the touch lasted
|
|
touchTime = new Date().getTime() - touchStart;
|
|
// The touch needs to be quick in order to consider it a tap
|
|
if (touchTime < 250) {
|
|
this.trigger('tap');
|
|
// It may be good to copy the touchend event object and change the
|
|
// type to tap, if the other event properties aren't exact after
|
|
// vjs.fixEvent runs (e.g. event.target)
|
|
}
|
|
}
|
|
});
|
|
};
|