Implement once with minimal change to other Events methods.

This commit is contained in:
Brad Dunbar
2012-12-07 18:59:58 -05:00
parent 615b08c9e8
commit 1be8eb0847
2 changed files with 44 additions and 17 deletions

View File

@@ -67,9 +67,6 @@
// Regular expression used to split event strings
var eventSplitter = /\s+/;
// Internal flag used to set event callbacks `once`.
var once = false;
// A module that can be mixed in to *any object* in order to provide it with
// custom events. You may bind with `on` or remove with `off` callback functions
// to an event; `trigger`-ing an event fires all callbacks in succession.
@@ -99,7 +96,7 @@
while (event = events.shift()) {
list = calls[event] || (calls[event] = []);
list.push(callback, context, once ? {} : null);
list.push(callback, context);
}
return this;
@@ -108,9 +105,22 @@
// Bind events to only be triggered a single time. After the first time
// the callback is invoked, it will be removed.
once: function(events, callback, context) {
once = true;
this.on(events, callback, context);
once = false;
if (_.isObject(events)) {
for (var key in events) {
this.once(key, events[key], callback);
}
return this;
}
_.each(events.split(eventSplitter), function(event) {
var f = _.once(function() {
this.off(event, f);
callback.apply(context, arguments);
});
f._callback = callback;
this.on(event, f, this);
}, this);
return this;
},
@@ -143,9 +153,12 @@
continue;
}
for (i = list.length - 3; i >= 0; i -= 3) {
if (!(callback && list[i] !== callback || context && list[i + 1] !== context)) {
list.splice(i, 3);
for (i = list.length - 2; i >= 0; i -= 2) {
if (!(
callback && list[i] !== callback && list[i]._callback !== callback
|| context && list[i + 1] !== context
)) {
list.splice(i, 2);
}
}
}
@@ -179,18 +192,15 @@
// Execute event callbacks.
if (list) {
for (i = 0, length = list.length; i < length; i += 3) {
callback = list[i], context = list[i + 1], onced = list[i + 2];
if (onced) calls[event].splice(i, 3);
if (!onced || !onced.dead) callback.apply(context || this, rest);
if (onced) onced.dead = true;
for (i = 0, length = list.length; i < length; i += 2) {
list[i].apply(list[i + 1] || this, rest);
}
}
// Execute "all" callbacks.
if (all) {
args = [event].concat(rest);
for (i = 0, length = all.length; i < length; i += 3) {
for (i = 0, length = all.length; i < length; i += 2) {
all[i].apply(all[i + 1] || this, args);
}
}

View File

@@ -227,7 +227,7 @@ $(document).ready(function() {
test("once", 2, function() {
// Same as the previous test, but we use once rather than having to explicitly unbind
var obj = { counterA: 0, counterB: 0 };
_.extend(obj,Backbone.Events);
_.extend(obj, Backbone.Events);
var incrA = function(){ obj.counterA += 1; obj.trigger('event'); };
var incrB = function(){ obj.counterB += 1 };
obj.once('event', incrA);
@@ -310,4 +310,21 @@ $(document).ready(function() {
obj.trigger('async');
});
test("Off during iteration with once.", 2, function() {
var obj = _.extend({}, Backbone.Events);
var f = function(){ this.off('event', f); };
obj.on('event', f);
obj.once('event', function(){});
obj.on('event', function(){ ok(true); });
obj.trigger('event');
obj.trigger('event');
});
test("once with multiple events.", 2, function() {
var obj = _.extend({}, Backbone.Events);
obj.once('x y', function() { ok(true); });
obj.trigger('x y');
});
});