Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds onFirstListener and onNoListener callbacks #4040

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# EditorConfig: http://EditorConfig.org
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
raw
*.sw?
.DS_Store
.idea
node_modules
bower_components
20 changes: 20 additions & 0 deletions backbone.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@
return events;
};

// An overridable callback to allow event objects to know when something starts
// listening to it when previously it had no listeners. Useful for knowing this
// object needs to start listening to something else.
// (which might be expensive to run all the time)
Events.onFirstListener = function() {};

// An overridable callback to allow event objects to know when the last listener
// stops listening to it. Useful to know this object can stop listening to it's
// sources.
Events.onNoListeners = function() {};

// Bind an event to a `callback` function. Passing `"all"` will bind
// the callback to all events fired.
Events.on = function(name, callback, context) {
Expand All @@ -165,6 +176,10 @@

// Guard the `listening` argument from the public API.
var internalOn = function(obj, name, callback, context, listening) {
if (!obj._events || _.isEmpty(obj._events)) {
obj.onFirstListener();
}

obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
context: context,
ctx: obj,
Expand Down Expand Up @@ -222,6 +237,11 @@
context: context,
listeners: this._listeners
});

if (!this._events || _.isEmpty(this._events)) {
this.onNoListeners();
}

return this;
};

Expand Down
40 changes: 40 additions & 0 deletions test/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,4 +703,44 @@
two.trigger('y', 2);
});

QUnit.test('onFirstListener & onNoListeners are triggered', function(assert) {
assert.expect(6);
var first = 1;
var none = 1;

var expectedFirsts = 0;
var expectedNones = 0;

var obj = _.extend({}, Backbone.Events, {
onFirstListener: function() { assert.ok(first++ === expectedFirsts); },
onNoListeners: function() { assert.ok(none++ === expectedNones); }
});
var obj2 = _.extend({}, Backbone.Events, {
onFirstListener: function() { assert.ok(false, 'Should not have been called'); },
onNoListeners: function() { assert.ok(false, 'Should not have been called'); }
});
var fn = function() {};

expectedFirsts = 1;
obj.on('a', fn); // expects a call to onFirstListener
obj.on('b', fn);

obj.off('a', fn); // no call
expectedNones = 1;
obj.off('b', fn); // expects a call to onNoListeners

expectedFirsts = 2;
obj2.listenTo(obj, 'a', fn);
obj2.listenTo(obj, 'b', fn);

obj2.stopListening(obj, 'a', fn);
expectedNones = 2;
obj2.stopListening(obj, 'b', fn);

expectedFirsts = 3;
obj2.listenToOnce(obj, 'a', fn);
expectedNones = 3;
obj.trigger('a');
});

})();