/* Copyright (c) 2010,2011,2012,2013,2014 Morgan Roderick http://roderick.dk License: MIT - http://mrgnrdrck.mit-license.org https://github.com/mroderick/PubSubJS */ (function (root, factory){ 'use strict'; if (typeof define === 'function' && define.amd){ // AMD. Register as an anonymous module. define(['exports'], factory); } else if (typeof exports === 'object'){ // CommonJS factory(exports); } // Browser globals var PubSub = {}; root.PubSub = PubSub; factory(PubSub); }(( typeof window === 'object' && window ) || this, function (PubSub){ 'use strict'; var messages = {}, lastUid = -1; function hasKeys(obj){ var key; for (key in obj){ if ( obj.hasOwnProperty(key) ){ return true; } } return false; } /** * Returns a function that throws the passed exception, for use as argument for setTimeout * @param { Object } ex An Error object */ function throwException( ex ){ return function reThrowException(){ throw ex; }; } function callSubscriberWithDelayedExceptions( subscriber, message, data ){ try { subscriber( message, data ); } catch( ex ){ setTimeout( throwException( ex ), 0); } } function callSubscriberWithImmediateExceptions( subscriber, message, data ){ subscriber( message, data ); } function deliverMessage( originalMessage, matchedMessage, data, immediateExceptions ){ var subscribers = messages[matchedMessage], callSubscriber = immediateExceptions ? callSubscriberWithImmediateExceptions : callSubscriberWithDelayedExceptions, s; if ( !messages.hasOwnProperty( matchedMessage ) ) { return; } for (s in subscribers){ if ( subscribers.hasOwnProperty(s)){ callSubscriber( subscribers[s], originalMessage, data ); } } } function createDeliveryFunction( message, data, immediateExceptions ){ return function deliverNamespaced(){ var topic = String( message ), position = topic.lastIndexOf( '.' ); // deliver the message as it is now deliverMessage(message, message, data, immediateExceptions); // trim the hierarchy and deliver message to each level while( position !== -1 ){ topic = topic.substr( 0, position ); position = topic.lastIndexOf('.'); deliverMessage( message, topic, data, immediateExceptions ); } }; } function messageHasSubscribers( message ){ var topic = String( message ), found = Boolean(messages.hasOwnProperty( topic ) && hasKeys(messages[topic])), position = topic.lastIndexOf( '.' ); while ( !found && position !== -1 ){ topic = topic.substr( 0, position ); position = topic.lastIndexOf( '.' ); found = Boolean(messages.hasOwnProperty( topic ) && hasKeys(messages[topic])); } return found; } function publish( message, data, sync, immediateExceptions ){ var deliver = createDeliveryFunction( message, data, immediateExceptions ), hasSubscribers = messageHasSubscribers( message ); if ( !hasSubscribers ){ return false; } if ( sync === true ){ deliver(); } else { setTimeout( deliver, 0 ); } return true; } /** * PubSub.publish( message[, data] ) -> Boolean * - message (String): The message to publish * - data: The data to pass to subscribers * Publishes the the message, passing the data to it's subscribers **/ PubSub.publish = function( message, data ){ return publish( message, data, false, PubSub.immediateExceptions ); }; /** * PubSub.publishSync( message[, data] ) -> Boolean * - message (String): The message to publish * - data: The data to pass to subscribers * Publishes the the message synchronously, passing the data to it's subscribers **/ PubSub.publishSync = function( message, data ){ return publish( message, data, true, PubSub.immediateExceptions ); }; /** * PubSub.subscribe( message, func ) -> String * - message (String): The message to subscribe to * - func (Function): The function to call when a new message is published * Subscribes the passed function to the passed message. Every returned token is unique and should be stored if * you need to unsubscribe **/ PubSub.subscribe = function( message, func ){ if ( typeof func !== 'function'){ return false; } // message is not registered yet if ( !messages.hasOwnProperty( message ) ){ messages[message] = {}; } // forcing token as String, to allow for future expansions without breaking usage // and allow for easy use as key names for the 'messages' object var token = 'uid_' + String(++lastUid); messages[message][token] = func; // return token for unsubscribing return token; }; /* Public: Clears all subscriptions */ PubSub.clearAllSubscriptions = function clearAllSubscriptions(){ messages = {}; }; /*Public: Clear subscriptions by the topic */ PubSub.clearSubscriptions = function clearSubscriptions(topic){ var m; for (m in messages){ if (messages.hasOwnProperty(m) && m.indexOf(topic) === 0){ delete messages[m]; } } }; /* Public: removes subscriptions. * When passed a token, removes a specific subscription. * When passed a function, removes all subscriptions for that function * When passed a topic, removes all subscriptions for that topic (hierarchy) * * value - A token, function or topic to unsubscribe. * * Examples * * // Example 1 - unsubscribing with a token * var token = PubSub.subscribe('mytopic', myFunc); * PubSub.unsubscribe(token); * * // Example 2 - unsubscribing with a function * PubSub.unsubscribe(myFunc); * * // Example 3 - unsubscribing a topic * PubSub.unsubscribe('mytopic'); */ PubSub.unsubscribe = function(value){ var isTopic = typeof value === 'string' && messages.hasOwnProperty(value), isToken = !isTopic && typeof value === 'string', isFunction = typeof value === 'function', result = false, m, message, t; if (isTopic){ delete messages[value]; return; } for ( m in messages ){ if ( messages.hasOwnProperty( m ) ){ message = messages[m]; if ( isToken && message[value] ){ delete message[value]; result = value; // tokens are unique, so we can just stop here break; } if (isFunction) { for ( t in message ){ if (message.hasOwnProperty(t) && message[t] === value){ delete message[t]; result = true; } } } } } return result; }; }));