mirror of
https://github.com/wahyd4/cdnjs.git
synced 2026-08-16 16:27:12 +10:00
Updated packages via auto-update.js
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.0
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
}
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
(Object.prototype.toString.call(arguments[0]) === '[object String]' ?
|
||||
arguments[0] : { topic: arguments[0] }) : { topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( fn, 0, data );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
for ( ; idx < len; idx++ ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [sources] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.1
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
}
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
(Object.prototype.toString.call(arguments[0]) === '[object String]' ?
|
||||
arguments[0] : { topic: arguments[0] }) : { topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( fn, 0, data );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function(subDef, envelope) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while(idx < len) {
|
||||
if( subDef = subscribers[idx++] ){
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
for ( ; idx < len; idx++ ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [sources] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+450
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.10
|
||||
*/
|
||||
/*jshint -W098 */
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
};
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var postal;
|
||||
|
||||
/*jshint -W098 */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
/* global postal, SubscriptionDefinition */
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || postal.configuration.DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
( Object.prototype.toString.call( arguments[0] ) === "[object String]" ?
|
||||
{ topic : arguments[0] } :
|
||||
arguments[0] ) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
/* global postal */
|
||||
/*jshint -W117 */
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if ( !this.inactive ) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( self.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds, immediate ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds, !!immediate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = ( this.cache[ topic ] && this.cache[ topic ][ binding ] );
|
||||
if ( typeof result !== "undefined" ) {
|
||||
return result;
|
||||
}
|
||||
if ( !( rgx = this.regex[ binding ] )) {
|
||||
pattern = "^" + _.map( binding.split( "." ),function ( segment ) {
|
||||
var res = "";
|
||||
if ( !!prevSegment ) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if ( segment === "#" ) {
|
||||
res += "[\\s\\S]*";
|
||||
} else if ( segment === "*" ) {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join( "" ) + "$";
|
||||
rgx = this.regex[ binding ] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[ topic ] = this.cache[ topic ] || {};
|
||||
this.cache[ topic ][ binding ] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
/* global postal */
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === "function" ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
unSubQueue.shift().unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* global localBus, bindingsResolver, ChannelDefinition, SubscriptionDefinition, postal */
|
||||
/*jshint -W020 */
|
||||
postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : "/",
|
||||
SYSTEM_CHANNEL : "postal"
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || postal.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [ sources ] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || postal.configuration.DEFAULT_CHANNEL,
|
||||
topic : sourceTopic,
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
|
||||
/*jshint -W106 */
|
||||
if ( global && Object.prototype.hasOwnProperty.call( global, "__postalReady__" ) && _.isArray( global.__postalReady__ ) ) {
|
||||
while(global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(postal);
|
||||
}
|
||||
}
|
||||
/*jshint +W106 */
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+450
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.11
|
||||
*/
|
||||
/*jshint -W098 */
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
};
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var postal;
|
||||
|
||||
/*jshint -W098 */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
/* global postal, SubscriptionDefinition */
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || postal.configuration.DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
( Object.prototype.toString.call( arguments[0] ) === "[object String]" ?
|
||||
{ topic : arguments[0] } :
|
||||
arguments[0] ) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
/* global postal */
|
||||
/*jshint -W117 */
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if ( !this.inactive ) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( self.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds, immediate ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds, !!immediate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = ( this.cache[ topic ] && this.cache[ topic ][ binding ] );
|
||||
if ( typeof result !== "undefined" ) {
|
||||
return result;
|
||||
}
|
||||
if ( !( rgx = this.regex[ binding ] )) {
|
||||
pattern = "^" + _.map( binding.split( "." ),function ( segment ) {
|
||||
var res = "";
|
||||
if ( !!prevSegment ) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if ( segment === "#" ) {
|
||||
res += "[\\s\\S]*";
|
||||
} else if ( segment === "*" ) {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join( "" ) + "$";
|
||||
rgx = this.regex[ binding ] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[ topic ] = this.cache[ topic ] || {};
|
||||
this.cache[ topic ][ binding ] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
/* global postal */
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === "function" ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
localBus.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* global localBus, bindingsResolver, ChannelDefinition, SubscriptionDefinition, postal */
|
||||
/*jshint -W020 */
|
||||
postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : "/",
|
||||
SYSTEM_CHANNEL : "postal"
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || postal.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [ sources ] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || postal.configuration.DEFAULT_CHANNEL,
|
||||
topic : sourceTopic,
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
|
||||
/*jshint -W106 */
|
||||
if ( global && Object.prototype.hasOwnProperty.call( global, "__postalReady__" ) && _.isArray( global.__postalReady__ ) ) {
|
||||
while(global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(postal);
|
||||
}
|
||||
}
|
||||
/*jshint +W106 */
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+430
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.6
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
}
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
(Object.prototype.toString.call(arguments[0]) === '[object String]' ?
|
||||
{ topic: arguments[0] } :
|
||||
arguments[0]) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if(!this.inactive) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if(typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if(!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split('.'), function(segment) {
|
||||
var res = "";
|
||||
if (!!prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if(segment === "#") {
|
||||
res += "[\\s\\S]*"
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+"
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join('') + "$";
|
||||
rgx = this.regex[binding] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
unSubQueue.shift().unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [sources] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc )) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+430
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.7
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
}
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
(Object.prototype.toString.call(arguments[0]) === '[object String]' ?
|
||||
{ topic: arguments[0] } :
|
||||
arguments[0]) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if(!this.inactive) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn( data, env );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn( data, env );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if(typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if(!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split('.'), function(segment) {
|
||||
var res = "";
|
||||
if (!!prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if(segment === "#") {
|
||||
res += "[\\s\\S]*"
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+"
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join('') + "$";
|
||||
rgx = this.regex[binding] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
unSubQueue.shift().unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [sources] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc )) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+450
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.8
|
||||
*/
|
||||
/*jshint -W098 */
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
};
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var postal;
|
||||
|
||||
/*jshint -W098 */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
/* global postal, SubscriptionDefinition */
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || postal.configuration.DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
( Object.prototype.toString.call( arguments[0] ) === "[object String]" ?
|
||||
{ topic : arguments[0] } :
|
||||
arguments[0] ) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
/* global postal */
|
||||
/*jshint -W117 */
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if ( !this.inactive ) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( self.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds, immediate ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds, !!immediate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = ( this.cache[ topic ] && this.cache[ topic ][ binding ] );
|
||||
if ( typeof result !== "undefined" ) {
|
||||
return result;
|
||||
}
|
||||
if ( !( rgx = this.regex[ binding ] )) {
|
||||
pattern = "^" + _.map( binding.split( "." ),function ( segment ) {
|
||||
var res = "";
|
||||
if ( !!prevSegment ) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if ( segment === "#" ) {
|
||||
res += "[\\s\\S]*";
|
||||
} else if ( segment === "*" ) {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join( "" ) + "$";
|
||||
rgx = this.regex[ binding ] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[ topic ] = this.cache[ topic ] || {};
|
||||
this.cache[ topic ][ binding ] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
/* global postal */
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === "function" ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
unSubQueue.shift().unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* global localBus, bindingsResolver, ChannelDefinition, SubscriptionDefinition, postal */
|
||||
/*jshint -W020 */
|
||||
postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : "/",
|
||||
SYSTEM_CHANNEL : "postal"
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || postal.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [ sources ] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || postal.configuration.DEFAULT_CHANNEL,
|
||||
topic : sourceTopic,
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
|
||||
/*jshint -W106 */
|
||||
if ( global.hasOwnProperty( "__postalReady__" ) && _.isArray( global.__postalReady__ ) ) {
|
||||
while(global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(postal);
|
||||
}
|
||||
}
|
||||
/*jshint +W106 */
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
Executable
+450
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.9
|
||||
*/
|
||||
/*jshint -W098 */
|
||||
(function ( root, factory ) {
|
||||
if ( typeof module === "object" && module.exports ) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function ( _ ) {
|
||||
_ = _ || require( "underscore" );
|
||||
return factory( _ );
|
||||
};
|
||||
} else if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var postal;
|
||||
|
||||
/*jshint -W098 */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
var eq = false;
|
||||
if ( _.isString( data ) ) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual( data, previous );
|
||||
previous = _.clone( data );
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
|
||||
return function ( data ) {
|
||||
var isDistinct = !_.any( previous, function ( p ) {
|
||||
if ( _.isObject( data ) || _.isArray( data ) ) {
|
||||
return _.isEqual( data, p );
|
||||
}
|
||||
return data === p;
|
||||
} );
|
||||
if ( isDistinct ) {
|
||||
previous.push( data );
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
/* global postal, SubscriptionDefinition */
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || postal.configuration.DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ?
|
||||
( Object.prototype.toString.call( arguments[0] ) === "[object String]" ?
|
||||
{ topic : arguments[0] } :
|
||||
arguments[0] ) :
|
||||
{ topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
/* global postal */
|
||||
/*jshint -W117 */
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
postal.configuration.bus.subscribe( this );
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
if ( !this.inactive ) {
|
||||
this.inactive = true;
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, 0 );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.callback = function () {
|
||||
fn.apply( self.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds, immediate ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds, !!immediate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var self = this;
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data, env ) {
|
||||
setTimeout( function () {
|
||||
fn.call( self.context, data, env );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
/*jshint -W098 */
|
||||
var bindingsResolver = {
|
||||
cache : {},
|
||||
regex : {},
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
var pattern, rgx, prevSegment, result = ( this.cache[ topic ] && this.cache[ topic ][ binding ] );
|
||||
if ( typeof result !== "undefined" ) {
|
||||
return result;
|
||||
}
|
||||
if ( !( rgx = this.regex[ binding ] )) {
|
||||
pattern = "^" + _.map( binding.split( "." ),function ( segment ) {
|
||||
var res = "";
|
||||
if ( !!prevSegment ) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if ( segment === "#" ) {
|
||||
res += "[\\s\\S]*";
|
||||
} else if ( segment === "*" ) {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
} ).join( "" ) + "$";
|
||||
rgx = this.regex[ binding ] = new RegExp( pattern );
|
||||
}
|
||||
this.cache[ topic ] = this.cache[ topic ] || {};
|
||||
this.cache[ topic ][ binding ] = result = rgx.test( topic );
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
/* global postal */
|
||||
var fireSub = function ( subDef, envelope ) {
|
||||
if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === "function" ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while ( unSubQueue.length ) {
|
||||
unSubQueue.shift().unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
++pubInProgress;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( subscribers ) {
|
||||
var idx = 0, len = subscribers.length, subDef;
|
||||
while ( idx < len ) {
|
||||
if ( subDef = subscribers[idx++] ) {
|
||||
fireSub( subDef, envelope );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( --pubInProgress === 0 ) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var channel = this.subscriptions[subDef.channel], subs;
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( pubInProgress ) {
|
||||
unSubQueue.push( config );
|
||||
return;
|
||||
}
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
while ( idx < len ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/* global localBus, bindingsResolver, ChannelDefinition, SubscriptionDefinition, postal */
|
||||
/*jshint -W020 */
|
||||
postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : "/",
|
||||
SYSTEM_CHANNEL : "postal"
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
return new SubscriptionDefinition( options.channel || postal.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
sources = !_.isArray( sources ) ? [ sources ] : sources;
|
||||
destinations = !_.isArray( destinations ) ? [destinations] : destinations;
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || postal.configuration.DEFAULT_CHANNEL,
|
||||
topic : sourceTopic,
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ];
|
||||
if ( arguments.length === 1 ) {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
Object.prototype.hasOwnProperty.call( postal.configuration.bus.subscriptions[ channel ], tpc ) ) {
|
||||
return postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
localBus.subscriptions[postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
|
||||
/*jshint -W106 */
|
||||
if ( global && global.hasOwnProperty( "__postalReady__" ) && _.isArray( global.__postalReady__ ) ) {
|
||||
while(global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(postal);
|
||||
}
|
||||
}
|
||||
/*jshint +W106 */
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
+7
File diff suppressed because one or more lines are too long
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function (subDef) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length,
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function () {
|
||||
var channel = arguments[0],
|
||||
tpc = arguments[1];
|
||||
if (arguments.length === 1) {
|
||||
channel = arguments[0].channel || this.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[0].topic;
|
||||
}
|
||||
if (this.subscriptions[channel] && Object.prototype.hasOwnProperty.call(this.subscriptions[channel], tpc)) {
|
||||
return this.subscriptions[channel][tpc];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
reset: function () {
|
||||
if (this.subscriptions) {
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (topic) {
|
||||
while (topic.length) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.subscriptions = {};
|
||||
}
|
||||
this.configuration.resolver.reset();
|
||||
}
|
||||
};
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(n,t){"object"==typeof module&&module.exports?module.exports=t(require("underscore"),this):"function"==typeof define&&define.amd?define(["underscore"],function(i){return t(i,n)}):n.postal=t(n._,n)})(this,function(n,t){var i,e=t.postal,s=function(n){this.channel=n||i.configuration.DEFAULT_CHANNEL,this.initialize()};s.prototype.initialize=function(){},s.prototype.subscribe=function(){return i.subscribe({channel:this.channel,topic:1===arguments.length?arguments[0].topic:arguments[0],callback:1===arguments.length?arguments[0].callback:arguments[1]})},s.prototype.publish=function(){var n=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};n.channel=this.channel,i.publish(n)};var c=function(n,t,i){if(3!==arguments.length)throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");if(0===t.length)throw new Error("Topics cannot be empty");this.channel=n,this.topic=t,this.subscribe(i)};c.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.unsubscribe(this))},subscribe:function(n){return this.callback=n,this},withContext:function(n){return this.context=n,this}};var o={cache:{},regex:{},compare:function(t,i){var e,s,c,o=this.cache[i]&&this.cache[i][t];return"undefined"!=typeof o?o:((s=this.regex[t])||(e="^"+n.map(t.split("."),function(n){var t="";return c&&(t="#"!==c?"\\.\\b":"\\b"),t+="#"===n?"[\\s\\S]*":"*"===n?"[^.]+":n,c=n,t}).join("")+"$",s=this.regex[t]=new RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][t]=o=s.test(i),o)},reset:function(){this.cache={},this.regex={}}},r=function(n,t){!n.inactive&&i.configuration.resolver.compare(n.topic,t.topic)&&n.callback.call(n.context||this,t.data,t)},a=0,u=[],h=function(){for(;u.length;)i.unsubscribe(u.shift())};if(i={configuration:{resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},subscriptions:{},wireTaps:[],ChannelDefinition:s,SubscriptionDefinition:c,channel:function(n){return new s(n)},subscribe:function(n){var t,i=new c(n.channel||this.configuration.DEFAULT_CHANNEL,n.topic,n.callback),e=this.subscriptions[i.channel];return this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:i.channel,topic:i.topic}}),e||(e=this.subscriptions[i.channel]={}),t=this.subscriptions[i.channel][i.topic],t||(t=this.subscriptions[i.channel][i.topic]=[]),t.push(i),i},publish:function(t){++a,t.channel=t.channel||this.configuration.DEFAULT_CHANNEL,t.timeStamp=new Date,n.each(this.wireTaps,function(n){n(t.data,t)}),this.subscriptions[t.channel]&&n.each(this.subscriptions[t.channel],function(n){for(var i,e=0,s=n.length;s>e;)(i=n[e++])&&r(i,t)}),0===--a&&h()},unsubscribe:function(n){if(a)return void u.push(n);if(this.subscriptions[n.channel]&&this.subscriptions[n.channel][n.topic])for(var t=this.subscriptions[n.channel][n.topic].length,i=0;t>i;){if(this.subscriptions[n.channel][n.topic][i]===n){this.subscriptions[n.channel][n.topic].splice(i,1);break}i+=1}this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:n.channel,topic:n.topic}})},addWireTap:function(n){var t=this;return t.wireTaps.push(n),function(){var i=t.wireTaps.indexOf(n);-1!==i&&t.wireTaps.splice(i,1)}},noConflict:function(){if("undefined"==typeof window||"undefined"!=typeof window&&"function"==typeof define&&define.amd)throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");return t.postal=e,this},getSubscribersFor:function(){var n=arguments[0],t=arguments[1];return 1===arguments.length&&(n=arguments[0].channel||this.configuration.DEFAULT_CHANNEL,t=arguments[0].topic),this.subscriptions[n]&&Object.prototype.hasOwnProperty.call(this.subscriptions[n],t)?this.subscriptions[n][t]:[]},reset:function(){this.subscriptions&&(n.each(this.subscriptions,function(t){n.each(t,function(n){for(;n.length;)n.pop().unsubscribe()})}),this.subscriptions={}),this.configuration.resolver.reset()}},i.subscriptions[i.configuration.SYSTEM_CHANNEL]={},t&&Object.prototype.hasOwnProperty.call(t,"__postalReady__")&&n.isArray(t.__postalReady__))for(;t.__postalReady__.length;)t.__postalReady__.shift().onReady(i);return i});
|
||||
@@ -0,0 +1,541 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var Conduit = function (options) {
|
||||
if (typeof options.target !== "function") {
|
||||
throw new Error("You can only make functions into Conduits.");
|
||||
}
|
||||
var _steps = {
|
||||
pre: options.pre || [],
|
||||
post: options.post || [],
|
||||
all: []
|
||||
};
|
||||
var _defaultContext = options.context;
|
||||
var _targetStep = {
|
||||
isTarget: true,
|
||||
fn: function (next) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
options.target.apply(_defaultContext, args);
|
||||
next.apply(this, args);
|
||||
}
|
||||
};
|
||||
var _genPipeline = function () {
|
||||
_steps.all = _steps.pre.concat([_targetStep].concat(_steps.post));
|
||||
};
|
||||
_genPipeline();
|
||||
var conduit = function () {
|
||||
var idx = 0;
|
||||
var next = function next() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var thisIdx = idx;
|
||||
var step;
|
||||
idx += 1;
|
||||
if (thisIdx < _steps.all.length) {
|
||||
step = _steps.all[thisIdx];
|
||||
step.fn.apply(step.context || _defaultContext, [next].concat(args));
|
||||
}
|
||||
};
|
||||
next.apply(this, arguments);
|
||||
};
|
||||
conduit.steps = function () {
|
||||
return _steps.all;
|
||||
};
|
||||
conduit.context = function (ctx) {
|
||||
if (arguments.length === 0) {
|
||||
return _defaultContext;
|
||||
} else {
|
||||
_defaultContext = ctx;
|
||||
}
|
||||
};
|
||||
conduit.before = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.pre.unshift(step);
|
||||
} else {
|
||||
_steps.pre.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.after = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.post.unshift(step);
|
||||
} else {
|
||||
_steps.post.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.clear = function () {
|
||||
_steps = {
|
||||
pre: [],
|
||||
post: [],
|
||||
all: []
|
||||
};
|
||||
};
|
||||
return conduit;
|
||||
};
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function (subDef) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length,
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function () {
|
||||
var channel = arguments[0],
|
||||
tpc = arguments[1];
|
||||
if (arguments.length === 1) {
|
||||
channel = arguments[0].channel || this.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[0].topic;
|
||||
}
|
||||
if (this.subscriptions[channel] && Object.prototype.hasOwnProperty.call(this.subscriptions[channel], tpc)) {
|
||||
return this.subscriptions[channel][tpc];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
reset: function () {
|
||||
if (this.subscriptions) {
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (topic) {
|
||||
while (topic.length) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.subscriptions = {};
|
||||
}
|
||||
this.configuration.resolver.reset();
|
||||
}
|
||||
};
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
_postal.linkChannels = function (sources, destinations) {
|
||||
var result = [],
|
||||
self = this;
|
||||
sources = !_.isArray(sources) ? [sources] : sources;
|
||||
destinations = !_.isArray(destinations) ? [destinations] : destinations;
|
||||
_.each(sources, function (source) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each(destinations, function (destination) {
|
||||
var destChannel = destination.channel || self.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
self.subscribe({
|
||||
channel: source.channel || self.configuration.DEFAULT_CHANNEL,
|
||||
topic: sourceTopic,
|
||||
callback: function (data, env) {
|
||||
var newEnv = _.clone(env);
|
||||
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
self.publish(newEnv);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
+8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function (postal) {
|
||||
return factory(postal, this);
|
||||
};
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["postal"], function (postal) {
|
||||
return factory(postal, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root.postal, root);
|
||||
}
|
||||
}(this, function (postal, global, undefined) {
|
||||
(function (SubscriptionDefinition) {
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
}(postal.SubscriptionDefinition));
|
||||
return postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return n(t,this)}:"function"==typeof define&&define.amd?define(["postal"],function(i){return n(i,t)}):t.postal=n(t.postal,t)})(this,function(t){return function(t){var n=function(){var t;return function(n){var i=!1;return _.isString(n)?(i=n===t,t=n):(i=_.isEqual(n,t),t=_.clone(n)),!i}},i=function(){var t=[];return function(n){var i=!_.any(t,function(t){return _.isObject(n)||_.isArray(n)?_.isEqual(n,t):n===t});return i&&t.push(n),i}},e={withDelay:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withDelay",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},defer:function(){return this.withDelay(0)},stopAfter:function(t,n){if(_.isNaN(t)||0>=t)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=_.after(t,n);return{name:"stopAfter",fn:function(t,n,e){i(),t(n,e)}}},withThrottle:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withThrottle",fn:_.throttle(function(t,n,i){t(n,i)},t)}},withDebounce:function(t,n){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"debounce",fn:_.debounce(function(t,n,i){t(n,i)},t,!!n)}},withConstraint:function(t){if(!_.isFunction(t))throw"Predicate constraint must be a function";return{name:"withConstraint",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var e=function(t){return t[0]},o=t.all?new i(e):new n(e);return{name:"distinct",fn:function(t,n,i){o(n)&&t(n,i)}}}};t.prototype.defer=function(){return this.callback.before(e.defer()),this},t.prototype.disposeAfter=function(t){var n=this;return n.callback.before(e.stopAfter(t,function(){n.unsubscribe.call(n)})),n},t.prototype.distinctUntilChanged=function(){return this.callback.before(e.distinct()),this},t.prototype.distinct=function(){return this.callback.before(e.distinct({all:!0})),this},t.prototype.once=function(){return this.disposeAfter(1),this},t.prototype.withConstraint=function(t){return this.callback.before(e.withConstraint(t)),this},t.prototype.withConstraints=function(t){for(;t.length;)this.callback.before(e.withConstraint(t.shift()));return this},t.prototype.withDebounce=function(t,n){return this.callback.before(e.withDebounce(t,n)),this},t.prototype.withDelay=function(t){return this.callback.before(e.withDelay(t)),this},t.prototype.withThrottle=function(t){return this.callback.before(e.withThrottle(t)),this},t.prototype.subscribe=function(t){return this.callback=new Conduit({target:t,context:this}),this},t.prototype.withContext=function(t){return this.callback.context(t),this},t.prototype.after=function(){this.callback.after.apply(this,arguments)},t.prototype.before=function(){this.callback.before.apply(this,arguments)},ChannelDefinition.prototype.initialize=function(){var t=this.publish;this.publish=new Conduit({target:t,context:this})}}(t.SubscriptionDefinition),t});
|
||||
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc3
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
var getPredicate = function (options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function () {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
// Dear lord, it's an iterative pyramid of doom!
|
||||
// I suppose I could optimize this by adding
|
||||
// a data structure that flattens the total
|
||||
// list of subscription definition instances...
|
||||
// we'll see if it becomes necessary
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
toDispose = toDispose.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
var _publish = _postal.publish;
|
||||
_postal.publish = new Conduit({
|
||||
target: _publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc3
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(n,t){"object"==typeof module&&module.exports?module.exports=t(require("underscore"),this):"function"==typeof define&&define.amd?define(["underscore"],function(i){return t(i,n)}):n.postal=t(n._,n)})(this,function(n,t){var i,e=t.postal,c=function(n){this.channel=n||i.configuration.DEFAULT_CHANNEL,this.initialize()};c.prototype.initialize=function(){},c.prototype.subscribe=function(){return i.subscribe({channel:this.channel,topic:1===arguments.length?arguments[0].topic:arguments[0],callback:1===arguments.length?arguments[0].callback:arguments[1]})},c.prototype.publish=function(){var n=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};n.channel=this.channel,i.publish(n)};var s=function(n,t,i){if(3!==arguments.length)throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");if(0===t.length)throw new Error("Topics cannot be empty");this.channel=n,this.topic=t,this.subscribe(i)};s.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.unsubscribe(this))},subscribe:function(n){return this.callback=n,this},withContext:function(n){return this.context=n,this}};var o={cache:{},regex:{},compare:function(t,i){var e,c,s,o=this.cache[i]&&this.cache[i][t];return"undefined"!=typeof o?o:((c=this.regex[t])||(e="^"+n.map(t.split("."),function(n){var t="";return s&&(t="#"!==s?"\\.\\b":"\\b"),t+="#"===n?"[\\s\\S]*":"*"===n?"[^.]+":n,s=n,t}).join("")+"$",c=this.regex[t]=new RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][t]=o=c.test(i),o)},reset:function(){this.cache={},this.regex={}}},r=function(n,t){!n.inactive&&i.configuration.resolver.compare(n.topic,t.topic)&&n.callback.call(n.context||this,t.data,t)},a=0,u=[],h=function(){for(;u.length;)i.unsubscribe(u.shift())},p=function(t){return"function"==typeof t?t:t?function(e){var c=0,s=0;return n.each(t,function(n,o){c+=1,("topic"===o&&i.configuration.resolver.compare(e.topic,t.topic)||"context"===o&&t.context===(e.callback.context&&e.callback.context()||e.context)||e[o]===t[o])&&(s+=1)}),c===s}:function(){return!0}};i={configuration:{resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},subscriptions:{},wireTaps:[],ChannelDefinition:c,SubscriptionDefinition:s,channel:function(n){return new c(n)},subscribe:function(n){var t,i=new s(n.channel||this.configuration.DEFAULT_CHANNEL,n.topic,n.callback),e=this.subscriptions[i.channel];return this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:i.channel,topic:i.topic}}),e||(e=this.subscriptions[i.channel]={}),t=this.subscriptions[i.channel][i.topic],t||(t=this.subscriptions[i.channel][i.topic]=[]),t.push(i),i},publish:function(t){++a,t.channel=t.channel||this.configuration.DEFAULT_CHANNEL,t.timeStamp=new Date,n.each(this.wireTaps,function(n){n(t.data,t)}),this.subscriptions[t.channel]&&n.each(this.subscriptions[t.channel],function(n){for(var i,e=0,c=n.length;c>e;)(i=n[e++])&&r(i,t)}),0===--a&&h()},unsubscribe:function(){for(var n,t=0,i=Array.prototype.slice.call(arguments,0);n=i.shift();){if(a)return void u.push(n);if(this.subscriptions[n.channel]&&this.subscriptions[n.channel][n.topic]){var e=this.subscriptions[n.channel][n.topic].length;for(t=0;e>t;){if(this.subscriptions[n.channel][n.topic][t]===n){this.subscriptions[n.channel][n.topic].splice(t,1);break}t+=1}}this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:n.channel,topic:n.topic}})}},addWireTap:function(n){var t=this;return t.wireTaps.push(n),function(){var i=t.wireTaps.indexOf(n);-1!==i&&t.wireTaps.splice(i,1)}},noConflict:function(){if("undefined"==typeof window||"undefined"!=typeof window&&"function"==typeof define&&define.amd)throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");return t.postal=e,this},getSubscribersFor:function(t){var i=[];return n.each(this.subscriptions,function(e){n.each(e,function(e){i=i.concat(n.filter(e,p(t)))})}),i},reset:function(){this.unsubscribeFor(),this.configuration.resolver.reset(),this.subscriptions={}},unsubscribeFor:function(t){var i=[];this.subscriptions&&(n.each(this.subscriptions,function(e){n.each(e,function(e){i=i.concat(n.filter(e,p(t)))})}),this.unsubscribe.apply(this,i))}};var l=i.publish;if(i.publish=new Conduit({target:l,context:i}),i.subscriptions[i.configuration.SYSTEM_CHANNEL]={},t&&Object.prototype.hasOwnProperty.call(t,"__postalReady__")&&n.isArray(t.__postalReady__))for(;t.__postalReady__.length;)t.__postalReady__.shift().onReady(i);return i});
|
||||
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc3
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var Conduit = function (options) {
|
||||
if (typeof options.target !== "function") {
|
||||
throw new Error("You can only make functions into Conduits.");
|
||||
}
|
||||
var _steps = {
|
||||
pre: options.pre || [],
|
||||
post: options.post || [],
|
||||
all: []
|
||||
};
|
||||
var _defaultContext = options.context;
|
||||
var _targetStep = {
|
||||
isTarget: true,
|
||||
fn: function (next) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
options.target.apply(_defaultContext, args);
|
||||
next.apply(this, args);
|
||||
}
|
||||
};
|
||||
var _genPipeline = function () {
|
||||
_steps.all = _steps.pre.concat([_targetStep].concat(_steps.post));
|
||||
};
|
||||
_genPipeline();
|
||||
var conduit = function () {
|
||||
var idx = 0;
|
||||
var next = function next() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var thisIdx = idx;
|
||||
var step;
|
||||
idx += 1;
|
||||
if (thisIdx < _steps.all.length) {
|
||||
step = _steps.all[thisIdx];
|
||||
step.fn.apply(step.context || _defaultContext, [next].concat(args));
|
||||
}
|
||||
};
|
||||
next.apply(this, arguments);
|
||||
};
|
||||
conduit.steps = function () {
|
||||
return _steps.all;
|
||||
};
|
||||
conduit.context = function (ctx) {
|
||||
if (arguments.length === 0) {
|
||||
return _defaultContext;
|
||||
} else {
|
||||
_defaultContext = ctx;
|
||||
}
|
||||
};
|
||||
conduit.before = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.pre.unshift(step);
|
||||
} else {
|
||||
_steps.pre.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.after = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.post.unshift(step);
|
||||
} else {
|
||||
_steps.post.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.clear = function () {
|
||||
_steps = {
|
||||
pre: [],
|
||||
post: [],
|
||||
all: []
|
||||
};
|
||||
};
|
||||
return conduit;
|
||||
};
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
var getPredicate = function (options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function () {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
// Dear lord, it's an iterative pyramid of doom!
|
||||
// I suppose I could optimize this by adding
|
||||
// a data structure that flattens the total
|
||||
// list of subscription definition instances...
|
||||
// we'll see if it becomes necessary
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
toDispose = toDispose.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
var _publish = _postal.publish;
|
||||
_postal.publish = new Conduit({
|
||||
target: _publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
_postal.linkChannels = function (sources, destinations) {
|
||||
var result = [],
|
||||
self = this;
|
||||
sources = !_.isArray(sources) ? [sources] : sources;
|
||||
destinations = !_.isArray(destinations) ? [destinations] : destinations;
|
||||
_.each(sources, function (source) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each(destinations, function (destination) {
|
||||
var destChannel = destination.channel || self.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
self.subscribe({
|
||||
channel: source.channel || self.configuration.DEFAULT_CHANNEL,
|
||||
topic: sourceTopic,
|
||||
callback: function (data, env) {
|
||||
var newEnv = _.clone(env);
|
||||
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
self.publish(newEnv);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
+8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc3
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function (postal) {
|
||||
return factory(postal, this);
|
||||
};
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["postal"], function (postal) {
|
||||
return factory(postal, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root.postal, root);
|
||||
}
|
||||
}(this, function (postal, global, undefined) {
|
||||
(function (SubscriptionDefinition) {
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
}
|
||||
else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
}(postal.SubscriptionDefinition));
|
||||
return postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0-rc3
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return n(t,this)}:"function"==typeof define&&define.amd?define(["postal"],function(i){return n(i,t)}):t.postal=n(t.postal,t)})(this,function(t){return function(t){var n=function(){var t;return function(n){var i=!1;return _.isString(n)?(i=n===t,t=n):(i=_.isEqual(n,t),t=_.clone(n)),!i}},i=function(){var t=[];return function(n){var i=!_.any(t,function(t){return _.isObject(n)||_.isArray(n)?_.isEqual(n,t):n===t});return i&&t.push(n),i}},e={withDelay:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withDelay",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},defer:function(){return this.withDelay(0)},stopAfter:function(t,n){if(_.isNaN(t)||0>=t)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=_.after(t,n);return{name:"stopAfter",fn:function(t,n,e){i(),t(n,e)}}},withThrottle:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withThrottle",fn:_.throttle(function(t,n,i){t(n,i)},t)}},withDebounce:function(t,n){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"debounce",fn:_.debounce(function(t,n,i){t(n,i)},t,!!n)}},withConstraint:function(t){if(!_.isFunction(t))throw"Predicate constraint must be a function";return{name:"withConstraint",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var e=function(t){return t[0]},o=t.all?new i(e):new n(e);return{name:"distinct",fn:function(t,n,i){o(n)&&t(n,i)}}}};t.prototype.defer=function(){return this.callback.before(e.defer()),this},t.prototype.disposeAfter=function(t){var n=this;return n.callback.before(e.stopAfter(t,function(){n.unsubscribe.call(n)})),n},t.prototype.distinctUntilChanged=function(){return this.callback.before(e.distinct()),this},t.prototype.distinct=function(){return this.callback.before(e.distinct({all:!0})),this},t.prototype.once=function(){return this.disposeAfter(1),this},t.prototype.withConstraint=function(t){return this.callback.before(e.withConstraint(t)),this},t.prototype.withConstraints=function(t){for(;t.length;)this.callback.before(e.withConstraint(t.shift()));return this},t.prototype.withDebounce=function(t,n){return this.callback.before(e.withDebounce(t,n)),this},t.prototype.withDelay=function(t){return this.callback.before(e.withDelay(t)),this},t.prototype.withThrottle=function(t){return this.callback.before(e.withThrottle(t)),this},t.prototype.subscribe=function(t){return this.callback=new Conduit({target:t,context:this}),this},t.prototype.withContext=function(t){return this.callback.context(t),this},t.prototype.after=function(){this.callback.after.apply(this,arguments)},t.prototype.before=function(){this.callback.before.apply(this,arguments)},ChannelDefinition.prototype.initialize=function(){var t=this.publish;this.publish=new Conduit({target:t,context:this})}}(t.SubscriptionDefinition),t});
|
||||
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
var getPredicate = function (options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function () {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
toDispose = this.getSubscribersFor(options);
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
var _publish = _postal.publish;
|
||||
_postal.publish = new Conduit.Async({
|
||||
target: _publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=n(require("underscore"),this):"function"==typeof define&&define.amd?define(["underscore"],function(i){return n(i,t)}):t.postal=n(t._,t)})(this,function(t,n){var i,e=n.postal,c=function(t){this.channel=t||i.configuration.DEFAULT_CHANNEL,this.initialize()};c.prototype.initialize=function(){},c.prototype.subscribe=function(){return i.subscribe({channel:this.channel,topic:1===arguments.length?arguments[0].topic:arguments[0],callback:1===arguments.length?arguments[0].callback:arguments[1]})},c.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};t.channel=this.channel,i.publish(t)};var s=function(t,n,i){if(3!==arguments.length)throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");if(0===n.length)throw new Error("Topics cannot be empty");this.channel=t,this.topic=n,this.subscribe(i)};s.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,i.unsubscribe(this))},subscribe:function(t){return this.callback=t,this},withContext:function(t){return this.context=t,this}};var o={cache:{},regex:{},compare:function(n,i){var e,c,s,o=this.cache[i]&&this.cache[i][n];return"undefined"!=typeof o?o:((c=this.regex[n])||(e="^"+t.map(n.split("."),function(t){var n="";return s&&(n="#"!==s?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,s=t,n}).join("")+"$",c=this.regex[n]=new RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=o=c.test(i),o)},reset:function(){this.cache={},this.regex={}}},r=function(t,n){!t.inactive&&i.configuration.resolver.compare(t.topic,n.topic)&&t.callback.call(t.context||this,n.data,n)},a=0,u=[],h=function(){for(;u.length;)i.unsubscribe(u.shift())},p=function(n){return"function"==typeof n?n:n?function(e){var c=0,s=0;return t.each(n,function(t,o){c+=1,("topic"===o&&i.configuration.resolver.compare(e.topic,n.topic)||"context"===o&&n.context===(e.callback.context&&e.callback.context()||e.context)||e[o]===n[o])&&(s+=1)}),c===s}:function(){return!0}};i={configuration:{resolver:o,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},subscriptions:{},wireTaps:[],ChannelDefinition:c,SubscriptionDefinition:s,channel:function(t){return new c(t)},subscribe:function(t){var n,i=new s(t.channel||this.configuration.DEFAULT_CHANNEL,t.topic,t.callback),e=this.subscriptions[i.channel];return this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.created",data:{event:"subscription.created",channel:i.channel,topic:i.topic}}),e||(e=this.subscriptions[i.channel]={}),n=this.subscriptions[i.channel][i.topic],n||(n=this.subscriptions[i.channel][i.topic]=[]),n.push(i),i},publish:function(n){++a,n.channel=n.channel||this.configuration.DEFAULT_CHANNEL,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,e=0,c=t.length;c>e;)(i=t[e++])&&r(i,n)}),0===--a&&h()},unsubscribe:function(){for(var t,n=0,i=Array.prototype.slice.call(arguments,0);t=i.shift();){if(a)return void u.push(t);if(this.subscriptions[t.channel]&&this.subscriptions[t.channel][t.topic]){var e=this.subscriptions[t.channel][t.topic].length;for(n=0;e>n;){if(this.subscriptions[t.channel][t.topic][n]===t){this.subscriptions[t.channel][t.topic].splice(n,1);break}n+=1}}this.publish({channel:this.configuration.SYSTEM_CHANNEL,topic:"subscription.removed",data:{event:"subscription.removed",channel:t.channel,topic:t.topic}})}},addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},noConflict:function(){if("undefined"==typeof window||"undefined"!=typeof window&&"function"==typeof define&&define.amd)throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");return n.postal=e,this},getSubscribersFor:function(n){var i=[];return t.each(this.subscriptions,function(e){t.each(e,function(e){i=i.concat(t.filter(e,p(n)))})}),i},reset:function(){this.unsubscribeFor(),this.configuration.resolver.reset(),this.subscriptions={}},unsubscribeFor:function(t){var n=[];this.subscriptions&&(n=this.getSubscribersFor(t),this.unsubscribe.apply(this,n))}};var l=i.publish;if(i.publish=new Conduit.Async({target:l,context:i}),i.subscriptions[i.configuration.SYSTEM_CHANNEL]={},n&&Object.prototype.hasOwnProperty.call(n,"__postalReady__")&&t.isArray(n.__postalReady__))for(;n.__postalReady__.length;)n.__postalReady__.shift().onReady(i);return i});
|
||||
@@ -0,0 +1,605 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("underscore"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["underscore"], function (_) {
|
||||
return factory(_, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root);
|
||||
}
|
||||
}(this, function (_, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var Conduit = (function () {
|
||||
function Conduit(options) {
|
||||
if (typeof options.target !== "function") {
|
||||
throw new Error("You can only make functions into Conduits.");
|
||||
}
|
||||
var _steps = {
|
||||
pre: options.pre || [],
|
||||
post: options.post || [],
|
||||
all: []
|
||||
};
|
||||
var _defaultContext = options.context;
|
||||
var _targetStep = {
|
||||
isTarget: true,
|
||||
fn: options.sync ?
|
||||
function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var result = options.target.apply(_defaultContext, args);
|
||||
return result;
|
||||
} : function (next) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
args.splice(1, 1, options.target.apply(_defaultContext, args));
|
||||
next.apply(this, args);
|
||||
}
|
||||
};
|
||||
var _genPipeline = function () {
|
||||
_steps.all = _steps.pre.concat([_targetStep].concat(_steps.post));
|
||||
};
|
||||
_genPipeline();
|
||||
var conduit = function () {
|
||||
var idx = 0;
|
||||
var retval;
|
||||
var phase;
|
||||
var next = function next() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var thisIdx = idx;
|
||||
var step;
|
||||
var nextArgs;
|
||||
idx += 1;
|
||||
if (thisIdx < _steps.all.length) {
|
||||
step = _steps.all[thisIdx];
|
||||
phase = (phase === "target") ? "after" : (step.isTarget) ? "target" : "before";
|
||||
if (options.sync) {
|
||||
if (phase === "before") {
|
||||
nextArgs = step.fn.apply(step.context || _defaultContext, args);
|
||||
next.apply(this, nextArgs || args);
|
||||
} else {
|
||||
retval = step.fn.apply(step.context || _defaultContext, args) || retval;
|
||||
next.apply(this, [retval].concat(args));
|
||||
}
|
||||
} else {
|
||||
step.fn.apply(step.context || _defaultContext, [next].concat(args));
|
||||
}
|
||||
}
|
||||
};
|
||||
next.apply(this, arguments);
|
||||
return retval;
|
||||
};
|
||||
conduit.steps = function () {
|
||||
return _steps.all;
|
||||
};
|
||||
conduit.context = function (ctx) {
|
||||
if (arguments.length === 0) {
|
||||
return _defaultContext;
|
||||
} else {
|
||||
_defaultContext = ctx;
|
||||
}
|
||||
};
|
||||
conduit.before = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.pre.unshift(step);
|
||||
} else {
|
||||
_steps.pre.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.after = function (step, options) {
|
||||
step = typeof step === "function" ? {
|
||||
fn: step
|
||||
} : step;
|
||||
options = options || {};
|
||||
if (options.prepend) {
|
||||
_steps.post.unshift(step);
|
||||
} else {
|
||||
_steps.post.push(step);
|
||||
}
|
||||
_genPipeline();
|
||||
};
|
||||
conduit.clear = function () {
|
||||
_steps = {
|
||||
pre: [],
|
||||
post: [],
|
||||
all: []
|
||||
};
|
||||
_genPipeline();
|
||||
};
|
||||
return conduit;
|
||||
}
|
||||
return {
|
||||
Sync: function (options) {
|
||||
options.sync = true;
|
||||
return Conduit.call(this, options);
|
||||
},
|
||||
Async: function (options) {
|
||||
return Conduit.call(this, options);
|
||||
}
|
||||
};
|
||||
}());
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
}; /* global,SubscriptionDefinition,Conduit,ChannelDefinition */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
} else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit.Async({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit.Async({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
var clearUnSubQueue = function () {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
};
|
||||
var getPredicate = function (options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
};
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
subscribe: function (options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.created",
|
||||
data: {
|
||||
event: "subscription.created",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
},
|
||||
publish: function (envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
},
|
||||
unsubscribe: function () {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
this.publish({
|
||||
channel: this.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription.removed",
|
||||
data: {
|
||||
event: "subscription.removed",
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
toDispose = this.getSubscribersFor(options);
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
var _publish = _postal.publish;
|
||||
_postal.publish = new Conduit.Async({
|
||||
target: _publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
_postal.linkChannels = function (sources, destinations) {
|
||||
var result = [],
|
||||
self = this;
|
||||
sources = !_.isArray(sources) ? [sources] : sources;
|
||||
destinations = !_.isArray(destinations) ? [destinations] : destinations;
|
||||
_.each(sources, function (source) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each(destinations, function (destination) {
|
||||
var destChannel = destination.channel || self.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
self.subscribe({
|
||||
channel: source.channel || self.configuration.DEFAULT_CHANNEL,
|
||||
topic: sourceTopic,
|
||||
callback: function (data, env) {
|
||||
var newEnv = _.clone(env);
|
||||
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
self.publish(newEnv);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
+8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function (postal) {
|
||||
return factory(postal, this);
|
||||
};
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["postal"], function (postal) {
|
||||
return factory(postal, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root.postal, root);
|
||||
}
|
||||
}(this, function (postal, global, undefined) {
|
||||
(function (SubscriptionDefinition) { /* global,SubscriptionDefinition,Conduit,ChannelDefinition */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
} else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit.Async({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit.Async({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
}(postal.SubscriptionDefinition));
|
||||
return postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.0
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return n(t,this)}:"function"==typeof define&&define.amd?define(["postal"],function(i){return n(i,t)}):t.postal=n(t.postal,t)})(this,function(t){return function(t){var n=function(){var t;return function(n){var i=!1;return _.isString(n)?(i=n===t,t=n):(i=_.isEqual(n,t),t=_.clone(n)),!i}},i=function(){var t=[];return function(n){var i=!_.any(t,function(t){return _.isObject(n)||_.isArray(n)?_.isEqual(n,t):n===t});return i&&t.push(n),i}},e={withDelay:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withDelay",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},defer:function(){return this.withDelay(0)},stopAfter:function(t,n){if(_.isNaN(t)||0>=t)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=_.after(t,n);return{name:"stopAfter",fn:function(t,n,e){i(),t(n,e)}}},withThrottle:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withThrottle",fn:_.throttle(function(t,n,i){t(n,i)},t)}},withDebounce:function(t,n){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"debounce",fn:_.debounce(function(t,n,i){t(n,i)},t,!!n)}},withConstraint:function(t){if(!_.isFunction(t))throw"Predicate constraint must be a function";return{name:"withConstraint",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var e=function(t){return t[0]},o=t.all?new i(e):new n(e);return{name:"distinct",fn:function(t,n,i){o(n)&&t(n,i)}}}};t.prototype.defer=function(){return this.callback.before(e.defer()),this},t.prototype.disposeAfter=function(t){var n=this;return n.callback.before(e.stopAfter(t,function(){n.unsubscribe.call(n)})),n},t.prototype.distinctUntilChanged=function(){return this.callback.before(e.distinct()),this},t.prototype.distinct=function(){return this.callback.before(e.distinct({all:!0})),this},t.prototype.once=function(){return this.disposeAfter(1),this},t.prototype.withConstraint=function(t){return this.callback.before(e.withConstraint(t)),this},t.prototype.withConstraints=function(t){for(;t.length;)this.callback.before(e.withConstraint(t.shift()));return this},t.prototype.withDebounce=function(t,n){return this.callback.before(e.withDebounce(t,n)),this},t.prototype.withDelay=function(t){return this.callback.before(e.withDelay(t)),this},t.prototype.withThrottle=function(t){return this.callback.before(e.withThrottle(t)),this},t.prototype.subscribe=function(t){return this.callback=new Conduit.Async({target:t,context:this}),this},t.prototype.withContext=function(t){return this.callback.context(t),this},t.prototype.after=function(){this.callback.after.apply(this,arguments)},t.prototype.before=function(){this.callback.before.apply(this,arguments)},ChannelDefinition.prototype.initialize=function(){var t=this.publish;this.publish=new Conduit.Async({target:t,context:this})}}(t.SubscriptionDefinition),t});
|
||||
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("lodash"), require("conduitjs"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["lodash", "conduitjs"], function (_, Conduit) {
|
||||
return factory(_, Conduit, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root.Conduit, root);
|
||||
}
|
||||
}(this, function (_, Conduit, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
function clearUnSubQueue() {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
}
|
||||
function getSystemMessage(kind, subDef) {
|
||||
return {
|
||||
channel: _postal.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription." + kind,
|
||||
data: {
|
||||
event: "subscription." + kind,
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
};
|
||||
}
|
||||
function getPredicate(options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
}
|
||||
function subscribe(options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
}
|
||||
function publish(envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
}
|
||||
function unsubscribe() {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
_postal.publish(getSystemMessage("removed", subDef));
|
||||
}
|
||||
}
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
toDispose = this.getSubscribersFor(options);
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
_postal.subscribe = new Conduit.Sync({
|
||||
target: subscribe,
|
||||
context: _postal
|
||||
});
|
||||
_postal.publish = Conduit.Async({
|
||||
target: publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.unsubscribe = new Conduit.Sync({
|
||||
target: unsubscribe,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscribe.after(function (subDef /*, options */ ) {
|
||||
_postal.publish(getSystemMessage("created", subDef));
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=n(require("lodash"),require("conduitjs"),this):"function"==typeof define&&define.amd?define(["lodash","conduitjs"],function(i,e){return n(i,e,t)}):t.postal=n(t._,t.Conduit,t)})(this,function(t,n,i){function e(){for(;d.length;)u.unsubscribe(d.shift())}function c(t,n){return{channel:u.configuration.SYSTEM_CHANNEL,topic:"subscription."+t,data:{event:"subscription."+t,channel:n.channel,topic:n.topic}}}function s(n){return"function"==typeof n?n:n?function(i){var e=0,c=0;return t.each(n,function(t,s){e+=1,("topic"===s&&u.configuration.resolver.compare(i.topic,n.topic)||"context"===s&&n.context===(i.callback.context&&i.callback.context()||i.context)||i[s]===n[s])&&(c+=1)}),e===c}:function(){return!0}}function o(t){var n,i=new l(t.channel||this.configuration.DEFAULT_CHANNEL,t.topic,t.callback),e=this.subscriptions[i.channel];return e||(e=this.subscriptions[i.channel]={}),n=this.subscriptions[i.channel][i.topic],n||(n=this.subscriptions[i.channel][i.topic]=[]),n.push(i),i}function r(n){++g,n.channel=n.channel||this.configuration.DEFAULT_CHANNEL,n.timeStamp=new Date,t.each(this.wireTaps,function(t){t(n.data,n)}),this.subscriptions[n.channel]&&t.each(this.subscriptions[n.channel],function(t){for(var i,e=0,c=t.length;c>e;)(i=t[e++])&&b(i,n)}),0===--g&&e()}function a(){for(var t,n=0,i=Array.prototype.slice.call(arguments,0);t=i.shift();){if(g)return void d.push(t);if(this.subscriptions[t.channel]&&this.subscriptions[t.channel][t.topic]){var e=this.subscriptions[t.channel][t.topic].length;for(n=0;e>n;){if(this.subscriptions[t.channel][t.topic][n]===t){this.subscriptions[t.channel][t.topic].splice(n,1);break}n+=1}}u.publish(c("removed",t))}}var u,h=i.postal,p=function(t){this.channel=t||u.configuration.DEFAULT_CHANNEL,this.initialize()};p.prototype.initialize=function(){},p.prototype.subscribe=function(){return u.subscribe({channel:this.channel,topic:1===arguments.length?arguments[0].topic:arguments[0],callback:1===arguments.length?arguments[0].callback:arguments[1]})},p.prototype.publish=function(){var t=1===arguments.length?"[object String]"===Object.prototype.toString.call(arguments[0])?{topic:arguments[0]}:arguments[0]:{topic:arguments[0],data:arguments[1]};t.channel=this.channel,u.publish(t)};var l=function(t,n,i){if(3!==arguments.length)throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");if(0===n.length)throw new Error("Topics cannot be empty");this.channel=t,this.topic=n,this.subscribe(i)};l.prototype={unsubscribe:function(){this.inactive||(this.inactive=!0,u.unsubscribe(this))},subscribe:function(t){return this.callback=t,this},withContext:function(t){return this.context=t,this}};var f={cache:{},regex:{},compare:function(n,i){var e,c,s,o=this.cache[i]&&this.cache[i][n];return"undefined"!=typeof o?o:((c=this.regex[n])||(e="^"+t.map(n.split("."),function(t){var n="";return s&&(n="#"!==s?"\\.\\b":"\\b"),n+="#"===t?"[\\s\\S]*":"*"===t?"[^.]+":t,s=t,n}).join("")+"$",c=this.regex[n]=new RegExp(e)),this.cache[i]=this.cache[i]||{},this.cache[i][n]=o=c.test(i),o)},reset:function(){this.cache={},this.regex={}}},b=function(t,n){!t.inactive&&u.configuration.resolver.compare(t.topic,n.topic)&&t.callback.call(t.context||this,n.data,n)},g=0,d=[];if(u={configuration:{resolver:f,DEFAULT_CHANNEL:"/",SYSTEM_CHANNEL:"postal"},subscriptions:{},wireTaps:[],ChannelDefinition:p,SubscriptionDefinition:l,channel:function(t){return new p(t)},addWireTap:function(t){var n=this;return n.wireTaps.push(t),function(){var i=n.wireTaps.indexOf(t);-1!==i&&n.wireTaps.splice(i,1)}},noConflict:function(){if("undefined"==typeof window||"undefined"!=typeof window&&"function"==typeof define&&define.amd)throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");return i.postal=h,this},getSubscribersFor:function(n){var i=[];return t.each(this.subscriptions,function(e){t.each(e,function(e){i=i.concat(t.filter(e,s(n)))})}),i},reset:function(){this.unsubscribeFor(),this.configuration.resolver.reset(),this.subscriptions={}},unsubscribeFor:function(t){var n=[];this.subscriptions&&(n=this.getSubscribersFor(t),this.unsubscribe.apply(this,n))}},u.subscribe=new n.Sync({target:o,context:u}),u.publish=n.Async({target:r,context:u}),u.unsubscribe=new n.Sync({target:a,context:u}),u.subscribe.after(function(t){u.publish(c("created",t))}),u.subscriptions[u.configuration.SYSTEM_CHANNEL]={},i&&Object.prototype.hasOwnProperty.call(i,"__postalReady__")&&t.isArray(i.__postalReady__))for(;i.__postalReady__.length;)i.__postalReady__.shift().onReady(u);return u});
|
||||
@@ -0,0 +1,498 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = factory(require("lodash"), require("conduitjs"), this);
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["lodash", "conduitjs"], function (_, Conduit) {
|
||||
return factory(_, Conduit, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root._, root.Conduit, root);
|
||||
}
|
||||
}(this, function (_, Conduit, global, undefined) {
|
||||
var _postal;
|
||||
var prevPostal = global.postal;
|
||||
var ChannelDefinition = function (channelName) {
|
||||
this.channel = channelName || _postal.configuration.DEFAULT_CHANNEL;
|
||||
this.initialize();
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {};
|
||||
ChannelDefinition.prototype.subscribe = function () {
|
||||
return _postal.subscribe({
|
||||
channel: this.channel,
|
||||
topic: (arguments.length === 1 ? arguments[0].topic : arguments[0]),
|
||||
callback: (arguments.length === 1 ? arguments[0].callback : arguments[1])
|
||||
});
|
||||
};
|
||||
ChannelDefinition.prototype.publish = function () {
|
||||
var envelope = arguments.length === 1 ? (Object.prototype.toString.call(arguments[0]) === "[object String]" ? {
|
||||
topic: arguments[0]
|
||||
} : arguments[0]) : {
|
||||
topic: arguments[0],
|
||||
data: arguments[1]
|
||||
};
|
||||
envelope.channel = this.channel;
|
||||
_postal.publish(envelope);
|
||||
};
|
||||
var SubscriptionDefinition = function (channel, topic, callback) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error("You must provide a channel, topic and callback when creating a SubscriptionDefinition instance.");
|
||||
}
|
||||
if (topic.length === 0) {
|
||||
throw new Error("Topics cannot be empty");
|
||||
}
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.subscribe(callback);
|
||||
};
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe: function () {
|
||||
if (!this.inactive) {
|
||||
this.inactive = true;
|
||||
_postal.unsubscribe(this);
|
||||
}
|
||||
},
|
||||
subscribe: function (callback) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
},
|
||||
withContext: function (context) {
|
||||
this.context = context;
|
||||
return this;
|
||||
}
|
||||
}; /* global,SubscriptionDefinition,Conduit,ChannelDefinition */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
} else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit.Async({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit.Async({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
var bindingsResolver = {
|
||||
cache: {},
|
||||
regex: {},
|
||||
compare: function (binding, topic) {
|
||||
var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
|
||||
if (typeof result !== "undefined") {
|
||||
return result;
|
||||
}
|
||||
if (!(rgx = this.regex[binding])) {
|
||||
pattern = "^" + _.map(binding.split("."), function (segment) {
|
||||
var res = "";
|
||||
if ( !! prevSegment) {
|
||||
res = prevSegment !== "#" ? "\\.\\b" : "\\b";
|
||||
}
|
||||
if (segment === "#") {
|
||||
res += "[\\s\\S]*";
|
||||
} else if (segment === "*") {
|
||||
res += "[^.]+";
|
||||
} else {
|
||||
res += segment;
|
||||
}
|
||||
prevSegment = segment;
|
||||
return res;
|
||||
}).join("") + "$";
|
||||
rgx = this.regex[binding] = new RegExp(pattern);
|
||||
}
|
||||
this.cache[topic] = this.cache[topic] || {};
|
||||
this.cache[topic][binding] = result = rgx.test(topic);
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.cache = {};
|
||||
this.regex = {};
|
||||
}
|
||||
};
|
||||
var fireSub = function (subDef, envelope) {
|
||||
if (!subDef.inactive && _postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
|
||||
subDef.callback.call(subDef.context || this, envelope.data, envelope);
|
||||
}
|
||||
};
|
||||
var pubInProgress = 0;
|
||||
var unSubQueue = [];
|
||||
function clearUnSubQueue() {
|
||||
while (unSubQueue.length) {
|
||||
_postal.unsubscribe(unSubQueue.shift());
|
||||
}
|
||||
}
|
||||
function getSystemMessage(kind, subDef) {
|
||||
return {
|
||||
channel: _postal.configuration.SYSTEM_CHANNEL,
|
||||
topic: "subscription." + kind,
|
||||
data: {
|
||||
event: "subscription." + kind,
|
||||
channel: subDef.channel,
|
||||
topic: subDef.topic
|
||||
}
|
||||
};
|
||||
}
|
||||
function getPredicate(options) {
|
||||
if (typeof options === "function") {
|
||||
return options;
|
||||
} else if (!options) {
|
||||
return function () {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
return function (sub) {
|
||||
var compared = 0,
|
||||
matched = 0;
|
||||
_.each(options, function (val, prop) {
|
||||
compared += 1;
|
||||
if (
|
||||
// We use the bindings resolver to compare the options.topic to subDef.topic
|
||||
(prop === "topic" && _postal.configuration.resolver.compare(sub.topic, options.topic))
|
||||
// We need to account for the context possibly being available on callback due to Conduit
|
||||
|| (prop === "context" && options.context === (sub.callback.context && sub.callback.context() || sub.context))
|
||||
// Any other potential prop/value matching outside topic & context...
|
||||
|| (sub[prop] === options[prop])) {
|
||||
matched += 1;
|
||||
}
|
||||
});
|
||||
return compared === matched;
|
||||
};
|
||||
}
|
||||
}
|
||||
function subscribe(options) {
|
||||
var subDef = new SubscriptionDefinition(options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback);
|
||||
var channel = this.subscriptions[subDef.channel];
|
||||
var subs;
|
||||
if (!channel) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if (!subs) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push(subDef);
|
||||
return subDef;
|
||||
}
|
||||
function publish(envelope) {
|
||||
++pubInProgress;
|
||||
envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
_.each(this.wireTaps, function (tap) {
|
||||
tap(envelope.data, envelope);
|
||||
});
|
||||
if (this.subscriptions[envelope.channel]) {
|
||||
_.each(this.subscriptions[envelope.channel], function (subscribers) {
|
||||
var idx = 0,
|
||||
len = subscribers.length,
|
||||
subDef;
|
||||
while (idx < len) {
|
||||
if (subDef = subscribers[idx++]) {
|
||||
fireSub(subDef, envelope);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (--pubInProgress === 0) {
|
||||
clearUnSubQueue();
|
||||
}
|
||||
}
|
||||
function unsubscribe() {
|
||||
var idx = 0;
|
||||
var subs = Array.prototype.slice.call(arguments, 0);
|
||||
var subDef;
|
||||
while (subDef = subs.shift()) {
|
||||
if (pubInProgress) {
|
||||
unSubQueue.push(subDef);
|
||||
return;
|
||||
}
|
||||
if (this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic]) {
|
||||
var len = this.subscriptions[subDef.channel][subDef.topic].length;
|
||||
idx = 0;
|
||||
while (idx < len) {
|
||||
if (this.subscriptions[subDef.channel][subDef.topic][idx] === subDef) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
_postal.publish(getSystemMessage("removed", subDef));
|
||||
}
|
||||
}
|
||||
_postal = {
|
||||
configuration: {
|
||||
resolver: bindingsResolver,
|
||||
DEFAULT_CHANNEL: "/",
|
||||
SYSTEM_CHANNEL: "postal"
|
||||
},
|
||||
subscriptions: {},
|
||||
wireTaps: [],
|
||||
ChannelDefinition: ChannelDefinition,
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
channel: function (channelName) {
|
||||
return new ChannelDefinition(channelName);
|
||||
},
|
||||
addWireTap: function (callback) {
|
||||
var self = this;
|
||||
self.wireTaps.push(callback);
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf(callback);
|
||||
if (idx !== -1) {
|
||||
self.wireTaps.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
},
|
||||
noConflict: function () {
|
||||
if (typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd)) {
|
||||
throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
|
||||
}
|
||||
global.postal = prevPostal;
|
||||
return this;
|
||||
},
|
||||
getSubscribersFor: function (options) {
|
||||
var result = [];
|
||||
_.each(this.subscriptions, function (channel) {
|
||||
_.each(channel, function (subList) {
|
||||
result = result.concat(_.filter(subList, getPredicate(options)));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
reset: function () {
|
||||
this.unsubscribeFor();
|
||||
this.configuration.resolver.reset();
|
||||
this.subscriptions = {};
|
||||
},
|
||||
unsubscribeFor: function (options) {
|
||||
var toDispose = [];
|
||||
if (this.subscriptions) {
|
||||
toDispose = this.getSubscribersFor(options);
|
||||
this.unsubscribe.apply(this, toDispose);
|
||||
}
|
||||
}
|
||||
};
|
||||
_postal.subscribe = new Conduit.Sync({
|
||||
target: subscribe,
|
||||
context: _postal
|
||||
});
|
||||
_postal.publish = Conduit.Async({
|
||||
target: publish,
|
||||
context: _postal
|
||||
});
|
||||
_postal.unsubscribe = new Conduit.Sync({
|
||||
target: unsubscribe,
|
||||
context: _postal
|
||||
});
|
||||
_postal.subscribe.after(function (subDef /*, options */ ) {
|
||||
_postal.publish(getSystemMessage("created", subDef));
|
||||
});
|
||||
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};
|
||||
_postal.linkChannels = function (sources, destinations) {
|
||||
var result = [],
|
||||
self = this;
|
||||
sources = !_.isArray(sources) ? [sources] : sources;
|
||||
destinations = !_.isArray(destinations) ? [destinations] : destinations;
|
||||
_.each(sources, function (source) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each(destinations, function (destination) {
|
||||
var destChannel = destination.channel || self.configuration.DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
self.subscribe({
|
||||
channel: source.channel || self.configuration.DEFAULT_CHANNEL,
|
||||
topic: sourceTopic,
|
||||
callback: function (data, env) {
|
||||
var newEnv = _.clone(env);
|
||||
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
self.publish(newEnv);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
|
||||
while (global.__postalReady__.length) {
|
||||
global.__postalReady__.shift().onReady(_postal);
|
||||
}
|
||||
}
|
||||
return _postal;
|
||||
}));
|
||||
+8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
// Node, or CommonJS-Like environments
|
||||
module.exports = function (postal) {
|
||||
return factory(postal, this);
|
||||
};
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(["postal"], function (postal) {
|
||||
return factory(postal, root);
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory(root.postal, root);
|
||||
}
|
||||
}(this, function (postal, global, undefined) {
|
||||
(function (SubscriptionDefinition) { /* global,SubscriptionDefinition,Conduit,ChannelDefinition */
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function (data) {
|
||||
var eq = false;
|
||||
if (_.isString(data)) {
|
||||
eq = data === previous;
|
||||
previous = data;
|
||||
} else {
|
||||
eq = _.isEqual(data, previous);
|
||||
previous = _.clone(data);
|
||||
}
|
||||
return !eq;
|
||||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
return function (data) {
|
||||
var isDistinct = !_.any(previous, function (p) {
|
||||
if (_.isObject(data) || _.isArray(data)) {
|
||||
return _.isEqual(data, p);
|
||||
}
|
||||
return data === p;
|
||||
});
|
||||
if (isDistinct) {
|
||||
previous.push(data);
|
||||
}
|
||||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var strats = {
|
||||
withDelay: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withDelay",
|
||||
fn: function (next, data, envelope) {
|
||||
setTimeout(function () {
|
||||
next(data, envelope);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
},
|
||||
defer: function () {
|
||||
return this.withDelay(0);
|
||||
},
|
||||
stopAfter: function (maxCalls, callback) {
|
||||
if (_.isNaN(maxCalls) || maxCalls <= 0) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
var dispose = _.after(maxCalls, callback);
|
||||
return {
|
||||
name: "stopAfter",
|
||||
fn: function (next, data, envelope) {
|
||||
dispose();
|
||||
next(data, envelope);
|
||||
}
|
||||
};
|
||||
},
|
||||
withThrottle: function (ms) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "withThrottle",
|
||||
fn: _.throttle(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms)
|
||||
};
|
||||
},
|
||||
withDebounce: function (ms, immediate) {
|
||||
if (_.isNaN(ms)) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
return {
|
||||
name: "debounce",
|
||||
fn: _.debounce(function (next, data, envelope) {
|
||||
next(data, envelope);
|
||||
}, ms, !! immediate)
|
||||
};
|
||||
},
|
||||
withConstraint: function (pred) {
|
||||
if (!_.isFunction(pred)) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
return {
|
||||
name: "withConstraint",
|
||||
fn: function (next, data, envelope) {
|
||||
if (pred.call(this, data, envelope)) {
|
||||
next.call(this, data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
distinct: function (options) {
|
||||
options = options || {};
|
||||
var accessor = function (args) {
|
||||
return args[0];
|
||||
};
|
||||
var check = options.all ? new DistinctPredicate(accessor) : new ConsecutiveDistinctPredicate(accessor);
|
||||
return {
|
||||
name: "distinct",
|
||||
fn: function (next, data, envelope) {
|
||||
if (check(data)) {
|
||||
next(data, envelope);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SubscriptionDefinition.prototype.defer = function () {
|
||||
this.callback.before(strats.defer());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.disposeAfter = function (maxCalls) {
|
||||
var self = this;
|
||||
self.callback.before(strats.stopAfter(maxCalls, function () {
|
||||
self.unsubscribe.call(self);
|
||||
}));
|
||||
return self;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinctUntilChanged = function () {
|
||||
this.callback.before(strats.distinct());
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.distinct = function () {
|
||||
this.callback.before(strats.distinct({
|
||||
all: true
|
||||
}));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.once = function () {
|
||||
this.disposeAfter(1);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraint = function (predicate) {
|
||||
this.callback.before(strats.withConstraint(predicate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withConstraints = function (preds) {
|
||||
while (preds.length) {
|
||||
this.callback.before(strats.withConstraint(preds.shift()));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDebounce = function (milliseconds, immediate) {
|
||||
this.callback.before(strats.withDebounce(milliseconds, immediate));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withDelay = function (milliseconds) {
|
||||
this.callback.before(strats.withDelay(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withThrottle = function (milliseconds) {
|
||||
this.callback.before(strats.withThrottle(milliseconds));
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.subscribe = function (callback) {
|
||||
this.callback = new Conduit.Async({
|
||||
target: callback,
|
||||
context: this
|
||||
});
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.withContext = function (context) {
|
||||
this.callback.context(context);
|
||||
return this;
|
||||
};
|
||||
SubscriptionDefinition.prototype.after = function () {
|
||||
this.callback.after.apply(this, arguments);
|
||||
};
|
||||
SubscriptionDefinition.prototype.before = function () {
|
||||
this.callback.before.apply(this, arguments);
|
||||
};
|
||||
ChannelDefinition.prototype.initialize = function () {
|
||||
var oldPub = this.publish;
|
||||
this.publish = new Conduit.Async({
|
||||
target: oldPub,
|
||||
context: this
|
||||
});
|
||||
};
|
||||
}(postal.SubscriptionDefinition));
|
||||
return postal;
|
||||
}));
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* postal - Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.
|
||||
* Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
* Version: v0.9.1
|
||||
* Url: http://github.com/postaljs/postal.js
|
||||
* License(s): MIT, GPL
|
||||
*/
|
||||
(function(t,n){"object"==typeof module&&module.exports?module.exports=function(t){return n(t,this)}:"function"==typeof define&&define.amd?define(["postal"],function(i){return n(i,t)}):t.postal=n(t.postal,t)})(this,function(t){return function(t){var n=function(){var t;return function(n){var i=!1;return _.isString(n)?(i=n===t,t=n):(i=_.isEqual(n,t),t=_.clone(n)),!i}},i=function(){var t=[];return function(n){var i=!_.any(t,function(t){return _.isObject(n)||_.isArray(n)?_.isEqual(n,t):n===t});return i&&t.push(n),i}},e={withDelay:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withDelay",fn:function(n,i,e){setTimeout(function(){n(i,e)},t)}}},defer:function(){return this.withDelay(0)},stopAfter:function(t,n){if(_.isNaN(t)||0>=t)throw"The value provided to disposeAfter (maxCalls) must be a number greater than zero.";var i=_.after(t,n);return{name:"stopAfter",fn:function(t,n,e){i(),t(n,e)}}},withThrottle:function(t){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"withThrottle",fn:_.throttle(function(t,n,i){t(n,i)},t)}},withDebounce:function(t,n){if(_.isNaN(t))throw"Milliseconds must be a number";return{name:"debounce",fn:_.debounce(function(t,n,i){t(n,i)},t,!!n)}},withConstraint:function(t){if(!_.isFunction(t))throw"Predicate constraint must be a function";return{name:"withConstraint",fn:function(n,i,e){t.call(this,i,e)&&n.call(this,i,e)}}},distinct:function(t){t=t||{};var e=function(t){return t[0]},o=t.all?new i(e):new n(e);return{name:"distinct",fn:function(t,n,i){o(n)&&t(n,i)}}}};t.prototype.defer=function(){return this.callback.before(e.defer()),this},t.prototype.disposeAfter=function(t){var n=this;return n.callback.before(e.stopAfter(t,function(){n.unsubscribe.call(n)})),n},t.prototype.distinctUntilChanged=function(){return this.callback.before(e.distinct()),this},t.prototype.distinct=function(){return this.callback.before(e.distinct({all:!0})),this},t.prototype.once=function(){return this.disposeAfter(1),this},t.prototype.withConstraint=function(t){return this.callback.before(e.withConstraint(t)),this},t.prototype.withConstraints=function(t){for(;t.length;)this.callback.before(e.withConstraint(t.shift()));return this},t.prototype.withDebounce=function(t,n){return this.callback.before(e.withDebounce(t,n)),this},t.prototype.withDelay=function(t){return this.callback.before(e.withDelay(t)),this},t.prototype.withThrottle=function(t){return this.callback.before(e.withThrottle(t)),this},t.prototype.subscribe=function(t){return this.callback=new Conduit.Async({target:t,context:this}),this},t.prototype.withContext=function(t){return this.callback.context(t),this},t.prototype.after=function(){this.callback.after.apply(this,arguments)},t.prototype.before=function(){this.callback.before.apply(this,arguments)},ChannelDefinition.prototype.initialize=function(){var t=this.publish;this.publish=new Conduit.Async({target:t,context:this})}}(t.SubscriptionDefinition),t});
|
||||
@@ -1,85 +1,87 @@
|
||||
{
|
||||
"name" : "postal.js",
|
||||
"filename": "postal.min.js",
|
||||
"description" : "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
|
||||
"version" : "0.8.5",
|
||||
"url" : "http://github.com/postaljs/postal.js",
|
||||
"homepage" : "http://github.com/postaljs/postal.js",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
"url" : "git://github.com/postaljs/postal.js.git"
|
||||
"name": "postal.js",
|
||||
"filename": "postal.min.js",
|
||||
"description": "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
|
||||
"version": "0.9.1",
|
||||
"url": "http://github.com/postaljs/postal.js",
|
||||
"homepage": "http://github.com/postaljs/postal.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/postaljs/postal.js.git"
|
||||
},
|
||||
"author": "Jim Cowart (http://freshbrewedcode.com/jimcowart)",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Jim Cowart",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://freshbrewedcode.com/jimcowart"
|
||||
},
|
||||
"author" : "Jim Cowart (http://freshbrewedcode.com/jimcowart)",
|
||||
"contributors": [
|
||||
{
|
||||
"name" : "Jim Cowart",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://freshbrewedcode.com/jimcowart"
|
||||
},
|
||||
{
|
||||
"name" : "Alex Robson",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://freshbrewedcode.com/alexrobson"
|
||||
},
|
||||
{
|
||||
"name" : "Nicholas Cloud",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://nicholascloud.com"
|
||||
},
|
||||
{
|
||||
"name" : "Doug Neiner",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://dougneiner.com"
|
||||
},
|
||||
{
|
||||
"name" : "Jonathan Creamer",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://freshbrewedcode.com/jonathancreamer"
|
||||
},
|
||||
{
|
||||
"name" : "Elijah Manor",
|
||||
"email" : "WhyNotJustComment@OnMyBlog.com",
|
||||
"url" : "http://www.elijahmanor.com"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"pub/sub",
|
||||
"pub",
|
||||
"sub",
|
||||
"messaging",
|
||||
"message",
|
||||
"bus",
|
||||
"event",
|
||||
"mediator",
|
||||
"broker",
|
||||
"envelope"
|
||||
],
|
||||
"engines" : {
|
||||
"node" : ">=0.4.0"
|
||||
{
|
||||
"name": "Alex Robson",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://freshbrewedcode.com/alexrobson"
|
||||
},
|
||||
"dependencies" : {
|
||||
"underscore" : ">=1.1.7"
|
||||
{
|
||||
"name": "Nicholas Cloud",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://nicholascloud.com"
|
||||
},
|
||||
"licenses" : [
|
||||
{
|
||||
"type" : "MIT",
|
||||
"url" : "http://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
{
|
||||
"type" : "GPL",
|
||||
"url" : "http://www.opensource.org/licenses/gpl-3.0.html"
|
||||
}
|
||||
],
|
||||
"npmName": "postal",
|
||||
"npmFileMap": [{
|
||||
"basePath": "/lib/",
|
||||
"files": [
|
||||
"postal.js",
|
||||
"postal.min.js",
|
||||
"basic/postal.basic.js",
|
||||
"basic/postal.basic.min.js",
|
||||
"strategies-add-on/postal.strategies.js",
|
||||
"strategies-add-on/postal.strategies.min.js"
|
||||
]
|
||||
}]
|
||||
}
|
||||
{
|
||||
"name": "Doug Neiner",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://dougneiner.com"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Creamer",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://freshbrewedcode.com/jonathancreamer"
|
||||
},
|
||||
{
|
||||
"name": "Elijah Manor",
|
||||
"email": "WhyNotJustComment@OnMyBlog.com",
|
||||
"url": "http://www.elijahmanor.com"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"pub/sub",
|
||||
"pub",
|
||||
"sub",
|
||||
"messaging",
|
||||
"message",
|
||||
"bus",
|
||||
"event",
|
||||
"mediator",
|
||||
"broker",
|
||||
"envelope"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore": ">=1.1.7"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
{
|
||||
"type": "GPL",
|
||||
"url": "http://www.opensource.org/licenses/gpl-3.0.html"
|
||||
}
|
||||
],
|
||||
"npmName": "postal",
|
||||
"npmFileMap": [
|
||||
{
|
||||
"basePath": "/lib/",
|
||||
"files": [
|
||||
"postal.js",
|
||||
"postal.min.js",
|
||||
"basic/postal.basic.js",
|
||||
"basic/postal.basic.min.js",
|
||||
"strategies-add-on/postal.strategies.js",
|
||||
"strategies-add-on/postal.strategies.min.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user