Skip to content

Commit

Permalink
Added support for listening to multiple events through a types array. c…
Browse files Browse the repository at this point in the history
…loses videojs#1231
  • Loading branch information
themicp authored and paullryan committed Jul 17, 2014
1 parent 5eb3055 commit 285dffb
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CHANGELOG
* Exported missing Player API methods (remainingTime, supportsFullScreen, enterFullWindow, exitFullWindow, preload) ([view](https://github.com/videojs/video.js/pull/1328))
* Added a base for running saucelabs tests from grunt ([view](https://github.com/videojs/video.js/pull/1215))
* Added additional browsers for saucelabs testing ([view](https://github.com/videojs/video.js/pull/1216))
* Added support for listening to multiple events through a types array ([view](https://github.com/videojs/video.js/pull/1231))

--------------------

Expand Down
32 changes: 29 additions & 3 deletions src/js/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
* and adds a generic handler to the element's event,
* along with a unique id (guid) to the element.
* @param {Element|Object} elem Element or object to bind listeners to
* @param {String} type Type of event to bind to.
* @param {String|Array} type Type of event to bind to.
* @param {Function} fn Event listener.
* @private
*/
vjs.on = function(elem, type, fn){
if (vjs.obj.isArray(type)) {
return _handleMultipleEvents(vjs.on, elem, type, fn);
}

var data = vjs.getData(elem);

// We need a place to store all our handler data
Expand Down Expand Up @@ -64,7 +68,7 @@ vjs.on = function(elem, type, fn){
/**
* Removes event listeners from an element
* @param {Element|Object} elem Object to remove listeners from
* @param {String=} type Type of listener to remove. Don't include to remove all events from element.
* @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element.
* @param {Function} fn Specific listener to remove. Don't incldue to remove listeners for an event type.
* @private
*/
Expand All @@ -77,6 +81,10 @@ vjs.off = function(elem, type, fn) {
// If no events exist, nothing to unbind
if (!data.handlers) { return; }

if (vjs.obj.isArray(type)) {
return _handleMultipleEvents(vjs.off, elem, type, fn);
}

// Utility function
var removeType = function(t){
data.handlers[t] = [];
Expand Down Expand Up @@ -337,15 +345,33 @@ vjs.trigger = function(elem, event) {
/**
* Trigger a listener only once for an event
* @param {Element|Object} elem Element or object to
* @param {String} type
* @param {String|Array} type
* @param {Function} fn
* @private
*/
vjs.one = function(elem, type, fn) {
if (vjs.obj.isArray(type)) {
return _handleMultipleEvents(vjs.one, elem, type, fn);
}
var func = function(){
vjs.off(elem, type, func);
fn.apply(this, arguments);
};
// copy the guid to the new function so it can removed using the original function's ID
func.guid = fn.guid = fn.guid || vjs.guid++;
vjs.on(elem, type, func);
};

/**
* Loops through an array of event types and calls the requested method for each type.
* @param {Function} fn The event method we want to use.
* @param {Element|Object} elem Element or object to bind listeners to
* @param {String} type Type of event to bind to.
* @param {Function} callback Event listener.
* @private
*/
function _handleMultipleEvents(fn, elem, type, callback) {
vjs.arr.forEach(type, function(type) {
fn(elem, type, callback); //Call the event method for each one of the types
});
}
25 changes: 25 additions & 0 deletions src/js/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -843,3 +843,28 @@ vjs.findPosition = function(el) {
top: vjs.round(top)
};
};

/**
* Array functions container
* @type {Object}
* @private
*/
vjs.arr = {};

/*
* Loops through an array and runs a function for each item inside it.
* @param {Array} array The array
* @param {Function} callback The function to be run for each item
* @param {*} thisArg The `this` binding of callback
* @returns {Array} The array
* @private
*/
vjs.arr.forEach = function(array, callback, thisArg) {
if (vjs.obj.isArray(array) && callback instanceof Function) {
for (var i = 0, len = array.length; i < len; ++i) {
callback.call(thisArg || vjs, array[i], i, array);
}
}

return array;
};
67 changes: 67 additions & 0 deletions test/unit/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ test('should add and remove an event listener to an element', function(){
vjs.trigger(el, 'click'); // No click should happen.
});

test('should add and remove multiple event listeners to an element with a single call', function(){
expect(6);

var el = document.createElement('div');
var listener = function(){
ok(true, 'Callback triggered');
};

vjs.on(el, ['click', 'event1', 'event2'], listener);

vjs.trigger(el, 'click');
vjs.trigger(el, 'click');
vjs.off(el, 'click', listener);
vjs.trigger(el, 'click'); // No click should happen.

vjs.trigger(el, 'event1');
vjs.trigger(el, 'event1');
vjs.off(el, 'event1', listener);
vjs.trigger(el, 'event1'); // No event1 should happen.

vjs.trigger(el, 'event2');
vjs.trigger(el, 'event2');
vjs.off(el, 'event2', listener);
vjs.trigger(el, 'event2'); // No event2 should happen.
});

test('should remove all listeners of a type', function(){
var el = document.createElement('div');
var clicks = 0;
Expand All @@ -36,6 +62,30 @@ test('should remove all listeners of a type', function(){
ok(clicks === 2, 'no click listeners fired');
});

test('should remove all listeners of an array of types', function(){
var el = document.createElement('div');
var calls = 0;
var listener = function(){
calls++;
};
var listener2 = function(){
calls++;
};

vjs.on(el, ['click', 'event1'], listener);
vjs.on(el, ['click', 'event1'], listener2);
vjs.trigger(el, 'click'); // 2 calls
vjs.trigger(el, 'event1'); // 2 calls

ok(calls === 4, 'both click listeners fired');

vjs.off(el, ['click', 'event1']);
vjs.trigger(el, 'click'); // No click should happen.
vjs.trigger(el, 'event1'); // No event1 should happen.

ok(calls === 4, 'no event listeners fired');
});

test('should remove all listeners from an element', function(){
expect(2);

Expand Down Expand Up @@ -73,6 +123,23 @@ test('should listen only once', function(){
vjs.trigger(el, 'click'); // No click should happen.
});

test( 'should listen only once in multiple events from a single call', function(){
expect(3);

var el = document.createElement('div');
var listener = function(){
ok(true, 'Callback Triggered');
};

vjs.one(el, ['click', 'event1', 'event2'], listener);
vjs.trigger(el, 'click'); // 1 click
vjs.trigger(el, 'click'); // No click should happen.
vjs.trigger(el, 'event1'); // event1 must be handled.
vjs.trigger(el, 'event1'); // No event1 should be handled.
vjs.trigger(el, 'event2'); // event2 must be handled.
vjs.trigger(el, 'event2'); // No event2 should be handled.
});

test('should stop immediate propagtion', function(){
expect(1);

Expand Down
22 changes: 22 additions & 0 deletions test/unit/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,25 @@ test('should confirm logging functions work', function() {
console.error = origError;
}
});

test('should loop through each element of an array', function() {
expect(10);
var a = [1, 2, 3];
var sum = 0;
var i = 0;
var thisArg = {};

vjs.arr.forEach(a, function(item, iterator, array) {
sum += item;
deepEqual(array, a, 'The array arg should match the original array');
equal(i++, iterator, 'The indexes should match');
equal(this, thisArg, 'The context should equal the thisArg');
}, thisArg);
ok(sum, 6);

vjs.arr.forEach(a, function(){
if (this !== vjs) {
ok(false, 'default context should be vjs');
}
});
});

0 comments on commit 285dffb

Please sign in to comment.