Build now runs on gulp.js, updated how dependencies are managed

This commit is contained in:
ifandelse 2014-01-23 22:02:06 -05:00
parent d25b1d0f48
commit f4787f506b
55 changed files with 2424 additions and 40011 deletions

3
.bowerrc Normal file
View file

@ -0,0 +1,3 @@
{
"directory": "bower"
}

2
.gitignore vendored
View file

@ -13,3 +13,5 @@ desktop.ini
.anvil
*node_modules
*npm-debug*
report/
bower/

View file

@ -1,3 +1,4 @@
.anvil/
node_modules/
ext/
ext/
report/

View file

@ -8,9 +8,9 @@ lib/amd
diags
.gitignore
.idea
build.json
header.js
.anvil
component.json
.npmignore
classic-resolver*
classic-resolver*
bower*

View file

@ -130,6 +130,20 @@ There are four main ways you can extend Postal:
It's also possible to extend the monitoring of messages passing through Postal by adding a "wire tap". A wire tap is a callback that will get invoked for any published message (even if no actual subscriptions would bind to the message's topic). Wire taps should _not_ be used in lieu of an actual subscription - but instead should be used for diagnostics, logging, forwarding (to a websocket publisher or a local storage wrapper, for example) or other concerns that fall along those lines. This repository used to include a console logging wiretap called postal.diagnostics.js - you can now find it [here in it's own repo](https://github.com/postaljs/postal.diagnostics). This diagnostics wiretap can be configured with filters to limit the firehose of message data to specific channels/topics and more.
## Build, Dependencies, etc.
* postal depends on [underscore.js](http://underscorejs.org/)
* postal uses [gulp.js](http://gulpjs.com/) for building, running tests and examples.
* To build
* run `npm install` (to install all deps)
* run `npm run build` - then check the lib folder for the output
* To run tests & examples
* To run node-based tests: `npm run test`
* To run browser-based tests & examples:
* run `npm start`
* navigate in your browser to <http://localhost:3080/>
## Can I contribute?
Please - by all means! While I hope the API is relatively stable, I'm open to pull requests. (Hint - if you want a feature implemented, a pull request gives it a much higher probability of being included than simply asking me.) As I said, pull requests are most certainly welcome - but please include tests for your additions. Otherwise, it will disappear into the ether.

View file

@ -1,31 +1,42 @@
{
"name": "postal.js",
"description": "Client-side messaging library.",
"homepage": "https://github.com/postaljs/postal.js",
"keywords": [
"pub/sub",
"pub",
"sub",
"messaging",
"message",
"bus",
"event",
"eventing",
"mediator",
"broker",
"envelope"
],
"author": "Jim Cowart <jim@ifandelse.com>",
"repository": {
"type": "git",
"url": "git://github.com/postaljs/postal.js.git"
},
"version": "0.8.11",
"main": [
"lib/postal.min.js",
"lib/postal.js"
],
"dependencies": {
"underscore": "~1.3.0"
}
}
"name": "postal.js",
"version": "0.8.11",
"description": "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
"homepage": "https://github.com/postaljs/postal.js",
"keywords": [
"pub/sub",
"pub",
"sub",
"messaging",
"message",
"bus",
"event",
"eventing",
"mediator",
"broker",
"envelope"
],
"author": "Jim Cowart <jim@ifandelse.com>",
"repository": {
"type": "git",
"url": "git://github.com/postaljs/postal.js.git"
},
"main": [
"lib/postal.min.js",
"lib/postal.js"
],
"dependencies": {
"underscore": "~1.5.2"
},
"devDependencies": {
"jquery": "~1.10.2",
"bootstrap": "~3.0.3",
"expect": "0.1.2",
"mocha": "~1.17.0",
"requirejs": "~2.1.10",
"postal.diagnostics": "~0.7.0"
},
"resolutions": {
"underscore": "~1.5.2"
}
}

View file

@ -1,14 +0,0 @@
{
"anvil.uglify" : { "all" :true },
"httpPaths": {
"/": "./"
},
"output": {
"full": "lib",
"partial": {
"/lib/postal.js" : [ "/example/standard/js", "/example/amd/js/libs/postal" ],
"/lib/postal.min.js" : [ "/example/standard/js", "/example/amd/js/libs/postal" ]
}
},
"dependencies": [ "anvil.uglify" ]
}

View file

@ -4,7 +4,7 @@
<head>
<title>Postal Examples (AMD/require.js Lib Format)</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script data-main="js/main" src="js/libs/require/require.js"></script>
<script data-main="js/main" src="../../bower/requirejs/require.js"></script>
</head>
<body>
<div>

File diff suppressed because it is too large Load diff

View file

@ -1,89 +0,0 @@
// This is the amd module version of postal.diagnostics.js
// If you need the standard lib version, go to http://github.com/ifandelse/postal.js
define( [ "postal", "underscore" ], function ( postal, _, undefined ) {
var filters = [],
applyFilter = function ( filter, env ) {
var match = 0, possible = 0;
_.each( filter, function ( item, key ) {
if ( env[key] ) {
possible++;
if ( _.isRegExp( item ) && item.test( env[key] ) ) {
match++;
}
else if ( _.isObject( env[key] ) && !_.isArray( env[key] ) ) {
if ( applyFilter( item, env[key] ) ) {
match++;
}
}
else {
if ( _.isEqual( env[key], item ) ) {
match++;
}
}
}
} );
return match === possible;
};
// this returns a callback that, if invoked, removes the wireTap
var wireTap = postal.addWireTap( function ( data, envelope ) {
if ( !filters.length || _.any( filters, function ( filter ) {
return applyFilter( filter, envelope );
} ) ) {
if ( !JSON ) {
throw "This browser or environment does not provide JSON support";
}
try {
console.log( JSON.stringify( envelope ) );
}
catch ( exception ) {
try {
var env = _.extend( {}, envelope );
delete env.data;
console.log( JSON.stringify( env ) + "\n\t" + "JSON.stringify Error: " + exception.message );
}
catch ( ex ) {
console.log( "Unable to parse data to JSON: " + exception );
}
}
}
} );
postal.diagnostics = postal.diagnostics || {};
postal.diagnostics.console = {
clearFilters : function () {
filters = [];
},
removeFilter : function ( filter ) {
filters = _.filter( filters, function ( item ) {
return !_.isEqual( item, filter );
} );
},
addFilter : function ( constraint ) {
if ( !_.isArray( constraint ) ) {
constraint = [ constraint ];
}
_.each( constraint, function ( item ) {
if ( filters.length === 0 || !_.any( filters, function ( filter ) {
return _.isEqual( filter, item );
} ) ) {
filters.push( item );
}
} );
},
getCurrentFilters : function () {
return filters;
},
removeWireTap : function () {
if ( wireTap ) {
wireTap();
}
}
};
} );

View file

@ -1 +0,0 @@
define(["postal","underscore"],function(a,b,c){var d=[],e=function(a,c){var d=0,f=0;return b.each(a,function(a,g){c[g]&&(f++,b.isRegExp(a)&&a.test(c[g])?d++:b.isObject(c[g])&&!b.isArray(c[g])?e(a,c[g])&&d++:b.isEqual(c[g],a)&&d++)}),d===f},f=a.addWireTap(function(a,c){if(!d.length||b.any(d,function(a){return e(a,c)})){if(!JSON)throw"This browser or environment does not provide JSON support";try{console.log(JSON.stringify(c))}catch(f){try{var g=b.extend({},c);delete g.data,console.log(JSON.stringify(g)+"\n "+"JSON.stringify Error: "+f.message)}catch(h){console.log("Unable to parse data to JSON: "+f)}}}});a.diagnostics=a.diagnostics||{},a.diagnostics.console={clearFilters:function(){d=[]},removeFilter:function(a){d=b.filter(d,function(c){return!b.isEqual(c,a)})},addFilter:function(a){b.isArray(a)||(a=[a]),b.each(a,function(a){(d.length===0||!b.any(d,function(c){return b.isEqual(c,a)}))&&d.push(a)})},getCurrentFilters:function(){return d},removeWireTap:function(){f&&f()}}})

View file

@ -1,450 +0,0 @@
/*
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;
} ));

File diff suppressed because one or more lines are too long

View file

@ -1,621 +0,0 @@
/*
RequireJS 1.0.2 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
var requirejs, require, define;
(function () {
function J( a ) {
return M.call( a ) === "[object Function]"
}
function E( a ) {
return M.call( a ) === "[object Array]"
}
function Z( a, c, h ) {
for ( var k in c ) {
if ( !(k in K) && (!(k in a) || h) ) {
a[k] = c[k];
}
}
return d
}
function N( a, c, d ) {
a = Error( c + "\nhttp://requirejs.org/docs/errors.html#" + a );
if ( d ) {
a.originalError = d;
}
return a
}
function $( a, c, d ) {
var k, j, q;
for ( k = 0; q = c[k]; k++ ) {
q = typeof q === "string" ? {name : q} : q;
j = q.location;
if ( d && (!j || j.indexOf( "/" ) !== 0 && j.indexOf( ":" ) === -1) ) {
j = d + "/" + (j || q.name);
}
a[q.name] = {name : q.name, location : j ||
q.name, main : (q.main || "main").replace( ea, "" ).replace( aa, "" )}
}
}
function V( a, c ) {
a.holdReady ? a.holdReady( c ) : c ? a.readyWait += 1 : a.ready( !0 )
}
function fa( a ) {
function c( b, l ) {
var f, a;
if ( b && b.charAt( 0 ) === "." ) {
if ( l ) {
p.pkgs[l] ? l = [l] : (l = l.split( "/" ), l = l.slice( 0, l.length - 1 ));
f = b = l.concat( b.split( "/" ) );
var c;
for ( a = 0; c = f[a]; a++ ) {
if ( c === "." ) {
f.splice( a, 1 ), a -= 1;
} else if ( c === ".." ) {
if ( a === 1 && (f[2] === ".." || f[0] === "..") ) {
break;
} else {
a > 0 && (f.splice( a - 1, 2 ), a -= 2);
}
}
}
a = p.pkgs[f = b[0]];
b = b.join( "/" );
a && b === f + "/" + a.main && (b = f)
} else {
b.indexOf( "./" ) ===
0 && (b = b.substring( 2 ));
}
}
return b
}
function h( b, l ) {
var f = b ? b.indexOf( "!" ) : -1, a = null, d = l ? l.name : null, i = b, e, h;
f !== -1 && (a = b.substring( 0, f ), b = b.substring( f + 1, b.length ));
a && (a = c( a, d ));
b && (a ? e = (f = m[a]) && f.normalize ? f.normalize( b, function ( b ) {
return c( b, d )
} ) : c( b, d ) : (e = c( b, d ), h = E[e], h || (h = g.nameToUrl( e, null, l ), E[e] = h)));
return{prefix : a, name : e, parentMap : l, url : h, originalName : i, fullName : a ? a + "!" + (e || "") : e}
}
function k() {
var b = !0, l = p.priorityWait, f, a;
if ( l ) {
for ( a = 0; f = l[a]; a++ ) {
if ( !s[f] ) {
b = !1;
break
}
}
b && delete p.priorityWait
}
return b
}
function j( b, l, f ) {
return function () {
var a = ga.call( arguments, 0 ), c;
if ( f && J( c = a[a.length - 1] ) ) {
c.__requireJsBuild = !0;
}
a.push( l );
return b.apply( null, a )
}
}
function q( b, l ) {
var a = j( g.require, b, l );
Z( a, {nameToUrl : j( g.nameToUrl, b ), toUrl : j( g.toUrl, b ), defined : j( g.requireDefined, b ), specified : j( g.requireSpecified, b ), isBrowser : d.isBrowser} );
return a
}
function o( b ) {
var l, a, c, C = b.callback, i = b.map, e = i.fullName, ba = b.deps;
c = b.listeners;
if ( C && J( C ) ) {
if ( p.catchError.define ) {
try {
a = d.execCb( e, b.callback, ba, m[e] )
} catch ( k ) {
l = k
}
} else {
a =
d.execCb( e, b.callback, ba, m[e] );
}
if ( e ) {
(C = b.cjsModule) && C.exports !== void 0 && C.exports !== m[e] ? a = m[e] = b.cjsModule.exports : a === void 0 && b.usingExports ? a = m[e] : (m[e] = a, F[e] && (Q[e] = !0))
}
} else {
e && (a = m[e] = C, F[e] && (Q[e] = !0));
}
if ( D[b.id] ) {
delete D[b.id], b.isDone = !0, g.waitCount -= 1, g.waitCount === 0 && (I = []);
}
delete R[e];
if ( d.onResourceLoad && !b.placeholder ) {
d.onResourceLoad( g, i, b.depArray );
}
if ( l ) {
return a = (e ? h( e ).url : "") || l.fileName || l.sourceURL, c = l.moduleTree, l = N( "defineerror", 'Error evaluating module "' + e + '" at location "' +
a + '":\n' + l + "\nfileName:" + a + "\nlineNumber: " + (l.lineNumber || l.line), l ), l.moduleName = e, l.moduleTree = c, d.onError( l );
}
for ( l = 0; C = c[l]; l++ ) {
C( a )
}
}
function r( b, a ) {
return function ( f ) {
b.depDone[a] || (b.depDone[a] = !0, b.deps[a] = f, b.depCount -= 1, b.depCount || o( b ))
}
}
function u( b, a ) {
var f = a.map, c = f.fullName, h = f.name, i = L[b] || (L[b] = m[b]), e;
if ( !a.loading ) {
a.loading = !0, e = function ( b ) {
a.callback = function () {
return b
};
o( a );
s[a.id] = !0;
w()
}, e.fromText = function ( b, a ) {
var l = O;
s[b] = !1;
g.scriptCount += 1;
g.fake[b] = !0;
l && (O = !1);
d.exec( a );
l && (O = !0);
g.completeLoad( b )
}, c in m ? e( m[c] ) : i.load( h, q( f.parentMap, !0 ), e, p )
}
}
function v( b ) {
D[b.id] || (D[b.id] = b, I.push( b ), g.waitCount += 1)
}
function B( b ) {
this.listeners.push( b )
}
function t( b, a ) {
var f = b.fullName, c = b.prefix, d = c ? L[c] || (L[c] = m[c]) : null, i, e;
f && (i = R[f]);
if ( !i && (e = !0, i = {id : (c && !d ? M++ + "__p@:" : "") + (f || "__r@" + M++), map : b, depCount : 0, depDone : [], depCallbacks : [], deps : [], listeners : [], add : B}, y[i.id] = !0, f && (!c || L[c])) ) {
R[f] = i;
}
c && !d ? (f = t( h( c ), !0 ), f.add( function () {
var a = h( b.originalName, b.parentMap ), a = t( a,
!0 );
i.placeholder = !0;
a.add( function ( b ) {
i.callback = function () {
return b
};
o( i )
} )
} )) : e && a && (s[i.id] = !1, g.paused.push( i ), v( i ));
return i
}
function x( b, a, f, c ) {
var b = h( b, c ), d = b.name, i = b.fullName, e = t( b ), k = e.id, j = e.deps, n;
if ( i ) {
if ( i in m || s[k] === !0 || i === "jquery" && p.jQuery && p.jQuery !== f().fn.jquery ) {
return;
}
y[k] = !0;
s[k] = !0;
i === "jquery" && f && S( f() )
}
e.depArray = a;
e.callback = f;
for ( f = 0; f < a.length; f++ ) {
if ( k = a[f] ) {
k = h( k, d ? b : c ), n = k.fullName, a[f] = n, n === "require" ? j[f] = q( b ) : n === "exports" ? (j[f] = m[i] = {}, e.usingExports = !0) : n ===
"module" ? e.cjsModule = j[f] = {id : d, uri : d ? g.nameToUrl( d, null, c ) : void 0, exports : m[i]} : n in m && !(n in D) && (!(i in F) || i in F && Q[n]) ? j[f] = m[n] : (i in F && (F[n] = !0, delete m[n], T[k.url] = !1), e.depCount += 1, e.depCallbacks[f] = r( e, f ), t( k, !0 ).add( e.depCallbacks[f] ));
}
}
e.depCount ? v( e ) : o( e )
}
function n( b ) {
x.apply( null, b )
}
function z( b, a ) {
if ( !b.isDone ) {
var c = b.map.fullName, d = b.depArray, g, i, e, k;
if ( c ) {
if ( a[c] ) {
return m[c];
}
a[c] = !0
}
if ( d ) {
for ( g = 0; g < d.length; g++ ) {
if ( i = d[g] ) {
if ( (e = h( i ).prefix) && (k = D[e]) && z( k, a ), (e = D[i]) && !e.isDone &&
s[i] ) {
i = z( e, a ), b.depCallbacks[g]( i );
}
}
}
}
return c ? m[c] : void 0
}
}
function A() {
var b = p.waitSeconds * 1E3, a = b && g.startTime + b < (new Date).getTime(), b = "", c = !1, h = !1, j;
if ( !(g.pausedCount > 0) ) {
if ( p.priorityWait ) {
if ( k() ) {
w();
} else {
return;
}
}
for ( j in s ) {
if ( !(j in K) && (c = !0, !s[j]) ) {
if ( a ) {
b += j + " ";
} else {
h = !0;
break
}
}
}
if ( c || g.waitCount ) {
if ( a && b ) {
return j = N( "timeout", "Load timeout for modules: " + b ), j.requireType = "timeout", j.requireModules = b, d.onError( j );
}
if ( h || g.scriptCount ) {
if ( (G || ca) && !W ) {
W = setTimeout( function () {
W = 0;
A()
}, 50 )
}
} else {
if ( g.waitCount ) {
for ( H =
0; b = I[H]; H++ ) {
z( b, {} );
}
g.paused.length && w();
X < 5 && (X += 1, A())
}
X = 0;
d.checkReadyState()
}
}
}
}
var g, w, p = {waitSeconds : 7, baseUrl : "./", paths : {}, pkgs : {}, catchError : {}}, P = [], y = {require : !0, exports : !0, module : !0}, E = {}, m = {}, s = {}, D = {}, I = [], T = {}, M = 0, R = {}, L = {}, F = {}, Q = {}, Y = 0;
S = function ( b ) {
if ( !g.jQuery && (b = b || (typeof jQuery !== "undefined" ? jQuery : null)) && !(p.jQuery && b.fn.jquery !== p.jQuery) && ("holdReady"in b || "readyWait"in b) ) {
if ( g.jQuery = b, n( ["jquery", [], function () {
return jQuery
}] ), g.scriptCount ) {
V( b, !0 ), g.jQueryIncremented =
!0
}
}
};
w = function () {
var b, a, c, h, j, i;
Y += 1;
if ( g.scriptCount <= 0 ) {
g.scriptCount = 0;
}
for ( ; P.length; ) {
if ( b = P.shift(), b[0] === null ) {
return d.onError( N( "mismatch", "Mismatched anonymous define() module: " + b[b.length - 1] ) );
} else {
n( b );
}
}
if ( !p.priorityWait || k() ) {
for ( ; g.paused.length; ) {
j = g.paused;
g.pausedCount += j.length;
g.paused = [];
for ( h = 0; b = j[h]; h++ ) {
a = b.map, c = a.url, i = a.fullName, a.prefix ? u( a.prefix, b ) : !T[c] && !s[i] && (d.load( g, i, c ), c.indexOf( "empty:" ) !== 0 && (T[c] = !0));
}
g.startTime = (new Date).getTime();
g.pausedCount -= j.length
}
}
Y ===
1 && A();
Y -= 1
};
g = {contextName : a, config : p, defQueue : P, waiting : D, waitCount : 0, specified : y, loaded : s, urlMap : E, urlFetched : T, scriptCount : 0, defined : m, paused : [], pausedCount : 0, plugins : L, needFullExec : F, fake : {}, fullExec : Q, managerCallbacks : R, makeModuleMap : h, normalize : c, configure : function ( b ) {
var a, c, d;
b.baseUrl && b.baseUrl.charAt( b.baseUrl.length - 1 ) !== "/" && (b.baseUrl += "/");
a = p.paths;
d = p.pkgs;
Z( p, b, !0 );
if ( b.paths ) {
for ( c in b.paths ) {
c in K || (a[c] = b.paths[c]);
}
p.paths = a
}
if ( (a = b.packagePaths) || b.packages ) {
if ( a ) {
for ( c in a ) {
c in
K || $( d, a[c], c );
}
}
b.packages && $( d, b.packages );
p.pkgs = d
}
if ( b.priority ) {
c = g.requireWait, g.requireWait = !1, g.takeGlobalQueue(), w(), g.require( b.priority ), w(), g.requireWait = c, p.priorityWait = b.priority;
}
if ( b.deps || b.callback ) {
g.require( b.deps || [], b.callback )
}
}, requireDefined : function ( b, a ) {
return h( b, a ).fullName in m
}, requireSpecified : function ( b, a ) {
return h( b, a ).fullName in y
}, require : function ( b, c, f ) {
if ( typeof b === "string" ) {
if ( J( c ) ) {
return d.onError( N( "requireargs", "Invalid require call" ) );
}
if ( d.get ) {
return d.get( g,
b, c );
}
c = h( b, c );
b = c.fullName;
return!(b in m) ? d.onError( N( "notloaded", "Module name '" + c.fullName + "' has not been loaded yet for context: " + a ) ) : m[b]
}
(b && b.length || c) && x( null, b, c, f );
if ( !g.requireWait ) {
for ( ; !g.scriptCount && g.paused.length; ) {
g.takeGlobalQueue(), w();
}
}
return g.require
}, takeGlobalQueue : function () {
U.length && (ha.apply( g.defQueue, [g.defQueue.length - 1, 0].concat( U ) ), U = [])
}, completeLoad : function ( b ) {
var a;
for ( g.takeGlobalQueue(); P.length; ) {
if ( a = P.shift(), a[0] === null ) {
a[0] = b;
break
} else if ( a[0] === b ) {
break;
}
else {
n( a ), a = null;
}
}
a ? n( a ) : n( [b, [], b === "jquery" && typeof jQuery !== "undefined" ? function () {
return jQuery
} : null] );
S();
d.isAsync && (g.scriptCount -= 1);
w();
d.isAsync || (g.scriptCount -= 1)
}, toUrl : function ( a, c ) {
var d = a.lastIndexOf( "." ), h = null;
d !== -1 && (h = a.substring( d, a.length ), a = a.substring( 0, d ));
return g.nameToUrl( a, h, c )
}, nameToUrl : function ( a, h, f ) {
var j, k, i, e, m = g.config, a = c( a, f && f.fullName );
if ( d.jsExtRegExp.test( a ) ) {
h = a + (h ? h : "");
} else {
j = m.paths;
k = m.pkgs;
f = a.split( "/" );
for ( e = f.length; e > 0; e-- ) {
if ( i = f.slice( 0, e ).join( "/" ),
j[i] ) {
f.splice( 0, e, j[i] );
break
} else if ( i = k[i] ) {
a = a === i.name ? i.location + "/" + i.main : i.location;
f.splice( 0, e, a );
break
}
}
h = f.join( "/" ) + (h || ".js");
h = (h.charAt( 0 ) === "/" || h.match( /^\w+:/ ) ? "" : m.baseUrl) + h
}
return m.urlArgs ? h + ((h.indexOf( "?" ) === -1 ? "?" : "&") + m.urlArgs) : h
}};
g.jQueryCheck = S;
g.resume = w;
return g
}
function ia() {
var a, c, d;
if ( n && n.readyState === "interactive" ) {
return n;
}
a = document.getElementsByTagName( "script" );
for ( c = a.length - 1; c > -1 && (d = a[c]); c-- ) {
if ( d.readyState === "interactive" ) {
return n = d;
}
}
return null
}
var ja =
/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, ka = /require\(\s*["']([^'"\s]+)["']\s*\)/g, ea = /^\.\//, aa = /\.js$/, M = Object.prototype.toString, r = Array.prototype, ga = r.slice, ha = r.splice, G = !!(typeof window !== "undefined" && navigator && document), ca = !G && typeof importScripts !== "undefined", la = G && navigator.platform === "PLAYSTATION 3" ? /^complete$/ : /^(complete|loaded)$/, da = typeof opera !== "undefined" && opera.toString() === "[object Opera]", K = {}, t = {}, U = [], n = null, X = 0, O = !1, d, r = {}, I, v, x, y, u, z, A, H, B, S, W;
if ( typeof define === "undefined" ) {
if ( typeof requirejs !==
"undefined" ) {
if ( J( requirejs ) ) {
return;
} else {
r = requirejs, requirejs = void 0;
}
}
typeof require !== "undefined" && !J( require ) && (r = require, require = void 0);
d = requirejs = function ( a, c, d ) {
var k = "_", j;
!E( a ) && typeof a !== "string" && (j = a, E( c ) ? (a = c, c = d) : a = []);
if ( j && j.context ) {
k = j.context;
}
d = t[k] || (t[k] = fa( k ));
j && d.configure( j );
return d.require( a, c )
};
d.config = function ( a ) {
return d( a )
};
require || (require = d);
d.toUrl = function ( a ) {
return t._.toUrl( a )
};
d.version = "1.0.2";
d.jsExtRegExp = /^\/|:|\?|\.js$/;
v = d.s = {contexts : t, skipAsync : {}};
if ( d.isAsync =
d.isBrowser = G ) {
if ( x = v.head = document.getElementsByTagName( "head" )[0], y = document.getElementsByTagName( "base" )[0] ) {
x = v.head = y.parentNode;
}
}
d.onError = function ( a ) {
throw a;
};
d.load = function ( a, c, h ) {
d.resourcesReady( !1 );
a.scriptCount += 1;
d.attach( h, a, c );
if ( a.jQuery && !a.jQueryIncremented ) {
V( a.jQuery, !0 ), a.jQueryIncremented = !0
}
};
define = function ( a, c, d ) {
var k, j;
typeof a !== "string" && (d = c, c = a, a = null);
E( c ) || (d = c, c = []);
!c.length && J( d ) && d.length && (d.toString().replace( ja, "" ).replace( ka, function ( a, d ) {
c.push( d )
} ), c = (d.length ===
1 ? ["require"] : ["require", "exports", "module"]).concat( c ));
if ( O && (k = I || ia()) ) {
a || (a = k.getAttribute( "data-requiremodule" )), j = t[k.getAttribute( "data-requirecontext" )];
}
(j ? j.defQueue : U).push( [a, c, d] )
};
define.amd = {multiversion : !0, plugins : !0, jQuery : !0};
d.exec = function ( a ) {
return eval( a )
};
d.execCb = function ( a, c, d, k ) {
return c.apply( k, d )
};
d.addScriptToDom = function ( a ) {
I = a;
y ? x.insertBefore( a, y ) : x.appendChild( a );
I = null
};
d.onScriptLoad = function ( a ) {
var c = a.currentTarget || a.srcElement, h;
if ( a.type === "load" || c && la.test( c.readyState ) ) {
n =
null, a = c.getAttribute( "data-requirecontext" ), h = c.getAttribute( "data-requiremodule" ), t[a].completeLoad( h ), c.detachEvent && !da ? c.detachEvent( "onreadystatechange", d.onScriptLoad ) : c.removeEventListener( "load", d.onScriptLoad, !1 )
}
};
d.attach = function ( a, c, h, k, j, n ) {
var o;
if ( G ) {
return k = k || d.onScriptLoad, o = c && c.config && c.config.xhtml ? document.createElementNS( "http://www.w3.org/1999/xhtml", "html:script" ) : document.createElement( "script" ), o.type = j || "text/javascript", o.charset = "utf-8", o.async = !v.skipAsync[a], c && o.setAttribute( "data-requirecontext",
c.contextName ), o.setAttribute( "data-requiremodule", h ), o.attachEvent && !da ? (O = !0, n ? o.onreadystatechange = function () {
if ( o.readyState === "loaded" ) {
o.onreadystatechange = null, o.attachEvent( "onreadystatechange", k ), n( o )
}
} : o.attachEvent( "onreadystatechange", k )) : o.addEventListener( "load", k, !1 ), o.src = a, n || d.addScriptToDom( o ), o;
} else {
ca && (importScripts( a ), c.completeLoad( h ));
}
return null
};
if ( G ) {
u = document.getElementsByTagName( "script" );
for ( H = u.length - 1; H > -1 && (z = u[H]); H-- ) {
if ( !x ) {
x = z.parentNode;
}
if ( A = z.getAttribute( "data-main" ) ) {
if ( !r.baseUrl ) {
u =
A.split( "/" ), z = u.pop(), u = u.length ? u.join( "/" ) + "/" : "./", r.baseUrl = u, A = z.replace( aa, "" );
}
r.deps = r.deps ? r.deps.concat( A ) : [A];
break
}
}
}
d.checkReadyState = function () {
var a = v.contexts, c;
for ( c in a ) {
if ( !(c in K) && a[c].waitCount ) {
return;
}
}
d.resourcesReady( !0 )
};
d.resourcesReady = function ( a ) {
var c, h;
d.resourcesDone = a;
if ( d.resourcesDone ) {
for ( h in a = v.contexts, a ) {
if ( !(h in K) && (c = a[h], c.jQueryIncremented) ) {
V( c.jQuery, !1 ), c.jQueryIncremented = !1
}
}
}
};
d.pageLoaded = function () {
if ( document.readyState !== "complete" ) {
document.readyState =
"complete"
}
};
if ( G && document.addEventListener && !document.readyState ) {
document.readyState = "loading", window.addEventListener( "load", d.pageLoaded, !1 );
}
d( r );
if ( d.isAsync && typeof setTimeout !== "undefined" ) {
B = v.contexts[r.context || "_"], B.requireWait = !0, setTimeout( function () {
B.requireWait = !1;
B.takeGlobalQueue();
B.jQueryCheck();
B.scriptCount || B.resume();
d.checkReadyState()
}, 0 )
}
}
})();

View file

@ -1,695 +0,0 @@
// Underscore.js 1.2.2
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function () {
function r( a, c, d ) {
if ( a === c ) {
return a !== 0 || 1 / a == 1 / c;
}
if ( a == null || c == null ) {
return a === c;
}
if ( a._chain ) {
a = a._wrapped;
}
if ( c._chain ) {
c = c._wrapped;
}
if ( b.isFunction( a.isEqual ) ) {
return a.isEqual( c );
}
if ( b.isFunction( c.isEqual ) ) {
return c.isEqual( a );
}
var e = l.call( a );
if ( e != l.call( c ) ) {
return false;
}
switch ( e ) {
case "[object String]":
return String( a ) == String( c );
case "[object Number]":
return a = +a, c = +c, a != a ? c != c : a == 0 ? 1 / a == 1 / c : a == c;
case "[object Date]":
case "[object Boolean]":
return+a == +c;
case "[object RegExp]":
return a.source ==
c.source && a.global == c.global && a.multiline == c.multiline && a.ignoreCase == c.ignoreCase
}
if ( typeof a != "object" || typeof c != "object" ) {
return false;
}
for ( var f = d.length; f--; ) {
if ( d[f] == a ) {
return true;
}
}
d.push( a );
var f = 0, g = true;
if ( e == "[object Array]" ) {
if ( f = a.length, g = f == c.length ) {
for ( ; f--; ) {
if ( !(g = f in a == f in c && r( a[f], c[f], d )) ) {
break
}
}
}
} else {
if ( "constructor"in a != "constructor"in c || a.constructor != c.constructor ) {
return false;
}
for ( var h in a ) {
if ( m.call( a, h ) && (f++, !(g = m.call( c, h ) && r( a[h], c[h], d ))) ) {
break;
}
}
if ( g ) {
for ( h in c ) {
if ( m.call( c,
h ) && !f-- ) {
break;
}
}
g = !f
}
}
d.pop();
return g
}
var s = this, F = s._, o = {}, k = Array.prototype, p = Object.prototype, i = k.slice, G = k.unshift, l = p.toString, m = p.hasOwnProperty, v = k.forEach, w = k.map, x = k.reduce, y = k.reduceRight, z = k.filter, A = k.every, B = k.some, q = k.indexOf, C = k.lastIndexOf, p = Array.isArray, H = Object.keys, t = Function.prototype.bind, b = function ( a ) {
return new n( a )
};
if ( typeof exports !== "undefined" ) {
if ( typeof module !== "undefined" && module.exports ) {
exports = module.exports = b;
}
exports._ = b
} else {
typeof define === "function" && define.amd ?
define( "underscore", function () {
return b
} ) : s._ = b;
}
b.VERSION = "1.2.2";
var j = b.each = b.forEach = function ( a, c, b ) {
if ( a != null ) {
if ( v && a.forEach === v ) {
a.forEach( c, b );
} else if ( a.length === +a.length ) {
for ( var e = 0, f = a.length; e < f; e++ ) {
if ( e in a && c.call( b, a[e], e, a ) === o ) {
break
}
}
} else {
for ( e in a ) {
if ( m.call( a, e ) && c.call( b, a[e], e, a ) === o ) {
break
}
}
}
}
};
b.map = function ( a, c, b ) {
var e = [];
if ( a == null ) {
return e;
}
if ( w && a.map === w ) {
return a.map( c, b );
}
j( a, function ( a, g, h ) {
e[e.length] = c.call( b, a, g, h )
} );
return e
};
b.reduce = b.foldl = b.inject = function ( a, c, d, e ) {
var f =
d !== void 0;
a == null && (a = []);
if ( x && a.reduce === x ) {
return e && (c = b.bind( c, e )), f ? a.reduce( c, d ) : a.reduce( c );
}
j( a, function ( a, b, i ) {
f ? d = c.call( e, d, a, b, i ) : (d = a, f = true)
} );
if ( !f ) {
throw new TypeError( "Reduce of empty array with no initial value" );
}
return d
};
b.reduceRight = b.foldr = function ( a, c, d, e ) {
a == null && (a = []);
if ( y && a.reduceRight === y ) {
return e && (c = b.bind( c, e )), d !== void 0 ? a.reduceRight( c, d ) : a.reduceRight( c );
}
a = (b.isArray( a ) ? a.slice() : b.toArray( a )).reverse();
return b.reduce( a, c, d, e )
};
b.find = b.detect = function ( a, c, b ) {
var e;
D( a, function ( a, g, h ) {
if ( c.call( b, a, g, h ) ) {
return e = a, true
}
} );
return e
};
b.filter = b.select = function ( a, c, b ) {
var e = [];
if ( a == null ) {
return e;
}
if ( z && a.filter === z ) {
return a.filter( c, b );
}
j( a, function ( a, g, h ) {
c.call( b, a, g, h ) && (e[e.length] = a)
} );
return e
};
b.reject = function ( a, c, b ) {
var e = [];
if ( a == null ) {
return e;
}
j( a, function ( a, g, h ) {
c.call( b, a, g, h ) || (e[e.length] = a)
} );
return e
};
b.every = b.all = function ( a, c, b ) {
var e = true;
if ( a == null ) {
return e;
}
if ( A && a.every === A ) {
return a.every( c, b );
}
j( a, function ( a, g, h ) {
if ( !(e = e && c.call( b, a, g, h )) ) {
return o
}
} );
return e
};
var D = b.some = b.any = function ( a, c, d ) {
var c = c || b.identity, e = false;
if ( a == null ) {
return e;
}
if ( B && a.some === B ) {
return a.some( c, d );
}
j( a, function ( a, b, h ) {
if ( e || (e = c.call( d, a, b, h )) ) {
return o
}
} );
return!!e
};
b.include = b.contains = function ( a, c ) {
var b = false;
if ( a == null ) {
return b;
}
return q && a.indexOf === q ? a.indexOf( c ) != -1 : b = D( a, function ( a ) {
return a === c
} )
};
b.invoke = function ( a, c ) {
var d = i.call( arguments, 2 );
return b.map( a, function ( a ) {
return(c.call ? c || a : a[c]).apply( a, d )
} )
};
b.pluck = function ( a, c ) {
return b.map( a, function ( a ) {
return a[c]
} )
};
b.max = function ( a, c, d ) {
if ( !c && b.isArray( a ) ) {
return Math.max.apply( Math, a );
}
if ( !c && b.isEmpty( a ) ) {
return-Infinity;
}
var e = {computed : -Infinity};
j( a, function ( a, b, h ) {
b = c ? c.call( d, a, b, h ) : a;
b >= e.computed && (e = {value : a, computed : b})
} );
return e.value
};
b.min = function ( a, c, d ) {
if ( !c && b.isArray( a ) ) {
return Math.min.apply( Math, a );
}
if ( !c && b.isEmpty( a ) ) {
return Infinity;
}
var e = {computed : Infinity};
j( a, function ( a, b, h ) {
b = c ? c.call( d, a, b, h ) : a;
b < e.computed && (e = {value : a, computed : b})
} );
return e.value
};
b.shuffle = function ( a ) {
var c = [], b;
j( a, function ( a, f ) {
f == 0 ? c[0] = a : (b = Math.floor( Math.random() * (f + 1) ), c[f] = c[b], c[b] = a)
} );
return c
};
b.sortBy = function ( a, c, d ) {
return b.pluck( b.map( a,
function ( a, b, g ) {
return{value : a, criteria : c.call( d, a, b, g )}
} ).sort( function ( a, c ) {
var b = a.criteria, d = c.criteria;
return b < d ? -1 : b > d ? 1 : 0
} ), "value" )
};
b.groupBy = function ( a, c ) {
var d = {}, e = b.isFunction( c ) ? c : function ( a ) {
return a[c]
};
j( a, function ( a, c ) {
var b = e( a, c );
(d[b] || (d[b] = [])).push( a )
} );
return d
};
b.sortedIndex = function ( a, c, d ) {
d || (d = b.identity);
for ( var e = 0, f = a.length; e <
f; ) {
var g = e + f >> 1;
d( a[g] ) < d( c ) ? e = g + 1 : f = g
}
return e
};
b.toArray = function ( a ) {
return!a ? [] : a.toArray ? a.toArray() : b.isArray( a ) ? i.call( a ) : b.isArguments( a ) ? i.call( a ) : b.values( a )
};
b.size = function ( a ) {
return b.toArray( a ).length
};
b.first = b.head = function ( a, b, d ) {
return b != null && !d ? i.call( a, 0, b ) : a[0]
};
b.initial = function ( a, b, d ) {
return i.call( a, 0, a.length - (b == null || d ? 1 : b) )
};
b.last = function ( a, b, d ) {
return b != null && !d ? i.call( a, Math.max( a.length - b, 0 ) ) : a[a.length - 1]
};
b.rest = b.tail = function ( a, b, d ) {
return i.call( a, b == null ||
d ? 1 : b )
};
b.compact = function ( a ) {
return b.filter( a, function ( a ) {
return!!a
} )
};
b.flatten = function ( a, c ) {
return b.reduce( a, function ( a, e ) {
if ( b.isArray( e ) ) {
return a.concat( c ? e : b.flatten( e ) );
}
a[a.length] = e;
return a
}, [] )
};
b.without = function ( a ) {
return b.difference( a, i.call( arguments, 1 ) )
};
b.uniq = b.unique = function ( a, c, d ) {
var d = d ? b.map( a, d ) : a, e = [];
b.reduce( d, function ( d, g, h ) {
if ( 0 == h || (c === true ? b.last( d ) != g : !b.include( d, g )) ) {
d[d.length] = g, e[e.length] = a[h];
}
return d
}, [] );
return e
};
b.union = function () {
return b.uniq( b.flatten( arguments,
true ) )
};
b.intersection = b.intersect = function ( a ) {
var c = i.call( arguments, 1 );
return b.filter( b.uniq( a ), function ( a ) {
return b.every( c, function ( c ) {
return b.indexOf( c, a ) >= 0
} )
} )
};
b.difference = function ( a, c ) {
return b.filter( a, function ( a ) {
return!b.include( c, a )
} )
};
b.zip = function () {
for ( var a = i.call( arguments ), c = b.max( b.pluck( a, "length" ) ), d = Array( c ), e = 0; e < c; e++ ) {
d[e] = b.pluck( a, "" + e );
}
return d
};
b.indexOf = function ( a, c, d ) {
if ( a == null ) {
return-1;
}
var e;
if ( d ) {
return d = b.sortedIndex( a, c ), a[d] === c ? d : -1;
}
if ( q && a.indexOf === q ) {
return a.indexOf( c );
}
for ( d = 0, e = a.length; d < e; d++ ) {
if ( a[d] === c ) {
return d;
}
}
return-1
};
b.lastIndexOf = function ( a, b ) {
if ( a == null ) {
return-1;
}
if ( C && a.lastIndexOf === C ) {
return a.lastIndexOf( b );
}
for ( var d = a.length; d--; ) {
if ( a[d] === b ) {
return d;
}
}
return-1
};
b.range = function ( a, b, d ) {
arguments.length <= 1 && (b = a || 0, a = 0);
for ( var d = arguments[2] || 1, e = Math.max( Math.ceil( (b - a) / d ), 0 ), f = 0, g = Array( e ); f < e; ) {
g[f++] = a, a += d;
}
return g
};
var E = function () {
};
b.bind = function ( a, c ) {
var d, e;
if ( a.bind === t && t ) {
return t.apply( a, i.call( arguments, 1 ) );
}
if ( !b.isFunction( a ) ) {
throw new TypeError;
}
e = i.call( arguments, 2 );
return d = function () {
if ( !(this instanceof d) ) {
return a.apply( c, e.concat( i.call( arguments ) ) );
}
E.prototype = a.prototype;
var b = new E, g = a.apply( b, e.concat( i.call( arguments ) ) );
return Object( g ) === g ? g : b
}
};
b.bindAll = function ( a ) {
var c = i.call( arguments, 1 );
c.length == 0 && (c = b.functions( a ));
j( c, function ( c ) {
a[c] = b.bind( a[c], a )
} );
return a
};
b.memoize = function ( a, c ) {
var d = {};
c || (c = b.identity);
return function () {
var b = c.apply( this, arguments );
return m.call( d, b ) ? d[b] : d[b] = a.apply( this, arguments )
}
};
b.delay =
function ( a, b ) {
var d = i.call( arguments, 2 );
return setTimeout( function () {
return a.apply( a, d )
}, b )
};
b.defer = function ( a ) {
return b.delay.apply( b, [a, 1].concat( i.call( arguments, 1 ) ) )
};
b.throttle = function ( a, c ) {
var d, e, f, g, h, i = b.debounce( function () {
h = g = false
}, c );
return function () {
d = this;
e = arguments;
var b;
f || (f = setTimeout( function () {
f = null;
h && a.apply( d, e );
i()
}, c ));
g ? h = true : a.apply( d, e );
i();
g = true
}
};
b.debounce = function ( a, b ) {
var d;
return function () {
var e = this, f = arguments;
clearTimeout( d );
d = setTimeout( function () {
d =
null;
a.apply( e, f )
}, b )
}
};
b.once = function ( a ) {
var b = false, d;
return function () {
if ( b ) {
return d;
}
b = true;
return d = a.apply( this, arguments )
}
};
b.wrap = function ( a, b ) {
return function () {
var d = [a].concat( i.call( arguments ) );
return b.apply( this, d )
}
};
b.compose = function () {
var a = i.call( arguments );
return function () {
for ( var b = i.call( arguments ), d = a.length - 1; d >= 0; d-- ) {
b = [a[d].apply( this, b )];
}
return b[0]
}
};
b.after = function ( a, b ) {
return a <= 0 ? b() : function () {
if ( --a < 1 ) {
return b.apply( this, arguments )
}
}
};
b.keys = H || function ( a ) {
if ( a !==
Object( a ) ) {
throw new TypeError( "Invalid object" );
}
var b = [], d;
for ( d in a ) {
m.call( a, d ) && (b[b.length] = d);
}
return b
};
b.values = function ( a ) {
return b.map( a, b.identity )
};
b.functions = b.methods = function ( a ) {
var c = [], d;
for ( d in a ) {
b.isFunction( a[d] ) && c.push( d );
}
return c.sort()
};
b.extend = function ( a ) {
j( i.call( arguments, 1 ), function ( b ) {
for ( var d in b ) {
b[d] !== void 0 && (a[d] = b[d])
}
} );
return a
};
b.defaults = function ( a ) {
j( i.call( arguments, 1 ), function ( b ) {
for ( var d in b ) {
a[d] == null && (a[d] = b[d])
}
} );
return a
};
b.clone = function ( a ) {
return!b.isObject( a ) ?
a : b.isArray( a ) ? a.slice() : b.extend( {}, a )
};
b.tap = function ( a, b ) {
b( a );
return a
};
b.isEqual = function ( a, b ) {
return r( a, b, [] )
};
b.isEmpty = function ( a ) {
if ( b.isArray( a ) || b.isString( a ) ) {
return a.length === 0;
}
for ( var c in a ) {
if ( m.call( a, c ) ) {
return false;
}
}
return true
};
b.isElement = function ( a ) {
return!!(a && a.nodeType == 1)
};
b.isArray = p || function ( a ) {
return l.call( a ) == "[object Array]"
};
b.isObject = function ( a ) {
return a === Object( a )
};
b.isArguments = l.call( arguments ) == "[object Arguments]" ? function ( a ) {
return l.call( a ) == "[object Arguments]"
} :
function ( a ) {
return!(!a || !m.call( a, "callee" ))
};
b.isFunction = function ( a ) {
return l.call( a ) == "[object Function]"
};
b.isString = function ( a ) {
return l.call( a ) == "[object String]"
};
b.isNumber = function ( a ) {
return l.call( a ) == "[object Number]"
};
b.isNaN = function ( a ) {
return a !== a
};
b.isBoolean = function ( a ) {
return a === true || a === false || l.call( a ) == "[object Boolean]"
};
b.isDate = function ( a ) {
return l.call( a ) == "[object Date]"
};
b.isRegExp = function ( a ) {
return l.call( a ) == "[object RegExp]"
};
b.isNull = function ( a ) {
return a === null
};
b.isUndefined = function ( a ) {
return a === void 0
};
b.noConflict = function () {
s._ = F;
return this
};
b.identity = function ( a ) {
return a
};
b.times = function ( a, b, d ) {
for ( var e = 0; e < a; e++ ) {
b.call( d, e )
}
};
b.escape = function ( a ) {
return("" + a).replace( /&/g, "&amp;" ).replace( /</g, "&lt;" ).replace( />/g, "&gt;" ).replace( /"/g, "&quot;" ).replace( /'/g, "&#x27;" ).replace( /\//g, "&#x2F;" )
};
b.mixin = function ( a ) {
j( b.functions( a ), function ( c ) {
I( c, b[c] = a[c] )
} )
};
var J = 0;
b.uniqueId = function ( a ) {
var b = J++;
return a ? a + b : b
};
b.templateSettings = {evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g};
b.template = function ( a, c ) {
var d = b.templateSettings, d = "var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('" + a.replace( /\\/g, "\\\\" ).replace( /'/g, "\\'" ).replace( d.escape,
function ( a, b ) {
return"',_.escape(" + b.replace( /\\'/g, "'" ) + "),'"
} ).replace( d.interpolate,
function ( a, b ) {
return"'," + b.replace( /\\'/g, "'" ) + ",'"
} ).replace( d.evaluate || null,
function ( a, b ) {
return"');" + b.replace( /\\'/g, "'" ).replace( /[\r\n\t]/g, " " ) + ";__p.push('"
} ).replace( /\r/g,
"\\r" ).replace( /\n/g, "\\n" ).replace( /\t/g, "\\t" ) + "');}return __p.join('');", e = new Function( "obj", "_", d );
return c ? e( c, b ) : function ( a ) {
return e( a, b )
}
};
var n = function ( a ) {
this._wrapped = a
};
b.prototype = n.prototype;
var u = function ( a, c ) {
return c ? b( a ).chain() : a
}, I = function ( a, c ) {
n.prototype[a] = function () {
var a = i.call( arguments );
G.call( a, this._wrapped );
return u( c.apply( b, a ), this._chain )
}
};
b.mixin( b );
j( "pop,push,reverse,shift,sort,splice,unshift".split( "," ), function ( a ) {
var b = k[a];
n.prototype[a] = function () {
b.apply( this._wrapped,
arguments );
return u( this._wrapped, this._chain )
}
} );
j( ["concat", "join", "slice"], function ( a ) {
var b = k[a];
n.prototype[a] = function () {
return u( b.apply( this._wrapped, arguments ), this._chain )
}
} );
n.prototype.chain = function () {
this._chain = true;
return this
};
n.prototype.value = function () {
return this._wrapped
}
}).call( this );

View file

@ -1,10 +1,15 @@
require.config( {
paths : {
underscore : "libs/underscore/underscore-min",
postal : "libs/postal/postal",
postaldiags : "libs/postal/postal.diagnostics",
jquery : "libs/jquery/jquery-min"
}
underscore : "../../../bower/underscore/underscore-min",
postal : "../../../lib/postal",
postaldiags : "../../../bower/postal.diagnostics/lib/postal.diagnostics",
jquery : "../../../bower/jquery/jquery.min"
},
shim : {
underscore: {
exports: "_"
}
}
} );
require( [ "jquery" ], function ( $ ) {

View file

@ -4,10 +4,10 @@
<head>
<title>Postal Examples (Standard Lib Format)</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="js/underscore.js"></script>
<script type="text/javascript" src="js/jquery-1.7.1.js"></script>
<script type="text/javascript" src="js/postal.js"></script>
<script type="text/javascript" src="js/postal.diagnostics.js"></script>
<script type="text/javascript" src="../../bower/underscore/underscore-min.js"></script>
<script type="text/javascript" src="../../bower/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../../lib/postal.js"></script>
<script type="text/javascript" src="../../bower/postal.diagnostics/lib/postal.diagnostics.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</head>
<body>

File diff suppressed because it is too large Load diff

View file

@ -1,89 +0,0 @@
// This is the standard lib version of postal.diagnostics.js
// If you need the amd-module style version, go to http://github.com/ifandelse/postal.js
(function ( postal, _, undefined ) {
var filters = [],
applyFilter = function ( filter, env ) {
var match = 0, possible = 0;
_.each( filter, function ( item, key ) {
if ( env[key] ) {
possible++;
if ( _.isRegExp( item ) && item.test( env[key] ) ) {
match++;
}
else if ( _.isObject( env[key] ) && !_.isArray( env[key] ) ) {
if ( applyFilter( item, env[key] ) ) {
match++;
}
}
else {
if ( _.isEqual( env[key], item ) ) {
match++;
}
}
}
} );
return match === possible;
};
// this returns a callback that, if invoked, removes the wireTap
var wireTap = postal.addWireTap( function ( data, envelope ) {
if ( !filters.length || _.any( filters, function ( filter ) {
return applyFilter( filter, envelope );
} ) ) {
if ( !JSON ) {
throw "This browser or environment does not provide JSON support";
}
try {
console.log( JSON.stringify( envelope ) );
}
catch ( exception ) {
try {
var env = _.extend( {}, envelope );
delete env.data;
console.log( JSON.stringify( env ) + "\n\t" + "JSON.stringify Error: " + exception.message );
}
catch ( ex ) {
console.log( "Unable to parse data to JSON: " + exception );
}
}
}
} );
postal.diagnostics = postal.diagnostics || {};
postal.diagnostics.console = {
clearFilters : function () {
filters = [];
},
removeFilter : function ( filter ) {
filters = _.filter( filters, function ( item ) {
return !_.isEqual( item, filter );
} );
},
addFilter : function ( constraint ) {
if ( !_.isArray( constraint ) ) {
constraint = [ constraint ];
}
_.each( constraint, function ( item ) {
if ( filters.length === 0 || !_.any( filters, function ( filter ) {
return _.isEqual( filter, item );
} ) ) {
filters.push( item );
}
} );
},
getCurrentFilters : function () {
return filters;
},
removeWireTap : function () {
if ( wireTap ) {
wireTap();
}
}
};
})( postal, _ );

View file

@ -1 +0,0 @@
(function(a,b,c){var d=[],e=function(a,c){var d=0,f=0;return b.each(a,function(a,g){c[g]&&(f++,b.isRegExp(a)&&a.test(c[g])?d++:b.isObject(c[g])&&!b.isArray(c[g])?e(a,c[g])&&d++:b.isEqual(c[g],a)&&d++)}),d===f},f=a.addWireTap(function(a,c){if(!d.length||b.any(d,function(a){return e(a,c)})){if(!JSON)throw"This browser or environment does not provide JSON support";try{console.log(JSON.stringify(c))}catch(f){try{var g=b.extend({},c);delete g.data,console.log(JSON.stringify(g)+"\n "+"JSON.stringify Error: "+f.message)}catch(h){console.log("Unable to parse data to JSON: "+f)}}}});a.diagnostics=a.diagnostics||{},a.diagnostics.console={clearFilters:function(){d=[]},removeFilter:function(a){d=b.filter(d,function(c){return!b.isEqual(c,a)})},addFilter:function(a){b.isArray(a)||(a=[a]),b.each(a,function(a){(d.length===0||!b.any(d,function(c){return b.isEqual(c,a)}))&&d.push(a)})},getCurrentFilters:function(){return d},removeWireTap:function(){f&&f()}}})(postal,_)

View file

@ -1,450 +0,0 @@
/*
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;
} ));

File diff suppressed because one or more lines are too long

View file

@ -1,999 +0,0 @@
// Underscore.js 1.1.7
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function () {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function ( obj ) {
return new wrapper( obj );
};
// Export the Underscore object for **CommonJS**, with backwards-compatibility
// for the old `require()` API. If we're not in CommonJS, add `_` to the
// global object.
if ( typeof module !== 'undefined' && module.exports ) {
module.exports = _;
_._ = _;
} else {
// Exported as a string, for Closure Compiler "advanced" mode.
root['_'] = _;
}
// Current version.
_.VERSION = '1.1.7';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function ( obj, iterator, context ) {
if ( obj == null ) {
return;
}
if ( nativeForEach && obj.forEach === nativeForEach ) {
obj.forEach( iterator, context );
} else if ( obj.length === +obj.length ) {
for ( var i = 0, l = obj.length; i < l; i++ ) {
if ( i in obj && iterator.call( context, obj[i], i, obj ) === breaker ) {
return;
}
}
} else {
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
if ( iterator.call( context, obj[key], key, obj ) === breaker ) {
return;
}
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
if ( nativeMap && obj.map === nativeMap ) {
return obj.map( iterator, context );
}
each( obj, function ( value, index, list ) {
results[results.length] = iterator.call( context, value, index, list );
} );
return results;
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function ( obj, iterator, memo, context ) {
var initial = memo !== void 0;
if ( obj == null ) {
obj = [];
}
if ( nativeReduce && obj.reduce === nativeReduce ) {
if ( context ) {
iterator = _.bind( iterator, context );
}
return initial ? obj.reduce( iterator, memo ) : obj.reduce( iterator );
}
each( obj, function ( value, index, list ) {
if ( !initial ) {
memo = value;
initial = true;
} else {
memo = iterator.call( context, memo, value, index, list );
}
} );
if ( !initial ) {
throw new TypeError( "Reduce of empty array with no initial value" );
}
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function ( obj, iterator, memo, context ) {
if ( obj == null ) {
obj = [];
}
if ( nativeReduceRight && obj.reduceRight === nativeReduceRight ) {
if ( context ) {
iterator = _.bind( iterator, context );
}
return memo !== void 0 ? obj.reduceRight( iterator, memo ) : obj.reduceRight( iterator );
}
var reversed = (_.isArray( obj ) ? obj.slice() : _.toArray( obj )).reverse();
return _.reduce( reversed, iterator, memo, context );
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function ( obj, iterator, context ) {
var result;
any( obj, function ( value, index, list ) {
if ( iterator.call( context, value, index, list ) ) {
result = value;
return true;
}
} );
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
if ( nativeFilter && obj.filter === nativeFilter ) {
return obj.filter( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( iterator.call( context, value, index, list ) ) {
results[results.length] = value;
}
} );
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
each( obj, function ( value, index, list ) {
if ( !iterator.call( context, value, index, list ) ) {
results[results.length] = value;
}
} );
return results;
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function ( obj, iterator, context ) {
var result = true;
if ( obj == null ) {
return result;
}
if ( nativeEvery && obj.every === nativeEvery ) {
return obj.every( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( !(result = result && iterator.call( context, value, index, list )) ) {
return breaker;
}
} );
return result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function ( obj, iterator, context ) {
iterator = iterator || _.identity;
var result = false;
if ( obj == null ) {
return result;
}
if ( nativeSome && obj.some === nativeSome ) {
return obj.some( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( result |= iterator.call( context, value, index, list ) ) {
return breaker;
}
} );
return !!result;
};
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function ( obj, target ) {
var found = false;
if ( obj == null ) {
return found;
}
if ( nativeIndexOf && obj.indexOf === nativeIndexOf ) {
return obj.indexOf( target ) != -1;
}
any( obj, function ( value ) {
if ( found = value === target ) {
return true;
}
} );
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function ( obj, method ) {
var args = slice.call( arguments, 2 );
return _.map( obj, function ( value ) {
return (method.call ? method || value : value[method]).apply( value, args );
} );
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function ( obj, key ) {
return _.map( obj, function ( value ) {
return value[key];
} );
};
// Return the maximum element or (element-based computation).
_.max = function ( obj, iterator, context ) {
if ( !iterator && _.isArray( obj ) ) {
return Math.max.apply( Math, obj );
}
var result = {computed : -Infinity};
each( obj, function ( value, index, list ) {
var computed = iterator ? iterator.call( context, value, index, list ) : value;
computed >= result.computed && (result = {value : value, computed : computed});
} );
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function ( obj, iterator, context ) {
if ( !iterator && _.isArray( obj ) ) {
return Math.min.apply( Math, obj );
}
var result = {computed : Infinity};
each( obj, function ( value, index, list ) {
var computed = iterator ? iterator.call( context, value, index, list ) : value;
computed < result.computed && (result = {value : value, computed : computed});
} );
return result.value;
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function ( obj, iterator, context ) {
return _.pluck( _.map( obj,
function ( value, index, list ) {
return {
value : value,
criteria : iterator.call( context, value, index, list )
};
} ).sort( function ( left, right ) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
} ), 'value' );
};
// Groups the object's values by a criterion produced by an iterator
_.groupBy = function ( obj, iterator ) {
var result = {};
each( obj, function ( value, index ) {
var key = iterator( value, index );
(result[key] || (result[key] = [])).push( value );
} );
return result;
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function ( array, obj, iterator ) {
iterator || (iterator = _.identity);
var low = 0, high = array.length;
while ( low < high ) {
var mid = (low + high) >> 1;
iterator( array[mid] ) < iterator( obj ) ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function ( iterable ) {
if ( !iterable ) {
return [];
}
if ( iterable.toArray ) {
return iterable.toArray();
}
if ( _.isArray( iterable ) ) {
return slice.call( iterable );
}
if ( _.isArguments( iterable ) ) {
return slice.call( iterable );
}
return _.values( iterable );
};
// Return the number of elements in an object.
_.size = function ( obj ) {
return _.toArray( obj ).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function ( array, n, guard ) {
return (n != null) && !guard ? slice.call( array, 0, n ) : array[0];
};
// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function ( array, index, guard ) {
return slice.call( array, (index == null) || guard ? 1 : index );
};
// Get the last element of an array.
_.last = function ( array ) {
return array[array.length - 1];
};
// Trim out all falsy values from an array.
_.compact = function ( array ) {
return _.filter( array, function ( value ) {
return !!value;
} );
};
// Return a completely flattened version of an array.
_.flatten = function ( array ) {
return _.reduce( array, function ( memo, value ) {
if ( _.isArray( value ) ) {
return memo.concat( _.flatten( value ) );
}
memo[memo.length] = value;
return memo;
}, [] );
};
// Return a version of the array that does not contain the specified value(s).
_.without = function ( array ) {
return _.difference( array, slice.call( arguments, 1 ) );
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function ( array, isSorted ) {
return _.reduce( array, function ( memo, el, i ) {
if ( 0 == i || (isSorted === true ? _.last( memo ) != el : !_.include( memo, el )) ) {
memo[memo.length] = el;
}
return memo;
}, [] );
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function () {
return _.uniq( _.flatten( arguments ) );
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function ( array ) {
var rest = slice.call( arguments, 1 );
return _.filter( _.uniq( array ), function ( item ) {
return _.every( rest, function ( other ) {
return _.indexOf( other, item ) >= 0;
} );
} );
};
// Take the difference between one array and another.
// Only the elements present in just the first array will remain.
_.difference = function ( array, other ) {
return _.filter( array, function ( value ) {
return !_.include( other, value );
} );
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function () {
var args = slice.call( arguments );
var length = _.max( _.pluck( args, 'length' ) );
var results = new Array( length );
for ( var i = 0; i < length; i++ ) {
results[i] = _.pluck( args, "" + i );
}
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function ( array, item, isSorted ) {
if ( array == null ) {
return -1;
}
var i, l;
if ( isSorted ) {
i = _.sortedIndex( array, item );
return array[i] === item ? i : -1;
}
if ( nativeIndexOf && array.indexOf === nativeIndexOf ) {
return array.indexOf( item );
}
for ( i = 0, l = array.length; i < l; i++ ) {
if ( array[i] === item ) {
return i;
}
}
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function ( array, item ) {
if ( array == null ) {
return -1;
}
if ( nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf ) {
return array.lastIndexOf( item );
}
var i = array.length;
while ( i-- ) {
if ( array[i] === item ) {
return i;
}
}
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function ( start, stop, step ) {
if ( arguments.length <= 1 ) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max( Math.ceil( (stop - start) / step ), 0 );
var idx = 0;
var range = new Array( len );
while ( idx < len ) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function ( func, obj ) {
if ( func.bind === nativeBind && nativeBind ) {
return nativeBind.apply( func, slice.call( arguments, 1 ) );
}
var args = slice.call( arguments, 2 );
return function () {
return func.apply( obj, args.concat( slice.call( arguments ) ) );
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function ( obj ) {
var funcs = slice.call( arguments, 1 );
if ( funcs.length == 0 ) {
funcs = _.functions( obj );
}
each( funcs, function ( f ) {
obj[f] = _.bind( obj[f], obj );
} );
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function ( func, hasher ) {
var memo = {};
hasher || (hasher = _.identity);
return function () {
var key = hasher.apply( this, arguments );
return hasOwnProperty.call( memo, key ) ? memo[key] : (memo[key] = func.apply( this, arguments ));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function ( func, wait ) {
var args = slice.call( arguments, 2 );
return setTimeout( function () {
return func.apply( func, args );
}, wait );
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function ( func ) {
return _.delay.apply( _, [func, 1].concat( slice.call( arguments, 1 ) ) );
};
// Internal function used to implement `_.throttle` and `_.debounce`.
var limit = function ( func, wait, debounce ) {
var timeout;
return function () {
var context = this, args = arguments;
var throttler = function () {
timeout = null;
func.apply( context, args );
};
if ( debounce ) {
clearTimeout( timeout );
}
if ( debounce || !timeout ) {
timeout = setTimeout( throttler, wait );
}
};
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function ( func, wait ) {
return limit( func, wait, false );
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function ( func, wait ) {
return limit( func, wait, true );
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function ( func ) {
var ran = false, memo;
return function () {
if ( ran ) {
return memo;
}
ran = true;
return memo = func.apply( this, arguments );
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function ( func, wrapper ) {
return function () {
var args = [func].concat( slice.call( arguments ) );
return wrapper.apply( this, args );
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function () {
var funcs = slice.call( arguments );
return function () {
var args = slice.call( arguments );
for ( var i = funcs.length - 1; i >= 0; i-- ) {
args = [funcs[i].apply( this, args )];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function ( times, func ) {
return function () {
if ( --times < 1 ) {
return func.apply( this, arguments );
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function ( obj ) {
if ( obj !== Object( obj ) ) {
throw new TypeError( 'Invalid object' );
}
var keys = [];
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
keys[keys.length] = key;
}
}
return keys;
};
// Retrieve the values of an object's properties.
_.values = function ( obj ) {
return _.map( obj, _.identity );
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function ( obj ) {
var names = [];
for ( var key in obj ) {
if ( _.isFunction( obj[key] ) ) {
names.push( key );
}
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function ( obj ) {
each( slice.call( arguments, 1 ), function ( source ) {
for ( var prop in source ) {
if ( source[prop] !== void 0 ) {
obj[prop] = source[prop];
}
}
} );
return obj;
};
// Fill in a given object with default properties.
_.defaults = function ( obj ) {
each( slice.call( arguments, 1 ), function ( source ) {
for ( var prop in source ) {
if ( obj[prop] == null ) {
obj[prop] = source[prop];
}
}
} );
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function ( obj ) {
return _.isArray( obj ) ? obj.slice() : _.extend( {}, obj );
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function ( obj, interceptor ) {
interceptor( obj );
return obj;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function ( a, b ) {
// Check object identity.
if ( a === b ) {
return true;
}
// Different types?
var atype = typeof(a), btype = typeof(b);
if ( atype != btype ) {
return false;
}
// Basic equality test (watch out for coercions).
if ( a == b ) {
return true;
}
// One is falsy and the other truthy.
if ( (!a && b) || (a && !b) ) {
return false;
}
// Unwrap any wrapped objects.
if ( a._chain ) {
a = a._wrapped;
}
if ( b._chain ) {
b = b._wrapped;
}
// One of them implements an isEqual()?
if ( a.isEqual ) {
return a.isEqual( b );
}
if ( b.isEqual ) {
return b.isEqual( a );
}
// Check dates' integer values.
if ( _.isDate( a ) && _.isDate( b ) ) {
return a.getTime() === b.getTime();
}
// Both are NaN?
if ( _.isNaN( a ) && _.isNaN( b ) ) {
return false;
}
// Compare regular expressions.
if ( _.isRegExp( a ) && _.isRegExp( b ) ) {
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline;
}
// If a is not an object by this point, we can't handle it.
if ( atype !== 'object' ) {
return false;
}
// Check for different array lengths before comparing contents.
if ( a.length && (a.length !== b.length) ) {
return false;
}
// Nothing else worked, deep compare the contents.
var aKeys = _.keys( a ), bKeys = _.keys( b );
// Different object sizes?
if ( aKeys.length != bKeys.length ) {
return false;
}
// Recursive comparison of contents.
for ( var key in a ) {
if ( !(key in b) || !_.isEqual( a[key], b[key] ) ) {
return false;
}
}
return true;
};
// Is a given array or object empty?
_.isEmpty = function ( obj ) {
if ( _.isArray( obj ) || _.isString( obj ) ) {
return obj.length === 0;
}
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
return false;
}
}
return true;
};
// Is a given value a DOM element?
_.isElement = function ( obj ) {
return !!(obj && obj.nodeType == 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function ( obj ) {
return toString.call( obj ) === '[object Array]';
};
// Is a given variable an object?
_.isObject = function ( obj ) {
return obj === Object( obj );
};
// Is a given variable an arguments object?
_.isArguments = function ( obj ) {
return !!(obj && hasOwnProperty.call( obj, 'callee' ));
};
// Is a given value a function?
_.isFunction = function ( obj ) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
// Is a given value a string?
_.isString = function ( obj ) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
};
// Is a given value a number?
_.isNumber = function ( obj ) {
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
};
// Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
// that does not equal itself.
_.isNaN = function ( obj ) {
return obj !== obj;
};
// Is a given value a boolean?
_.isBoolean = function ( obj ) {
return obj === true || obj === false;
};
// Is a given value a date?
_.isDate = function ( obj ) {
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
};
// Is the given value a regular expression?
_.isRegExp = function ( obj ) {
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
};
// Is a given value equal to null?
_.isNull = function ( obj ) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function ( obj ) {
return obj === void 0;
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function () {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function ( value ) {
return value;
};
// Run a function **n** times.
_.times = function ( n, iterator, context ) {
for ( var i = 0; i < n; i++ ) {
iterator.call( context, i );
}
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function ( obj ) {
each( _.functions( obj ), function ( name ) {
addToWrapper( name, _[name] = obj[name] );
} );
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function ( prefix ) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function ( str, data ) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace( /\\/g, '\\\\' )
.replace( /'/g, "\\'" )
.replace( c.interpolate, function ( match, code ) {
return "'," + code.replace( /\\'/g, "'" ) + ",'";
} )
.replace( c.evaluate || null, function ( match, code ) {
return "');" + code.replace( /\\'/g, "'" )
.replace( /[\r\n\t]/g, ' ' ) + "__p.push('";
} )
.replace( /\r/g, '\\r' )
.replace( /\n/g, '\\n' )
.replace( /\t/g, '\\t' )
+ "');}return __p.join('');";
var func = new Function( 'obj', tmpl );
return data ? func( data ) : func;
};
// The OOP Wrapper
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function ( obj ) {
this._wrapped = obj;
};
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function ( obj, chain ) {
return chain ? _( obj ).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function ( name, func ) {
wrapper.prototype[name] = function () {
var args = slice.call( arguments );
unshift.call( args, this._wrapped );
return result( func.apply( _, args ), this._chain );
};
};
// Add all of the Underscore functions to the wrapper object.
_.mixin( _ );
// Add all mutator Array functions to the wrapper.
each( ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function ( name ) {
var method = ArrayProto[name];
wrapper.prototype[name] = function () {
method.apply( this._wrapped, arguments );
return result( this._wrapped, this._chain );
};
} );
// Add all accessor Array functions to the wrapper.
each( ['concat', 'join', 'slice'], function ( name ) {
var method = ArrayProto[name];
wrapper.prototype[name] = function () {
return result( method.apply( this._wrapped, arguments ), this._chain );
};
} );
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function () {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function () {
return this._wrapped;
};
})();

View file

@ -1,100 +0,0 @@
/*!
* Amplify Core @VERSION
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function ( global, undefined ) {
var slice = [].slice,
subscriptions = {};
var amplify = global.amplify = {
publish : function ( topic ) {
var args = slice.call( arguments, 1 ),
subscription,
length,
i = 0,
ret;
if ( !subscriptions[ topic ] ) {
return true;
}
for ( length = subscriptions[ topic ].length; i < length; i++ ) {
subscription = subscriptions[ topic ][ i ];
ret = subscription.callback.apply( subscription.context, args );
if ( ret === false ) {
break;
}
}
return ret !== false;
},
subscribe : function ( topic, context, callback, priority ) {
if ( arguments.length === 3 && typeof callback === "number" ) {
priority = callback;
callback = context;
context = null;
}
if ( arguments.length === 2 ) {
callback = context;
context = null;
}
priority = priority || 10;
var topicIndex = 0,
topics = topic.split( /\s/ ),
topicLength = topics.length,
added;
for ( ; topicIndex < topicLength; topicIndex++ ) {
topic = topics[ topicIndex ];
added = false;
if ( !subscriptions[ topic ] ) {
subscriptions[ topic ] = [];
}
var i = subscriptions[ topic ].length - 1,
subscriptionInfo = {
callback : callback,
context : context,
priority : priority
};
for ( ; i >= 0; i-- ) {
if ( subscriptions[ topic ][ i ].priority <= priority ) {
subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo );
added = true;
break;
}
}
if ( !added ) {
subscriptions[ topic ].unshift( subscriptionInfo );
}
}
return callback;
},
unsubscribe : function ( topic, callback ) {
if ( !subscriptions[ topic ] ) {
return;
}
var length = subscriptions[ topic ].length,
i = 0;
for ( ; i < length; i++ ) {
if ( subscriptions[ topic ][ i ].callback === callback ) {
subscriptions[ topic ].splice( i, 1 );
break;
}
}
}
};
}( this ) );

View file

@ -1,281 +0,0 @@
/*!
* Amplify Store - Persistent Client-Side Storage @VERSION
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function ( amplify, undefined ) {
var store = amplify.store = function ( key, value, options, type ) {
var type = store.type;
if ( options && options.type && options.type in store.types ) {
type = options.type;
}
return store.types[ type ]( key, value, options || {} );
};
store.types = {};
store.type = null;
store.addType = function ( type, storage ) {
if ( !store.type ) {
store.type = type;
}
store.types[ type ] = storage;
store[ type ] = function ( key, value, options ) {
options = options || {};
options.type = type;
return store( key, value, options );
};
}
store.error = function () {
return "amplify.store quota exceeded";
};
var rprefix = /^__amplify__/;
function createFromStorageInterface( storageType, storage ) {
store.addType( storageType, function ( key, value, options ) {
var storedValue, parsed, i, remove,
ret = value,
now = (new Date()).getTime();
if ( !key ) {
ret = {};
remove = [];
i = 0;
try {
// accessing the length property works around a localStorage bug
// in Firefox 4.0 where the keys don't update cross-page
// we assign to key just to avoid Closure Compiler from removing
// the access as "useless code"
// https://bugzilla.mozilla.org/show_bug.cgi?id=662511
key = storage.length;
while ( key = storage.key( i++ ) ) {
if ( rprefix.test( key ) ) {
parsed = JSON.parse( storage.getItem( key ) );
if ( parsed.expires && parsed.expires <= now ) {
remove.push( key );
} else {
ret[ key.replace( rprefix, "" ) ] = parsed.data;
}
}
}
while ( key = remove.pop() ) {
storage.removeItem( key );
}
} catch ( error ) {
}
return ret;
}
// protect against name collisions with direct storage
key = "__amplify__" + key;
if ( value === undefined ) {
storedValue = storage.getItem( key );
parsed = storedValue ? JSON.parse( storedValue ) : { expires : -1 };
if ( parsed.expires && parsed.expires <= now ) {
storage.removeItem( key );
} else {
return parsed.data;
}
} else {
if ( value === null ) {
storage.removeItem( key );
} else {
parsed = JSON.stringify( {
data : value,
expires : options.expires ? now + options.expires : null
} );
try {
storage.setItem( key, parsed );
// quota exceeded
} catch ( error ) {
// expire old data and try again
store[ storageType ]();
try {
storage.setItem( key, parsed );
} catch ( error ) {
throw store.error();
}
}
}
}
return ret;
} );
}
// localStorage + sessionStorage
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
for ( var webStorageType in { localStorage : 1, sessionStorage : 1 } ) {
// try/catch for file protocol in Firefox
try {
if ( window[ webStorageType ].getItem ) {
createFromStorageInterface( webStorageType, window[ webStorageType ] );
}
} catch ( e ) {
}
}
// globalStorage
// non-standard: Firefox 2+
// https://developer.mozilla.org/en/dom/storage#globalStorage
if ( window.globalStorage ) {
// try/catch for file protocol in Firefox
try {
createFromStorageInterface( "globalStorage",
window.globalStorage[ window.location.hostname ] );
// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
// make sure we default to globalStorage
// but don't default to globalStorage in 3.5+ which also has localStorage
if ( store.type === "sessionStorage" ) {
store.type = "globalStorage";
}
} catch ( e ) {
}
}
// userData
// non-standard: IE 5+
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
(function () {
// IE 9 has quirks in userData that are a huge pain
// rather than finding a way to detect these quirks
// we just don't register userData if we have localStorage
if ( store.types.localStorage ) {
return;
}
// append to html instead of body so we can do this from the head
var div = document.createElement( "div" ),
attrKey = "amplify";
div.style.display = "none";
document.getElementsByTagName( "head" )[ 0 ].appendChild( div );
if ( div.addBehavior ) {
div.addBehavior( "#default#userdata" );
store.addType( "userData", function ( key, value, options ) {
div.load( attrKey );
var attr, parsed, prevValue, i, remove,
ret = value,
now = (new Date()).getTime();
if ( !key ) {
ret = {};
remove = [];
i = 0;
while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) {
parsed = JSON.parse( attr.value );
if ( parsed.expires && parsed.expires <= now ) {
remove.push( attr.name );
} else {
ret[ attr.name ] = parsed.data;
}
}
while ( key = remove.pop() ) {
div.removeAttribute( key );
}
div.save( attrKey );
return ret;
}
// convert invalid characters to dashes
// http://www.w3.org/TR/REC-xml/#NT-Name
// simplified to assume the starting character is valid
// also removed colon as it is invalid in HTML attribute names
key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" );
if ( value === undefined ) {
attr = div.getAttribute( key );
parsed = attr ? JSON.parse( attr ) : { expires : -1 };
if ( parsed.expires && parsed.expires <= now ) {
div.removeAttribute( key );
} else {
return parsed.data;
}
} else {
if ( value === null ) {
div.removeAttribute( key );
} else {
// we need to get the previous value in case we need to rollback
prevValue = div.getAttribute( key );
parsed = JSON.stringify( {
data : value,
expires : (options.expires ? (now + options.expires) : null)
} );
div.setAttribute( key, parsed );
}
}
try {
div.save( attrKey );
// quota exceeded
} catch ( error ) {
// roll the value back to the previous value
if ( prevValue === null ) {
div.removeAttribute( key );
} else {
div.setAttribute( key, prevValue );
}
// expire old data and try again
store.userData();
try {
div.setAttribute( key, parsed );
div.save( attrKey );
} catch ( error ) {
// roll the value back to the previous value
if ( prevValue === null ) {
div.removeAttribute( key );
} else {
div.setAttribute( key, prevValue );
}
throw store.error();
}
}
return ret;
} );
}
}() );
// in-memory storage
// fallback for all browsers to enable the API even if we can't persist data
(function () {
var memory = {};
function copy( obj ) {
return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) );
}
store.addType( "memory", function ( key, value, options ) {
if ( !key ) {
return copy( memory );
}
if ( value === undefined ) {
return copy( memory[ key ] );
}
if ( value === null ) {
delete memory[ key ];
return null;
}
memory[ key ] = value;
if ( options.expires ) {
setTimeout( function () {
delete memory[ key ];
}, options.expires );
}
return value;
} );
}() );
}( this.amplify = this.amplify || {} ) );

View file

@ -1,26 +0,0 @@
var socket, port;
$( function () {
port = window.location.port
socket = io.connect( "http://" + document.domain + ':' + port + '/' );
socket.on( 'connect', function () {
socket.on( 'refresh', function () {
window.location.reload();
} );
socket.on( 'reconnecting', function () {
console.log( 'Lost connection to anvil, attempting to reconnect', 'warning' );
} );
socket.on( 'reconnect', function () {
alert( 'Reconnection to anvil succeeded' );
} );
socket.on( 'reconnect_failed', function () {
console.log( 'Reconnected to anvil failed', 'error' );
} );
socket.on( 'connect_failed', function () {
console.log( 'Could not connect to anvil', 'error' );
} );
socket.on( 'disconnect', function () {
alert( 'Anvil server has disconnected', 'error' );
} );
} );
} );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

9266
ext/jquery-1.7.1.js vendored

File diff suppressed because it is too large Load diff

3320
ext/jquery.js vendored

File diff suppressed because it is too large Load diff

View file

@ -1,259 +0,0 @@
@charset "utf-8";
body {
margin:0;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 60px 50px;
}
#mocha ul, #mocha li {
margin: 0;
padding: 0;
}
#mocha ul {
list-style: none;
}
#mocha h1, #mocha h2 {
margin: 0;
}
#mocha h1 {
margin-top: 15px;
font-size: 1em;
font-weight: 200;
}
#mocha h1 a {
text-decoration: none;
color: inherit;
}
#mocha h1 a:hover {
text-decoration: underline;
}
#mocha .suite .suite h1 {
margin-top: 0;
font-size: .8em;
}
#mocha .hidden {
display: none;
}
#mocha h2 {
font-size: 12px;
font-weight: normal;
cursor: pointer;
}
#mocha .suite {
margin-left: 15px;
}
#mocha .test {
margin-left: 15px;
overflow: hidden;
}
#mocha .test.pending:hover h2::after {
content: '(pending)';
font-family: arial, sans-serif;
}
#mocha .test.pass.medium .duration {
background: #C09853;
}
#mocha .test.pass.slow .duration {
background: #B94A48;
}
#mocha .test.pass::before {
content: '✓';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #00d6b2;
}
#mocha .test.pass .duration {
font-size: 9px;
margin-left: 5px;
padding: 2px 5px;
color: white;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#mocha .test.pass.fast .duration {
display: none;
}
#mocha .test.pending {
color: #0b97c4;
}
#mocha .test.pending::before {
content: '◦';
color: #0b97c4;
}
#mocha .test.fail {
color: #c00;
}
#mocha .test.fail pre {
color: black;
}
#mocha .test.fail::before {
content: '✖';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #c00;
}
#mocha .test pre.error {
color: #c00;
max-height: 300px;
overflow: auto;
}
#mocha .test pre {
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
border-bottom-color: #ddd;
-webkit-border-radius: 3px;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-border-radius: 3px;
-moz-box-shadow: 0 1px 3px #eee;
}
#mocha .test h2 {
position: relative;
}
#mocha .test a.replay {
position: absolute;
top: 3px;
right: 0;
text-decoration: none;
vertical-align: middle;
display: block;
width: 15px;
height: 15px;
line-height: 15px;
text-align: center;
background: #eee;
font-size: 15px;
-moz-border-radius: 15px;
border-radius: 15px;
-webkit-transition: opacity 200ms;
-moz-transition: opacity 200ms;
transition: opacity 200ms;
opacity: 0.3;
color: #888;
}
#mocha .test:hover a.replay {
opacity: 1;
}
#mocha-report.pass .test.fail {
display: none;
}
#mocha-report.fail .test.pass {
display: none;
}
#mocha-report.pending .test.pass,
#mocha-report.pending .test.fail {
display: none;
}
#mocha-report.pending .test.pass.pending {
display: block;
}
#mocha-error {
color: #c00;
font-size: 1.5em;
font-weight: 100;
letter-spacing: 1px;
}
#mocha-stats {
position: fixed;
top: 15px;
right: 10px;
font-size: 12px;
margin: 0;
color: #888;
z-index: 1;
}
#mocha-stats .progress {
float: right;
padding-top: 0;
}
#mocha-stats em {
color: black;
}
#mocha-stats a {
text-decoration: none;
color: inherit;
}
#mocha-stats a:hover {
border-bottom: 1px solid #eee;
}
#mocha-stats li {
display: inline-block;
margin: 0 5px;
list-style: none;
padding-top: 11px;
}
#mocha-stats canvas {
width: 40px;
height: 40px;
}
#mocha code .comment { color: #ddd }
#mocha code .init { color: #2F6FAD }
#mocha code .string { color: #5890AD }
#mocha code .keyword { color: #8A6343 }
#mocha code .number { color: #2F6FAD }
@media screen and (max-device-width: 480px) {
#mocha {
margin: 60px 0px;
}
#mocha #stats {
position: absolute;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,723 +0,0 @@
// moment.js
// version : 1.5.0
// author : Tim Wood
// license : MIT
// momentjs.com
(function ( Date, undefined ) {
var moment, round = Math.round,
languages = {},
hasModule = (typeof module !== 'undefined'),
paramsToParse = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split( '|' ),
i, jsonRegex = /^\/?Date\((\-?\d+)/i,
charactersToReplace = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g,
nonuppercaseLetters = /[^A-Z]/g,
timezoneRegex = /\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g,
tokenCharacters = /(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g,
inputCharacters = /(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi,
isoRegex = /\d{4}.\d\d.\d\d(T(\d\d(.\d\d(.\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,
isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
isoTimes = [
['HH:mm:ss', /T\d\d:\d\d:\d\d/],
['HH:mm', /T\d\d:\d\d/],
['HH', /T\d\d/]
],
timezoneParseRegex = /([\+\-]|\d\d)/gi,
VERSION = "1.5.0",
shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split( '|' );
// Moment prototype object
function Moment( date, isUTC ) {
this._d = date;
this._isUTC = !!isUTC;
}
// left zero fill a number
// see http://jsperf.com/left-zero-filling for performance comparison
function leftZeroFill( number, targetLength ) {
var output = number + '';
while ( output.length < targetLength ) {
output = '0' + output;
}
return output;
}
// helper function for _.addTime and _.subtractTime
function dateAddRemove( date, _input, adding, val ) {
var isString = (typeof _input === 'string'),
input = isString ? {} : _input,
ms, d, M, currentDate;
if ( isString && val ) {
input[_input] = +val;
}
ms = (input.ms || input.milliseconds || 0) + (input.s || input.seconds || 0) * 1e3 + // 1000
(input.m || input.minutes || 0) * 6e4 + // 1000 * 60
(input.h || input.hours || 0) * 36e5; // 1000 * 60 * 60
d = (input.d || input.days || 0) + (input.w || input.weeks || 0) * 7;
M = (input.M || input.months || 0) + (input.y || input.years || 0) * 12;
if ( ms ) {
date.setTime( +date + ms * adding );
}
if ( d ) {
date.setDate( date.getDate() + d * adding );
}
if ( M ) {
currentDate = date.getDate();
date.setDate( 1 );
date.setMonth( date.getMonth() + M * adding );
date.setDate( Math.min( new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate(), currentDate ) );
}
return date;
}
// check if is an array
function isArray( input ) {
return Object.prototype.toString.call( input ) === '[object Array]';
}
// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
function dateFromArray( input ) {
return new Date( input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0 );
}
// format date using native date object
function formatMoment( m, inputString ) {
var currentMonth = m.month(),
currentDate = m.date(),
currentYear = m.year(),
currentDay = m.day(),
currentHours = m.hours(),
currentMinutes = m.minutes(),
currentSeconds = m.seconds(),
currentZone = -m.zone(),
ordinal = moment.ordinal,
meridiem = moment.meridiem;
// check if the character is a format
// return formatted string or non string.
//
// uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380)
// for minification and performance
// see http://jsperf.com/object-of-functions-vs-switch for performance comparison
function replaceFunction( input ) {
// create a couple variables to be used later inside one of the cases.
var a, b;
switch ( input ) {
// MONTH
case 'M':
return currentMonth + 1;
case 'Mo':
return (currentMonth + 1) + ordinal( currentMonth + 1 );
case 'MM':
return leftZeroFill( currentMonth + 1, 2 );
case 'MMM':
return moment.monthsShort[currentMonth];
case 'MMMM':
return moment.months[currentMonth];
// DAY OF MONTH
case 'D':
return currentDate;
case 'Do':
return currentDate + ordinal( currentDate );
case 'DD':
return leftZeroFill( currentDate, 2 );
// DAY OF YEAR
case 'DDD':
a = new Date( currentYear, currentMonth, currentDate );
b = new Date( currentYear, 0, 1 );
return ~~(((a - b) / 864e5) + 1.5);
case 'DDDo':
a = replaceFunction( 'DDD' );
return a + ordinal( a );
case 'DDDD':
return leftZeroFill( replaceFunction( 'DDD' ), 3 );
// WEEKDAY
case 'd':
return currentDay;
case 'do':
return currentDay + ordinal( currentDay );
case 'ddd':
return moment.weekdaysShort[currentDay];
case 'dddd':
return moment.weekdays[currentDay];
// WEEK OF YEAR
case 'w':
a = new Date( currentYear, currentMonth, currentDate - currentDay + 5 );
b = new Date( a.getFullYear(), 0, 4 );
return ~~((a - b) / 864e5 / 7 + 1.5);
case 'wo':
a = replaceFunction( 'w' );
return a + ordinal( a );
case 'ww':
return leftZeroFill( replaceFunction( 'w' ), 2 );
// YEAR
case 'YY':
return leftZeroFill( currentYear % 100, 2 );
case 'YYYY':
return currentYear;
// AM / PM
case 'a':
return currentHours > 11 ? meridiem.pm : meridiem.am;
case 'A':
return currentHours > 11 ? meridiem.PM : meridiem.AM;
// 24 HOUR
case 'H':
return currentHours;
case 'HH':
return leftZeroFill( currentHours, 2 );
// 12 HOUR
case 'h':
return currentHours % 12 || 12;
case 'hh':
return leftZeroFill( currentHours % 12 || 12, 2 );
// MINUTE
case 'm':
return currentMinutes;
case 'mm':
return leftZeroFill( currentMinutes, 2 );
// SECOND
case 's':
return currentSeconds;
case 'ss':
return leftZeroFill( currentSeconds, 2 );
// TIMEZONE
case 'zz':
// depreciating 'zz' fall through to 'z'
case 'z':
return (m._d.toString().match( timezoneRegex ) || [''])[0].replace( nonuppercaseLetters, '' );
case 'Z':
return (currentZone < 0 ? '-' : '+') + leftZeroFill( ~~(Math.abs( currentZone ) / 60), 2 ) + ':' + leftZeroFill( ~~(Math.abs( currentZone ) % 60), 2 );
case 'ZZ':
return (currentZone < 0 ? '-' : '+') + leftZeroFill( ~~(10 * Math.abs( currentZone ) / 6), 4 );
// LONG DATES
case 'L':
case 'LL':
case 'LLL':
case 'LLLL':
case 'LT':
return formatMoment( m, moment.longDateFormat[input] );
// DEFAULT
default:
return input.replace( /(^\[)|(\\)|\]$/g, "" );
}
}
return inputString.replace( charactersToReplace, replaceFunction );
}
// date from string and format string
function makeDateFromStringAndFormat( string, format ) {
var inArray = [0, 0, 1, 0, 0, 0, 0],
timezoneHours = 0,
timezoneMinutes = 0,
isUsingUTC = false,
inputParts = string.match( inputCharacters ),
formatParts = format.match( tokenCharacters ),
len = Math.min( inputParts.length, formatParts.length ),
i, isPm;
// function to convert string input to date
function addTime( format, input ) {
var a;
switch ( format ) {
// MONTH
case 'M':
// fall through to MM
case 'MM':
inArray[1] = ~~input - 1;
break;
case 'MMM':
// fall through to MMMM
case 'MMMM':
for ( a = 0; a < 12; a++ ) {
if ( moment.monthsParse[a].test( input ) ) {
inArray[1] = a;
break;
}
}
break;
// DAY OF MONTH
case 'D':
// fall through to DDDD
case 'DD':
// fall through to DDDD
case 'DDD':
// fall through to DDDD
case 'DDDD':
inArray[2] = ~~input;
break;
// YEAR
case 'YY':
input = ~~input;
inArray[0] = input + (input > 70 ? 1900 : 2000);
break;
case 'YYYY':
inArray[0] = ~~Math.abs( input );
break;
// AM / PM
case 'a':
// fall through to A
case 'A':
isPm = (input.toLowerCase() === 'pm');
break;
// 24 HOUR
case 'H':
// fall through to hh
case 'HH':
// fall through to hh
case 'h':
// fall through to hh
case 'hh':
inArray[3] = ~~input;
break;
// MINUTE
case 'm':
// fall through to mm
case 'mm':
inArray[4] = ~~input;
break;
// SECOND
case 's':
// fall through to ss
case 'ss':
inArray[5] = ~~input;
break;
// TIMEZONE
case 'Z':
// fall through to ZZ
case 'ZZ':
isUsingUTC = true;
a = (input || '').match( timezoneParseRegex );
if ( a && a[1] ) {
timezoneHours = ~~a[1];
}
if ( a && a[2] ) {
timezoneMinutes = ~~a[2];
}
// reverse offsets
if ( a && a[0] === '+' ) {
timezoneHours = -timezoneHours;
timezoneMinutes = -timezoneMinutes;
}
break;
}
}
for ( i = 0; i < len; i++ ) {
addTime( formatParts[i], inputParts[i] );
}
// handle am pm
if ( isPm && inArray[3] < 12 ) {
inArray[3] += 12;
}
// if is 12 am, change hours to 0
if ( isPm === false && inArray[3] === 12 ) {
inArray[3] = 0;
}
// handle timezone
inArray[3] += timezoneHours;
inArray[4] += timezoneMinutes;
// return
return isUsingUTC ? new Date( Date.UTC.apply( {}, inArray ) ) : dateFromArray( inArray );
}
// compare two arrays, return the number of differences
function compareArrays( array1, array2 ) {
var len = Math.min( array1.length, array2.length ),
lengthDiff = Math.abs( array1.length - array2.length ),
diffs = 0,
i;
for ( i = 0; i < len; i++ ) {
if ( ~~array1[i] !== ~~array2[i] ) {
diffs++;
}
}
return diffs + lengthDiff;
}
// date from string and array of format strings
function makeDateFromStringAndArray( string, formats ) {
var output, inputParts = string.match( inputCharacters ),
scores = [],
scoreToBeat = 99,
i, curDate, curScore;
for ( i = 0; i < formats.length; i++ ) {
curDate = makeDateFromStringAndFormat( string, formats[i] );
curScore = compareArrays( inputParts, formatMoment( new Moment( curDate ), formats[i] ).match( inputCharacters ) );
if ( curScore < scoreToBeat ) {
scoreToBeat = curScore;
output = curDate;
}
}
return output;
}
// date from iso format
function makeDateFromString( string ) {
var format = 'YYYY-MM-DDT',
i;
if ( isoRegex.exec( string ) ) {
for ( i = 0; i < 3; i++ ) {
if ( isoTimes[i][1].exec( string ) ) {
format += isoTimes[i][0];
break;
}
}
return makeDateFromStringAndFormat( string, format + 'Z' );
}
return new Date( string );
}
// helper function for _date.from() and _date.fromNow()
function substituteTimeAgo( string, number, withoutSuffix ) {
var rt = moment.relativeTime[string];
return (typeof rt === 'function') ? rt( number || 1, !!withoutSuffix, string ) : rt.replace( /%d/i, number || 1 );
}
function relativeTime( milliseconds, withoutSuffix ) {
var seconds = round( Math.abs( milliseconds ) / 1000 ),
minutes = round( seconds / 60 ),
hours = round( minutes / 60 ),
days = round( hours / 24 ),
years = round( days / 365 ),
args = seconds < 45 && ['s', seconds] || minutes === 1 && ['m'] || minutes < 45 && ['mm', minutes] || hours === 1 && ['h'] || hours < 22 && ['hh', hours] || days === 1 && ['d'] || days <= 25 && ['dd', days] || days <= 45 && ['M'] || days < 345 && ['MM', round( days / 30 )] || years === 1 && ['y'] || ['yy', years];
args[2] = withoutSuffix;
return substituteTimeAgo.apply( {}, args );
}
moment = function ( input, format ) {
if ( input === null || input === '' ) {
return null;
}
var date, matched;
// parse Moment object
if ( input && input._d instanceof Date ) {
date = new Date( +input._d );
// parse string and format
} else if ( format ) {
if ( isArray( format ) ) {
date = makeDateFromStringAndArray( input, format );
} else {
date = makeDateFromStringAndFormat( input, format );
}
// evaluate it as a JSON-encoded date
} else {
matched = jsonRegex.exec( input );
date = input === undefined ? new Date() : matched ? new Date( +matched[1] ) : input instanceof Date ? input : isArray( input ) ? dateFromArray( input ) : typeof input === 'string' ? makeDateFromString( input ) : new Date( input );
}
return new Moment( date );
};
// creating with utc
moment.utc = function ( input, format ) {
if ( isArray( input ) ) {
return new Moment( new Date( Date.UTC.apply( {}, input ) ), true );
}
return (format && input) ? moment( input + ' 0', format + ' Z' ).utc() : moment( input ).utc();
};
// humanizeDuration
moment.humanizeDuration = function ( num, type, withSuffix ) {
var difference = +num,
rel = moment.relativeTime,
output;
switch ( type ) {
case "seconds":
difference *= 1000; // 1000
break;
case "minutes":
difference *= 60000; // 60 * 1000
break;
case "hours":
difference *= 3600000; // 60 * 60 * 1000
break;
case "days":
difference *= 86400000; // 24 * 60 * 60 * 1000
break;
case "weeks":
difference *= 604800000; // 7 * 24 * 60 * 60 * 1000
break;
case "months":
difference *= 2592000000; // 30 * 24 * 60 * 60 * 1000
break;
case "years":
difference *= 31536000000; // 365 * 24 * 60 * 60 * 1000
break;
default:
withSuffix = !!type;
break;
}
output = relativeTime( difference, !withSuffix );
return withSuffix ? (difference <= 0 ? rel.past : rel.future).replace( /%s/i, output ) : output;
};
// version number
moment.version = VERSION;
// default format
moment.defaultFormat = isoFormat;
// language switching and caching
moment.lang = function ( key, values ) {
var i, param, req, parse = [];
if ( values ) {
for ( i = 0; i < 12; i++ ) {
parse[i] = new RegExp( '^' + values.months[i] + '|^' + values.monthsShort[i].replace( '.', '' ), 'i' );
}
values.monthsParse = values.monthsParse || parse;
languages[key] = values;
}
if ( languages[key] ) {
for ( i = 0; i < paramsToParse.length; i++ ) {
param = paramsToParse[i];
moment[param] = languages[key][param] || moment[param];
}
} else {
if ( hasModule ) {
req = require( './lang/' + key );
moment.lang( key, req );
}
}
};
// set default language
moment.lang( 'en', {
months : "January_February_March_April_May_June_July_August_September_October_November_December".split( "_" ),
monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split( "_" ),
weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split( "_" ),
weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split( "_" ),
longDateFormat : {
LT : "h:mm A",
L : "MM/DD/YYYY",
LL : "MMMM D YYYY",
LLL : "MMMM D YYYY LT",
LLLL : "dddd, MMMM D YYYY LT"
},
meridiem : {
AM : 'AM',
am : 'am',
PM : 'PM',
pm : 'pm'
},
calendar : {
sameDay : '[Today at] LT',
nextDay : '[Tomorrow at] LT',
nextWeek : 'dddd [at] LT',
lastDay : '[Yesterday at] LT',
lastWeek : '[last] dddd [at] LT',
sameElse : 'L'
},
relativeTime : {
future : "in %s",
past : "%s ago",
s : "a few seconds",
m : "a minute",
mm : "%d minutes",
h : "an hour",
hh : "%d hours",
d : "a day",
dd : "%d days",
M : "a month",
MM : "%d months",
y : "a year",
yy : "%d years"
},
ordinal : function ( number ) {
var b = number % 10;
return (~~(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th';
}
} );
// compare moment object
moment.isMoment = function ( obj ) {
return obj instanceof Moment;
};
// shortcut for prototype
moment.fn = Moment.prototype = {
clone : function () {
return moment( this );
},
valueOf : function () {
return +this._d;
},
'native' : function () {
return this._d;
},
toString : function () {
return this._d.toString();
},
toDate : function () {
return this._d;
},
utc : function () {
this._isUTC = true;
return this;
},
local : function () {
this._isUTC = false;
return this;
},
format : function ( inputString ) {
return formatMoment( this, inputString ? inputString : moment.defaultFormat );
},
add : function ( input, val ) {
this._d = dateAddRemove( this._d, input, 1, val );
return this;
},
subtract : function ( input, val ) {
this._d = dateAddRemove( this._d, input, -1, val );
return this;
},
diff : function ( input, val, asFloat ) {
var inputMoment = moment( input ),
zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
diff = this._d - inputMoment._d - zoneDiff,
year = this.year() - inputMoment.year(),
month = this.month() - inputMoment.month(),
date = this.date() - inputMoment.date(),
output;
if ( val === 'months' ) {
output = year * 12 + month + date / 30;
} else if ( val === 'years' ) {
output = year + month / 12;
} else {
output = val === 'seconds' ? diff / 1e3 : // 1000
val === 'minutes' ? diff / 6e4 : // 1000 * 60
val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
diff;
}
return asFloat ? output : round( output );
},
from : function ( time, withoutSuffix ) {
return moment.humanizeDuration( this.diff( time ), !withoutSuffix );
},
fromNow : function ( withoutSuffix ) {
return this.from( moment(), withoutSuffix );
},
calendar : function () {
var diff = this.diff( moment().sod(), 'days', true ),
calendar = moment.calendar,
allElse = calendar.sameElse,
format = diff < -6 ? allElse : diff < -1 ? calendar.lastWeek : diff < 0 ? calendar.lastDay : diff < 1 ? calendar.sameDay : diff < 2 ? calendar.nextDay : diff < 7 ? calendar.nextWeek : allElse;
return this.format( typeof format === 'function' ? format.apply( this ) : format );
},
isLeapYear : function () {
var year = this.year();
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
},
isDST : function () {
return (this.zone() < moment( [this.year()] ).zone() || this.zone() < moment( [this.year(), 5] ).zone());
},
day : function ( input ) {
var day = this._d.getDay();
return input == null ? day : this.add( {
d : input - day
} );
},
sod : function () {
return this.clone().hours( 0 ).minutes( 0 ).seconds( 0 ).milliseconds( 0 );
},
eod : function () {
// end of day = start of day plus 1 day, minus 1 millisecond
return this.sod().add( {
d : 1,
ms : -1
} );
},
zone : function () {
return this._isUTC ? 0 : this._d.getTimezoneOffset();
},
daysInMonth : function () {
return this.clone().month( this.month() + 1 ).date( 0 ).date();
}
};
// helper for adding shortcuts
function makeShortcut( name, key ) {
moment.fn[name] = function ( input ) {
var utc = this._isUTC ? 'UTC' : '';
if ( input != null ) {
this._d['set' + utc + key]( input );
return this;
} else {
return this._d['get' + utc + key]();
}
};
}
// loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
for ( i = 0; i < shortcuts.length; i++ ) {
makeShortcut( shortcuts[i].toLowerCase(), shortcuts[i] );
}
// add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
makeShortcut( 'year', 'FullYear' );
// CommonJS module is defined
if ( hasModule ) {
module.exports = moment;
}
if ( typeof window !== 'undefined' ) {
window.moment = moment;
}
/*global define:false */
if ( typeof define === "function" && define.amd ) {
define( "moment", [], function () {
return moment;
} );
}
})( Date );

