mirror of
https://github.com/Hopiu/postal.js.git
synced 2026-03-16 22:20:23 +00:00
Build now runs on gulp.js, updated how dependencies are managed
This commit is contained in:
parent
d25b1d0f48
commit
f4787f506b
55 changed files with 2424 additions and 40011 deletions
3
.bowerrc
Normal file
3
.bowerrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "bower"
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -13,3 +13,5 @@ desktop.ini
|
|||
.anvil
|
||||
*node_modules
|
||||
*npm-debug*
|
||||
report/
|
||||
bower/
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
.anvil/
|
||||
node_modules/
|
||||
ext/
|
||||
ext/
|
||||
report/
|
||||
|
|
@ -8,9 +8,9 @@ lib/amd
|
|||
diags
|
||||
.gitignore
|
||||
.idea
|
||||
build.json
|
||||
header.js
|
||||
.anvil
|
||||
component.json
|
||||
.npmignore
|
||||
classic-resolver*
|
||||
classic-resolver*
|
||||
bower*
|
||||
14
README.md
14
README.md
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
71
bower.json
71
bower.json
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
build.json
14
build.json
|
|
@ -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" ]
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
3320
example/amd/js/libs/jquery/jquery-min.js
vendored
3320
example/amd/js/libs/jquery/jquery-min.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} );
|
||||
|
|
@ -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()}}})
|
||||
|
|
@ -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;
|
||||
} ));
|
||||
7
example/amd/js/libs/postal/postal.min.js
vendored
7
example/amd/js/libs/postal/postal.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -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 )
|
||||
}
|
||||
}
|
||||
})();
|
||||
695
example/amd/js/libs/underscore/underscore-min.js
vendored
695
example/amd/js/libs/underscore/underscore-min.js
vendored
|
|
@ -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, "&" ).replace( /</g, "<" ).replace( />/g, ">" ).replace( /"/g, """ ).replace( /'/g, "'" ).replace( /\//g, "/" )
|
||||
};
|
||||
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 );
|
||||
|
|
@ -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 ( $ ) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
9262
example/standard/js/jquery-1.7.1.js
vendored
9262
example/standard/js/jquery-1.7.1.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -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, _ );
|
||||
|
|
@ -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,_)
|
||||
|
|
@ -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;
|
||||
} ));
|
||||
7
example/standard/js/postal.min.js
vendored
7
example/standard/js/postal.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
@ -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 ) );
|
||||
|
|
@ -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 || {} ) );
|
||||
|
|
@ -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' );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
9
ext/bootstrap-responsive.min.css
vendored
9
ext/bootstrap-responsive.min.css
vendored
File diff suppressed because one or more lines are too long
9
ext/bootstrap.min.css
vendored
9
ext/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
6
ext/bootstrap.min.js
vendored
6
ext/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
1284
ext/expect.js
1284
ext/expect.js
File diff suppressed because it is too large
Load diff
9266
ext/jquery-1.7.1.js
vendored
9266
ext/jquery-1.7.1.js
vendored
File diff suppressed because it is too large
Load diff
3320
ext/jquery.js
vendored
3320
ext/jquery.js
vendored
File diff suppressed because it is too large
Load diff
259
ext/mocha.css
259
ext/mocha.css
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
5471
ext/mocha.js
5471
ext/mocha.js
File diff suppressed because it is too large
Load diff
723
ext/moment.js
723
ext/moment.js
|
|
@ -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 );
|
||||
|
|
@ -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
70
gulpfile.js
Normal 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" );
|
||||
});
|
||||
11
index.html
11
index.html
|
|
@ -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
904
lib/postal.js
Executable file → Normal 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
13
lib/postal.min.js
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
121
package.json
121
package.json
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
@ -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" } );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
@ -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 );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
@ -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" } );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
475
spec/bindingsResolver.spec.js
Normal file
475
spec/bindingsResolver.spec.js
Normal 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();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
}());
|
||||
76
spec/channelDefinition.spec.js
Normal file
76
spec/channelDefinition.spec.js
Normal 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" } );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
}());
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
697
spec/postaljs.spec.js
Normal 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" );
|
||||
} );
|
||||
} );
|
||||
});
|
||||
}());
|
||||
282
spec/subscriptionDefinition.spec.js
Normal file
282
spec/subscriptionDefinition.spec.js
Normal 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
66
spec/utils.spec.js
Normal 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();
|
||||
} );
|
||||
} );
|
||||
});
|
||||
}());
|
||||
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue