diff --git a/src/tags-input.js b/src/tags-input.js index 137a00f5..4afc66d1 100644 --- a/src/tags-input.js +++ b/src/tags-input.js @@ -230,7 +230,7 @@ tagsInput.directive('tagsInput', function($timeout, $document, $window, $q, tags return $scope.templateScope; }, on: function(name, handler) { - $scope.events.on(name, handler); + $scope.events.on(name, handler, true); return this; } }; diff --git a/src/util.js b/src/util.js index 7e5d39f1..6d2e2fe8 100644 --- a/src/util.js +++ b/src/util.js @@ -106,12 +106,13 @@ tagsInput.factory('tiUtil', function($timeout, $q) { self.simplePubSub = function() { var events = {}; return { - on: function(names, handler) { + on: function(names, handler, first) { names.split(' ').forEach(function(name) { if (!events[name]) { events[name] = []; } - events[name].unshift(handler); + var method = first ? [].unshift : [].push; + method.call(events[name], handler); }); return this; }, diff --git a/test/tags-input.spec.js b/test/tags-input.spec.js index b0dd93a7..3a72d514 100644 --- a/test/tags-input.spec.js +++ b/test/tags-input.spec.js @@ -2142,7 +2142,7 @@ describe('tags-input directive', function() { // Assert expect(obj).toBe(autocompleteObj); - expect(isolateScope.events.on).toHaveBeenCalledWith('dummy event', callback); + expect(isolateScope.events.on).toHaveBeenCalledWith('dummy event', callback, true); }); }); diff --git a/test/util.spec.js b/test/util.spec.js index 0b5f0a12..45f739d8 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -29,6 +29,18 @@ describe('tiUtil factory', function() { expect(callback).toHaveBeenCalledWith('some data'); }); + it('subscribes to an event in reverse order', function() { + // Arrange + var callback = jasmine.createSpy(); + + // Act + sut.on('foo', callback, true); + sut.trigger('foo', 'some data'); + + // Assert + expect(callback).toHaveBeenCalledWith('some data'); + }); + it('subscribes to multiple events', function() { // Arrange var callback = jasmine.createSpy(); @@ -59,19 +71,79 @@ describe('tiUtil factory', function() { expect(callback2).toHaveBeenCalledWith('some data'); }); - it('stops the propagation of an event', function() { + it('subscribes multiple times to the same event in reverse order', function() { // Arrange var callback1 = jasmine.createSpy(), - callback2 = jasmine.createSpy().and.returnValue(false); + callback2 = jasmine.createSpy(); // Act sut.on('foo', callback1); - sut.on('foo', callback2); + sut.on('foo', callback2, true); sut.trigger('foo', 'some data'); // Assert + expect(callback1).toHaveBeenCalledWith('some data'); expect(callback2).toHaveBeenCalledWith('some data'); + }); + + it('guarantees the order of invocation is correct (regular order)', function() { + // Arrange + var calls = [], + callback1 = function() { calls.push('callback1'); }, + callback2 = function() { calls.push('callback2'); }; + + // Act + sut.on('foo', callback1); + sut.on('foo', callback2); + sut.trigger('foo', 'some data'); + + // Assert + expect(calls).toEqual(['callback1', 'callback2']); + }); + + it('guarantees the order of invocation is correct (reverse order)', function() { + // Arrange + var calls = [], + callback1 = function() { calls.push('callback1'); }, + callback2 = function() { calls.push('callback2'); }; + + // Act + sut.on('foo', callback1); + sut.on('foo', callback2, true); + sut.trigger('foo', 'some data'); + + // Assert + expect(calls).toEqual(['callback2', 'callback1']); + }); + + it('stops the propagation of an event (regular order)', function() { + // Arrange + var callback1 = jasmine.createSpy().and.returnValue(false), + callback2 = jasmine.createSpy(); + + // Act + sut.on('foo', callback1); + sut.on('foo', callback2); + sut.trigger('foo', 'some data'); + + // Assert + expect(callback1).toHaveBeenCalledWith('some data'); + expect(callback2).not.toHaveBeenCalled(); + }); + + it('stops the propagation of an event (reverse order)', function() { + // Arrange + var callback1 = jasmine.createSpy(), + callback2 = jasmine.createSpy().and.returnValue(false); + + // Act + sut.on('foo', callback1); + sut.on('foo', callback2, true); + sut.trigger('foo', 'some data'); + + // Assert expect(callback1).not.toHaveBeenCalled(); + expect(callback2).toHaveBeenCalledWith('some data'); }); it('returns the object instance so calls can be chained', function() {