mirror of
https://github.com/videojs/video.js.git
synced 2024-12-25 02:42:10 +02:00
@heff enhanced the event listener API to allow for auto-cleanup of listeners on other componenets and elements. closes #1588
This commit is contained in:
parent
b8cc047a2e
commit
4b818d9ebf
@ -9,6 +9,7 @@ CHANGELOG
|
||||
* @thijstriemstra added a Dutch translation ([view](https://github.com/videojs/video.js/pull/1566))
|
||||
* @heff updated the poster to use CSS styles to display; fixed the poster not showing if not originally set ([view](https://github.com/videojs/video.js/pull/1568))
|
||||
* @mmcc fixed an issue where errors on source tags could get missed ([view](https://github.com/videojs/video.js/pull/1575))
|
||||
* @heff enhanced the event listener API to allow for auto-cleanup of listeners on other componenets and elements ([view](https://github.com/videojs/video.js/pull/1588))
|
||||
|
||||
--------------------
|
||||
|
||||
|
@ -531,46 +531,169 @@ vjs.Component.prototype.buildCSSClass = function(){
|
||||
* Add an event listener to this component's element
|
||||
*
|
||||
* var myFunc = function(){
|
||||
* var myPlayer = this;
|
||||
* var myComponent = this;
|
||||
* // Do something when the event is fired
|
||||
* };
|
||||
*
|
||||
* myPlayer.on("eventName", myFunc);
|
||||
* myComponent.on('eventType', myFunc);
|
||||
*
|
||||
* The context will be the component.
|
||||
* The context of myFunc will be myComponent unless previously bound.
|
||||
*
|
||||
* @param {String} type The event type e.g. 'click'
|
||||
* @param {Function} fn The event listener
|
||||
* @return {vjs.Component} self
|
||||
* Alternatively, you can add a listener to another element or component.
|
||||
*
|
||||
* myComponent.on(otherElement, 'eventName', myFunc);
|
||||
* myComponent.on(otherComponent, 'eventName', myFunc);
|
||||
*
|
||||
* The benefit of using this over `vjs.on(otherElement, 'eventName', myFunc)`
|
||||
* and `otherComponent.on('eventName', myFunc)` is that this way the listeners
|
||||
* will be automatically cleaned up when either component is diposed.
|
||||
* It will also bind myComponent as the context of myFunc.
|
||||
*
|
||||
* **NOTE**: When using this on elements in the page other than window
|
||||
* and document (both permanent), if you remove the element from the DOM
|
||||
* you need to call `vjs.trigger(el, 'dispose')` on it to clean up
|
||||
* references to it and allow the browser to garbage collect it.
|
||||
*
|
||||
* @param {String|vjs.Component} first The event type or other component
|
||||
* @param {Function|String} second The event handler or event type
|
||||
* @param {Function} third The event handler
|
||||
* @return {vjs.Component} self
|
||||
*/
|
||||
vjs.Component.prototype.on = function(type, fn){
|
||||
vjs.on(this.el_, type, vjs.bind(this, fn));
|
||||
vjs.Component.prototype.on = function(first, second, third){
|
||||
var target, type, fn, removeOnDispose, cleanRemover, thisComponent;
|
||||
|
||||
if (typeof first === 'string' || vjs.obj.isArray(first)) {
|
||||
vjs.on(this.el_, first, vjs.bind(this, second));
|
||||
|
||||
// Targeting another component or element
|
||||
} else {
|
||||
target = first;
|
||||
type = second;
|
||||
fn = vjs.bind(this, third);
|
||||
thisComponent = this;
|
||||
|
||||
// When this component is disposed, remove the listener from the other component
|
||||
removeOnDispose = function(){
|
||||
thisComponent.off(target, type, fn);
|
||||
};
|
||||
// Use the same function ID so we can remove it later it using the ID
|
||||
// of the original listener
|
||||
removeOnDispose.guid = fn.guid;
|
||||
this.on('dispose', removeOnDispose);
|
||||
|
||||
// If the other component is disposed first we need to clean the reference
|
||||
// to the other component in this component's removeOnDispose listener
|
||||
// Otherwise we create a memory leak.
|
||||
cleanRemover = function(){
|
||||
thisComponent.off('dispose', removeOnDispose);
|
||||
};
|
||||
// Add the same function ID so we can easily remove it later
|
||||
cleanRemover.guid = fn.guid;
|
||||
|
||||
// Check if this is a DOM node
|
||||
if (first.nodeName) {
|
||||
// Add the listener to the other element
|
||||
vjs.on(target, type, fn);
|
||||
vjs.on(target, 'dispose', cleanRemover);
|
||||
|
||||
// Should be a component
|
||||
// Not using `instanceof vjs.Component` because it makes mock players difficult
|
||||
} else if (typeof first.on === 'function') {
|
||||
// Add the listener to the other component
|
||||
target.on(type, fn);
|
||||
target.on('dispose', cleanRemover);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an event listener from the component's element
|
||||
* Remove an event listener from this component's element
|
||||
*
|
||||
* myComponent.off("eventName", myFunc);
|
||||
* myComponent.off('eventType', 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.
|
||||
* If myFunc is excluded, ALL listeners for the event type will be removed.
|
||||
* If eventType is excluded, ALL listeners will be removed from the component.
|
||||
*
|
||||
* Alternatively you can use `off` to remove listeners that were added to other
|
||||
* elements or components using `myComponent.on(otherComponent...`.
|
||||
* In this case both the event type and listener function are REQUIRED.
|
||||
*
|
||||
* myComponent.off(otherElement, 'eventType', myFunc);
|
||||
* myComponent.off(otherComponent, 'eventType', myFunc);
|
||||
*
|
||||
* @param {String=|vjs.Component} first The event type or other component
|
||||
* @param {Function=|String} second The listener function or event type
|
||||
* @param {Function=} third The listener for other component
|
||||
* @return {vjs.Component}
|
||||
*/
|
||||
vjs.Component.prototype.off = function(type, fn){
|
||||
vjs.off(this.el_, type, fn);
|
||||
vjs.Component.prototype.off = function(first, second, third){
|
||||
var target, otherComponent, type, fn, otherEl;
|
||||
|
||||
if (!first || typeof first === 'string' || vjs.obj.isArray(first)) {
|
||||
vjs.off(this.el_, first, second);
|
||||
} else {
|
||||
target = first;
|
||||
type = second;
|
||||
// Ensure there's at least a guid, even if the function hasn't been used
|
||||
fn = vjs.bind(this, third);
|
||||
|
||||
// Remove the dispose listener on this component,
|
||||
// which was given the same guid as the event listener
|
||||
this.off('dispose', fn);
|
||||
|
||||
if (first.nodeName) {
|
||||
// Remove the listener
|
||||
vjs.off(target, type, fn);
|
||||
// Remove the listener for cleaning the dispose listener
|
||||
vjs.off(target, 'dispose', fn);
|
||||
} else {
|
||||
target.off(type, fn);
|
||||
target.off('dispose', 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
|
||||
* myComponent.one('eventName', myFunc);
|
||||
*
|
||||
* Alternatively you can add a listener to another element or component
|
||||
* that will be triggered only once.
|
||||
*
|
||||
* myComponent.one(otherElement, 'eventName', myFunc);
|
||||
* myComponent.one(otherComponent, 'eventName', myFunc);
|
||||
*
|
||||
* @param {String|vjs.Component} first The event type or other component
|
||||
* @param {Function|String} second The listener function or event type
|
||||
* @param {Function=} third The listener function for other component
|
||||
* @return {vjs.Component}
|
||||
*/
|
||||
vjs.Component.prototype.one = function(type, fn) {
|
||||
vjs.one(this.el_, type, vjs.bind(this, fn));
|
||||
vjs.Component.prototype.one = function(first, second, third) {
|
||||
var target, type, fn, thisComponent, newFunc;
|
||||
|
||||
if (typeof first === 'string' || vjs.obj.isArray(first)) {
|
||||
vjs.one(this.el_, first, vjs.bind(this, second));
|
||||
} else {
|
||||
target = first;
|
||||
type = second;
|
||||
fn = vjs.bind(this, third);
|
||||
thisComponent = this;
|
||||
|
||||
newFunc = function(){
|
||||
thisComponent.off(target, type, newFunc);
|
||||
fn.apply(this, arguments);
|
||||
};
|
||||
// Keep the same function ID so we can remove it later
|
||||
newFunc.guid = fn.guid;
|
||||
|
||||
this.on(target, type, newFunc);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -10,19 +10,20 @@ vjs.MuteToggle = vjs.Button.extend({
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
player.on('volumechange', vjs.bind(this, this.update));
|
||||
this.on(player, 'volumechange', this.update);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
if (player.tech && player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
player.on('loadstart', vjs.bind(this, function(){
|
||||
|
||||
this.on(player, 'loadstart', function(){
|
||||
if (player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -10,8 +10,8 @@ vjs.PlayToggle = vjs.Button.extend({
|
||||
init: function(player, options){
|
||||
vjs.Button.call(this, player, options);
|
||||
|
||||
player.on('play', vjs.bind(this, this.onPlay));
|
||||
player.on('pause', vjs.bind(this, this.onPause));
|
||||
this.on(player, 'play', this.onPlay);
|
||||
this.on(player, 'pause', this.onPause);
|
||||
}
|
||||
});
|
||||
|
||||
@ -32,14 +32,14 @@ vjs.PlayToggle.prototype.onClick = function(){
|
||||
|
||||
// OnPlay - Add the vjs-playing class to the element so it can change appearance
|
||||
vjs.PlayToggle.prototype.onPlay = function(){
|
||||
vjs.removeClass(this.el_, 'vjs-paused');
|
||||
vjs.addClass(this.el_, 'vjs-playing');
|
||||
this.removeClass('vjs-paused');
|
||||
this.addClass('vjs-playing');
|
||||
this.el_.children[0].children[0].innerHTML = this.localize('Pause'); // change the button text to "Pause"
|
||||
};
|
||||
|
||||
// OnPause - Add the vjs-paused class to the element so it can change appearance
|
||||
vjs.PlayToggle.prototype.onPause = function(){
|
||||
vjs.removeClass(this.el_, 'vjs-playing');
|
||||
vjs.addClass(this.el_, 'vjs-paused');
|
||||
this.removeClass('vjs-playing');
|
||||
this.addClass('vjs-paused');
|
||||
this.el_.children[0].children[0].innerHTML = this.localize('Play'); // change the button text to "Play"
|
||||
};
|
||||
|
@ -13,8 +13,8 @@ vjs.PlaybackRateMenuButton = vjs.MenuButton.extend({
|
||||
this.updateVisibility();
|
||||
this.updateLabel();
|
||||
|
||||
player.on('loadstart', vjs.bind(this, this.updateVisibility));
|
||||
player.on('ratechange', vjs.bind(this, this.updateLabel));
|
||||
this.on(player, 'loadstart', this.updateVisibility);
|
||||
this.on(player, 'ratechange', this.updateLabel);
|
||||
}
|
||||
});
|
||||
|
||||
@ -115,7 +115,7 @@ vjs.PlaybackRateMenuItem = vjs.MenuItem.extend({
|
||||
options['selected'] = rate === 1;
|
||||
vjs.MenuItem.call(this, player, options);
|
||||
|
||||
this.player().on('ratechange', vjs.bind(this, this.update));
|
||||
this.on(player, 'ratechange', this.update);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -36,7 +36,7 @@ vjs.SeekBar = vjs.Slider.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Slider.call(this, player, options);
|
||||
player.on('timeupdate', vjs.bind(this, this.updateARIAAttributes));
|
||||
this.on(player, 'timeupdate', this.updateARIAAttributes);
|
||||
player.ready(vjs.bind(this, this.updateARIAAttributes));
|
||||
}
|
||||
});
|
||||
@ -118,7 +118,7 @@ vjs.LoadProgressBar = vjs.Component.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
player.on('progress', vjs.bind(this, this.update));
|
||||
this.on(player, 'progress', this.update);
|
||||
}
|
||||
});
|
||||
|
||||
@ -197,7 +197,7 @@ vjs.PlayProgressBar.prototype.createEl = function(){
|
||||
vjs.SeekHandle = vjs.SliderHandle.extend({
|
||||
init: function(player, options) {
|
||||
vjs.SliderHandle.call(this, player, options);
|
||||
player.on('timeupdate', vjs.bind(this, this.updateContent));
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,7 +9,7 @@ vjs.CurrentTimeDisplay = vjs.Component.extend({
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
|
||||
player.on('timeupdate', vjs.bind(this, this.updateContent));
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
}
|
||||
});
|
||||
|
||||
@ -50,7 +50,7 @@ vjs.DurationDisplay = vjs.Component.extend({
|
||||
// so the value cannot be written out using this method.
|
||||
// Once the order of durationchange and this.player_.duration() being set is figured out,
|
||||
// this can be updated.
|
||||
player.on('timeupdate', vjs.bind(this, this.updateContent));
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
}
|
||||
});
|
||||
|
||||
@ -110,7 +110,7 @@ vjs.RemainingTimeDisplay = vjs.Component.extend({
|
||||
init: function(player, options){
|
||||
vjs.Component.call(this, player, options);
|
||||
|
||||
player.on('timeupdate', vjs.bind(this, this.updateContent));
|
||||
this.on(player, 'timeupdate', this.updateContent);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -14,13 +14,13 @@ vjs.VolumeControl = vjs.Component.extend({
|
||||
if (player.tech && player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
player.on('loadstart', vjs.bind(this, function(){
|
||||
this.on(player, 'loadstart', function(){
|
||||
if (player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ vjs.VolumeBar = vjs.Slider.extend({
|
||||
/** @constructor */
|
||||
init: function(player, options){
|
||||
vjs.Slider.call(this, player, options);
|
||||
player.on('volumechange', vjs.bind(this, this.updateARIAAttributes));
|
||||
this.on(player, 'volumechange', this.updateARIAAttributes);
|
||||
player.ready(vjs.bind(this, this.updateARIAAttributes));
|
||||
}
|
||||
});
|
||||
|
@ -8,19 +8,19 @@ vjs.VolumeMenuButton = vjs.MenuButton.extend({
|
||||
vjs.MenuButton.call(this, player, options);
|
||||
|
||||
// Same listeners as MuteToggle
|
||||
player.on('volumechange', vjs.bind(this, this.update));
|
||||
this.on(player, 'volumechange', this.update);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
if (player.tech && player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
player.on('loadstart', vjs.bind(this, function(){
|
||||
this.on(player, 'loadstart', function(){
|
||||
if (player.tech['featuresVolumeControl'] === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
}));
|
||||
});
|
||||
this.addClass('vjs-menu-button');
|
||||
}
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ vjs.ErrorDisplay = vjs.Component.extend({
|
||||
vjs.Component.call(this, player, options);
|
||||
|
||||
this.update();
|
||||
player.on('error', vjs.bind(this, this.update));
|
||||
this.on(player, 'error', this.update);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -92,10 +92,10 @@ vjs.Flash = vjs.MediaTechController.extend({
|
||||
// bugzilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=836786
|
||||
if (vjs.IS_FIREFOX) {
|
||||
this.ready(function(){
|
||||
vjs.on(this.el(), 'mousemove', vjs.bind(this, function(){
|
||||
this.on('mousemove', function(){
|
||||
// since it's a custom event, don't bubble higher than the player
|
||||
this.player().trigger({ 'type':'mousemove', 'bubbles': false });
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ vjs.Html5.prototype.createEl = function(){
|
||||
// Triggers removed using this.off when disposed
|
||||
vjs.Html5.prototype.setupTriggers = function(){
|
||||
for (var i = vjs.Html5.Events.length - 1; i >= 0; i--) {
|
||||
vjs.on(this.el_, vjs.Html5.Events[i], vjs.bind(this, this.eventHandler));
|
||||
this.on(vjs.Html5.Events[i], this.eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
@ -214,16 +214,16 @@ vjs.Html5.prototype.enterFullScreen = function(){
|
||||
var video = this.el_;
|
||||
|
||||
if ('webkitDisplayingFullscreen' in video) {
|
||||
this.one('webkitbeginfullscreen', vjs.bind(this, function(e) {
|
||||
this.one('webkitbeginfullscreen', function() {
|
||||
this.player_.isFullscreen(true);
|
||||
|
||||
this.one('webkitendfullscreen', vjs.bind(this, function(e) {
|
||||
this.one('webkitendfullscreen', function() {
|
||||
this.player_.isFullscreen(false);
|
||||
this.player_.trigger('fullscreenchange');
|
||||
}));
|
||||
});
|
||||
|
||||
this.player_.trigger('fullscreenchange');
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (video.paused && video.networkState <= video.HAVE_METADATA) {
|
||||
|
@ -53,24 +53,21 @@ vjs.MediaTechController = vjs.Component.extend({
|
||||
* any controls will still keep the user active
|
||||
*/
|
||||
vjs.MediaTechController.prototype.initControlsListeners = function(){
|
||||
var player, tech, activateControls, deactivateControls;
|
||||
var player, activateControls;
|
||||
|
||||
tech = this;
|
||||
player = this.player();
|
||||
|
||||
var activateControls = function(){
|
||||
activateControls = function(){
|
||||
if (player.controls() && !player.usingNativeControls()) {
|
||||
tech.addControlsListeners();
|
||||
this.addControlsListeners();
|
||||
}
|
||||
};
|
||||
|
||||
deactivateControls = vjs.bind(tech, tech.removeControlsListeners);
|
||||
|
||||
// Set up event listeners once the tech is ready and has an element to apply
|
||||
// listeners to
|
||||
this.ready(activateControls);
|
||||
player.on('controlsenabled', activateControls);
|
||||
player.on('controlsdisabled', deactivateControls);
|
||||
this.on(player, 'controlsenabled', activateControls);
|
||||
this.on(player, 'controlsdisabled', this.removeControlsListeners);
|
||||
|
||||
// if we're loading the playback object after it has started loading or playing the
|
||||
// video (often with autoplay on) then the loadstart event has already fired and we
|
||||
@ -201,10 +198,12 @@ vjs.MediaTechController.prototype.stopTrackingProgress = function(){ clearInterv
|
||||
|
||||
/*! Time Tracking -------------------------------------------------------------- */
|
||||
vjs.MediaTechController.prototype.manualTimeUpdatesOn = function(){
|
||||
var player = this.player_;
|
||||
|
||||
this.manualTimeUpdates = true;
|
||||
|
||||
this.player().on('play', vjs.bind(this, this.trackCurrentTime));
|
||||
this.player().on('pause', vjs.bind(this, this.stopTrackingCurrentTime));
|
||||
this.on(player, 'play', this.trackCurrentTime);
|
||||
this.on(player, 'pause', this.stopTrackingCurrentTime);
|
||||
// timeupdate is also called by .currentTime whenever current time is set
|
||||
|
||||
// Watch for native timeupdate event
|
||||
|
@ -22,13 +22,10 @@ vjs.Slider = vjs.Component.extend({
|
||||
this.on('blur', this.onBlur);
|
||||
this.on('click', this.onClick);
|
||||
|
||||
this.player_.on('controlsvisible', vjs.bind(this, this.update));
|
||||
|
||||
player.on(this.playerEvent, vjs.bind(this, this.update));
|
||||
this.on(player, 'controlsvisible', this.update);
|
||||
this.on(player, this.playerEvent, this.update);
|
||||
|
||||
this.boundEvents = {};
|
||||
|
||||
|
||||
this.boundEvents.move = vjs.bind(this, this.onMouseMove);
|
||||
this.boundEvents.end = vjs.bind(this, this.onMouseUp);
|
||||
}
|
||||
|
@ -733,7 +733,7 @@ vjs.TextTrackMenuItem = vjs.MenuItem.extend({
|
||||
options['selected'] = track.dflt();
|
||||
vjs.MenuItem.call(this, player, options);
|
||||
|
||||
this.player_.on(track.kind() + 'trackchange', vjs.bind(this, this.update));
|
||||
this.on(player, track.kind() + 'trackchange', this.update);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -161,6 +161,132 @@ test('should trigger a listener once using one()', function(){
|
||||
comp.trigger('test-event');
|
||||
});
|
||||
|
||||
test('should add listeners to other components and remove them', function(){
|
||||
var player = getFakePlayer(),
|
||||
comp1 = new vjs.Component(player),
|
||||
comp2 = new vjs.Component(player),
|
||||
listenerFired = 0,
|
||||
testListener;
|
||||
|
||||
testListener = function(){
|
||||
equal(this, comp1, 'listener has the first component as context');
|
||||
listenerFired++;
|
||||
};
|
||||
|
||||
comp1.on(comp2, 'test-event', testListener);
|
||||
comp2.trigger('test-event');
|
||||
equal(listenerFired, 1, 'listener was fired once');
|
||||
|
||||
listenerFired = 0;
|
||||
comp1.off(comp2, 'test-event', testListener);
|
||||
comp2.trigger('test-event');
|
||||
equal(listenerFired, 0, 'listener was not fired after being removed');
|
||||
|
||||
// this component is disposed first
|
||||
listenerFired = 0;
|
||||
comp1.on(comp2, 'test-event', testListener);
|
||||
comp1.dispose();
|
||||
comp2.trigger('test-event');
|
||||
equal(listenerFired, 0, 'listener was removed when this component was disposed first');
|
||||
comp1.off = function(){ throw 'Comp1 off called'; };
|
||||
comp2.dispose();
|
||||
ok(true, 'this component removed dispose listeners from other component');
|
||||
});
|
||||
|
||||
test('should add listeners to other components and remove when them other component is disposed', function(){
|
||||
var player = getFakePlayer(),
|
||||
comp1 = new vjs.Component(player),
|
||||
comp2 = new vjs.Component(player),
|
||||
listenerFired = 0,
|
||||
testListener;
|
||||
|
||||
testListener = function(){
|
||||
equal(this, comp1, 'listener has the first component as context');
|
||||
listenerFired++;
|
||||
};
|
||||
|
||||
comp1.on(comp2, 'test-event', testListener);
|
||||
comp2.dispose();
|
||||
comp2.off = function(){ throw 'Comp2 off called'; };
|
||||
comp1.dispose();
|
||||
ok(true, 'this component removed dispose listener from this component that referenced other component');
|
||||
});
|
||||
|
||||
test('should add listeners to other components that are fired once', function(){
|
||||
var player = getFakePlayer(),
|
||||
comp1 = new vjs.Component(player),
|
||||
comp2 = new vjs.Component(player),
|
||||
listenerFired = 0,
|
||||
testListener;
|
||||
|
||||
testListener = function(){
|
||||
equal(this, comp1, 'listener has the first component as context');
|
||||
listenerFired++;
|
||||
};
|
||||
|
||||
comp1.one(comp2, 'test-event', testListener);
|
||||
comp2.trigger('test-event');
|
||||
equal(listenerFired, 1, 'listener was executed once');
|
||||
comp2.trigger('test-event');
|
||||
equal(listenerFired, 1, 'listener was executed only once');
|
||||
});
|
||||
|
||||
test('should add listeners to other element and remove them', function(){
|
||||
var player = getFakePlayer(),
|
||||
comp1 = new vjs.Component(player),
|
||||
el = document.createElement('div'),
|
||||
listenerFired = 0,
|
||||
testListener;
|
||||
|
||||
testListener = function(){
|
||||
equal(this, comp1, 'listener has the first component as context');
|
||||
listenerFired++;
|
||||
};
|
||||
|
||||
comp1.on(el, 'test-event', testListener);
|
||||
vjs.trigger(el, 'test-event');
|
||||
equal(listenerFired, 1, 'listener was fired once');
|
||||
|
||||
listenerFired = 0;
|
||||
comp1.off(el, 'test-event', testListener);
|
||||
vjs.trigger(el, 'test-event');
|
||||
equal(listenerFired, 0, 'listener was not fired after being removed from other element');
|
||||
|
||||
// this component is disposed first
|
||||
listenerFired = 0;
|
||||
comp1.on(el, 'test-event', testListener);
|
||||
comp1.dispose();
|
||||
vjs.trigger(el, 'test-event');
|
||||
equal(listenerFired, 0, 'listener was removed when this component was disposed first');
|
||||
comp1.off = function(){ throw 'Comp1 off called'; };
|
||||
try {
|
||||
vjs.trigger(el, 'dispose');
|
||||
} catch(e) {
|
||||
ok(false, 'listener was not removed from other element');
|
||||
}
|
||||
vjs.trigger(el, 'dispose');
|
||||
ok(true, 'this component removed dispose listeners from other element');
|
||||
});
|
||||
|
||||
test('should add listeners to other components that are fired once', function(){
|
||||
var player = getFakePlayer(),
|
||||
comp1 = new vjs.Component(player),
|
||||
el = document.createElement('div'),
|
||||
listenerFired = 0,
|
||||
testListener;
|
||||
|
||||
testListener = function(){
|
||||
equal(this, comp1, 'listener has the first component as context');
|
||||
listenerFired++;
|
||||
};
|
||||
|
||||
comp1.one(el, 'test-event', testListener);
|
||||
vjs.trigger(el, 'test-event');
|
||||
equal(listenerFired, 1, 'listener was executed once');
|
||||
vjs.trigger(el, 'test-event');
|
||||
equal(listenerFired, 1, 'listener was executed only once');
|
||||
});
|
||||
|
||||
test('should trigger a listener when ready', function(){
|
||||
expect(2);
|
||||
|
||||
|
23
test/unit/controls.js
vendored
23
test/unit/controls.js
vendored
@ -35,7 +35,10 @@ test('should test and toggle volume control on `loadstart`', function(){
|
||||
language: noop,
|
||||
languages: noop,
|
||||
on: function(event, callback){
|
||||
listeners.push(callback);
|
||||
// don't fire dispose listeners
|
||||
if (event !== 'dispose') {
|
||||
listeners.push(callback);
|
||||
}
|
||||
},
|
||||
ready: noop,
|
||||
volume: function(){
|
||||
@ -53,30 +56,24 @@ test('should test and toggle volume control on `loadstart`', function(){
|
||||
volumeControl = new vjs.VolumeControl(player);
|
||||
muteToggle = new vjs.MuteToggle(player);
|
||||
|
||||
ok(volumeControl.el().className.indexOf('vjs-hidden') < 0,
|
||||
'volumeControl is hidden initially');
|
||||
ok(muteToggle.el().className.indexOf('vjs-hidden') < 0,
|
||||
'muteToggle is hidden initially');
|
||||
equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl is hidden initially');
|
||||
equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle is hidden initially');
|
||||
|
||||
player.tech['featuresVolumeControl'] = false;
|
||||
for (i = 0; i < listeners.length; i++) {
|
||||
listeners[i]();
|
||||
}
|
||||
|
||||
ok(volumeControl.el().className.indexOf('vjs-hidden') >= 0,
|
||||
'volumeControl does not hide itself');
|
||||
ok(muteToggle.el().className.indexOf('vjs-hidden') >= 0,
|
||||
'muteToggle does not hide itself');
|
||||
equal(volumeControl.hasClass('vjs-hidden'), true, 'volumeControl does not hide itself');
|
||||
equal(muteToggle.hasClass('vjs-hidden'), true, 'muteToggle does not hide itself');
|
||||
|
||||
player.tech['featuresVolumeControl'] = true;
|
||||
for (i = 0; i < listeners.length; i++) {
|
||||
listeners[i]();
|
||||
}
|
||||
|
||||
ok(volumeControl.el().className.indexOf('vjs-hidden') < 0,
|
||||
'volumeControl does not show itself');
|
||||
ok(muteToggle.el().className.indexOf('vjs-hidden') < 0,
|
||||
'muteToggle does not show itself');
|
||||
equal(volumeControl.hasClass('vjs-hidden'), false, 'volumeControl does not show itself');
|
||||
equal(muteToggle.hasClass('vjs-hidden'), false, 'muteToggle does not show itself');
|
||||
});
|
||||
|
||||
test('calculateDistance should use changedTouches, if available', function() {
|
||||
|
@ -120,6 +120,7 @@ test('dispose() should stop time tracking', function() {
|
||||
var tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: noop,
|
||||
off: noop,
|
||||
trigger: noop
|
||||
});
|
||||
tech.dispose();
|
||||
|
Loading…
Reference in New Issue
Block a user