View file

@ -1,999 +0,0 @@
// Underscore.js 1.1.7
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function () {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function ( obj ) {
return new wrapper( obj );
};
// Export the Underscore object for **CommonJS**, with backwards-compatibility
// for the old `require()` API. If we're not in CommonJS, add `_` to the
// global object.
if ( typeof module !== 'undefined' && module.exports ) {
module.exports = _;
_._ = _;
} else {
// Exported as a string, for Closure Compiler "advanced" mode.
root['_'] = _;
}
// Current version.
_.VERSION = '1.1.7';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function ( obj, iterator, context ) {
if ( obj == null ) {
return;
}
if ( nativeForEach && obj.forEach === nativeForEach ) {
obj.forEach( iterator, context );
} else if ( obj.length === +obj.length ) {
for ( var i = 0, l = obj.length; i < l; i++ ) {
if ( i in obj && iterator.call( context, obj[i], i, obj ) === breaker ) {
return;
}
}
} else {
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
if ( iterator.call( context, obj[key], key, obj ) === breaker ) {
return;
}
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
if ( nativeMap && obj.map === nativeMap ) {
return obj.map( iterator, context );
}
each( obj, function ( value, index, list ) {
results[results.length] = iterator.call( context, value, index, list );
} );
return results;
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function ( obj, iterator, memo, context ) {
var initial = memo !== void 0;
if ( obj == null ) {
obj = [];
}
if ( nativeReduce && obj.reduce === nativeReduce ) {
if ( context ) {
iterator = _.bind( iterator, context );
}
return initial ? obj.reduce( iterator, memo ) : obj.reduce( iterator );
}
each( obj, function ( value, index, list ) {
if ( !initial ) {
memo = value;
initial = true;
} else {
memo = iterator.call( context, memo, value, index, list );
}
} );
if ( !initial ) {
throw new TypeError( "Reduce of empty array with no initial value" );
}
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function ( obj, iterator, memo, context ) {
if ( obj == null ) {
obj = [];
}
if ( nativeReduceRight && obj.reduceRight === nativeReduceRight ) {
if ( context ) {
iterator = _.bind( iterator, context );
}
return memo !== void 0 ? obj.reduceRight( iterator, memo ) : obj.reduceRight( iterator );
}
var reversed = (_.isArray( obj ) ? obj.slice() : _.toArray( obj )).reverse();
return _.reduce( reversed, iterator, memo, context );
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function ( obj, iterator, context ) {
var result;
any( obj, function ( value, index, list ) {
if ( iterator.call( context, value, index, list ) ) {
result = value;
return true;
}
} );
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
if ( nativeFilter && obj.filter === nativeFilter ) {
return obj.filter( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( iterator.call( context, value, index, list ) ) {
results[results.length] = value;
}
} );
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function ( obj, iterator, context ) {
var results = [];
if ( obj == null ) {
return results;
}
each( obj, function ( value, index, list ) {
if ( !iterator.call( context, value, index, list ) ) {
results[results.length] = value;
}
} );
return results;
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function ( obj, iterator, context ) {
var result = true;
if ( obj == null ) {
return result;
}
if ( nativeEvery && obj.every === nativeEvery ) {
return obj.every( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( !(result = result && iterator.call( context, value, index, list )) ) {
return breaker;
}
} );
return result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function ( obj, iterator, context ) {
iterator = iterator || _.identity;
var result = false;
if ( obj == null ) {
return result;
}
if ( nativeSome && obj.some === nativeSome ) {
return obj.some( iterator, context );
}
each( obj, function ( value, index, list ) {
if ( result |= iterator.call( context, value, index, list ) ) {
return breaker;
}
} );
return !!result;
};
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function ( obj, target ) {
var found = false;
if ( obj == null ) {
return found;
}
if ( nativeIndexOf && obj.indexOf === nativeIndexOf ) {
return obj.indexOf( target ) != -1;
}
any( obj, function ( value ) {
if ( found = value === target ) {
return true;
}
} );
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function ( obj, method ) {
var args = slice.call( arguments, 2 );
return _.map( obj, function ( value ) {
return (method.call ? method || value : value[method]).apply( value, args );
} );
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function ( obj, key ) {
return _.map( obj, function ( value ) {
return value[key];
} );
};
// Return the maximum element or (element-based computation).
_.max = function ( obj, iterator, context ) {
if ( !iterator && _.isArray( obj ) ) {
return Math.max.apply( Math, obj );
}
var result = {computed : -Infinity};
each( obj, function ( value, index, list ) {
var computed = iterator ? iterator.call( context, value, index, list ) : value;
computed >= result.computed && (result = {value : value, computed : computed});
} );
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function ( obj, iterator, context ) {
if ( !iterator && _.isArray( obj ) ) {
return Math.min.apply( Math, obj );
}
var result = {computed : Infinity};
each( obj, function ( value, index, list ) {
var computed = iterator ? iterator.call( context, value, index, list ) : value;
computed < result.computed && (result = {value : value, computed : computed});
} );
return result.value;
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function ( obj, iterator, context ) {
return _.pluck( _.map( obj,
function ( value, index, list ) {
return {
value : value,
criteria : iterator.call( context, value, index, list )
};
} ).sort( function ( left, right ) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
} ), 'value' );
};
// Groups the object's values by a criterion produced by an iterator
_.groupBy = function ( obj, iterator ) {
var result = {};
each( obj, function ( value, index ) {
var key = iterator( value, index );
(result[key] || (result[key] = [])).push( value );
} );
return result;
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function ( array, obj, iterator ) {
iterator || (iterator = _.identity);
var low = 0, high = array.length;
while ( low < high ) {
var mid = (low + high) >> 1;
iterator( array[mid] ) < iterator( obj ) ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function ( iterable ) {
if ( !iterable ) {
return [];
}
if ( iterable.toArray ) {
return iterable.toArray();
}
if ( _.isArray( iterable ) ) {
return slice.call( iterable );
}
if ( _.isArguments( iterable ) ) {
return slice.call( iterable );
}
return _.values( iterable );
};
// Return the number of elements in an object.
_.size = function ( obj ) {
return _.toArray( obj ).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function ( array, n, guard ) {
return (n != null) && !guard ? slice.call( array, 0, n ) : array[0];
};
// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function ( array, index, guard ) {
return slice.call( array, (index == null) || guard ? 1 : index );
};
// Get the last element of an array.
_.last = function ( array ) {
return array[array.length - 1];
};
// Trim out all falsy values from an array.
_.compact = function ( array ) {
return _.filter( array, function ( value ) {
return !!value;
} );
};
// Return a completely flattened version of an array.
_.flatten = function ( array ) {
return _.reduce( array, function ( memo, value ) {
if ( _.isArray( value ) ) {
return memo.concat( _.flatten( value ) );
}
memo[memo.length] = value;
return memo;
}, [] );
};
// Return a version of the array that does not contain the specified value(s).
_.without = function ( array ) {
return _.difference( array, slice.call( arguments, 1 ) );
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function ( array, isSorted ) {
return _.reduce( array, function ( memo, el, i ) {
if ( 0 == i || (isSorted === true ? _.last( memo ) != el : !_.include( memo, el )) ) {
memo[memo.length] = el;
}
return memo;
}, [] );
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function () {
return _.uniq( _.flatten( arguments ) );
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function ( array ) {
var rest = slice.call( arguments, 1 );
return _.filter( _.uniq( array ), function ( item ) {
return _.every( rest, function ( other ) {
return _.indexOf( other, item ) >= 0;
} );
} );
};
// Take the difference between one array and another.
// Only the elements present in just the first array will remain.
_.difference = function ( array, other ) {
return _.filter( array, function ( value ) {
return !_.include( other, value );
} );
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function () {
var args = slice.call( arguments );
var length = _.max( _.pluck( args, 'length' ) );
var results = new Array( length );
for ( var i = 0; i < length; i++ ) {
results[i] = _.pluck( args, "" + i );
}
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function ( array, item, isSorted ) {
if ( array == null ) {
return -1;
}
var i, l;
if ( isSorted ) {
i = _.sortedIndex( array, item );
return array[i] === item ? i : -1;
}
if ( nativeIndexOf && array.indexOf === nativeIndexOf ) {
return array.indexOf( item );
}
for ( i = 0, l = array.length; i < l; i++ ) {
if ( array[i] === item ) {
return i;
}
}
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function ( array, item ) {
if ( array == null ) {
return -1;
}
if ( nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf ) {
return array.lastIndexOf( item );
}
var i = array.length;
while ( i-- ) {
if ( array[i] === item ) {
return i;
}
}
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function ( start, stop, step ) {
if ( arguments.length <= 1 ) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max( Math.ceil( (stop - start) / step ), 0 );
var idx = 0;
var range = new Array( len );
while ( idx < len ) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function ( func, obj ) {
if ( func.bind === nativeBind && nativeBind ) {
return nativeBind.apply( func, slice.call( arguments, 1 ) );
}
var args = slice.call( arguments, 2 );
return function () {
return func.apply( obj, args.concat( slice.call( arguments ) ) );
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function ( obj ) {
var funcs = slice.call( arguments, 1 );
if ( funcs.length == 0 ) {
funcs = _.functions( obj );
}
each( funcs, function ( f ) {
obj[f] = _.bind( obj[f], obj );
} );
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function ( func, hasher ) {
var memo = {};
hasher || (hasher = _.identity);
return function () {
var key = hasher.apply( this, arguments );
return hasOwnProperty.call( memo, key ) ? memo[key] : (memo[key] = func.apply( this, arguments ));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function ( func, wait ) {
var args = slice.call( arguments, 2 );
return setTimeout( function () {
return func.apply( func, args );
}, wait );
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function ( func ) {
return _.delay.apply( _, [func, 1].concat( slice.call( arguments, 1 ) ) );
};
// Internal function used to implement `_.throttle` and `_.debounce`.
var limit = function ( func, wait, debounce ) {
var timeout;
return function () {
var context = this, args = arguments;
var throttler = function () {
timeout = null;
func.apply( context, args );
};
if ( debounce ) {
clearTimeout( timeout );
}
if ( debounce || !timeout ) {
timeout = setTimeout( throttler, wait );
}
};
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function ( func, wait ) {
return limit( func, wait, false );
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function ( func, wait ) {
return limit( func, wait, true );
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function ( func ) {
var ran = false, memo;
return function () {
if ( ran ) {
return memo;
}
ran = true;
return memo = func.apply( this, arguments );
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function ( func, wrapper ) {
return function () {
var args = [func].concat( slice.call( arguments ) );
return wrapper.apply( this, args );
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function () {
var funcs = slice.call( arguments );
return function () {
var args = slice.call( arguments );
for ( var i = funcs.length - 1; i >= 0; i-- ) {
args = [funcs[i].apply( this, args )];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function ( times, func ) {
return function () {
if ( --times < 1 ) {
return func.apply( this, arguments );
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function ( obj ) {
if ( obj !== Object( obj ) ) {
throw new TypeError( 'Invalid object' );
}
var keys = [];
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
keys[keys.length] = key;
}
}
return keys;
};
// Retrieve the values of an object's properties.
_.values = function ( obj ) {
return _.map( obj, _.identity );
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function ( obj ) {
var names = [];
for ( var key in obj ) {
if ( _.isFunction( obj[key] ) ) {
names.push( key );
}
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function ( obj ) {
each( slice.call( arguments, 1 ), function ( source ) {
for ( var prop in source ) {
if ( source[prop] !== void 0 ) {
obj[prop] = source[prop];
}
}
} );
return obj;
};
// Fill in a given object with default properties.
_.defaults = function ( obj ) {
each( slice.call( arguments, 1 ), function ( source ) {
for ( var prop in source ) {
if ( obj[prop] == null ) {
obj[prop] = source[prop];
}
}
} );
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function ( obj ) {
return _.isArray( obj ) ? obj.slice() : _.extend( {}, obj );
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function ( obj, interceptor ) {
interceptor( obj );
return obj;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function ( a, b ) {
// Check object identity.
if ( a === b ) {
return true;
}
// Different types?
var atype = typeof(a), btype = typeof(b);
if ( atype != btype ) {
return false;
}
// Basic equality test (watch out for coercions).
if ( a == b ) {
return true;
}
// One is falsy and the other truthy.
if ( (!a && b) || (a && !b) ) {
return false;
}
// Unwrap any wrapped objects.
if ( a._chain ) {
a = a._wrapped;
}
if ( b._chain ) {
b = b._wrapped;
}
// One of them implements an isEqual()?
if ( a.isEqual ) {
return a.isEqual( b );
}
if ( b.isEqual ) {
return b.isEqual( a );
}
// Check dates' integer values.
if ( _.isDate( a ) && _.isDate( b ) ) {
return a.getTime() === b.getTime();
}
// Both are NaN?
if ( _.isNaN( a ) && _.isNaN( b ) ) {
return false;
}
// Compare regular expressions.
if ( _.isRegExp( a ) && _.isRegExp( b ) ) {
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline;
}
// If a is not an object by this point, we can't handle it.
if ( atype !== 'object' ) {
return false;
}
// Check for different array lengths before comparing contents.
if ( a.length && (a.length !== b.length) ) {
return false;
}
// Nothing else worked, deep compare the contents.
var aKeys = _.keys( a ), bKeys = _.keys( b );
// Different object sizes?
if ( aKeys.length != bKeys.length ) {
return false;
}
// Recursive comparison of contents.
for ( var key in a ) {
if ( !(key in b) || !_.isEqual( a[key], b[key] ) ) {
return false;
}
}
return true;
};
// Is a given array or object empty?
_.isEmpty = function ( obj ) {
if ( _.isArray( obj ) || _.isString( obj ) ) {
return obj.length === 0;
}
for ( var key in obj ) {
if ( hasOwnProperty.call( obj, key ) ) {
return false;
}
}
return true;
};
// Is a given value a DOM element?
_.isElement = function ( obj ) {
return !!(obj && obj.nodeType == 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function ( obj ) {
return toString.call( obj ) === '[object Array]';
};
// Is a given variable an object?
_.isObject = function ( obj ) {
return obj === Object( obj );
};
// Is a given variable an arguments object?
_.isArguments = function ( obj ) {
return !!(obj && hasOwnProperty.call( obj, 'callee' ));
};
// Is a given value a function?
_.isFunction = function ( obj ) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
// Is a given value a string?
_.isString = function ( obj ) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
};
// Is a given value a number?
_.isNumber = function ( obj ) {
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
};
// Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
// that does not equal itself.
_.isNaN = function ( obj ) {
return obj !== obj;
};
// Is a given value a boolean?
_.isBoolean = function ( obj ) {
return obj === true || obj === false;
};
// Is a given value a date?
_.isDate = function ( obj ) {
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
};
// Is the given value a regular expression?
_.isRegExp = function ( obj ) {
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
};
// Is a given value equal to null?
_.isNull = function ( obj ) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function ( obj ) {
return obj === void 0;
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function () {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function ( value ) {
return value;
};
// Run a function **n** times.
_.times = function ( n, iterator, context ) {
for ( var i = 0; i < n; i++ ) {
iterator.call( context, i );
}
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function ( obj ) {
each( _.functions( obj ), function ( name ) {
addToWrapper( name, _[name] = obj[name] );
} );
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function ( prefix ) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function ( str, data ) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace( /\\/g, '\\\\' )
.replace( /'/g, "\\'" )
.replace( c.interpolate, function ( match, code ) {
return "'," + code.replace( /\\'/g, "'" ) + ",'";
} )
.replace( c.evaluate || null, function ( match, code ) {
return "');" + code.replace( /\\'/g, "'" )
.replace( /[\r\n\t]/g, ' ' ) + "__p.push('";
} )
.replace( /\r/g, '\\r' )
.replace( /\n/g, '\\n' )
.replace( /\t/g, '\\t' )
+ "');}return __p.join('');";
var func = new Function( 'obj', tmpl );
return data ? func( data ) : func;
};
// The OOP Wrapper
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function ( obj ) {
this._wrapped = obj;
};
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function ( obj, chain ) {
return chain ? _( obj ).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function ( name, func ) {
wrapper.prototype[name] = function () {
var args = slice.call( arguments );
unshift.call( args, this._wrapped );
return result( func.apply( _, args ), this._chain );
};
};
// Add all of the Underscore functions to the wrapper object.
_.mixin( _ );
// Add all mutator Array functions to the wrapper.
each( ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function ( name ) {
var method = ArrayProto[name];
wrapper.prototype[name] = function () {
method.apply( this._wrapped, arguments );
return result( this._wrapped, this._chain );
};
} );
// Add all accessor Array functions to the wrapper.
each( ['concat', 'join', 'slice'], function ( name ) {
var method = ArrayProto[name];
wrapper.prototype[name] = function () {
return result( method.apply( this._wrapped, arguments ), this._chain );
};
} );
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function () {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function () {
return this._wrapped;
};
})();

70
gulpfile.js Normal file
View file

@ -0,0 +1,70 @@
var gulp = require("gulp");
var fileImports = require("gulp-imports");
var header = require("gulp-header");
var beautify = require("gulp-beautify");
var hintNot = require("gulp-hint-not");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");
var plato = require("gulp-plato");
var rimraf = require("gulp-rimraf");
var gutil = require("gulp-util");
var express = require("express");
var path = require("path");
var tinylr = require("tiny-lr");
var pkg = require("./package.json");
var open = require("open");
var port = 3080;
var banner = ["/**",
" * <%= pkg.name %> - <%= pkg.description %>",
" * Author: <%= pkg.author %>",
" * Version: v<%= pkg.version %>",
" * Url: <%= pkg.homepage %>",
" * License(s): <% pkg.licenses.forEach(function( license, idx ){ %><%= license.type %><% if(idx !== pkg.licenses.length-1) { %>, <% } %><% }); %>",
" */",
""].join("\n");
gulp.task("combine", function() {
gulp.src(["./src/postal.js"])
.pipe(header(banner, { pkg : pkg }))
.pipe(fileImports())
.pipe(hintNot())
.pipe(beautify({indentSize: 4}))
.pipe(gulp.dest("./lib/"))
.pipe(uglify({ compress: { negate_iife: false }}))
.pipe(header(banner, { pkg : pkg }))
.pipe(rename("postal.min.js"))
.pipe(gulp.dest("./lib/"));
});
gulp.task("default", function() {
gulp.run("combine");
});
gulp.task("report", function () {
gulp.src("./lib/postal.js")
.pipe(plato("report"));
});
var createServer = function(port) {
var p = path.resolve("./");
var app = express();
app.use(express.static(p));
app.listen(port, function() {
gutil.log("Listening on", port);
});
return {
app: app
};
};
var servers;
gulp.task("server", function(){
gulp.run("report");
if(!servers) {
servers = createServer(port);
}
open( "http://localhost:" + port + "/index.html" );
});

View file

@ -2,8 +2,8 @@
<html>
<head>
<title>postal.js</title>
<link rel="stylesheet" href="ext/bootstrap-responsive.min.css" type="text/css">
<link rel="stylesheet" href="ext/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="bower/bootstrap/dist/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="bower/bootstrap/dist/css/bootstrap-theme.min.css" type="text/css">
<style type="text/css">
.banner {
position: relative;
@ -36,8 +36,8 @@
margin-bottom: 20px;
}
</style>
<script type="text/javascript" src="/ext/jquery-1.7.1.js"></script>
<script type="text/javascript" src="/ext/bootstrap.min.js"></script>
<script type="text/javascript" src="bower/jquery/jquery.min.js"></script>
<script type="text/javascript" src="bower/bootstrap/dist/js/bootstrap.min.js"></script>
</head>
<body>
<header class="banner">
@ -53,6 +53,9 @@
<div class="well well-large">
<a href="spec/">Tests</a>
</div>
<div class="well well-large">
<a href="report/">Plato Reports</a>
</div>
<div class="well well-large">
<a href="example/amd">AMD Example Usage</a>
</div>

904
lib/postal.js Executable file → Normal file
View file

@ -1,456 +1,458 @@
/*
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
/**
* 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.8.11
* Url: http://github.com/postaljs/postal.js
* License(s): MIT, GPL
*/
/*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;
(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) {
/*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 ) {
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.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] = {};
var postal;
/*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;
} ));
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 || 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);
};
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.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, options) {
options = options || {};
if (_.isNaN(milliseconds)) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle(fn, milliseconds, options);
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) {
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;
}
}
}
};
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] = {};
if (global && Object.prototype.hasOwnProperty.call(global, "__postalReady__") && _.isArray(global.__postalReady__)) {
while (global.__postalReady__.length) {
global.__postalReady__.shift().onReady(postal);
}
}
return postal;
}));

13
lib/postal.min.js vendored Executable file → Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,52 +1,52 @@
{
"name" : "postal",
"description" : "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
"version" : "0.8.11",
"homepage" : "http://github.com/postaljs/postal.js",
"repository" : {
"type" : "git",
"url" : "git://github.com/postaljs/postal.js.git"
"name": "postal",
"description": "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
"version": "0.8.11",
"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)",
"author": "Jim Cowart (http://freshbrewedcode.com/jimcowart)",
"contributors": [
{
"name" : "Jim Cowart",
"email" : "WhyNotJustComment@OnMyBlog.com",
"url" : "http://freshbrewedcode.com/jimcowart"
"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": "Alex Robson",
"email": "WhyNotJustComment@OnMyBlog.com",
"url": "http://freshbrewedcode.com/alexrobson"
},
{
"name" : "Nicholas Cloud",
"email" : "WhyNotJustComment@OnMyBlog.com",
"url" : "http://nicholascloud.com"
"name": "Nicholas Cloud",
"email": "WhyNotJustComment@OnMyBlog.com",
"url": "http://nicholascloud.com"
},
{
"name" : "Doug Neiner",
"email" : "WhyNotJustComment@OnMyBlog.com",
"url" : "http://dougneiner.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": "Jonathan Creamer",
"email": "WhyNotJustComment@OnMyBlog.com",
"url": "http://freshbrewedcode.com/jonathancreamer"
},
{
"name" : "Elijah Manor",
"email" : "WhyNotJustComment@OnMyBlog.com",
"url" : "http://www.elijahmanor.com"
"name": "Elijah Manor",
"email": "WhyNotJustComment@OnMyBlog.com",
"url": "http://www.elijahmanor.com"
},
{
"name" : "Ger Hobbelt",
"email" : "ger@hobbelt.com",
"url" : "http://hebbut.net/"
"name": "Ger Hobbelt",
"email": "ger@hobbelt.com",
"url": "http://hebbut.net/"
},
{
"name" : "Christian Haas",
"url" : "http://github.com/dertseha"
"name": "Christian Haas",
"url": "http://github.com/dertseha"
}
],
"keywords": [
@ -61,28 +61,53 @@
"broker",
"envelope"
],
"bugs" : {
"email" : "PleaseJustUseTheIssuesPage@github.com",
"url" : "http://github.com/postaljs/postal.js/issues"
"bugs": {
"email": "PleaseJustUseTheIssuesPage@github.com",
"url": "http://github.com/postaljs/postal.js/issues"
},
"directories" : { "lib" : "lib" },
"main" : "lib/postal.js",
"engines" : {
"node" : ">=0.4.0"
"directories": {
"lib": "lib"
},
"dependencies" : {
"underscore" : ">=1.1.7"
"main": "lib/postal.js",
"engines": {
"node": ">=0.4.0"
},
"bundleDependencies" : [ "underscore" ],
"devDependencies" : {},
"licenses" : [
"dependencies": {
"underscore": "~1.5.2"
},
"bundleDependencies": [
"underscore"
],
"devDependencies": {
"bower": "~1.2.8",
"gulp-util": "~2.2.9",
"gulp": "~3.3.1",
"gulp-imports": "~0.0.1",
"gulp-header": "~1.0.2",
"gulp-hint-not": "~0.0.3",
"gulp-uglify": "~0.1.0",
"gulp-rename": "~0.2.1",
"gulp-plato": "~0.1.0",
"gulp-beautify": "~1.0.3",
"tiny-lr": "0.0.5",
"express": "~3.4.7",
"gulp-rimraf": "0.0.8",
"open": "~0.0.4",
"expect.js": "~0.2.0"
},
"licenses": [
{
"type" : "MIT",
"url" : "http://www.opensource.org/licenses/mit-license.php"
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
},
{
"type" : "GPL",
"url" : "http://www.opensource.org/licenses/gpl-3.0.html"
"type": "GPL",
"url": "http://www.opensource.org/licenses/gpl-3.0.html"
}
]
],
"scripts": {
"test": "mocha ./spec/*.js",
"build": "gulp",
"start": "gulp server"
}
}

View file

@ -1,470 +0,0 @@
/* global describe, postal, it, after, before, expect, bindingsResolver */
describe( "amqpBindingsResolver", function () {
describe( "When calling compare", function () {
beforeEach( function () {
bindingsResolver.reset();
} );
describe( "With '*' wildcards", function () {
// Passing matches
describe( "With topic Top.Middle.Bottom and binding *.Middle.Bottom", function () {
var result = bindingsResolver.compare( "*.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.Middle.*", function () {
var result = bindingsResolver.compare( "Top.Middle.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "Top.*.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "*.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding *.Middle.*", function () {
var result = bindingsResolver.compare( "*.Middle.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.Middle.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding *.*.*", function () {
var result = bindingsResolver.compare( "*.*.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.*.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing Matches
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.Middle.Bottom", function () {
var result = bindingsResolver.compare( "*.Middle.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok()
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok()
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok()
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok()
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.Middle.*", function () {
var result = bindingsResolver.compare( "Top.Middle.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.Middle.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "Top.*.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.*.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "*.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.Middle.*", function () {
var result = bindingsResolver.compare( "*.Middle.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.Middle.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.*.*", function () {
var result = bindingsResolver.compare( "*.*.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.*.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With '#' wildcards", function () {
// Passing matches
// # at beginning of binding
describe( "With topic Top.Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.SubTop.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Middle.Bottom" ),
cached = bindingsResolver.cache["Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// # in middle of binding
describe( "With topic Top.Middle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// # at end of binding
describe( "With topic Top.Middle.Bottom and binding Top.Middle.#", function () {
var result = bindingsResolver.compare( "Top.Middle.#", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.Bottom and binding Top.SubTop.#", function () {
var result = bindingsResolver.compare( "Top.SubTop.#", "Top.SubTop.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.Bottom"]["Top.SubTop.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding Middle.#", function () {
var result = bindingsResolver.compare( "Middle.#", "Middle.Bottom" ),
cached = bindingsResolver.cache["Middle.Bottom"]["Middle.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing matches
// # at beginning of binding
describe( "With topic Top.Middle.SubMiddle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding #.Middle.SubMiddle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
// # in middle of binding
describe( "With topic Top.Middle.Bottom and binding Top.SubTop.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.SubTop.#.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"] && bindingsResolver.cache["Top.Middle.Bottom"]["Top.SubTop.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom.SubBottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.SubMiddle.Bottom.SubBottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom.SubBottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom.SubBottom"]["Top.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding Top.#.Middle.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Middle.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["Top.#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Bottom and binding SubTop.#.Bottom", function () {
var result = bindingsResolver.compare( "SubTop.#.Bottom", "Top.SubTop.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Bottom"] && bindingsResolver.cache["Top.SubTop.Bottom"]["SubTop.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
// # at end of binding
describe( "With topic Top.Bottom and binding Top.Middle.#", function () {
var result = bindingsResolver.compare( "Top.Middle.#", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["Top.Middle.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.SubTop.#", function () {
var result = bindingsResolver.compare( "Top.SubTop.#", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"] && bindingsResolver.cache["Top.Middle.Bottom"]["Top.SubTop.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Bottom and binding Middle.#", function () {
var result = bindingsResolver.compare( "Middle.#", "Bottom" ),
cached = bindingsResolver.cache["Bottom"] && bindingsResolver.cache["Bottom"]["Middle.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With both '#' and '*' wildcards", function () {
// Passing matches
describe( "With topic Top.Middle.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding *.#.Bottom", function () {
var result = bindingsResolver.compare( "*.#.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["*.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing matches
describe( "With topic Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Bottom" ),
cached = bindingsResolver.cache["Bottom"] && bindingsResolver.cache["Bottom"]["#.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.Middle.SubMiddle.#.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.Middle.SubMiddle.#.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.Middle.SubMiddle.#.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding #.*.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Middle.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["#.*.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With plain string matching", function () {
describe( "With topic Top.Middle.Bottom and binding Top.Middle.Bottom", function () {
var result = bindingsResolver.compare( "Top.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic 'Topic' and binding 'Topic'", function () {
var result = bindingsResolver.compare( "Topic", "Topic" ),
cached = bindingsResolver.cache["Topic"]["Topic"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic '/sample/topic' and binding '/sample/topic'", function () {
var result = bindingsResolver.compare( "/sample/topic", "/sample/topic" ),
cached = bindingsResolver.cache["/sample/topic"] ? bindingsResolver.cache["/sample/topic"]["/sample/topic"] : null;
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
} );
} );
} );

View file

@ -1,52 +0,0 @@
/* global describe, postal, it, after, before, expect, ChannelDefinition */
describe( "ChannelDefinition", function () {
describe( "When initializing a channel definition", function () {
var chDef = new ChannelDefinition( "TestChannel" );
it( "should set channel to TestChannel", function () {
expect( chDef.channel ).to.be( "TestChannel" );
} );
} );
describe( "When calling subscribe", function () {
var ch = new ChannelDefinition( "TestChannel" ),
sub = ch.subscribe( "TestTopic", function () {} );
it( "subscription should be instance of SubscriptionDefinition", function () {
expect( sub instanceof SubscriptionDefinition ).to.be.ok();
} );
} );
describe( "When publishing from a channel definition", function () {
var channel, subscription;
beforeEach( function () {
channel = postal.channel( "OhHai" );
} );
afterEach( function () {
postal.utils.reset();
channel = undefined;
subscription = undefined;
} );
it( "Should allow a topic only to be used", function ( done ) {
subscription = channel.subscribe( "topic.only", function ( d, e ) {
expect( typeof d === "undefined" ).to.be( true );
expect( e.topic ).to.be( "topic.only" );
done();
} );
channel.publish( "topic.only" );
} );
it( "Should allow a topic and data argument to be used", function ( done ) {
subscription = channel.subscribe( "topic.and.data", function ( d, e ) {
expect( d ).to.be( "hai" );
expect( e.topic ).to.be( "topic.and.data" );
done();
} );
channel.publish( "topic.and.data", "hai" );
} );
it( "Should allow an envelope argument to be used", function ( done ) {
subscription = channel.subscribe( "envelope", function ( d, e ) {
expect( e.channel ).to.be( "OhHai" );
expect( e.data ).to.be( "hai" );
expect( e.foo ).to.be( "bar" );
done();
} );
channel.publish( { topic : "envelope", data : "hai", foo : "bar" } );
} );
} );
} );

View file

@ -1,761 +0,0 @@
/* global describe, postal, it, after, before, expect */
describe( "Postal", function () {
var subscription,
sub,
channel,
caughtSubscribeEvent = false,
caughtUnsubscribeEvent = false;
describe( "When creating basic subscription", function () {
var systemSubscription = {};
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.created",
callback : function ( data, envelope ) {
if ( data.event &&
data.event == "subscription.created" &&
data.channel == "MyChannel" &&
data.topic == "MyTopic" ) {
caughtSubscribeEvent = true;
}
}
} );
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () {} );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
} );
after( function () {
systemSubscription.unsubscribe();
postal.utils.reset();
} );
it( "should create a channel called MyChannel", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"] !== undefined ).to.be.ok();
} );
it( "should create a topic under MyChannel called MyTopic", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"]["MyTopic"] !== undefined ).to.be.ok();
} );
it( "should have set subscription channel value", function () {
expect( sub.channel ).to.be( "MyChannel" );
} );
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
it( "should have defaulted the subscription constraints array", function () {
expect( sub.constraints.length ).to.be( 0 );
} );
it( "should have defaulted the subscription context value", function () {
expect( sub.context ).to.be( null );
} );
it( "should have captured subscription creation event", function () {
expect( caughtSubscribeEvent ).to.be.ok();
} );
} );
describe( "When unsubscribing", function () {
describe( "With a single subscription", function () {
var subExistsBefore = false,
subExistsAfter = true;
var systemSubscription = {};
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.*",
callback : function ( data, env ) {
if ( data.event &&
data.event === "subscription.removed" &&
data.channel === "MyChannel" &&
data.topic === "MyTopic" ) {
caughtUnsubscribeEvent = true;
}
}
} );
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () { });
subExistsBefore = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0] !== undefined;
subscription.unsubscribe();
subExistsAfter = postal.configuration.bus.subscriptions.MyChannel.MyTopic.length !== 0;
} );
after( function () {
systemSubscription.unsubscribe();
postal.utils.reset();
} );
it( "subscription should exist before unsubscribe", function () {
expect( subExistsBefore ).to.be.ok();
} );
it( "subscription should not exist after unsubscribe", function () {
expect( subExistsAfter ).to.not.be.ok();
} );
it( "should have captured unsubscription creation event", function () {
expect( caughtUnsubscribeEvent ).to.be.ok();
} );
it( "postal.getSubscribersFor('MyChannel', 'MyTopic') should not return any subscriptions", function () {
expect( postal.utils.getSubscribersFor("MyChannel", "MyTopic").length ).to.be(0);
} );
} );
describe( "With multiple subscribers on one channel", function () {
var subscription1, subscription2, results = [];
before( function () {
channel = postal.channel();
subscription1 = channel.subscribe( 'test',function () {
results.push( '1 received message' );
} ).once();
subscription2 = channel.subscribe( 'test', function () {
results.push( '2 received message' );
} );
channel.publish( 'test' );
channel.publish( 'test' );
} );
after( function () {
subscription2.unsubscribe();
postal.utils.reset();
} );
it( "should produce expected messages", function () {
expect( results.length ).to.be( 3 );
expect( results[0] ).to.be( "1 received message" );
expect( results[1] ).to.be( "2 received message" );
expect( results[2] ).to.be( "2 received message" );
} );
} );
describe( "With nested publishing", function () {
var subscription1, subscription2, sysub, results = [];
before( function () {
channel = postal.channel();
sysub = postal.subscribe( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.removed",
callback : function ( d, e ) {
results.push( "unsubscribed" );
}
} );
subscription1 = channel.subscribe( 'nest.test',function () {
results.push( '1 received message' );
channel.publish( "nest.test2", "Hai" );
} ).once();
subscription2 = channel.subscribe( 'nest.test2', function () {
results.push( '2 received message' );
} );
channel.publish( 'nest.test' );
channel.publish( 'nest.test' );
} );
after( function () {
//subscription2.unsubscribe();
sysub.unsubscribe();
postal.utils.reset();
} );
it( "should produce expected messages", function () {
expect( results.length ).to.be( 3 );
expect( results[0] ).to.be( "1 received message" );
expect( results[1] ).to.be( "2 received message" );
expect( results[2] ).to.be( "unsubscribed" );
} );
} );
} );
describe( "When publishing a message", function () {
var msgReceivedCnt = 0,
msgData;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
msgReceivedCnt++;
msgData = data;
} );
channel.publish( "MyTopic", "Testing123" );
subscription.unsubscribe();
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked once", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
it( "subscription callback should receive published data", function () {
expect( msgData ).to.be( "Testing123" );
} );
} );
describe( "When subscribing with a disposeAfter of 5", function () {
var msgReceivedCnt = 0, subExistsAfter, systemSubscription;
before( function () {
caughtUnsubscribeEvent = false;
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function () {
msgReceivedCnt++;
}).disposeAfter( 5 );
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.*",
callback : function ( data, env ) {
if ( data.event &&
data.event === "subscription.removed" &&
data.channel === "MyChannel" &&
data.topic === "MyTopic" ) {
caughtUnsubscribeEvent = true;
}
}
} );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
subExistsAfter = postal.configuration.bus.subscriptions.MyChannel.MyTopic.length !== 0;
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked 5 times", function () {
expect( msgReceivedCnt ).to.be( 5 );
} );
it( "subscription should not exist after unsubscribe", function () {
expect( subExistsAfter ).to.not.be.ok();
} );
it( "should have captured unsubscription creation event", function () {
expect( caughtUnsubscribeEvent ).to.be.ok();
} );
it( "postal.getSubscribersFor('MyChannel', 'MyTopic') should not return any subscriptions", function () {
expect( postal.utils.getSubscribersFor("MyChannel", "MyTopic").length ).to.be(0);
} );
} );
describe( "When subscribing with once()", function () {
var msgReceivedCnt = 0;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic",function ( data ) {
msgReceivedCnt++;
} ).once();
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked 1 time", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
} );
describe( "When subscribing and ignoring duplicates", function () {
var subInvokedCnt = 0;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
subInvokedCnt++;
} )
.distinctUntilChanged();
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
subInvokedCnt = 0;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "subscription callback should be invoked once", function () {
expect( subInvokedCnt ).to.be( 1 );
} );
} );
describe( "When subscribing with one constraint returning true", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraint( function () {
return true;
} );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "should have invoked the subscription callback", function () {
expect( recvd ).to.be.ok();
} );
} );
describe( "When subscribing with one constraint returning false", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraint( function () {
return false;
} );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "should not have invoked the subscription callback", function () {
expect( recvd ).to.not.be.ok();
} );
} );
describe( "When subscribing with multiple constraints returning true", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraints( [function () {
return true;
},
function () {
return true;
},
function () {
return true;
}] );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
} );
it( "should have invoked the callback", function () {
expect( recvd ).to.be.ok();
} );
} );
describe( "When subscribing with multiple constraints and one returning false", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraints( [function () {
return true;
},
function () {
return false;
},
function () {
return true;
}] );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
} );
it( "should not have invoked the callback", function () {
expect( recvd ).to.not.be.ok()
} );
} );
describe( "When subscribing with the context being set", function () {
var count = 0,
obj = {
increment : function () {
count++;
}
};
before( function () {
channel = postal.channel( "ContextChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
this.increment();
} )
.withContext( obj );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have called obj.increment", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with defer", function () {
var results = [];
before( function () {
channel = postal.channel( "DeferChannel" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have met expected results", function ( done ) {
subscription = channel.subscribe( "MyTopic",
function ( data ) {
results.push( "second" );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
done();
} ).defer();
channel.publish( "MyTopic", "Testing123" );
results.push( "first" );
} );
} );
describe( "When subscribing with delay", function () {
var results = [];
before( function () {
channel = postal.channel( "DelayChannel" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have met expected results", function ( done ) {
subscription = channel.subscribe( "MyTopic",
function ( data ) {
results.push( "second" );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
done();
} ).withDelay( 500 );
channel.publish( "MyTopic", "Testing123" );
results.push( "first" );
} );
} );
describe( "When subscribing with debounce", function () {
var results = [], debouncedChannel;
before( function () {
debouncedChannel = postal.channel( "DebounceChannel" );
subscription = debouncedChannel.subscribe( "MyTopic",
function ( data ) {
results.push( data );
} ).withDebounce( 800 );
} );
after( function () {
postal.utils.reset();
} );
it( "should have only invoked debounced callback once", function ( done ) {
debouncedChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 2 );
}, 20 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 3 );
}, 80 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 4 );
}, 250 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 5 );
}, 500 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 6 );
}, 1000 ); // should invoke callback
setTimeout( function () {
expect( results[0] ).to.be( 6 );
expect( results.length ).to.be( 1 );
done();
}, 2400 );
} );
} );
describe( "When subscribing with throttle", function () {
var results = [], throttledChannel;
before( function () {
throttledChannel = postal.channel( "ThrottleChannel" );
subscription = throttledChannel.subscribe( "MyTopic",
function ( data ) {
results.push( data );
} ).withThrottle( 500 );
} );
after( function () {
postal.utils.reset();
} );
it( "should have only invoked throttled callback twice", function ( done ) {
throttledChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
setTimeout( function () {
throttledChannel.publish( "MyTopic", 800 );
}, 800 ); // should invoke callback
for ( var i = 0; i < 20; i++ ) {
(function ( x ) {
throttledChannel.publish( "MyTopic", x );
})( i );
}
setTimeout( function () {
expect( results[0] ).to.be( 1 );
expect( results[1] ).to.be( 800 );
expect( results.length ).to.be( 2 );
done();
}, 1500 );
} );
} );
describe( "When subscribing with a hierarchical binding, no wildcards", function () {
var count = 0, channelB, channelC;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.MiddleTopic", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback only once", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with a hierarchical binding, using #", function () {
var count, channelB, channelC, channelD, channelE;
before( function () {
count = 0;
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.#.SubTopic", function ( data, env ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubMiddle.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 2 );
} );
} );
describe( "When subscribing with a hierarchical binding, using *", function () {
var count = 0, channelB, channelC, channelD;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.MiddleTopic.*", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with a hierarchical binding, using # and *", function () {
var count = 0, channelB, channelC, channelD, channelE;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.#.*", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
channel.publish( "OtherTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 3 );
} );
} );
describe( "When using global publish api", function () {
var msgReceivedCnt = 0,
msgData;
before( function () {
channel = postal.channel( "MyGlobalChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
msgReceivedCnt++;
msgData = data;
} );
postal.publish( { channel : "MyGlobalChannel", topic : "MyTopic", data : "Testing123" } );
subscription.unsubscribe();
postal.publish( { channel : "MyGlobalChannel", topic : "MyTopic", data : "Testing123" } );
} );
after( function () {
postal.utils.reset();
msgReceivedCnt = 0;
} );
it( "channel should be of type ChannelDefinition", function () {
expect( channel instanceof ChannelDefinition ).to.be.ok();
} );
it( "subscription callback should be invoked once", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
it( "subscription callback should receive published data", function () {
expect( msgData ).to.be( "Testing123" );
} );
} );
describe( "When using global subscribe api", function () {
before( function () {
subscription = postal.subscribe( {
channel : "MyChannel",
topic : "MyTopic",
callback : function () {
}
} );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
} );
after( function () {
postal.utils.reset();
} );
it( "subscription should be of type SubscriptionDefinition", function () {
expect( subscription instanceof SubscriptionDefinition ).to.be.ok();
} );
it( "should create an channel called MyChannel", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"] !== undefined ).to.be.ok();
} );
it( "should create a topic under MyChannel called MyTopic", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"]["MyTopic"] !== undefined ).to.be.ok();
} );
it( "should have set subscription channel value", function () {
expect( sub.channel ).to.be( "MyChannel" );
} );
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
it( "should have defaulted the subscription constraints array", function () {
expect( sub.constraints.length ).to.be( 0 );
} );
it( "should have defaulted the subscription context value", function () {
expect( sub.context ).to.be( null );
} );
} );
describe( "When using global channel api", function () {
var gch;
describe( "With no channel name provided", function () {
describe( "Using string argument", function () {
before( function () {
gch = postal.channel( "SomeChannel" );
} );
after( function () {
gch = undefined;
} );
it( "channel should be of type ChannelDefinition", function () {
expect( gch instanceof ChannelDefinition ).to.be.ok();
} );
it( "should set channel name to SomeChannel", function () {
expect( gch.channel ).to.be( "SomeChannel" );
} );
} );
} );
} );
describe( "When subscribing and unsubscribing a wire tap", function () {
var wireTapData,
wireTapEnvelope,
wiretap;
before( function () {
caughtUnsubscribeEvent = false;
wireTapData = [];
wireTapEnvelope = [];
wiretap = postal.addWireTap( function ( msg, envelope ) {
wireTapData.push( msg );
wireTapEnvelope.push( envelope );
} );
postal.publish( { topic : "Oh.Hai.There", data : "I'm in yer bus, tappin' yer subscriptionz..."} );
wiretap();
postal.publish( { topic : "Oh.Hai.There", data : "I'm in yer bus, tappin' yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "wire tap should have been invoked only once", function () {
expect( wireTapData.length ).to.be( 1 );
expect( wireTapEnvelope.length ).to.be( 1 );
} );
it( "wireTap data should match expected results", function () {
expect( wireTapData[0] ).to.be( "I'm in yer bus, tappin' yer subscriptionz..." );
} );
it( "wireTap envelope should match expected results", function () {
expect( wireTapEnvelope[0].channel ).to.be( postal.configuration.DEFAULT_CHANNEL );
expect( wireTapEnvelope[0].topic ).to.be( "Oh.Hai.There" );
} );
} );
describe( "When calling postal.utils.reset", function () {
var resolver;
before( function () {
postal.utils.reset();
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () {
} );
postal.channel( "MyChannel" ).publish( "MyTopic", "Oh Hai!" );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
resolver = postal.configuration.resolver.cache.MyTopic;
postal.utils.reset();
} );
after( function () {
} );
it( "should have created a subscription definition", function () {
expect( sub.channel ).to.be( "MyChannel" );
expect( sub.topic ).to.be( "MyTopic" );
expect( sub.constraints.length ).to.be( 0 );
expect( sub.context ).to.be( null );
} );
it( "should have created a resolver cache entry", function () {
expect( _.isEmpty( resolver ) ).to.not.be.ok()
expect( resolver["MyTopic"] ).to.be.ok();
} );
it( "subscriptions cache should now be empty", function () {
expect( _.isEmpty( postal.configuration.bus.subscriptions ) ).to.be.ok();
} );
it( "resolver cache should now be empty", function () {
expect( _.isEmpty( postal.configuration.resolver.cache ) ).to.be.ok();
} );
} );
describe( "When calling utils.getSubscribersFor", function () {
var subs = [], i;
before( function () {
i = 10;
var ch1 = postal.channel( "MyChannel" ),
ch2 = postal.channel( "MyChannel2" );
while ( i ) {
subs.push( ch1.subscribe( "MyTopic", function () {
} ) );
subs.push( ch2.subscribe( "MyTopic2", function () {
} ) );
i--;
}
} );
after( function () {
sub = [];
postal.utils.reset();
} );
it( "should return expected results for MyChannel/MyTopic", function () {
var results = postal.utils.getSubscribersFor( { channel : "MyChannel", topic : "MyTopic" } );
expect( results.length ).to.be( 10 );
} );
it( "should return expected results for MyChannel2/MyTopic2", function () {
var results = postal.utils.getSubscribersFor( { channel : "MyChannel2", topic : "MyTopic2" } );
expect( results.length ).to.be( 10 );
} );
} );
describe( "When publishing on a channel where no subscribers exist", function () {
it( "should return expected results for MyChannel/MyTopic", function () {
var env = postal.publish( {
channel : "NoOneIsUsingThisOne",
topic : "This.Is.A.Lonely.Topic",
data : "Y U NO SUBSCRIBE TO ME?"
} );
expect( !_.isEmpty( env ) ).to.be( true );
} );
} );
} );

View file

@ -1,275 +0,0 @@
/* global describe, postal, it, after, before, expect, SubscriptionDefinition */
var NO_OP = function () {};
describe( "SubscriptionDefinition", function () {
describe( "When initializing SubscriptionDefinition", function () {
var sDef,
caughtSubscribeEvent,
systemSubscription;
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.created",
callback : function ( data, envelope ) {
if ( data.event &&
data.event === "subscription.created" &&
data.channel === "SubDefTestChannel" &&
data.topic === "SubDefTestTopic" ) {
caughtSubscribeEvent = true;
}
}
} );
sDef = new SubscriptionDefinition( "SubDefTestChannel", "SubDefTestTopic", NO_OP );
} );
after( function () {
sDef.unsubscribe();
systemSubscription.unsubscribe();
caughtSubscribeEvent = false;
} );
it( "should set the channel to SubDefTestChannel", function () {
expect( sDef.channel ).to.be( "SubDefTestChannel" );
} );
it( "should set the topic to SubDefTestTopic", function () {
expect( sDef.topic ).to.be( "SubDefTestTopic" );
} );
it( "should set the callback", function () {
expect( sDef.callback ).to.be( NO_OP );
} );
it( "should default the constraints", function () {
expect( sDef.constraints.length ).to.be( 0 );
} );
it( "should default the context", function () {
expect( sDef.context ).to.be( null );
} );
it( "should fire the subscription.created message", function () {
expect( caughtSubscribeEvent ).to.be( true );
} );
} );
describe( "When setting distinctUntilChanged", function () {
var sDefa = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).distinctUntilChanged();
it( "Should add a DistinctPredicate constraint to the configuration constraints", function () {
expect( sDefa.constraints.length ).to.be( 1 );
} );
} );
describe( "When adding a constraint", function () {
var sDefb = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraint( function () {
} );
it( "Should add a constraint", function () {
expect( sDefb.constraints.length ).to.be( 1 );
} );
} );
describe( "When adding multiple constraints", function () {
var sDefc = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraints( [function () {
}, function () {
}, function () {
}] );
it( "Should add a constraint", function () {
expect( sDefc.constraints.length ).to.be( 3 );
} );
} );
describe( "When setting the context", function () {
var obj = { name : "Rose" },
name,
sDefd = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP )
.withContext( obj )
.withConstraint( function ( d, e ) {
name = this.name;
return true;
} );
postal.publish( { channel : "TestChannel", topic : "TestTopic", data : "Oh, hai"} )
it( "Should set context", function () {
expect( sDefd.context ).to.be( obj );
} );
it( "Should apply context to predicate/constraint", function () {
expect( name ).to.be( "Rose" );
} );
} );
describe( "When calling subscribe to set the callback", function () {
var sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ),
fn = function () {
};
sDefe.subscribe( fn );
it( "Should set the callback", function () {
expect( sDefe.callback ).to.be( fn );
} );
} );
describe( "When deferring the callback", function () {
var results = [], sDefe;
it( "Should defer the callback", function ( done ) {
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
results.push( data );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
expect( env.topic ).to.be( "TestTopic" );
done();
} ).defer();
sDefe.callback( "second", { topic : "TestTopic" } );
results.push( "first" );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).defer();
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
it( "Should keep the context intact when modified later", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).defer().withContext(context);
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
describe( "When throttling the callback", function () {
var results = [],
sDefe = new SubscriptionDefinition( "ThrottleTest", "TestTopic", function ( data ) {
results.push( data );
} ).withThrottle( 500 );
var timeout1, timeout2;
it( "should have only invoked throttled callback twice", function ( done ) {
sDefe.callback( 1 ); // starts the two second clock on debounce
timeout1 = setTimeout( function () {
sDefe.callback( 800 );
}, 900 ); // should invoke callback
for ( var i = 0; i < 20; i++ ) {
(function ( x ) {
sDefe.callback( x );
})( i );
}
timeout2 = setTimeout( function () {
expect( results[0] ).to.be( 1 );
expect( results[1] ).to.be( 800 );
expect( results.length ).to.be( 2 );
done();
}, 1500 );
} );
it( "Should keep the context intact", function( done ) {
var context = {
key : 'abcd'
};
sDefe = new SubscriptionDefinition( "ThrottleTest", "TestTopic", function( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext( context ).withThrottle( 500 );
sDefe.callback.call( sDefe.context, 1 );
} );
} );
describe( "When delaying the callback", function () {
var results = [], sDefe;
it( "Should delay the callback", function ( done ) {
sDefe = new SubscriptionDefinition( "DelayTest", "TestTopic", function ( data, env ) {
results.push( data );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
expect( env.topic ).to.be( "TestTopic" );
done();
} ).withDelay( 200 );
sDefe.callback( "second", { topic : "TestTopic" } );
results.push( "first" );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "DelayTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).withDelay( 200 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
describe( "When debouncing the callback", function () {
var results = [],
sDefe = new SubscriptionDefinition( "DebounceTest", "TestTopic", function ( data ) {
results.push( data );
} ).withDebounce( 800 );
it( "should have only invoked debounced callback once", function ( done ) {
sDefe.callback( 1 ); // starts the two second clock on debounce
setTimeout( function () {
sDefe.callback( 2 );
}, 20 ); // should not invoke callback
setTimeout( function () {
sDefe.callback( 3 );
}, 80 ); // should not invoke callback
setTimeout( function () {
sDefe.callback( 6 );
}, 800 ); // should invoke callback
setTimeout( function () {
expect( results[0] ).to.be( 6 );
expect( results.length ).to.be( 1 );
done();
}, 2300 );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 5678
};
var timeout;
sDefe = new SubscriptionDefinition( "DebounceTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
clearTimeout(timeout);
done();
} ).withContext(context).withDebounce( 100 );
sDefe.callback.call( sDefe.context, 1 );
timeout = setTimeout( function () {
sDefe.callback.call( sDefe.context, 2 );
}, 200 ); // should invoke callback
});
} );
describe( "When self disposing", function () {
var context = {
key : 'abcd'
};
it( "Should be inactive", function () {
var sDefe = new SubscriptionDefinition( "DisposeTest", "TestTopic", function ( data, env ) {
} ).withContext(context).disposeAfter( 1 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
expect( sDefe.inactive ).to.be( true );
} );
it( "Should keep the context intact", function ( done ) {
var sDefe = new SubscriptionDefinition( "DisposeTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).disposeAfter( 200 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
} );

View file

@ -0,0 +1,475 @@
/* global describe, postal, it, after, before, expect */
(function(){
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var bindingsResolver = postal.configuration.resolver;
describe( "amqpBindingsResolver", function () {
describe( "When calling compare", function () {
before( function () {
bindingsResolver.reset();
});
describe( "With '*' wildcards", function () {
// Passing matches
describe( "With topic Top.Middle.Bottom and binding *.Middle.Bottom", function () {
var result = bindingsResolver.compare( "*.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.Middle.*", function () {
var result = bindingsResolver.compare( "Top.Middle.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "Top.*.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.*.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "*.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding *.Middle.*", function () {
var result = bindingsResolver.compare( "*.Middle.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.Middle.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding *.*.*", function () {
var result = bindingsResolver.compare( "*.*.*", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["*.*.*"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing Matches
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.Middle.Bottom", function () {
var result = bindingsResolver.compare( "*.Middle.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok()
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok()
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok()
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok()
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.Middle.*", function () {
var result = bindingsResolver.compare( "Top.Middle.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.Middle.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "Top.*.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.*.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.*.*", function () {
var result = bindingsResolver.compare( "*.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.Middle.*", function () {
var result = bindingsResolver.compare( "*.Middle.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.Middle.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding *.*.*", function () {
var result = bindingsResolver.compare( "*.*.*", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["*.*.*"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With '#' wildcards", function () {
// Passing matches
// # at beginning of binding
describe( "With topic Top.Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.SubTop.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Middle.Bottom" ),
cached = bindingsResolver.cache["Middle.Bottom"]["#.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// # in middle of binding
describe( "With topic Top.Middle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["Top.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// # at end of binding
describe( "With topic Top.Middle.Bottom and binding Top.Middle.#", function () {
var result = bindingsResolver.compare( "Top.Middle.#", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.Bottom and binding Top.SubTop.#", function () {
var result = bindingsResolver.compare( "Top.SubTop.#", "Top.SubTop.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.Bottom"]["Top.SubTop.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding Middle.#", function () {
var result = bindingsResolver.compare( "Middle.#", "Middle.Bottom" ),
cached = bindingsResolver.cache["Middle.Bottom"]["Middle.#"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing matches
// # at beginning of binding
describe( "With topic Top.Middle.SubMiddle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding #.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Middle.Bottom and binding #.Middle.SubMiddle.Bottom", function () {
var result = bindingsResolver.compare( "#.Middle.Bottom", "Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Middle.SubMiddle.Bottom"]["#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
// # in middle of binding
describe( "With topic Top.Middle.Bottom and binding Top.SubTop.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.SubTop.#.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"] && bindingsResolver.cache["Top.Middle.Bottom"]["Top.SubTop.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom.SubBottom and binding Top.#.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Bottom", "Top.Middle.SubMiddle.Bottom.SubBottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom.SubBottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom.SubBottom"]["Top.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Middle.SubMiddle.Bottom and binding Top.#.Middle.Bottom", function () {
var result = bindingsResolver.compare( "Top.#.Middle.Bottom", "Top.SubTop.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.SubTop.Middle.SubMiddle.Bottom"]["Top.#.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.SubTop.Bottom and binding SubTop.#.Bottom", function () {
var result = bindingsResolver.compare( "SubTop.#.Bottom", "Top.SubTop.Bottom" ),
cached = bindingsResolver.cache["Top.SubTop.Bottom"] && bindingsResolver.cache["Top.SubTop.Bottom"]["SubTop.#.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
// # at end of binding
describe( "With topic Top.Bottom and binding Top.Middle.#", function () {
var result = bindingsResolver.compare( "Top.Middle.#", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["Top.Middle.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.Bottom and binding Top.SubTop.#", function () {
var result = bindingsResolver.compare( "Top.SubTop.#", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"] && bindingsResolver.cache["Top.Middle.Bottom"]["Top.SubTop.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Bottom and binding Middle.#", function () {
var result = bindingsResolver.compare( "Middle.#", "Bottom" ),
cached = bindingsResolver.cache["Bottom"] && bindingsResolver.cache["Bottom"]["Middle.#"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With both '#' and '*' wildcards", function () {
// Passing matches
describe( "With topic Top.Middle.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"]["#.*.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding *.#.Bottom", function () {
var result = bindingsResolver.compare( "*.#.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["*.#.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
// Failing matches
describe( "With topic Bottom and binding #.*.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Bottom", "Bottom" ),
cached = bindingsResolver.cache["Bottom"] && bindingsResolver.cache["Bottom"]["#.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Middle.SubMiddle.Bottom and binding Top.Middle.SubMiddle.#.*.Bottom", function () {
var result = bindingsResolver.compare( "Top.Middle.SubMiddle.#.*.Bottom", "Top.Middle.SubMiddle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"] && bindingsResolver.cache["Top.Middle.SubMiddle.Bottom"]["Top.Middle.SubMiddle.#.*.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
describe( "With topic Top.Bottom and binding #.*.Middle.Bottom", function () {
var result = bindingsResolver.compare( "#.*.Middle.Bottom", "Top.Bottom" ),
cached = bindingsResolver.cache["Top.Bottom"] && bindingsResolver.cache["Top.Bottom"]["#.*.Middle.Bottom"];
it( "Result should be false", function () {
expect( result ).to.not.be.ok();
} );
it( "Should *not* create a resolver cache entry", function () {
expect( cached ).to.not.be.ok();
} );
} );
} );
describe( "With plain string matching", function () {
describe( "With topic Top.Middle.Bottom and binding Top.Middle.Bottom", function () {
var result = bindingsResolver.compare( "Top.Middle.Bottom", "Top.Middle.Bottom" ),
cached = bindingsResolver.cache["Top.Middle.Bottom"]["Top.Middle.Bottom"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic 'Topic' and binding 'Topic'", function () {
var result = bindingsResolver.compare( "Topic", "Topic" ),
cached = bindingsResolver.cache["Topic"]["Topic"];
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
describe( "With topic '/sample/topic' and binding '/sample/topic'", function () {
var result = bindingsResolver.compare( "/sample/topic", "/sample/topic" ),
cached = bindingsResolver.cache["/sample/topic"] ? bindingsResolver.cache["/sample/topic"]["/sample/topic"] : null;
it( "Result should be true", function () {
expect( result ).to.be.ok();
} );
it( "Should create a resolver cache entry", function () {
expect( cached ).to.be.ok();
} );
} );
} );
} );
} );
}());

View file

@ -0,0 +1,76 @@
/* global describe, postal, it, after, before, expect */
(function(){
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var ChannelDefinition = postal.ChannelDefinition;
describe( "channel definition", function () {
describe( "When using global channel api", function () {
var gch;
describe( "With no channel name provided", function () {
describe( "Using string argument", function () {
before( function () {
gch = postal.channel( "SomeChannel" );
} );
after( function () {
gch = undefined;
} );
it( "channel should be of type ChannelDefinition", function () {
expect( gch instanceof ChannelDefinition ).to.be.ok();
} );
it( "should set channel name to SomeChannel", function () {
expect( gch.channel ).to.be( "SomeChannel" );
} );
} );
} );
} );
describe( "When initializing a channel definition", function () {
var chDef = new ChannelDefinition( "TestChannel" );
it( "should set channel to TestChannel", function () {
expect( chDef.channel ).to.be( "TestChannel" );
} );
} );
describe( "When calling subscribe", function () {
var ch = new ChannelDefinition( "TestChannel" ),
sub = ch.subscribe( "TestTopic", function () {} );
it( "subscription should be instance of SubscriptionDefinition", function () {
expect( sub instanceof postal.SubscriptionDefinition ).to.be.ok();
} );
} );
describe( "When publishing from a channel definition", function () {
var channel, subscription;
before( function () {
channel = postal.channel( "OhHai" );
} );
after( function () {
postal.utils.reset();
channel = undefined;
subscription = undefined;
} );
it( "Should allow a topic only to be used", function ( done ) {
subscription = channel.subscribe( "topic.only", function ( d, e ) {
expect( typeof d === "undefined" ).to.be( true );
expect( e.topic ).to.be( "topic.only" );
done();
} );
channel.publish( "topic.only" );
} );
it( "Should allow a topic and data argument to be used", function ( done ) {
subscription = channel.subscribe( "topic.and.data", function ( d, e ) {
expect( d ).to.be( "hai" );
expect( e.topic ).to.be( "topic.and.data" );
done();
} );
channel.publish( "topic.and.data", "hai" );
} );
it( "Should allow an envelope argument to be used", function ( done ) {
subscription = channel.subscribe( "envelope", function ( d, e ) {
expect( e.channel ).to.be( "OhHai" );
expect( e.data ).to.be( "hai" );
expect( e.foo ).to.be( "bar" );
done();
} );
channel.publish( { topic : "envelope", data : "hai", foo : "bar" } );
} );
} );
} );
}());

View file

@ -1,34 +1,25 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="../ext/mocha.css" />
<link rel="stylesheet" href="../bower/mocha/mocha.css" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="mocha"></div>
<script type="text/javascript" src="../ext/jquery-1.7.1.js"></script>
<script type="text/javascript" src="../ext/underscore.js"></script>
<script type="text/javascript" src="../ext/expect.js"></script>
<script type="text/javascript" src="../ext/mocha.js"></script>
<script type="text/javascript" src="../bower/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../bower/underscore/underscore-min.js"></script>
<script type="text/javascript" src="../bower/expect/expect.js"></script>
<script type="text/javascript" src="../bower/mocha/mocha.js"></script>
<script type="text/javascript">
mocha.setup({ ui: 'bdd', timeout: 60000 });
</script>
<script type="text/javascript" src="../ext/amplify.core.js"></script>
<script type="text/javascript" src="../ext/amplify.store.js"></script>
<script type="text/javascript" src="../ext/underscore.js"></script>
<script type="text/javascript" src="../src/DistinctPredicate.js"></script>
<script type="text/javascript" src="../src/ConsecutiveDistinctPredicate.js"></script>
<script type="text/javascript" src="../src/ChannelDefinition.js"></script>
<script type="text/javascript" src="../src/SubscriptionDefinition.js"></script>
<script type="text/javascript" src="../src/AmqpBindingsResolver.js"></script>
<script type="text/javascript" src="../src/LocalBus.js"></script>
<script type="text/javascript" src="../src/Api.js"></script>
<script type="text/javascript" src="DistinctPredicate.spec.js"></script>
<script type="text/javascript" src="ConsecutiveDistinctPredicate.spec.js"></script>
<script type="text/javascript" src="SubscriptionDefinition.spec.js"></script>
<script type="text/javascript" src="ChannelDefinition.spec.js"></script>
<script type="text/javascript" src="AmqpBindingsResolver.spec.js"></script>
<script type="text/javascript" src="Postal.spec.js"></script>
<script type="text/javascript" src="../lib/postal.js"></script>
<script type="text/javascript" src="postaljs.spec.js"></script>
<script type="text/javascript" src="utils.spec.js"></script>
<script type="text/javascript" src="linkedChannels.spec.js"></script>
<script type="text/javascript" src="bindingsResolver.spec.js"></script>
<script type="text/javascript" src="channelDefinition.spec.js"></script>
<script type="text/javascript" src="subscriptionDefinition.spec.js"></script>
<script type="text/javascript">
mocha.run();
</script>

View file

@ -1,122 +1,126 @@
/* global describe, postal, it, after, before, expect */
var subscription, linkages;
describe( "Linked Channels", function () {
describe( "When binding channel - one source to one destination", function () {
describe( "with only channel values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel" }, { channel : "destinationChannel" } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "Oh.Hai.There", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..." } );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "Oh.Hai.There" );
} );
} );
describe( "with channel and static topic values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel", topic : "Oh.Hai.There" }, { channel : "destinationChannel", topic : "kthxbye" } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "kthxbye", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "kthxbye" );
} );
} );
describe( "with channel and topic transform values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel" }, { channel : "destinationChannel", topic : function ( tpc ) {
return "NewTopic." + tpc;
} } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "NewTopic.Oh.Hai.There" );
} );
} );
} );
describe( "When binding channel - one source to multiple destinations", function () {
var destData = [],
destEnv = [],
callback = function ( data, env ) {
destData.push( data );
destEnv.push( env );
};
(function() {
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var subscription;
describe("linked channels", function () {
describe( "When binding channel - one source to one destination", function () {
describe( "with only channel values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel" }, { channel : "destinationChannel" } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "Oh.Hai.There", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..." } );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "Oh.Hai.There" );
} );
} );
describe( "with channel and static topic values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel", topic : "Oh.Hai.There" }, { channel : "destinationChannel", topic : "kthxbye" } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "kthxbye", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "kthxbye" );
} );
} );
describe( "with channel and topic transform values provided", function () {
var destData = [],
destEnv = [],
linkages;
before( function () {
linkages = postal.linkChannels( { channel : "sourceChannel" }, { channel : "destinationChannel", topic : function ( tpc ) {
return "NewTopic." + tpc;
} } );
subscription = postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There", callback : function ( data, env ) {
destData.push( data );
destEnv.push( env );
}} );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
linkages[0].unsubscribe();
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscription should only have been invoked once", function () {
expect( destData.length ).to.be( 1 );
expect( destEnv.length ).to.be( 1 );
} );
it( "linked subscription data should match expected results", function () {
expect( destData[0] ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
} );
it( "linked subscription envelope should match expected results", function () {
expect( destEnv[0].channel ).to.be( "destinationChannel" );
expect( destEnv[0].topic ).to.be( "NewTopic.Oh.Hai.There" );
} );
} );
} );
describe( "When binding channel - one source to multiple destinations", function () {
var destData = [],
destEnv = [],
callback = function ( data, env ) {
destData.push( data );
destEnv.push( env );
};
before( function () {
linkages = postal.linkChannels(
{ channel : "sourceChannel", topic : "Oh.Hai.There" },
[
{ channel : "destinationChannel", topic : "NewTopic.Oh.Hai" },
{ channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There" }
] );
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai", callback : callback} );
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There", callback : callback } );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscriptions should each have been called once", function () {
expect( destData.length ).to.be( 2 );
expect( destEnv.length ).to.be( 2 );
} );
} );
} );
before( function () {
linkages = postal.linkChannels(
{ channel : "sourceChannel", topic : "Oh.Hai.There" },
[
{ channel : "destinationChannel", topic : "NewTopic.Oh.Hai" },
{ channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There" }
] );
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai", callback : callback} );
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There", callback : callback } );
postal.publish( { channel : "sourceChannel", topic : "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "linked subscriptions should each have been called once", function () {
expect( destData.length ).to.be( 2 );
expect( destEnv.length ).to.be( 2 );
} );
} );
} );
}());

697
spec/postaljs.spec.js Normal file
View file

@ -0,0 +1,697 @@
/* global describe, postal, it, after, before, expect */
(function() {
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var _ = typeof window === "undefined" ? require("underscore") : window._;
var subscription;
var sub;
var channel;
var caughtSubscribeEvent = false;
var caughtUnsubscribeEvent = false;
describe("subscription creation", function(){
describe( "When creating basic subscription", function () {
var systemSubscription = {};
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.created",
callback : function ( data, envelope ) {
if ( data.event &&
data.event === "subscription.created" &&
data.channel === "MyChannel" &&
data.topic === "MyTopic" ) {
caughtSubscribeEvent = true;
}
}
} );
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () {} );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
} );
after( function () {
systemSubscription.unsubscribe();
postal.utils.reset();
} );
it( "should create a channel called MyChannel", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"] !== undefined ).to.be.ok();
} );
it( "should create a topic under MyChannel called MyTopic", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"]["MyTopic"] !== undefined ).to.be.ok();
} );
it( "should have set subscription channel value", function () {
expect( sub.channel ).to.be( "MyChannel" );
} );
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
it( "should have defaulted the subscription constraints array", function () {
expect( sub.constraints.length ).to.be( 0 );
} );
it( "should have defaulted the subscription context value", function () {
expect( sub.context ).to.be( null );
} );
it( "should have captured subscription creation event", function () {
expect( caughtSubscribeEvent ).to.be.ok();
} );
} );
describe( "When subscribing and ignoring duplicates", function () {
var subInvokedCnt = 0;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
subInvokedCnt++;
} )
.distinctUntilChanged();
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
subInvokedCnt = 0;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "subscription callback should be invoked once", function () {
expect( subInvokedCnt ).to.be( 1 );
} );
} );
describe( "When subscribing with a disposeAfter of 5", function () {
var msgReceivedCnt = 0, subExistsAfter, systemSubscription;
before( function () {
caughtUnsubscribeEvent = false;
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function () {
msgReceivedCnt++;
}).disposeAfter( 5 );
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.*",
callback : function ( data, env ) {
if ( data.event &&
data.event === "subscription.removed" &&
data.channel === "MyChannel" &&
data.topic === "MyTopic" ) {
caughtUnsubscribeEvent = true;
}
}
} );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
subExistsAfter = postal.configuration.bus.subscriptions.MyChannel.MyTopic.length !== 0;
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked 5 times", function () {
expect( msgReceivedCnt ).to.be( 5 );
} );
it( "subscription should not exist after unsubscribe", function () {
expect( subExistsAfter ).to.not.be.ok();
} );
it( "should have captured unsubscription creation event", function () {
expect( caughtUnsubscribeEvent ).to.be.ok();
} );
it( "postal.getSubscribersFor('MyChannel', 'MyTopic') should not return any subscriptions", function () {
expect( postal.utils.getSubscribersFor("MyChannel", "MyTopic").length ).to.be(0);
} );
} );
describe( "When subscribing with a hierarchical binding, no wildcards", function () {
var count = 0, channelB, channelC;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.MiddleTopic", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback only once", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with a hierarchical binding, using #", function () {
var count, channelB, channelC, channelD, channelE;
before( function () {
count = 0;
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.#.SubTopic", function ( data, env ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubMiddle.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 2 );
} );
} );
describe( "When subscribing with a hierarchical binding, using *", function () {
var count = 0, channelB, channelC, channelD;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.MiddleTopic.*", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with a hierarchical binding, using # and *", function () {
var count = 0, channelB, channelC, channelD, channelE;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic.#.*", function ( data ) {
count++;
} );
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
channel.publish( "OtherTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
} );
after( function () {
postal.utils.reset();
count = 0;
} );
it( "should have invoked subscription callback twice", function () {
expect( count ).to.be( 3 );
} );
} );
describe( "When subscribing with debounce", function () {
var results = [], debouncedChannel;
before( function () {
debouncedChannel = postal.channel( "DebounceChannel" );
subscription = debouncedChannel.subscribe( "MyTopic",
function ( data ) {
results.push( data );
} ).withDebounce( 800 );
} );
after( function () {
postal.utils.reset();
} );
it( "should have only invoked debounced callback once", function ( done ) {
debouncedChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 2 );
}, 20 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 3 );
}, 80 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 4 );
}, 250 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 5 );
}, 500 ); // should not invoke callback
setTimeout( function () {
debouncedChannel.publish( "MyTopic", 6 );
}, 1000 ); // should invoke callback
setTimeout( function () {
expect( results[0] ).to.be( 6 );
expect( results.length ).to.be( 1 );
done();
}, 1900 );
} );
} );
describe( "When subscribing with defer", function () {
var results = [];
before( function () {
channel = postal.channel( "DeferChannel" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have met expected results", function ( done ) {
subscription = channel.subscribe( "MyTopic",
function ( data ) {
results.push( "second" );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
done();
} ).defer();
channel.publish( "MyTopic", "Testing123" );
results.push( "first" );
} );
} );
describe( "When subscribing with delay", function () {
var results = [];
before( function () {
channel = postal.channel( "DelayChannel" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have met expected results", function ( done ) {
subscription = channel.subscribe( "MyTopic",
function ( data ) {
results.push( "second" );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
done();
} ).withDelay( 500 );
channel.publish( "MyTopic", "Testing123" );
results.push( "first" );
} );
} );
describe( "When subscribing with once()", function () {
var msgReceivedCnt = 0;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic",function ( data ) {
msgReceivedCnt++;
} ).once();
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked 1 time", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
} );
describe( "When subscribing with multiple constraints returning true", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraints( [function () {
return true;
},
function () {
return true;
},
function () {
return true;
}] );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
} );
it( "should have invoked the callback", function () {
expect( recvd ).to.be.ok();
} );
} );
describe( "When subscribing with multiple constraints and one returning false", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraints( [function () {
return true;
},
function () {
return false;
},
function () {
return true;
}] );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 3 );
} );
it( "should not have invoked the callback", function () {
expect( recvd ).to.not.be.ok()
} );
} );
describe( "When subscribing with one constraint returning false", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraint( function () {
return false;
} );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "should not have invoked the subscription callback", function () {
expect( recvd ).to.not.be.ok();
} );
} );
describe( "When subscribing with one constraint returning true", function () {
var recvd = false;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
recvd = true;
} )
.withConstraint( function () {
return true;
} );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
recvd = false;
} );
it( "should have a constraint on the subscription", function () {
expect( postal.configuration.bus.subscriptions.MyChannel.MyTopic[0].constraints.length ).to.be( 1 );
} );
it( "should have invoked the subscription callback", function () {
expect( recvd ).to.be.ok();
} );
} );
describe( "When subscribing with the context being set", function () {
var count = 0,
obj = {
increment : function () {
count++;
}
};
before( function () {
channel = postal.channel( "ContextChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
this.increment();
} )
.withContext( obj );
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "should have called obj.increment", function () {
expect( count ).to.be( 1 );
} );
} );
describe( "When subscribing with throttle", function () {
var results = [], throttledChannel;
before( function () {
throttledChannel = postal.channel( "ThrottleChannel" );
subscription = throttledChannel.subscribe( "MyTopic",
function ( data ) {
results.push( data );
} ).withThrottle( 500 );
} );
after( function () {
postal.utils.reset();
} );
it( "should have only invoked throttled callback twice", function ( done ) {
throttledChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
setTimeout( function () {
throttledChannel.publish( "MyTopic", 800 );
}, 800 ); // should invoke callback
for ( var i = 0; i < 20; i++ ) {
(function ( x ) {
throttledChannel.publish( "MyTopic", x );
})( i );
}
setTimeout( function () {
expect( results[0] ).to.be( 1 );
expect( results[2] ).to.be( 800 );
expect( results.length ).to.be( 3 );
done();
}, 1500 );
} );
} );
describe( "When using global subscribe api", function () {
before( function () {
subscription = postal.subscribe( {
channel : "MyChannel",
topic : "MyTopic",
callback : function () {
}
} );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
} );
after( function () {
postal.utils.reset();
} );
it( "subscription should be of type SubscriptionDefinition", function () {
expect( subscription instanceof postal.SubscriptionDefinition ).to.be.ok();
} );
it( "should create an channel called MyChannel", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"] !== undefined ).to.be.ok();
} );
it( "should create a topic under MyChannel called MyTopic", function () {
expect( postal.configuration.bus.subscriptions["MyChannel"]["MyTopic"] !== undefined ).to.be.ok();
} );
it( "should have set subscription channel value", function () {
expect( sub.channel ).to.be( "MyChannel" );
} );
it( "should have set subscription topic value", function () {
expect( sub.topic ).to.be( "MyTopic" );
} );
it( "should have defaulted the subscription constraints array", function () {
expect( sub.constraints.length ).to.be( 0 );
} );
it( "should have defaulted the subscription context value", function () {
expect( sub.context ).to.be( null );
} );
} );
});
describe("publishing", function() {
describe( "When publishing a message", function () {
var msgReceivedCnt = 0,
msgData;
before( function () {
channel = postal.channel( "MyChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
msgReceivedCnt++;
msgData = data;
} );
channel.publish( "MyTopic", "Testing123" );
subscription.unsubscribe();
channel.publish( "MyTopic", "Testing123" );
} );
after( function () {
postal.utils.reset();
} );
it( "subscription callback should be invoked once", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
it( "subscription callback should receive published data", function () {
expect( msgData ).to.be( "Testing123" );
} );
} );
describe( "When publishing on a channel where no subscribers exist", function () {
it( "should return expected results for MyChannel/MyTopic", function () {
var env = postal.publish( {
channel : "NoOneIsUsingThisOne",
topic : "This.Is.A.Lonely.Topic",
data : "Y U NO SUBSCRIBE TO ME?"
} );
expect( !_.isEmpty( env ) ).to.be( true );
} );
} );
describe( "When using global publish api", function () {
var msgReceivedCnt = 0,
msgData;
before( function () {
channel = postal.channel( "MyGlobalChannel" );
subscription = channel.subscribe( "MyTopic", function ( data ) {
msgReceivedCnt++;
msgData = data;
} );
postal.publish( { channel : "MyGlobalChannel", topic : "MyTopic", data : "Testing123" } );
subscription.unsubscribe();
postal.publish( { channel : "MyGlobalChannel", topic : "MyTopic", data : "Testing123" } );
} );
after( function () {
postal.utils.reset();
msgReceivedCnt = 0;
} );
it( "channel should be of type ChannelDefinition", function () {
expect( channel instanceof postal.ChannelDefinition ).to.be.ok();
} );
it( "subscription callback should be invoked once", function () {
expect( msgReceivedCnt ).to.be( 1 );
} );
it( "subscription callback should receive published data", function () {
expect( msgData ).to.be( "Testing123" );
} );
} );
});
describe("unsubscribing", function() {
describe( "With a single subscription", function () {
var subExistsBefore = false,
subExistsAfter = true;
var systemSubscription = {};
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.*",
callback : function ( data, env ) {
if ( data.event &&
data.event === "subscription.removed" &&
data.channel === "MyChannel" &&
data.topic === "MyTopic" ) {
caughtUnsubscribeEvent = true;
}
}
} );
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () { });
subExistsBefore = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0] !== undefined;
subscription.unsubscribe();
subExistsAfter = postal.configuration.bus.subscriptions.MyChannel.MyTopic.length !== 0;
} );
after( function () {
systemSubscription.unsubscribe();
postal.utils.reset();
} );
it( "subscription should exist before unsubscribe", function () {
expect( subExistsBefore ).to.be.ok();
} );
it( "subscription should not exist after unsubscribe", function () {
expect( subExistsAfter ).to.not.be.ok();
} );
it( "should have captured unsubscription creation event", function () {
expect( caughtUnsubscribeEvent ).to.be.ok();
} );
it( "postal.getSubscribersFor('MyChannel', 'MyTopic') should not return any subscriptions", function () {
expect( postal.utils.getSubscribersFor("MyChannel", "MyTopic").length ).to.be(0);
} );
} );
describe( "With multiple subscribers on one channel", function () {
var subscription1, subscription2, results = [];
before( function () {
channel = postal.channel();
subscription1 = channel.subscribe( 'test',function () {
results.push( '1 received message' );
} ).once();
subscription2 = channel.subscribe( 'test', function () {
results.push( '2 received message' );
} );
channel.publish( 'test' );
channel.publish( 'test' );
} );
after( function () {
subscription2.unsubscribe();
postal.utils.reset();
} );
it( "should produce expected messages", function () {
expect( results.length ).to.be( 3 );
expect( results[0] ).to.be( "1 received message" );
expect( results[1] ).to.be( "2 received message" );
expect( results[2] ).to.be( "2 received message" );
} );
} );
describe( "With nested publishing", function () {
var subscription1, subscription2, sysub, results = [];
before( function () {
channel = postal.channel();
sysub = postal.subscribe( {
channel : postal.configuration.SYSTEM_CHANNEL,
topic : "subscription.removed",
callback : function ( d, e ) {
results.push( "unsubscribed" );
}
} );
subscription1 = channel.subscribe( 'nest.test',function () {
results.push( '1 received message' );
channel.publish( "nest.test2", "Hai" );
} ).once();
subscription2 = channel.subscribe( 'nest.test2', function () {
results.push( '2 received message' );
} );
channel.publish( 'nest.test' );
channel.publish( 'nest.test' );
} );
after( function () {
//subscription2.unsubscribe();
sysub.unsubscribe();
postal.utils.reset();
} );
it( "should produce expected messages", function () {
expect( results.length ).to.be( 3 );
expect( results[0] ).to.be( "1 received message" );
expect( results[1] ).to.be( "2 received message" );
expect( results[2] ).to.be( "unsubscribed" );
} );
} );
});
describe("wiretaps", function() {
describe( "When subscribing and unsubscribing a wire tap", function () {
var wireTapData,
wireTapEnvelope,
wiretap;
before( function () {
caughtUnsubscribeEvent = false;
wireTapData = [];
wireTapEnvelope = [];
wiretap = postal.addWireTap( function ( msg, envelope ) {
wireTapData.push( msg );
wireTapEnvelope.push( envelope );
} );
postal.publish( { topic : "Oh.Hai.There", data : "I'm in yer bus, tappin' yer subscriptionz..."} );
wiretap();
postal.publish( { topic : "Oh.Hai.There", data : "I'm in yer bus, tappin' yer subscriptionz..."} );
} );
after( function () {
postal.utils.reset();
} );
it( "wire tap should have been invoked only once", function () {
expect( wireTapData.length ).to.be( 1 );
expect( wireTapEnvelope.length ).to.be( 1 );
} );
it( "wireTap data should match expected results", function () {
expect( wireTapData[0] ).to.be( "I'm in yer bus, tappin' yer subscriptionz..." );
} );
it( "wireTap envelope should match expected results", function () {
expect( wireTapEnvelope[0].channel ).to.be( postal.configuration.DEFAULT_CHANNEL );
expect( wireTapEnvelope[0].topic ).to.be( "Oh.Hai.There" );
} );
} );
});
}());

View file

@ -0,0 +1,282 @@
/* global describe, postal, it, after, before, expect */
(function() {
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var NO_OP = function () {};
var SubscriptionDefinition = postal.SubscriptionDefinition;
describe( "SubscriptionDefinition", function () {
describe( "When initializing SubscriptionDefinition", function () {
var sDef,
caughtSubscribeEvent,
systemSubscription;
before( function () {
systemSubscription = postal.subscribe( {
channel : "postal",
topic : "subscription.created",
callback : function ( data, envelope ) {
if ( data.event &&
data.event === "subscription.created" &&
data.channel === "SubDefTestChannel" &&
data.topic === "SubDefTestTopic" ) {
caughtSubscribeEvent = true;
}
}
} );
sDef = new SubscriptionDefinition( "SubDefTestChannel", "SubDefTestTopic", NO_OP );
} );
after( function () {
sDef.unsubscribe();
systemSubscription.unsubscribe();
caughtSubscribeEvent = false;
} );
it( "should set the channel to SubDefTestChannel", function () {
expect( sDef.channel ).to.be( "SubDefTestChannel" );
} );
it( "should set the topic to SubDefTestTopic", function () {
expect( sDef.topic ).to.be( "SubDefTestTopic" );
} );
it( "should set the callback", function () {
expect( sDef.callback ).to.be( NO_OP );
} );
it( "should default the constraints", function () {
expect( sDef.constraints.length ).to.be( 0 );
} );
it( "should default the context", function () {
expect( sDef.context ).to.be( null );
} );
it( "should fire the subscription.created message", function () {
expect( caughtSubscribeEvent ).to.be( true );
} );
} );
describe( "When setting distinctUntilChanged", function () {
var sDefa = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).distinctUntilChanged();
it( "Should add a DistinctPredicate constraint to the configuration constraints", function () {
expect( sDefa.constraints.length ).to.be( 1 );
} );
} );
describe( "When adding a constraint", function () {
var sDefb = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraint( function () {
} );
it( "Should add a constraint", function () {
expect( sDefb.constraints.length ).to.be( 1 );
} );
} );
describe( "When adding multiple constraints", function () {
var sDefc = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withConstraints( [function () {
}, function () {
}, function () {
}] );
it( "Should add a constraint", function () {
expect( sDefc.constraints.length ).to.be( 3 );
} );
} );
describe( "When setting the context", function () {
var obj = { name : "Rose" },
name,
sDefd = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP )
.withContext( obj )
.withConstraint( function ( d, e ) {
name = this.name;
return true;
} );
postal.publish( { channel : "TestChannel", topic : "TestTopic", data : "Oh, hai"} );
it( "Should set context", function () {
expect( sDefd.context ).to.be( obj );
} );
it( "Should apply context to predicate/constraint", function () {
expect( name ).to.be( "Rose" );
} );
} );
describe( "When calling subscribe to set the callback", function () {
var sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ),
fn = function () {
};
sDefe.subscribe( fn );
it( "Should set the callback", function () {
expect( sDefe.callback ).to.be( fn );
} );
} );
describe( "When deferring the callback", function () {
var results = [], sDefe;
it( "Should defer the callback", function ( done ) {
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
results.push( data );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
expect( env.topic ).to.be( "TestTopic" );
done();
} ).defer();
sDefe.callback( "second", { topic : "TestTopic" } );
results.push( "first" );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).defer();
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
it( "Should keep the context intact when modified later", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).defer().withContext(context);
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
describe( "When throttling the callback", function () {
it( "should have only invoked throttled callback twice", function ( done ) {
var results = [], timeout1, timeout2;
var sDefe = new SubscriptionDefinition( "ThrottleTest", "TestTopic", function ( data ) {
results.push( data );
} ).withThrottle( 1200, { leading: true } );
sDefe.callback( 1 ); // starts the clock on throttle
timeout1 = setTimeout( function () {
sDefe.callback( 700 );
}, 700 ); // should invoke callback
for ( var i = 0; i < 20; i++ ) {
(function ( x ) {
sDefe.callback( x );
})( i );
}
timeout2 = setTimeout( function () {
expect( results[0] ).to.be( 1 );
expect( results.length ).to.be( 2 );
done();
}, 1500 );
} );
it( "Should keep the context intact", function( done ) {
var results = [], timeout1, timeout2;
var sDefe = new SubscriptionDefinition( "ThrottleTest", "TestTopic", function ( data ) {
results.push( data );
} ).withThrottle( 1000, { leading: true } );
var context = {
key : 'abcd'
};
sDefe = new SubscriptionDefinition( "ThrottleTest", "TestTopic", function( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext( context ).withThrottle( 500 );
sDefe.callback.call( sDefe.context, 1 );
} );
} );
describe( "When delaying the callback", function () {
var results = [], sDefe;
it( "Should delay the callback", function ( done ) {
sDefe = new SubscriptionDefinition( "DelayTest", "TestTopic", function ( data, env ) {
results.push( data );
expect( results[0] ).to.be( "first" );
expect( results[1] ).to.be( "second" );
expect( env.topic ).to.be( "TestTopic" );
done();
} ).withDelay( 200 );
sDefe.callback( "second", { topic : "TestTopic" } );
results.push( "first" );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 1234
};
sDefe = new SubscriptionDefinition( "DelayTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).withDelay( 200 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
describe( "When debouncing the callback", function () {
var results = [],
sDefe = new SubscriptionDefinition( "DebounceTest", "TestTopic", function ( data ) {
results.push( data );
} ).withDebounce( 800 );
it( "should have only invoked debounced callback once", function ( done ) {
sDefe.callback( 1 ); // starts the two second clock on debounce
setTimeout( function () {
sDefe.callback( 2 );
}, 20 ); // should not invoke callback
setTimeout( function () {
sDefe.callback( 3 );
}, 80 ); // should not invoke callback
setTimeout( function () {
sDefe.callback( 6 );
}, 800 ); // should invoke callback
setTimeout( function () {
expect( results[0] ).to.be( 6 );
expect( results.length ).to.be( 1 );
done();
}, 1800 );
} );
it( "Should keep the context intact", function ( done ) {
var context = {
key : 5678
};
var timeout;
sDefe = new SubscriptionDefinition( "DebounceTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
clearTimeout(timeout);
done();
} ).withContext(context).withDebounce( 100 );
sDefe.callback.call( sDefe.context, 1 );
timeout = setTimeout( function () {
sDefe.callback.call( sDefe.context, 2 );
}, 200 ); // should invoke callback
});
} );
describe( "When self disposing", function () {
var context = {
key : 'abcd'
};
it( "Should be inactive", function () {
var sDefe = new SubscriptionDefinition( "DisposeTest", "TestTopic", function ( data, env ) {
} ).withContext(context).disposeAfter( 1 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
expect( sDefe.inactive ).to.be( true );
} );
it( "Should keep the context intact", function ( done ) {
var sDefe = new SubscriptionDefinition( "DisposeTest", "TestTopic", function ( data, env ) {
expect( this ).to.be( context );
done();
} ).withContext(context).disposeAfter( 200 );
sDefe.callback.call( sDefe.context, "stuff", { topic : "TestTopic" } );
} );
} );
} );
}());

66
spec/utils.spec.js Normal file
View file

@ -0,0 +1,66 @@
/* global describe, postal, it, after, before, expect */
(function() {
var postal = typeof window === "undefined" ? require("../lib/postal.js")() : window.postal;
var expect = typeof window === "undefined" ? require("expect.js") : window.expect;
var _ = typeof window === "undefined" ? require("underscore") : window._;
var NO_OP = function() {};
var subscription;
var sub;
describe("postal.utils", function() {
describe( "When calling postal.utils.getSubscribersFor", function () {
var subs = [], i;
before( function () {
i = 10;
var ch1 = postal.channel( "MyChannel" ),
ch2 = postal.channel( "MyChannel2" );
while ( i ) {
subs.push( ch1.subscribe( "MyTopic", NO_OP) );
subs.push( ch2.subscribe( "MyTopic2", NO_OP) );
i--;
}
} );
after( function () {
subs = [];
postal.utils.reset();
} );
it( "should return expected results for MyChannel/MyTopic", function () {
var results = postal.utils.getSubscribersFor( { channel : "MyChannel", topic : "MyTopic" } );
expect( results.length ).to.be( 10 );
} );
it( "should return expected results for MyChannel2/MyTopic2", function () {
var results = postal.utils.getSubscribersFor( { channel : "MyChannel2", topic : "MyTopic2" } );
expect( results.length ).to.be( 10 );
} );
} );
describe( "When calling postal.utils.reset", function () {
var resolver;
before( function () {
postal.utils.reset();
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () {});
postal.channel( "MyChannel" ).publish( "MyTopic", "Oh Hai!" );
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
resolver = postal.configuration.resolver.cache.MyTopic;
postal.utils.reset();
} );
after( function () {
} );
it( "should have created a subscription definition", function () {
expect( sub.channel ).to.be( "MyChannel" );
expect( sub.topic ).to.be( "MyTopic" );
expect( sub.constraints.length ).to.be( 0 );
expect( sub.context ).to.be( null );
} );
it( "should have created a resolver cache entry", function () {
expect( _.isEmpty( resolver ) ).to.not.be.ok()
expect( resolver.MyTopic ).to.be.ok();
} );
it( "subscriptions cache should now be empty", function () {
expect( _.isEmpty( postal.configuration.bus.subscriptions ) ).to.be.ok();
} );
it( "resolver cache should now be empty", function () {
expect( _.isEmpty( postal.configuration.resolver.cache ) ).to.be.ok();
} );
} );
});
}());

View file

@ -130,12 +130,13 @@ SubscriptionDefinition.prototype = {
return this;
},
withThrottle : function ( milliseconds ) {
withThrottle : function ( milliseconds, options ) {
options = options || { };
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle( fn, milliseconds );
this.callback = _.throttle( fn, milliseconds, options );
return this;
},