|
@@ -0,0 +1,359 @@
|
|
|
+/**
|
|
|
+ * 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';
|
|
|
+
|
|
|
+ var PubSub = {};
|
|
|
+
|
|
|
+ if (root.PubSub) {
|
|
|
+ PubSub = root.PubSub;
|
|
|
+ console.warn("PubSub already loaded, using existing version");
|
|
|
+ } else {
|
|
|
+ root.PubSub = PubSub;
|
|
|
+ factory(PubSub);
|
|
|
+ }
|
|
|
+ // CommonJS and Node.js module support
|
|
|
+ if (typeof exports === 'object'){
|
|
|
+ if (module !== undefined && module.exports) {
|
|
|
+ exports = module.exports = PubSub; // Node.js specific `module.exports`
|
|
|
+ }
|
|
|
+ exports.PubSub = PubSub; // CommonJS module 1.1.1 spec
|
|
|
+ module.exports = exports = PubSub; // CommonJS
|
|
|
+ }
|
|
|
+ // AMD support
|
|
|
+ /* eslint-disable no-undef */
|
|
|
+ else if (typeof define === 'function' && define.amd){
|
|
|
+ define(function() { return PubSub; });
|
|
|
+ /* eslint-enable no-undef */
|
|
|
+ }
|
|
|
+
|
|
|
+}(( typeof window === 'object' && window ) || this, function (PubSub){
|
|
|
+ 'use strict';
|
|
|
+
|
|
|
+ var messages = {},
|
|
|
+ lastUid = -1,
|
|
|
+ ALL_SUBSCRIBING_MSG = '*';
|
|
|
+
|
|
|
+ function hasKeys(obj){
|
|
|
+ var key;
|
|
|
+
|
|
|
+ for (key in obj){
|
|
|
+ if ( Object.prototype.hasOwnProperty.call(obj, key) ){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a function that throws the passed exception, for use as argument for setTimeout
|
|
|
+ * @alias throwException
|
|
|
+ * @function
|
|
|
+ * @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 ( !Object.prototype.hasOwnProperty.call( messages, matchedMessage ) ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (s in subscribers){
|
|
|
+ if ( Object.prototype.hasOwnProperty.call(subscribers, 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 );
|
|
|
+ }
|
|
|
+
|
|
|
+ deliverMessage(message, ALL_SUBSCRIBING_MSG, data, immediateExceptions);
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ function hasDirectSubscribersFor( message ) {
|
|
|
+ var topic = String( message ),
|
|
|
+ found = Boolean(Object.prototype.hasOwnProperty.call( messages, topic ) && hasKeys(messages[topic]));
|
|
|
+
|
|
|
+ return found;
|
|
|
+ }
|
|
|
+
|
|
|
+ function messageHasSubscribers( message ){
|
|
|
+ var topic = String( message ),
|
|
|
+ found = hasDirectSubscribersFor(topic) || hasDirectSubscribersFor(ALL_SUBSCRIBING_MSG),
|
|
|
+ position = topic.lastIndexOf( '.' );
|
|
|
+
|
|
|
+ while ( !found && position !== -1 ){
|
|
|
+ topic = topic.substr( 0, position );
|
|
|
+ position = topic.lastIndexOf( '.' );
|
|
|
+ found = hasDirectSubscribersFor(topic);
|
|
|
+ }
|
|
|
+
|
|
|
+ return found;
|
|
|
+ }
|
|
|
+
|
|
|
+ function publish( message, data, sync, immediateExceptions ){
|
|
|
+ message = (typeof message === 'symbol') ? message.toString() : message;
|
|
|
+
|
|
|
+ var deliver = createDeliveryFunction( message, data, immediateExceptions ),
|
|
|
+ hasSubscribers = messageHasSubscribers( message );
|
|
|
+
|
|
|
+ if ( !hasSubscribers ){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( sync === true ){
|
|
|
+ deliver();
|
|
|
+ } else {
|
|
|
+ setTimeout( deliver, 0 );
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Publishes the message, passing the data to it's subscribers
|
|
|
+ * @function
|
|
|
+ * @alias publish
|
|
|
+ * @param { String } message The message to publish
|
|
|
+ * @param {} data The data to pass to subscribers
|
|
|
+ * @return { Boolean }
|
|
|
+ */
|
|
|
+ PubSub.publish = function( message, data ){
|
|
|
+ return publish( message, data, false, PubSub.immediateExceptions );
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Publishes the message synchronously, passing the data to it's subscribers
|
|
|
+ * @function
|
|
|
+ * @alias publishSync
|
|
|
+ * @param { String } message The message to publish
|
|
|
+ * @param {} data The data to pass to subscribers
|
|
|
+ * @return { Boolean }
|
|
|
+ */
|
|
|
+ PubSub.publishSync = function( message, data ){
|
|
|
+ return publish( message, data, true, PubSub.immediateExceptions );
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Subscribes the passed function to the passed message. Every returned token is unique and should be stored if you need to unsubscribe
|
|
|
+ * @function
|
|
|
+ * @alias subscribe
|
|
|
+ * @param { String } message The message to subscribe to
|
|
|
+ * @param { Function } func The function to call when a new message is published
|
|
|
+ * @return { String }
|
|
|
+ */
|
|
|
+ PubSub.subscribe = function( message, func ){
|
|
|
+ if ( typeof func !== 'function'){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ message = (typeof message === 'symbol') ? message.toString() : message;
|
|
|
+
|
|
|
+ // message is not registered yet
|
|
|
+ if ( !Object.prototype.hasOwnProperty.call( messages, 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;
|
|
|
+ };
|
|
|
+
|
|
|
+ PubSub.subscribeAll = function( func ){
|
|
|
+ return PubSub.subscribe(ALL_SUBSCRIBING_MSG, func);
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Subscribes the passed function to the passed message once
|
|
|
+ * @function
|
|
|
+ * @alias subscribeOnce
|
|
|
+ * @param { String } message The message to subscribe to
|
|
|
+ * @param { Function } func The function to call when a new message is published
|
|
|
+ * @return { PubSub }
|
|
|
+ */
|
|
|
+ PubSub.subscribeOnce = function( message, func ){
|
|
|
+ var token = PubSub.subscribe( message, function(){
|
|
|
+ // before func apply, unsubscribe message
|
|
|
+ PubSub.unsubscribe( token );
|
|
|
+ func.apply( this, arguments );
|
|
|
+ });
|
|
|
+ return PubSub;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clears all subscriptions
|
|
|
+ * @function
|
|
|
+ * @public
|
|
|
+ * @alias clearAllSubscriptions
|
|
|
+ */
|
|
|
+ PubSub.clearAllSubscriptions = function clearAllSubscriptions(){
|
|
|
+ messages = {};
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clear subscriptions by the topic
|
|
|
+ * @function
|
|
|
+ * @public
|
|
|
+ * @alias clearAllSubscriptions
|
|
|
+ * @return { int }
|
|
|
+ */
|
|
|
+ PubSub.clearSubscriptions = function clearSubscriptions(topic){
|
|
|
+ var m;
|
|
|
+ for (m in messages){
|
|
|
+ if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0){
|
|
|
+ delete messages[m];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ Count subscriptions by the topic
|
|
|
+ * @function
|
|
|
+ * @public
|
|
|
+ * @alias countSubscriptions
|
|
|
+ * @return { Array }
|
|
|
+ */
|
|
|
+ PubSub.countSubscriptions = function countSubscriptions(topic){
|
|
|
+ var m;
|
|
|
+ // eslint-disable-next-line no-unused-vars
|
|
|
+ var token;
|
|
|
+ var count = 0;
|
|
|
+ for (m in messages) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0) {
|
|
|
+ for (token in messages[m]) {
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ Gets subscriptions by the topic
|
|
|
+ * @function
|
|
|
+ * @public
|
|
|
+ * @alias getSubscriptions
|
|
|
+ */
|
|
|
+ PubSub.getSubscriptions = function getSubscriptions(topic){
|
|
|
+ var m;
|
|
|
+ var list = [];
|
|
|
+ for (m in messages){
|
|
|
+ if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0){
|
|
|
+ list.push(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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)
|
|
|
+ * @function
|
|
|
+ * @public
|
|
|
+ * @alias subscribeOnce
|
|
|
+ * @param { String | Function } value A token, function or topic to unsubscribe from
|
|
|
+ * @example // Unsubscribing with a token
|
|
|
+ * var token = PubSub.subscribe('mytopic', myFunc);
|
|
|
+ * PubSub.unsubscribe(token);
|
|
|
+ * @example // Unsubscribing with a function
|
|
|
+ * PubSub.unsubscribe(myFunc);
|
|
|
+ * @example // Unsubscribing from a topic
|
|
|
+ * PubSub.unsubscribe('mytopic');
|
|
|
+ */
|
|
|
+ PubSub.unsubscribe = function(value){
|
|
|
+ var descendantTopicExists = function(topic) {
|
|
|
+ var m;
|
|
|
+ for ( m in messages ){
|
|
|
+ if ( Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0 ){
|
|
|
+ // a descendant of the topic exists:
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ isTopic = typeof value === 'string' && ( Object.prototype.hasOwnProperty.call(messages, value) || descendantTopicExists(value) ),
|
|
|
+ isToken = !isTopic && typeof value === 'string',
|
|
|
+ isFunction = typeof value === 'function',
|
|
|
+ result = false,
|
|
|
+ m, message, t;
|
|
|
+
|
|
|
+ if (isTopic){
|
|
|
+ PubSub.clearSubscriptions(value);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( m in messages ){
|
|
|
+ if ( Object.prototype.hasOwnProperty.call( messages, 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 (Object.prototype.hasOwnProperty.call(message, t) && message[t] === value){
|
|
|
+ delete message[t];
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+}));
|