Skip to content

Instantly share code, notes, and snippets.

@dtan
Created October 19, 2011 21:48
javascript observer pattern
var MyNs = {
events: {
Manager: {}
}
};
/**
* This creates a new Publisher/Subscriber object
* @example:
var eventManager = new MyNs.events.Manager,
events = {
something: {
awesome: function () {
console.log('this event does something awesome');
}
}
};
eventManager.addPublisher('event:name'); // 'event:name' is an arbitrary value
eventManager.subscribe('event:name', events, 'something.awesome');
... some super coding stuff ...
eventManager.publish('event:name');
// in the console:
// this event does something awesome
// to unsubscribe:
eventManager.unsubscribe('event:name', 'something.awesome');
*
* NOTE: if "event:name" is not firing, make sure "event:name" was added via addPublisher
* @return MyNs.events.Manager mapped to PubSubManager. Notice it does not create a "new" instance. This must be declared in your own script
*/
(function (window, MyNs) {
var SLICE = Array.prototype.slice;
function PubSubManager() {}
/**
* Adds an event to publish/fire
* @param arguments {Mixed} 1..n number of events to add
*/
PubSubManager.prototype.addPublisher = function () {
var events = arguments;
if (!this.publishers) {
this.publishers = {};
}
for (var i = 0, len = events.length; i < len; i++) {
if (!this.publishers[events[i]]) {
this.publishers[events[i]] = [];
}
console.log("created this.publishers[" + events[i] + "] with ", this.publishers[events[i]].length, ' publishers')
}
};
/**
* Fires off an event. If it does not fire, make sure it has been added via addPublisher
* @param event {String} The name of the event to fire.
*/
PubSubManager.prototype.publish = function (event) {
var args = SLICE.call(arguments, 1);
if (this.publishers[event]) {
console.log('publishing ', event)
for (var i = 0, len = this.publishers[event].length; i < len; i++) {
this.publishers[event][i].fn.apply(this, args);
}
}
};
/**
* Adds a subscriber to the publisher
* @param event {String} Event name
* @param scope {object} The scope of the @handlerName
* @param handlerName {String} Period delimited string of the namespace to target within the @scope
*/
PubSubManager.prototype.subscribe = function (event, scope, handlerName) {
var curryArr = SLICE .call(arguments, 3);
if (this.publishers[event]) {
// using an internal object allows for unusbscribing objects from events
this.publishers[event].push({
ns: handlerName,
fn: function () {
var normalArgs = SLICE.call(arguments, 0),
nested = handlerName.split('.');
scope = (nested.length > 1) ? getNestedObj(scope, nested) : scope[handlerName];
scope.apply((scope || window), curryArr.concat(normalArgs));
}
});
}
};
/**
* Removes a subscriber from a publisher
* @param event {String} The event/publisher name
* @param handlerName {String} Period delimited string of the namespace to target
*/
PubSubManager.prototype.unsubscribe = function (event, handlerName) {
if (this.publishers[event]) {
for (var i = this.publishers[event].length; i--;) {
if (this.publishers[event][i]) {
if (this.publishers[event][i].ns === handlerName) {
this.publishers[event].splice(i, 1);
return;
}
}
}
}
};
/**
* Recursively finds the "names" (object) within the scope
* @param scope {object} The scope to start from
* @param names {array} The "names" (object) to find within the scope
*/
function getNestedObj(scope, names) {
if (typeof names === 'string') {
return scope[names];
}
var len = names.length;
while (len--) {
var ns = names.shift();
if (scope[ns]) {
scope = scope[ns];
}
}
return scope;
}
MyNs.events.Manager = PubSubManager;
}(window, MyNs));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment