mirror of
https://github.com/Hopiu/postal.js.git
synced 2026-03-16 22:20:23 +00:00
Removed a *lot* of code. Postal's diet is in full force...
This commit is contained in:
parent
8530716096
commit
3acbb142e6
95 changed files with 448 additions and 19226 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "postal.js",
|
||||
"version": "0.7.3",
|
||||
"version": "0.8.0",
|
||||
"main": ["lib/standard/postal.min.js", "lib/standard/postal.js"],
|
||||
"dependencies": {
|
||||
"underscore": "~1.3.0"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
define( ['postal', 'postaldiags'], function ( postal, diags ) {
|
||||
|
||||
// The world's simplest subscription
|
||||
var channel = postal.channel( "Name.Changed" );
|
||||
// This gets you a handle to the default postal channel...
|
||||
var channel = postal.channel();
|
||||
// subscribe
|
||||
var subscription = channel.subscribe( function ( data ) {
|
||||
var subscription = channel.subscribe( "Name.Changed", function ( data ) {
|
||||
$( "#example1" ).html( "Name: " + data.name );
|
||||
} );
|
||||
// And someone publishes a first name change:
|
||||
channel.publish( { name : "Dr. Who" } );
|
||||
channel.publish( "Name.Changed", { name : "Dr. Who" } );
|
||||
subscription.unsubscribe();
|
||||
|
||||
|
||||
|
|
@ -15,14 +15,11 @@ define( ['postal', 'postaldiags'], function ( postal, diags ) {
|
|||
// The # symbol represents "one word" in a topic (i.e - the text between two periods of a topic).
|
||||
// By subscribing to "#.Changed", the binding will match
|
||||
// Name.Changed & Location.Changed but *not* for Changed.Companion
|
||||
var hashChannel = postal.channel( "#.Changed" ),
|
||||
chgSubscription = hashChannel.subscribe( function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example2" );
|
||||
} );
|
||||
postal.channel( "Name.Changed" )
|
||||
.publish( { type : "Name", value : "John Smith" } );
|
||||
postal.channel( "Location.Changed" )
|
||||
.publish( { type : "Location", value : "Early 20th Century England" } );
|
||||
var chgSubscription = channel.subscribe( "#.Changed", function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example2" );
|
||||
} );
|
||||
channel.publish( "Name.Changed", { type : "Name", value : "John Smith" } );
|
||||
channel.publish( "Location.Changed", { type : "Location", value : "Early 20th Century England" } );
|
||||
chgSubscription.unsubscribe();
|
||||
|
||||
|
||||
|
|
@ -30,120 +27,85 @@ define( ['postal', 'postaldiags'], function ( postal, diags ) {
|
|||
// The * symbol represents any number of characters/words in a topic string.
|
||||
// By subscribing to "DrWho.*.Changed", the binding will match
|
||||
// DrWho.NinthDoctor.Companion.Changed & DrWho.Location.Changed but *not* Changed
|
||||
var starChannel = postal.channel( "DrWho.*.Changed" ),
|
||||
starSubscription = starChannel.subscribe( function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example3" );
|
||||
} );
|
||||
postal.channel( "DrWho.NinthDoctor.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Rose" } );
|
||||
postal.channel( "DrWho.TenthDoctor.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Martha" } );
|
||||
postal.channel( "DrWho.Eleventh.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Amy" } );
|
||||
postal.channel( "DrWho.Location.Changed" )
|
||||
.publish( { type : "Location", value : "The Library" } );
|
||||
postal.channel( "TheMaster.DrumBeat.Changed" )
|
||||
.publish( { type : "DrumBeat", value : "This won't trigger any subscriptions" } );
|
||||
postal.channel( "Changed" )
|
||||
.publish( { type : "Useless", value : "This won't trigger any subscriptions either" } );
|
||||
var starSubscription = channel.subscribe( "DrWho.*.Changed", function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example3" );
|
||||
} );
|
||||
channel.publish( "DrWho.NinthDoctor.Companion.Changed", { type : "Companion Name", value : "Rose" } );
|
||||
channel.publish( "DrWho.TenthDoctor.Companion.Changed", { type : "Companion Name", value : "Martha" } );
|
||||
channel.publish( "DrWho.Eleventh.Companion.Changed", { type : "Companion Name", value : "Amy" } );
|
||||
channel.publish( "DrWho.Location.Changed", { type : "Location", value : "The Library" } );
|
||||
channel.publish( "TheMaster.DrumBeat.Changed", { type : "DrumBeat", value : "This won't trigger any subscriptions" } );
|
||||
channel.publish( "Changed", { type : "Useless", value : "This won't trigger any subscriptions either" } );
|
||||
starSubscription.unsubscribe();
|
||||
|
||||
// Applying distinctUntilChanged to a subscription
|
||||
var dupChannel = postal.channel( "WeepingAngel.*" ),
|
||||
dupSubscription = dupChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
|
||||
} ).distinctUntilChanged();
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontEvenBlink" )
|
||||
.publish( { value : "Don't Even Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Close Your Eyes" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
var dupSubscription = channel.subscribe( "WeepingAngel.*",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
|
||||
} ).distinctUntilChanged();
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontEvenBlink", { value : "Don't Even Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Close Your Eyes" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
dupSubscription.unsubscribe();
|
||||
|
||||
// Using disposeAfter(X) to remove subscription automagically after X number of receives
|
||||
var daChannel = postal.channel( "Donna.Noble.*" ),
|
||||
daSubscription = daChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example5" );
|
||||
} ).disposeAfter( 2 );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
var daSubscription = channel.subscribe( "Donna.Noble.*",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example5" );
|
||||
} ).disposeAfter( 2 );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
daSubscription.unsubscribe();
|
||||
|
||||
// Using withConstraint to apply a predicate to the subscription
|
||||
var drIsInTheTardis = false,
|
||||
wcChannel = postal.channel( "Tardis.Depart" ),
|
||||
wcSubscription = wcChannel.subscribe(
|
||||
wcSubscription = channel.subscribe( "Tardis.Depart",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example6" );
|
||||
} ).withConstraint( function () {
|
||||
return drIsInTheTardis;
|
||||
} );
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
drIsInTheTardis = true;
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
wcSubscription.unsubscribe();
|
||||
|
||||
// Using withContext to set the "this" context
|
||||
var ctxChannel = postal.channel( "Dalek.Meet.CyberMen" ),
|
||||
ctxSubscription = ctxChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( this );
|
||||
} ).withContext( $( "#example7" ) );
|
||||
postal.channel( "Dalek.Meet.CyberMen" )
|
||||
.publish( { value : "Exterminate!" } );
|
||||
postal.channel( "Dalek.Meet.CyberMen" )
|
||||
.publish( { value : "Delete!" } );
|
||||
var ctxSubscription = channel.subscribe( "Dalek.Meet.CyberMen",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( this );
|
||||
} ).withContext( $( "#example7" ) );
|
||||
channel.publish( "Dalek.Meet.CyberMen", { value : "Exterminate!" } );
|
||||
channel.publish( "Dalek.Meet.CyberMen", { value : "Delete!" } );
|
||||
ctxSubscription.unsubscribe();
|
||||
|
||||
// Using withDelay() to delay the subscription evaluation
|
||||
var wdChannel = postal.channel( "He.Will.Knock.Four.Times" ),
|
||||
wdSubscription = wdChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( $( "#example8" ) );
|
||||
} ).withDelay( 5000 );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
var wdSubscription = channel.subscribe( "He.Will.Knock.Four.Times",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( $( "#example8" ) );
|
||||
} ).withDelay( 5000 );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
wdSubscription.unsubscribe();
|
||||
|
||||
// Using distinct() to ignore duplicate messages
|
||||
var revealChannel = postal.channel('detect.cylon'),
|
||||
revealSubscription = revealChannel.subscribe(function (who) {
|
||||
$('<li></li>').text(who.name).appendTo($('#example9'));
|
||||
}).distinct();
|
||||
postal.channel('detect.cylon').publish({name: 'Boomer'});
|
||||
postal.channel('detect.cylon').publish({name: 'Saul Tigh'});
|
||||
postal.channel('detect.cylon').publish({name: 'William Adama'});
|
||||
postal.channel('detect.cylon').publish({name: 'Helo'});
|
||||
//ignored!
|
||||
postal.channel('detect.cylon').publish({name: 'Boomer'});
|
||||
postal.channel('detect.cylon').publish({name: 'Felix Gaeta'});
|
||||
//ignored!
|
||||
postal.channel('detect.cylon').publish({name: 'William Adama'});
|
||||
var revealSubscription = channel.subscribe( 'detect.cylon',function ( who ) {
|
||||
$( '<li></li>' ).text( who.name ).appendTo( $( '#example9' ) );
|
||||
} ).distinct();
|
||||
channel.publish( 'detect.cylon', {name : 'Boomer'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Saul Tigh'} );
|
||||
channel.publish( 'detect.cylon', {name : 'William Adama'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Helo'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Boomer'} ); //ignored!
|
||||
channel.publish( 'detect.cylon', {name : 'Felix Gaeta'} );
|
||||
channel.publish( 'detect.cylon', {name : 'William Adama'} );//ignored!
|
||||
} );
|
||||
|
|
@ -54,44 +54,22 @@
|
|||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName, defaultTopic ) {
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
publish : function () {
|
||||
var envelope = arguments.length === 1 ? arguments[0] : { topic: arguments[0], data: arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -302,6 +280,7 @@
|
|||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
|
@ -371,55 +350,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
var sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
|
@ -434,29 +365,22 @@
|
|||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
|
|
|
|||
2
example/amd/js/libs/postal/postal.min.js
vendored
2
example/amd/js/libs/postal/postal.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,9 +1,9 @@
|
|||
require.config( {
|
||||
paths : {
|
||||
underscore : 'libs/underscore/underscore-min',
|
||||
postal : 'libs/postal/postal',
|
||||
underscore : 'libs/underscore/underscore-min',
|
||||
postal : 'libs/postal/postal',
|
||||
postaldiags : 'libs/postal/postal.diagnostics',
|
||||
jquery : 'libs/jquery/jquery-min'
|
||||
jquery : 'libs/jquery/jquery-min'
|
||||
}
|
||||
} );
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
# postal.js sample app with browser and node.js components
|
||||
|
||||
## Explanation
|
||||
|
||||
As much as I want to convince you that this sample app was written with you in mind, I'd be lying if I said so. The browser examples in this repo are currently "bare bones", with no real usage examples beyond calling the API in browser js and then calling it a day. I wanted to demonstrate node.js usage of postal as well, and that gave birth to this example project.
|
||||
|
||||
## How to run
|
||||
|
||||
### Install depedencies first!
|
||||
Open a terminal/console to the example/node directory and run `npm install`.
|
||||
|
||||
### Now, to run...
|
||||
Open a terminal/console to the example/node directory and run `npm start`. Then open a browser to http://localhost:8002. Once you enter a search term in the client UI, it will trigger the server side twitter search module to start searching, etc.
|
||||
|
||||
## The Good, the Bad & the Caveats
|
||||
|
||||
###The Good:
|
||||
|
||||
* You'll see postal.js used in node.js and in the browser (#winning?)
|
||||
* This is way beyond a hello world example
|
||||
* The browser and node.js postal instances are being *bridged* by a websocket connection (geek brownie points, FTW)
|
||||
* I demonstrate a general usage pattern (message endpoint) I've fallen into with the node.js side of things, which I describe as "Local message bus at boundaries, events internal."
|
||||
* Note that the node/messaging/ folder contains "bus-adapter.js" and "collector-adapter.js"
|
||||
* These modules exist to adapt other bus-agnostics modules to postal.js
|
||||
* The "bus agnostic" modules (for ex. - anything in the "collectors" directory) can work just fine without a message bus in place, but any subscribers would have to have a direct reference to the module, causing the application to be more tightly coupled.
|
||||
* The adapters wrap the standard "events" that a module can produce and act as a bridge to then publish those events onto the message bus. This enables other clients to subscribe to said events without needing to know anything explicit about the event producer beyond the structure of the message (the message *is* the contract).
|
||||
|
||||
###The Bad:
|
||||
|
||||
* I have (had?) high hopes to refactor this into better organized code, but alas, I have a wife, two kids, friends, other OSS projects and about 200 books on my amazon wish list. So, while the code is readable, it's more verbose than it should be.
|
||||
* *IF* I get to refactor anything, I would target things like:
|
||||
* Less verbose app-level FSM on the node.js side
|
||||
* Tie postal.js into the Backbone.Sync process so that models could transparently get updates from the server w/o having to know about subscriptions for updates
|
||||
* More comments - especially around the bus-adapter/collector adpaters in the node.js code
|
||||
|
||||
###The Caveats:
|
||||
|
||||
* The web socket bridge in use in this project (postal.socket) is *highly experimental*! A real version is in the works, but this project served as a test bed for ideas I had floating around in my brain.
|
||||
* The actual functionality of this example app is truly silly and arbitrary:
|
||||
* clients connect and the first one to enter a search term owns the search until they disconnect or give control to another client
|
||||
* search terms are used against twitter's "REST-ish" API (not the stream)
|
||||
* as the server-side search agent gets tweet results, it publishes them local to node.js for the stat collectors to aggregate and produce stats
|
||||
* stat collectors publish messages to which browser clients have subscribed (browser clients have a "socket" channel type thanks to the experimental postal.socket plugin that subscribes not only locally, but pushes the subscription to the other end of the socket on the server side, causing messages on the server for that susbcription to be pushed over the socket, back to the client.)
|
||||
* If a client that doesn't own the search submits a search request, the owner can go to the "Search Requests" link on the top navbar and click on the term they want to allow. This gives control to the requester.
|
||||
* Beyond simple playing in the browser and repl, I've not performed any major testing of this app - good grief, man, it's a sample app I've written while mostly half asleep :-)
|
||||
|
||||
More real-world sample applications using postal will be included in this repository as I build ones I'm at liberty to share.
|
||||
|
||||
Thanks a ton for reading this if you made it this far. *You* are exactly why I write this stuff!
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
body {
|
||||
font-family: Tahoma, Arial;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
width: 285px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.images {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
border: .5pt solid black;
|
||||
}
|
||||
|
||||
.scrollableDiv {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 570px;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
float: left;
|
||||
margin: 0px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.nav-menu li {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.search {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.search input[type='text'] {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#stats {
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Twitter Hash Tag Stats Demo</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script data-main="js/main" src="js/lib/require-jquery.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'backbone',
|
||||
'bus',
|
||||
'infrastructure/router',
|
||||
'infrastructure/view-manager',
|
||||
'views/container',
|
||||
'views/menu',
|
||||
'views/tweet-count',
|
||||
'views/mention-count',
|
||||
'views/mentioner-count',
|
||||
'views/hash-tag-count',
|
||||
'views/profanity-percentage',
|
||||
'views/search-requests',
|
||||
'views/wiretap',
|
||||
], function ( $, Backbone, bus, Router, ViewManager, ContainerView, MenuView, TweetCountView,
|
||||
MentionCountView, MentionerCountView, HashTagCountView, ProfanityPercentage,
|
||||
SearchRequestView, WiretapView ) {
|
||||
var app = {
|
||||
bus : bus,
|
||||
router : new Router()
|
||||
};
|
||||
|
||||
// Set up UI concerns
|
||||
app.viewManager = new ViewManager();
|
||||
app.bus.viewManager.subscribe( "ui.show", function ( data, env ) {
|
||||
app.viewManager.UI[ data.name ].activate( data.context );
|
||||
} );
|
||||
|
||||
app.viewManager.registerViews( [
|
||||
{ name : "container", ctor : ContainerView },
|
||||
{ name : "menu", ctor : MenuView },
|
||||
{ name : "tweetCount", ctor : TweetCountView },
|
||||
{ name : "mentionCount", ctor : MentionCountView },
|
||||
{ name : "mentionerCount", ctor : MentionerCountView },
|
||||
{ name : "hashTagCount", ctor : HashTagCountView },
|
||||
{ name : "profanityPercentage", ctor : ProfanityPercentage },
|
||||
{ name : "searchRequests", ctor : SearchRequestView },
|
||||
{ name : "wiretap", ctor : WiretapView }
|
||||
] );
|
||||
app.viewManager.defineUIs( [
|
||||
{ name : "homeUI", dependencies : [ "container", "menu", "tweetCount", "mentionCount", "mentionerCount", "hashTagCount", "profanityPercentage" ] },
|
||||
{ name : "searchRequestUI", dependencies : [ "container", "menu", "searchRequests" ] },
|
||||
{ name : "wireTapLogUI", dependencies : [ "container", "menu", "wiretap" ] }
|
||||
] );
|
||||
|
||||
$( function () {
|
||||
Backbone.history.start( {
|
||||
pushState : true,
|
||||
root : $( "base" ).attr( "href" )
|
||||
} );
|
||||
} );
|
||||
|
||||
return app;
|
||||
} );
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
define( [
|
||||
'postal'
|
||||
], function ( postal ) {
|
||||
return {
|
||||
router : postal.channel( "router", "*" ),
|
||||
viewManager : postal.channel( "viewmanager", "*" ),
|
||||
data : postal.channel( "data", "*" ),
|
||||
app : postal.channel( "statsApp", "*", { type : "websocket" } ),
|
||||
stats : postal.channel( "stats", "*", { type : "websocket" } )
|
||||
}
|
||||
} );
|
||||
|
|
@ -1,298 +0,0 @@
|
|||
/*
|
||||
postal.socket
|
||||
Author: Jim Cowart
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.1.0
|
||||
*/
|
||||
|
||||
(function ( root, doc, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "underscore", "machina", "postal" ], function ( _, machina, postal ) {
|
||||
return factory( _, machina, postal, root, doc );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root._, root.machina, root.postal, root, doc );
|
||||
}
|
||||
}( this, document, function ( _, machina, postal, global, document, undefined ) {
|
||||
|
||||
/*
|
||||
adding a socket namespace to postal
|
||||
which provides the following members:
|
||||
1.) config - provides values used to manage the socket connection
|
||||
2.) goOffline() - tells the manager to close the connection intentionally
|
||||
3.) goOnline() - tells the manager to try to connect.
|
||||
4.) manifest - an array of objects describing the subscriptions that have been
|
||||
set up on the remote end.
|
||||
5.) publish() - takes a valid postal envelope and pushes it through the socket
|
||||
to be published to the server instance of postal.
|
||||
6.) socketMgr - the FSM managing the socket connection
|
||||
7.) socketNamespace - exposed currently for debugging only
|
||||
8.) subscribe() - passes an object through the socket to the server
|
||||
which contains data necessary to set up a remote subscription. The
|
||||
options passed to the socket would look similar to this:
|
||||
{
|
||||
"channel":"SomeChannel",
|
||||
"topic":"my.topic",
|
||||
"correlationId":"2073383865318591267"
|
||||
}
|
||||
The "correlationId" is used on the remote side to apply a constraint to
|
||||
the subscription, enabling just this specific client to be targeted on
|
||||
and otherwise public channel.
|
||||
9.) unsubscribe() - passes an object through the socket to the server
|
||||
which contains data necessary to remove a remote subscription. The options
|
||||
passed would look similar to the example above in #8.
|
||||
*/
|
||||
postal.connections = postal.connections || {};
|
||||
|
||||
var postalSocket = postal.connections.socket = (function () {
|
||||
var socketNamespace,
|
||||
fsm = new machina.Fsm( {
|
||||
retryFn : undefined,
|
||||
|
||||
session : undefined,
|
||||
|
||||
wireUpSocketEvents : function () {
|
||||
var self = this;
|
||||
_.each( [ "connect", "connecting", "connect_failed", "disconnect", "reconnect", "reconnect_failed",
|
||||
"reconnecting", "postal.socket.remote", "postal.socket.identified", "postal.socket.migration" ],
|
||||
function ( evnt ) {
|
||||
socketNamespace.on( evnt, function ( data ) {
|
||||
self.handle( evnt, data );
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
states : {
|
||||
uninitialized : {
|
||||
tryConnect : function () {
|
||||
this.transition( "initializing" );
|
||||
}
|
||||
},
|
||||
initializing : {
|
||||
_onEnter : function () {
|
||||
socketNamespace = io.connect( postalSocket.config.url, { "auto connect" : false } );
|
||||
this.wireUpSocketEvents();
|
||||
this.transition( "probing" )
|
||||
},
|
||||
socketTransmit : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
}
|
||||
},
|
||||
probing : {
|
||||
_onEnter : function () {
|
||||
clearTimeout( this.retryFn );
|
||||
if ( !socketNamespace.socket.connecting && !socketNamespace.socket.reconnecting ) {
|
||||
socketNamespace.socket.connect();
|
||||
}
|
||||
else {
|
||||
this.transition( "settingSessionInfo" );
|
||||
}
|
||||
},
|
||||
connect : function () {
|
||||
this.transition( "settingSessionInfo" );
|
||||
},
|
||||
connect_failed : function () {
|
||||
this.transition( "disconnected" );
|
||||
},
|
||||
maxAttempts : function () {
|
||||
this.transition( "offline" );
|
||||
},
|
||||
"postal.socket.remote" : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
},
|
||||
reconnect : function () {
|
||||
this.transition( "settingSessionInfo" );
|
||||
},
|
||||
reconnect_failed : function () {
|
||||
this.transition( "disconnected" );
|
||||
},
|
||||
socketTransmit : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
}
|
||||
},
|
||||
settingSessionInfo : {
|
||||
_onEnter : function () {
|
||||
var self = this;
|
||||
postal.utils.setSessionId( socketNamespace.socket.sessionid, function ( session ) {
|
||||
self.session = session;
|
||||
self.transition( "identifying" );
|
||||
} );
|
||||
}
|
||||
},
|
||||
identifying : {
|
||||
_onEnter : function () {
|
||||
var self = this;
|
||||
self.retryFn = setTimeout( function () {
|
||||
self.handle( "timeout.identifying" );
|
||||
}, postalSocket.config.reconnectInterval );
|
||||
self.handle( "client.identifier" );
|
||||
},
|
||||
"client.identifier" : function () {
|
||||
clearTimeout( this.retryFn );
|
||||
socketNamespace.emit( "postal.clientId", { id : this.session.id, lastId : this.session.lastId } );
|
||||
},
|
||||
connect_failed : function () {
|
||||
this.transition( "disconnected" );
|
||||
},
|
||||
disconnect : function () {
|
||||
this.transition( "probing" );
|
||||
},
|
||||
"postal.socket.identified" : function ( data ) {
|
||||
this.transition( "online" );
|
||||
},
|
||||
"postal.socket.migration" : function () {
|
||||
_.each( postal.connections.socket.manifest, function ( sub ) {
|
||||
fsm.handle( "socketTransmit", "postal.subscribe", sub );
|
||||
} );
|
||||
socketNamespace.emit( "postal.migrationComplete", {} );
|
||||
},
|
||||
"postal.socket.remote" : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
},
|
||||
reconnect_failed : function () {
|
||||
this.transition( "disconnected" );
|
||||
},
|
||||
socketTransmit : function ( evntName, envelope ) {
|
||||
if ( evntName === "postal.subscribe" ) {
|
||||
// we risk mutating the message here, so extend
|
||||
// and add the correlationId to the extended copy
|
||||
var socketEnv = _.extend( {}, envelope );
|
||||
socketEnv.correlationId = this.session.id;
|
||||
socketNamespace.emit( evntName, socketEnv );
|
||||
}
|
||||
else {
|
||||
this.deferUntilTransition( "online" );
|
||||
}
|
||||
},
|
||||
"timeout.identifying" : function () {
|
||||
this.transition( "probing" );
|
||||
}
|
||||
},
|
||||
online : {
|
||||
disconnect : function () {
|
||||
this.transition( "probing" );
|
||||
},
|
||||
goOffline : function () {
|
||||
this.transition( "offline" );
|
||||
},
|
||||
"postal.socket.remote" : function ( envelope ) {
|
||||
postal.publish( envelope );
|
||||
},
|
||||
socketTransmit : function ( evntName, envelope ) {
|
||||
// we risk mutating the message here, so extend
|
||||
// and add the correlationId to the extended copy
|
||||
var socketEnv = _.extend( {}, envelope );
|
||||
socketEnv.correlationId = this.session.id;
|
||||
socketNamespace.emit( evntName, socketEnv );
|
||||
}
|
||||
},
|
||||
offline : {
|
||||
_onEnter : function () {
|
||||
socketNamespace.socket.disconnect();
|
||||
},
|
||||
socketTransmit : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
},
|
||||
"tryConnect" : function () {
|
||||
this.transition( "probing" );
|
||||
}
|
||||
},
|
||||
disconnected : {
|
||||
_onEnter : function () {
|
||||
var self = this;
|
||||
self.retryFn = setTimeout( function () {
|
||||
self.transition( "probing" );
|
||||
}, postalSocket.config.reconnectInterval );
|
||||
},
|
||||
connecting : function () {
|
||||
this.transition( "probing" );
|
||||
},
|
||||
reconnecting : function () {
|
||||
this.transition( "probing" );
|
||||
},
|
||||
socketTransmit : function () {
|
||||
this.deferUntilTransition( "online" );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
postal.subscribe( {
|
||||
channel : "postal",
|
||||
topic : "sessionId.changed",
|
||||
callback : function () {
|
||||
fsm.handle( "postal.session.changed" );
|
||||
}
|
||||
} );
|
||||
return {
|
||||
config : {
|
||||
url : window.location.origin,
|
||||
reconnectInterval : 4000
|
||||
},
|
||||
goOffline : function () {
|
||||
fsm.handle( "goOffline" );
|
||||
},
|
||||
goOnline : function () {
|
||||
fsm.handle( "tryConnect" );
|
||||
},
|
||||
manifest : [],
|
||||
publish : function ( envelope ) {
|
||||
fsm.handle( "socketTransmit", "postal.publish", envelope );
|
||||
},
|
||||
subscribe : function ( options ) {
|
||||
options.channel = options.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
options.topic = options.topic || "*";
|
||||
if ( !_.any( this.manifest, function ( item ) {
|
||||
return item.channel === options.channel && item.topic === options.topic;
|
||||
} ) ) {
|
||||
this.manifest.push( options );
|
||||
fsm.handle( "socketTransmit", "postal.subscribe", options );
|
||||
}
|
||||
},
|
||||
socketMgr : fsm,
|
||||
socketNamespace : socketNamespace,
|
||||
unsubscribe : function ( options ) {
|
||||
options.channel = options.channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
options.topic = options.topic || "*";
|
||||
if ( !postal.getSubscribersFor( options.channel, options.topic ).length ) {
|
||||
fsm.handle( "socketTransmit", "postal.unsubscribe", options );
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
postal.connections.socket.goOnline();
|
||||
var SocketChannel = postal.channelTypes.websocket = function ( channelName, defaultTopic ) {
|
||||
var channel = postal.channel( channelName, defaultTopic ),
|
||||
localSubscribe = channel.subscribe,
|
||||
localPublish = channel.publish,
|
||||
localTopic = channel.topic;
|
||||
|
||||
channel.publish = function () {
|
||||
postalSocket.publish( localPublish.apply( channel, arguments ) );
|
||||
};
|
||||
|
||||
channel.subscribe = function () {
|
||||
var sub = localSubscribe.apply( channel, arguments ),
|
||||
origUnsubscribe;
|
||||
origUnsubscribe = sub.unsubscribe;
|
||||
sub.unsubscribe = function () {
|
||||
origUnsubscribe.call( sub );
|
||||
postalSocket.unsubscribe( { channel : sub.channel, topic : sub.topic } );
|
||||
};
|
||||
postalSocket.subscribe( { channel : sub.channel, topic : sub.topic } );
|
||||
return sub;
|
||||
};
|
||||
|
||||
channel.topic = function ( topic ) {
|
||||
if ( topic === channel._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new SocketChannel( this.channel, topic );
|
||||
};
|
||||
|
||||
return channel;
|
||||
};
|
||||
|
||||
} ));
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'backbone',
|
||||
'bus'
|
||||
], function ( $, Backbone, bus ) {
|
||||
|
||||
return Backbone.Router.extend( {
|
||||
routes : {
|
||||
"" : "home",
|
||||
"requests" : "requests",
|
||||
"wiretap" : "wiretap",
|
||||
"*anything" : "redirect"
|
||||
},
|
||||
|
||||
initialize : function () {
|
||||
var self = this;
|
||||
_.bindAll( self );
|
||||
|
||||
$( document ).delegate( "a.ps-nav", "click", function ( e ) {
|
||||
e.preventDefault();
|
||||
self.navigate( $( this ).attr( 'href' ), { trigger : true } );
|
||||
} );
|
||||
bus.router.publish( "initialized" );
|
||||
|
||||
},
|
||||
|
||||
activateUI : function ( uiName, context ) {
|
||||
bus.viewManager.publish( {
|
||||
topic : "ui.show",
|
||||
data : {
|
||||
name : uiName,
|
||||
context : context
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
home : function () {
|
||||
this.activateUI( "homeUI" );
|
||||
},
|
||||
|
||||
requests : function () {
|
||||
this.activateUI( "searchRequestUI" );
|
||||
},
|
||||
|
||||
wiretap : function () {
|
||||
this.activateUI( "wireTapLogUI" );
|
||||
},
|
||||
|
||||
redirect : function () {
|
||||
this.navigate( "/", { trigger : true } );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
define( [
|
||||
'underscore',
|
||||
'bus'
|
||||
], function ( _, bus ) {
|
||||
var ViewManager = function () {
|
||||
this.views = {}; // holds the views that are registered with the manager
|
||||
this.UI = {}; // holds the UI configurations that are defined
|
||||
this.priorContext = undefined; // holds the name of the last UI configuration
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// registerView - registers a view with the manager, under the name provided.
|
||||
// The handle to the constructor function (second arg) is used to create an
|
||||
// instance of the view the first time getInstance is called. The rendered
|
||||
// and visible booleans are used by the ViewManager to track state of the view.
|
||||
// The getInstance call can take an options object. If options.forceNew = true,
|
||||
// then the ViewManager will create a new instance of the view. If options.args
|
||||
// exists, it will be passed into the constructor function of the view.
|
||||
//----------------------------------------------------------------------------
|
||||
ViewManager.prototype.registerView = function ( name, viewCtor ) {
|
||||
this.views[name] = {
|
||||
rendered : false,
|
||||
visible : false,
|
||||
getInstance : (function () {
|
||||
var _instance;
|
||||
return function ( options ) {
|
||||
var _options = options || {};
|
||||
if ( !_instance || _options.forceNew ) {
|
||||
_instance = new viewCtor( _options.args || {} );
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
})()
|
||||
}
|
||||
};
|
||||
|
||||
ViewManager.prototype.registerViews = function ( views ) {
|
||||
_.each( views, function ( view ) {
|
||||
this.registerView( view.name, view.ctor );
|
||||
}, this );
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// defineUI - defines a UI configuration, which is effectively a named group
|
||||
// of views that need to be stood up, in order. The first argument is the UI
|
||||
// name, second arg is the array of view names (in the order they need to be
|
||||
// instantiated/rendered/shown)
|
||||
//----------------------------------------------------------------------------
|
||||
ViewManager.prototype.defineUI = function ( name, dependencies, options ) {
|
||||
var self = this;
|
||||
self.UI[ name ] = {
|
||||
options : options || {},
|
||||
dependencies : dependencies,
|
||||
activate : function ( data ) {
|
||||
data = data || {};
|
||||
data.priorContext = self.priorContext;
|
||||
data.targetContext = name;
|
||||
|
||||
if ( !this.options.noHide ) {
|
||||
// hide anything visible that's not in the dependencies for this UI configuration
|
||||
var shouldHide = _.reduce( self.views, function ( memo, val, key ) {
|
||||
if ( val.visible && !_.include( this.dependencies, key ) ) {
|
||||
memo.push( key );
|
||||
}
|
||||
return memo;
|
||||
}, [], this );
|
||||
|
||||
_.each( shouldHide, function ( viewName ) {
|
||||
var instance = self.views[ viewName ].getInstance();
|
||||
if ( instance.hide ) {
|
||||
instance.hide();
|
||||
}
|
||||
self.views[ viewName ].visible = false;
|
||||
} );
|
||||
}
|
||||
|
||||
// set up, render & show the dependencies for this UI configuration
|
||||
_.each( this.dependencies, function ( viewName ) {
|
||||
var instance = self.views[viewName].getInstance( data );
|
||||
if ( !self.views[viewName].rendered ) {
|
||||
instance.render( data );
|
||||
self.views[viewName].rendered = true;
|
||||
}
|
||||
if ( !self.views[viewName].visible ) {
|
||||
if ( instance.show ) {
|
||||
instance.show( data );
|
||||
}
|
||||
self.views[viewName].visible = true;
|
||||
}
|
||||
|
||||
if ( instance.update ) {
|
||||
instance.update( data );
|
||||
}
|
||||
} );
|
||||
self.priorContext = name;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ViewManager.prototype.defineUIs = function ( uis ) {
|
||||
_.each( uis, function ( ui ) {
|
||||
this.defineUI( ui.name, ui.dependencies, ui.options );
|
||||
}, this );
|
||||
};
|
||||
|
||||
ViewManager.prototype.addViewToUI = function ( uiName, viewName, viewCtor ) {
|
||||
var uis = _.isArray( uiName ) ? uiName : [ uiName ];
|
||||
|
||||
if ( !this.views[ viewName ] ) {
|
||||
this.registerView( viewName, viewCtor );
|
||||
}
|
||||
|
||||
_.each( uis, function ( ui ) {
|
||||
if ( this.UI[ ui ] ) {
|
||||
this.UI[ ui ].dependencies.push( viewName );
|
||||
}
|
||||
}, this );
|
||||
};
|
||||
|
||||
return ViewManager;
|
||||
} );
|
||||
|
|
@ -1,360 +0,0 @@
|
|||
/*!
|
||||
* AmplifyJS 1.0.0 - Core, Store, Request
|
||||
*
|
||||
* 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 ( a, b, c ) {
|
||||
typeof define == "function" && define.amd ? define( ["jquery"], function ( d ) {
|
||||
return c( d, a, b )
|
||||
} ) : c( a.jQuery, a, b )
|
||||
})( this, document, function ( a, b, c, d ) {
|
||||
return function ( a, b ) {
|
||||
var c = [].slice, d = {}, e = a.amplify = {publish : function ( a ) {
|
||||
var b = c.call( arguments, 1 ), e, f, g, h = 0, i;
|
||||
if ( !d[a] ) {
|
||||
return!0;
|
||||
}
|
||||
e = d[a].slice();
|
||||
for ( g = e.length; h < g; h++ ) {
|
||||
f = e[h], i = f.callback.apply( f.context, b );
|
||||
if ( i === !1 ) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i !== !1
|
||||
}, subscribe : function ( a, b, c, e ) {
|
||||
arguments.length === 3 && typeof c == "number" && (e = c, c = b, b = null), arguments.length === 2 && (c = b, b = null), e = e || 10;
|
||||
var f = 0, g = a.split( /\s/ ), h = g.length, i;
|
||||
for ( ; f < h; f++ ) {
|
||||
a = g[f], i = !1, d[a] || (d[a] = []);
|
||||
var j = d[a].length - 1, k = {callback : c, context : b, priority : e};
|
||||
for ( ; j >= 0; j-- ) {
|
||||
if ( d[a][j].priority <= e ) {
|
||||
d[a].splice( j + 1, 0, k ), i = !0;
|
||||
break
|
||||
}
|
||||
}
|
||||
i || d[a].unshift( k )
|
||||
}
|
||||
return c
|
||||
}, unsubscribe : function ( a, b ) {
|
||||
if ( !d[a] ) {
|
||||
return;
|
||||
}
|
||||
var c = d[a].length, e = 0;
|
||||
for ( ; e < c; e++ ) {
|
||||
if ( d[a][e].callback === b ) {
|
||||
d[a].splice( e, 1 );
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
}( this ), function ( a, b ) {
|
||||
function f( a, c ) {
|
||||
d.addType( a, function ( f, g, h ) {
|
||||
var i, j, k, l, m = g, n = (new Date).getTime();
|
||||
if ( !f ) {
|
||||
m = {}, l = [], k = 0;
|
||||
try {
|
||||
f = c.length;
|
||||
while ( f = c.key( k++ ) ) {
|
||||
e.test( f ) && (j = JSON.parse( c.getItem( f ) ), j.expires && j.expires <= n ? l.push( f ) : m[f.replace( e, "" )] = j.data);
|
||||
}
|
||||
while ( f = l.pop() ) {
|
||||
c.removeItem( f )
|
||||
}
|
||||
} catch ( o ) {
|
||||
}
|
||||
return m
|
||||
}
|
||||
f = "__amplify__" + f;
|
||||
if ( g === b ) {
|
||||
i = c.getItem( f ), j = i ? JSON.parse( i ) : {expires : -1};
|
||||
if ( !(j.expires && j.expires <= n) ) {
|
||||
return j.data;
|
||||
}
|
||||
c.removeItem( f )
|
||||
} else if ( g === null ) {
|
||||
c.removeItem( f );
|
||||
} else {
|
||||
j = JSON.stringify( {data : g, expires : h.expires ? n + h.expires : null} );
|
||||
try {
|
||||
c.setItem( f, j )
|
||||
} catch ( o ) {
|
||||
d[a]();
|
||||
try {
|
||||
c.setItem( f, j )
|
||||
} catch ( o ) {
|
||||
throw d.error()
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
} )
|
||||
}
|
||||
|
||||
var d = a.store = function ( a, b, c, e ) {
|
||||
var e = d.type;
|
||||
return c && c.type && c.type in d.types && (e = c.type), d.types[e]( a, b, c || {} )
|
||||
};
|
||||
d.types = {}, d.type = null, d.addType = function ( a, b ) {
|
||||
d.type || (d.type = a), d.types[a] = b, d[a] = function ( b, c, e ) {
|
||||
return e = e || {}, e.type = a, d( b, c, e )
|
||||
}
|
||||
}, d.error = function () {
|
||||
return"amplify.store quota exceeded"
|
||||
};
|
||||
var e = /^__amplify__/;
|
||||
for ( var g in{localStorage : 1, sessionStorage : 1} ) {
|
||||
try {
|
||||
window[g].getItem && f( g, window[g] )
|
||||
} catch ( h ) {
|
||||
}
|
||||
}
|
||||
if ( !d.types.localStorage && window.globalStorage ) {
|
||||
try {
|
||||
f( "globalStorage", window.globalStorage[window.location.hostname] ), d.type === "sessionStorage" && (d.type = "globalStorage")
|
||||
} catch ( h ) {
|
||||
}
|
||||
}
|
||||
(function () {
|
||||
if ( d.types.localStorage ) {
|
||||
return;
|
||||
}
|
||||
var a = c.createElement( "div" ), e = "amplify";
|
||||
a.style.display = "none", c.getElementsByTagName( "head" )[0].appendChild( a );
|
||||
try {
|
||||
a.addBehavior( "#default#userdata" ), a.load( e )
|
||||
} catch ( f ) {
|
||||
a.parentNode.removeChild( a );
|
||||
return
|
||||
}
|
||||
d.addType( "userData", function ( c, f, g ) {
|
||||
a.load( e );
|
||||
var h, i, j, k, l, m = f, n = (new Date).getTime();
|
||||
if ( !c ) {
|
||||
m = {}, l = [], k = 0;
|
||||
while ( h = a.XMLDocument.documentElement.attributes[k++] ) {
|
||||
i = JSON.parse( h.value ), i.expires && i.expires <= n ? l.push( h.name ) : m[h.name] = i.data;
|
||||
}
|
||||
while ( c = l.pop() ) {
|
||||
a.removeAttribute( c );
|
||||
}
|
||||
return a.save( e ), m
|
||||
}
|
||||
c = c.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" ), c = c.replace( /^-/, "_-" );
|
||||
if ( f === b ) {
|
||||
h = a.getAttribute( c ), i = h ? JSON.parse( h ) : {expires : -1};
|
||||
if ( !(i.expires && i.expires <= n) ) {
|
||||
return i.data;
|
||||
}
|
||||
a.removeAttribute( c )
|
||||
} else {
|
||||
f === null ? a.removeAttribute( c ) : (j = a.getAttribute( c ), i = JSON.stringify( {data : f, expires : g.expires ? n + g.expires : null} ), a.setAttribute( c, i ));
|
||||
}
|
||||
try {
|
||||
a.save( e )
|
||||
} catch ( o ) {
|
||||
j === null ? a.removeAttribute( c ) : a.setAttribute( c, j ), d.userData();
|
||||
try {
|
||||
a.setAttribute( c, i ), a.save( e )
|
||||
} catch ( o ) {
|
||||
throw j === null ? a.removeAttribute( c ) : a.setAttribute( c, j ), d.error()
|
||||
}
|
||||
}
|
||||
return m
|
||||
} )
|
||||
})(), function () {
|
||||
function e( a ) {
|
||||
return a === b ? b : JSON.parse( JSON.stringify( a ) )
|
||||
}
|
||||
|
||||
var a = {}, c = {};
|
||||
d.addType( "memory", function ( d, f, g ) {
|
||||
return d ? f === b ? e( a[d] ) : (c[d] && (clearTimeout( c[d] ), delete c[d]), f === null ? (delete a[d], null) : (a[d] = f, g.expires && (c[d] = setTimeout( function () {
|
||||
delete a[d], delete c[d]
|
||||
}, g.expires )), f)) : e( a )
|
||||
} )
|
||||
}()
|
||||
}( this.amplify = this.amplify || {} ), function ( a, b ) {
|
||||
function c() {
|
||||
}
|
||||
|
||||
function d( a ) {
|
||||
return{}.toString.call( a ) === "[object Function]"
|
||||
}
|
||||
|
||||
function e( a ) {
|
||||
var b = !1;
|
||||
return setTimeout( function () {
|
||||
b = !0
|
||||
}, 1 ), function () {
|
||||
var c = this, d = arguments;
|
||||
b ? a.apply( c, d ) : setTimeout( function () {
|
||||
a.apply( c, d )
|
||||
}, 1 )
|
||||
}
|
||||
}
|
||||
|
||||
a.request = function ( b, f, g ) {
|
||||
var h = b || {};
|
||||
typeof h == "string" && (d( f ) && (g = f, f = {}), h = {resourceId : b, data : f || {}, success : g});
|
||||
var i = {abort : c}, j = a.request.resources[h.resourceId], k = h.success || c, l = h.error || c;
|
||||
h.success = e( function ( b, c ) {
|
||||
c = c || "success", a.publish( "request.success", h, b, c ), a.publish( "request.complete", h, b, c ), k( b, c )
|
||||
} ), h.error = e( function ( b, c ) {
|
||||
c = c || "error", a.publish( "request.error", h, b, c ), a.publish( "request.complete", h, b, c ), l( b, c )
|
||||
} );
|
||||
if ( !j ) {
|
||||
throw h.resourceId ? "amplify.request: unknown resourceId: " + h.resourceId : "amplify.request: no resourceId provided";
|
||||
}
|
||||
if ( !a.publish( "request.before", h ) ) {
|
||||
h.error( null, "abort" );
|
||||
return
|
||||
}
|
||||
return a.request.resources[h.resourceId]( h, i ), i
|
||||
}, a.request.types = {}, a.request.resources = {}, a.request.define = function ( b, c, d ) {
|
||||
if ( typeof c == "string" ) {
|
||||
if ( !(c in a.request.types) ) {
|
||||
throw"amplify.request.define: unknown type: " + c;
|
||||
}
|
||||
d.resourceId = b, a.request.resources[b] = a.request.types[c]( d )
|
||||
} else {
|
||||
a.request.resources[b] = c
|
||||
}
|
||||
}
|
||||
}( amplify ), function ( a, b, c ) {
|
||||
var d = ["status", "statusText", "responseText", "responseXML", "readyState"], e = /\{([^\}]+)\}/g;
|
||||
a.request.types.ajax = function ( e ) {
|
||||
return e = b.extend( {type : "GET"}, e ), function ( f, g ) {
|
||||
function n( a, e ) {
|
||||
b.each( d, function ( a, b ) {
|
||||
try {
|
||||
m[b] = h[b]
|
||||
} catch ( c ) {
|
||||
}
|
||||
} ), /OK$/.test( m.statusText ) && (m.statusText = "success"), a === c && (a = null), l && (e = "abort"), /timeout|error|abort/.test( e ) ? m.error( a, e ) : m.success( a, e ), n = b.noop
|
||||
}
|
||||
|
||||
var h, i = e.url, j = g.abort, k = b.extend( !0, {}, e, {data : f.data} ), l = !1, m = {readyState : 0, setRequestHeader : function ( a, b ) {
|
||||
return h.setRequestHeader( a, b )
|
||||
}, getAllResponseHeaders : function () {
|
||||
return h.getAllResponseHeaders()
|
||||
}, getResponseHeader : function ( a ) {
|
||||
return h.getResponseHeader( a )
|
||||
}, overrideMimeType : function ( a ) {
|
||||
return h.overrideMideType( a )
|
||||
}, abort : function () {
|
||||
l = !0;
|
||||
try {
|
||||
h.abort()
|
||||
} catch ( a ) {
|
||||
}
|
||||
n( null, "abort" )
|
||||
}, success : function ( a, b ) {
|
||||
f.success( a, b )
|
||||
}, error : function ( a, b ) {
|
||||
f.error( a, b )
|
||||
}};
|
||||
a.publish( "request.ajax.preprocess", e, f, k, m ), b.extend( k, {success : function ( a, b ) {
|
||||
n( a, b )
|
||||
}, error : function ( a, b ) {
|
||||
n( null, b )
|
||||
}, beforeSend : function ( b, c ) {
|
||||
h = b, k = c;
|
||||
var d = e.beforeSend ? e.beforeSend.call( this, m, k ) : !0;
|
||||
return d && a.publish( "request.before.ajax", e, f, k, m )
|
||||
}} ), b.ajax( k ), g.abort = function () {
|
||||
m.abort(), j.call( this )
|
||||
}
|
||||
}
|
||||
}, a.subscribe( "request.ajax.preprocess", function ( a, c, d ) {
|
||||
var f = [], g = d.data;
|
||||
if ( typeof g == "string" ) {
|
||||
return;
|
||||
}
|
||||
g = b.extend( !0, {}, a.data, g ), d.url = d.url.replace( e, function ( a, b ) {
|
||||
if ( b in g ) {
|
||||
return f.push( b ), g[b]
|
||||
}
|
||||
} ), b.each( f, function ( a, b ) {
|
||||
delete g[b]
|
||||
} ), d.data = g
|
||||
} ), a.subscribe( "request.ajax.preprocess", function ( a, c, d ) {
|
||||
var e = d.data, f = a.dataMap;
|
||||
if ( !f || typeof e == "string" ) {
|
||||
return;
|
||||
}
|
||||
b.isFunction( f ) ? d.data = f( e ) : (b.each( a.dataMap, function ( a, b ) {
|
||||
a in e && (e[b] = e[a], delete e[a])
|
||||
} ), d.data = e)
|
||||
} );
|
||||
var f = a.request.cache = {_key : function ( a, b, c ) {
|
||||
function g() {
|
||||
return c.charCodeAt( e++ ) << 24 | c.charCodeAt( e++ ) << 16 | c.charCodeAt( e++ ) << 8 | c.charCodeAt( e++ ) << 0
|
||||
}
|
||||
|
||||
c = b + c;
|
||||
var d = c.length, e = 0, f = g();
|
||||
while ( e < d ) {
|
||||
f ^= g();
|
||||
}
|
||||
return"request-" + a + "-" + f
|
||||
}, _default : function () {
|
||||
var a = {};
|
||||
return function ( b, c, d, e ) {
|
||||
var g = f._key( c.resourceId, d.url, d.data ), h = b.cache;
|
||||
if ( g in a ) {
|
||||
return e.success( a[g] ), !1;
|
||||
}
|
||||
var i = e.success;
|
||||
e.success = function ( b ) {
|
||||
a[g] = b, typeof h == "number" && setTimeout( function () {
|
||||
delete a[g]
|
||||
}, h ), i.apply( this, arguments )
|
||||
}
|
||||
}
|
||||
}()};
|
||||
a.store && (b.each( a.store.types, function ( b ) {
|
||||
f[b] = function ( c, d, e, g ) {
|
||||
var h = f._key( d.resourceId, e.url, e.data ), i = a.store[b]( h );
|
||||
if ( i ) {
|
||||
return e.success( i ), !1;
|
||||
}
|
||||
var j = g.success;
|
||||
g.success = function ( d ) {
|
||||
a.store[b]( h, d, {expires : c.cache.expires} ), j.apply( this, arguments )
|
||||
}
|
||||
}
|
||||
} ), f.persist = f[a.store.type]), a.subscribe( "request.before.ajax", function ( a ) {
|
||||
var b = a.cache;
|
||||
if ( b ) {
|
||||
return b = b.type || b, f[b in f ? b : "_default"].apply( this, arguments )
|
||||
}
|
||||
} ), a.request.decoders = {jsend : function ( a, b, c, d, e ) {
|
||||
a.status === "success" ? d( a.data ) : a.status === "fail" ? e( a.data, "fail" ) : a.status === "error" && (delete a.status, e( a, "error" ))
|
||||
}}, a.subscribe( "request.before.ajax", function ( c, d, e, f ) {
|
||||
function j( a, b ) {
|
||||
g( a, b )
|
||||
}
|
||||
|
||||
function k( a, b ) {
|
||||
h( a, b )
|
||||
}
|
||||
|
||||
var g = f.success, h = f.error, i = b.isFunction( c.decoder ) ? c.decoder : c.decoder in a.request.decoders ? a.request.decoders[c.decoder] : a.request.decoders._default;
|
||||
if ( !i ) {
|
||||
return;
|
||||
}
|
||||
f.success = function ( a, b ) {
|
||||
i( a, b, f, j, k )
|
||||
}, f.error = function ( a, b ) {
|
||||
i( a, b, f, j, k )
|
||||
}
|
||||
} )
|
||||
}( amplify, jQuery ), amplify
|
||||
} )
|
||||
|
|
@ -1,710 +0,0 @@
|
|||
// Backbone.js 0.9.2
|
||||
|
||||
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://backbonejs.org
|
||||
(function ( h, g ) {
|
||||
typeof exports !== "undefined" ? g( h, exports, require( "underscore" ) ) : typeof define === "function" && define.amd ? define( ["underscore", "jquery", "exports"], function ( f, i, p ) {
|
||||
h.Backbone = g( h, p, f, i )
|
||||
} ) : h.Backbone = g( h, {}, h._, h.jQuery || h.Zepto || h.ender )
|
||||
})( this, function ( h, g, f, i ) {
|
||||
var p = h.Backbone, y = Array.prototype.slice, z = Array.prototype.splice;
|
||||
g.VERSION = "0.9.2";
|
||||
g.setDomLibrary = function ( a ) {
|
||||
i = a
|
||||
};
|
||||
g.noConflict = function () {
|
||||
h.Backbone = p;
|
||||
return g
|
||||
};
|
||||
g.emulateHTTP = false;
|
||||
g.emulateJSON = false;
|
||||
var q = /\s+/, l = g.Events =
|
||||
{on : function ( a, b, c ) {
|
||||
var d, e, f, g, j;
|
||||
if ( !b ) {
|
||||
return this;
|
||||
}
|
||||
a = a.split( q );
|
||||
for ( d = this._callbacks || (this._callbacks = {}); e = a.shift(); ) {
|
||||
f = (j = d[e]) ? j.tail : {}, f.next = g = {}, f.context = c, f.callback = b, d[e] = {tail : g, next : j ? j.next : f};
|
||||
}
|
||||
return this
|
||||
}, off : function ( a, b, c ) {
|
||||
var d, e, k, g, j, h;
|
||||
if ( e = this._callbacks ) {
|
||||
if ( !a && !b && !c ) {
|
||||
return delete this._callbacks, this;
|
||||
}
|
||||
for ( a = a ? a.split( q ) : f.keys( e ); d = a.shift(); ) {
|
||||
if ( k = e[d], delete e[d], k && (b || c) ) {
|
||||
for ( g = k.tail; (k = k.next) !== g; ) {
|
||||
if ( j = k.callback, h = k.context, b && j !== b || c && h !== c ) {
|
||||
this.on( d, j, h );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
},
|
||||
trigger : function ( a ) {
|
||||
var b, c, d, e, f, g;
|
||||
if ( !(d = this._callbacks) ) {
|
||||
return this;
|
||||
}
|
||||
f = d.all;
|
||||
a = a.split( q );
|
||||
for ( g = y.call( arguments, 1 ); b = a.shift(); ) {
|
||||
if ( c = d[b] ) {
|
||||
for ( e = c.tail; (c = c.next) !== e; ) {
|
||||
c.callback.apply( c.context || this, g );
|
||||
}
|
||||
}
|
||||
if ( c = f ) {
|
||||
e = c.tail;
|
||||
for ( b = [b].concat( g ); (c = c.next) !== e; ) {
|
||||
c.callback.apply( c.context || this, b )
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}};
|
||||
l.bind = l.on;
|
||||
l.unbind = l.off;
|
||||
var o = g.Model = function ( a, b ) {
|
||||
var c;
|
||||
a || (a = {});
|
||||
b && b.parse && (a = this.parse( a ));
|
||||
if ( c = n( this, "defaults" ) ) {
|
||||
a = f.extend( {}, c, a );
|
||||
}
|
||||
if ( b && b.collection ) {
|
||||
this.collection = b.collection;
|
||||
}
|
||||
this.attributes = {};
|
||||
this._escapedAttributes = {};
|
||||
this.cid = f.uniqueId( "c" );
|
||||
this.changed = {};
|
||||
this._silent = {};
|
||||
this._pending = {};
|
||||
this.set( a, {silent : true} );
|
||||
this.changed = {};
|
||||
this._silent = {};
|
||||
this._pending = {};
|
||||
this._previousAttributes = f.clone( this.attributes );
|
||||
this.initialize.apply( this, arguments )
|
||||
};
|
||||
f.extend( o.prototype, l, {changed : null, _silent : null, _pending : null, idAttribute : "id", initialize : function () {
|
||||
}, toJSON : function () {
|
||||
return f.clone( this.attributes )
|
||||
}, get : function ( a ) {
|
||||
return this.attributes[a]
|
||||
}, escape : function ( a ) {
|
||||
var b;
|
||||
if ( b = this._escapedAttributes[a] ) {
|
||||
return b;
|
||||
}
|
||||
b = this.get( a );
|
||||
return this._escapedAttributes[a] = f.escape( b == null ? "" : "" + b )
|
||||
}, has : function ( a ) {
|
||||
return this.get( a ) != null
|
||||
}, set : function ( a, b, c ) {
|
||||
var d, e;
|
||||
f.isObject( a ) || a == null ? (d = a, c = b) : (d = {}, d[a] = b);
|
||||
c || (c = {});
|
||||
if ( !d ) {
|
||||
return this;
|
||||
}
|
||||
if ( d instanceof o ) {
|
||||
d = d.attributes;
|
||||
}
|
||||
if ( c.unset ) {
|
||||
for ( e in d ) {
|
||||
d[e] = void 0;
|
||||
}
|
||||
}
|
||||
if ( !this._validate( d, c ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( this.idAttribute in d ) {
|
||||
this.id = d[this.idAttribute];
|
||||
}
|
||||
var b = c.changes = {}, g = this.attributes, h = this._escapedAttributes, j = this._previousAttributes ||
|
||||
{};
|
||||
for ( e in d ) {
|
||||
a = d[e];
|
||||
if ( !f.isEqual( g[e], a ) || c.unset && f.has( g, e ) ) {
|
||||
delete h[e], (c.silent ? this._silent : b)[e] = true;
|
||||
}
|
||||
c.unset ? delete g[e] : g[e] = a;
|
||||
!f.isEqual( j[e], a ) || f.has( g, e ) != f.has( j, e ) ? (this.changed[e] = a, c.silent || (this._pending[e] = true)) : (delete this.changed[e], delete this._pending[e])
|
||||
}
|
||||
c.silent || this.change( c );
|
||||
return this
|
||||
}, unset : function ( a, b ) {
|
||||
(b || (b = {})).unset = true;
|
||||
return this.set( a, null, b )
|
||||
}, clear : function ( a ) {
|
||||
(a || (a = {})).unset = true;
|
||||
return this.set( f.clone( this.attributes ), a )
|
||||
}, fetch : function ( a ) {
|
||||
var a =
|
||||
a ? f.clone( a ) : {}, b = this, c = a.success;
|
||||
a.success = function ( d, e, f ) {
|
||||
if ( !b.set( b.parse( d, f ), a ) ) {
|
||||
return false;
|
||||
}
|
||||
c && c( b, d )
|
||||
};
|
||||
a.error = g.wrapError( a.error, b, a );
|
||||
return(this.sync || g.sync).call( this, "read", this, a )
|
||||
}, save : function ( a, b, c ) {
|
||||
var d, e;
|
||||
f.isObject( a ) || a == null ? (d = a, c = b) : (d = {}, d[a] = b);
|
||||
c = c ? f.clone( c ) : {};
|
||||
if ( c.wait ) {
|
||||
if ( !this._validate( d, c ) ) {
|
||||
return false;
|
||||
}
|
||||
e = f.clone( this.attributes )
|
||||
}
|
||||
a = f.extend( {}, c, {silent : true} );
|
||||
if ( d && !this.set( d, c.wait ? a : c ) ) {
|
||||
return false;
|
||||
}
|
||||
var k = this, h = c.success;
|
||||
c.success = function ( a, b, e ) {
|
||||
b = k.parse( a, e );
|
||||
c.wait && (delete c.wait, b = f.extend( d || {}, b ));
|
||||
if ( !k.set( b, c ) ) {
|
||||
return false;
|
||||
}
|
||||
h ? h( k, a ) : k.trigger( "sync", k, a, c )
|
||||
};
|
||||
c.error = g.wrapError( c.error, k, c );
|
||||
b = this.isNew() ? "create" : "update";
|
||||
b = (this.sync || g.sync).call( this, b, this, c );
|
||||
c.wait && this.set( e, a );
|
||||
return b
|
||||
}, destroy : function ( a ) {
|
||||
var a = a ? f.clone( a ) : {}, b = this, c = a.success, d = function () {
|
||||
b.trigger( "destroy", b, b.collection, a )
|
||||
};
|
||||
if ( this.isNew() ) {
|
||||
return d(), false;
|
||||
}
|
||||
a.success = function ( e ) {
|
||||
a.wait && d();
|
||||
c ? c( b, e ) : b.trigger( "sync", b, e, a )
|
||||
};
|
||||
a.error = g.wrapError( a.error, b, a );
|
||||
var e = (this.sync ||
|
||||
g.sync).call( this, "delete", this, a );
|
||||
a.wait || d();
|
||||
return e
|
||||
}, url : function () {
|
||||
var a = n( this, "urlRoot" ) || n( this.collection, "url" ) || t();
|
||||
return this.isNew() ? a : a + (a.charAt( a.length - 1 ) == "/" ? "" : "/") + encodeURIComponent( this.id )
|
||||
}, parse : function ( a ) {
|
||||
return a
|
||||
}, clone : function () {
|
||||
return new this.constructor( this.attributes )
|
||||
}, isNew : function () {
|
||||
return this.id == null
|
||||
}, change : function ( a ) {
|
||||
a || (a = {});
|
||||
var b = this._changing;
|
||||
this._changing = true;
|
||||
for ( var c in this._silent ) {
|
||||
this._pending[c] = true;
|
||||
}
|
||||
var d = f.extend( {}, a.changes, this._silent );
|
||||
this._silent = {};
|
||||
for ( c in d ) {
|
||||
this.trigger( "change:" + c, this, this.get( c ), a );
|
||||
}
|
||||
if ( b ) {
|
||||
return this;
|
||||
}
|
||||
for ( ; !f.isEmpty( this._pending ); ) {
|
||||
this._pending = {};
|
||||
this.trigger( "change", this, a );
|
||||
for ( c in this.changed ) {
|
||||
!this._pending[c] && !this._silent[c] && delete this.changed[c];
|
||||
}
|
||||
this._previousAttributes = f.clone( this.attributes )
|
||||
}
|
||||
this._changing = false;
|
||||
return this
|
||||
}, hasChanged : function ( a ) {
|
||||
return!arguments.length ? !f.isEmpty( this.changed ) : f.has( this.changed, a )
|
||||
}, changedAttributes : function ( a ) {
|
||||
if ( !a ) {
|
||||
return this.hasChanged() ? f.clone( this.changed ) :
|
||||
false;
|
||||
}
|
||||
var b, c = false, d = this._previousAttributes, e;
|
||||
for ( e in a ) {
|
||||
if ( !f.isEqual( d[e], b = a[e] ) ) {
|
||||
(c || (c = {}))[e] = b;
|
||||
}
|
||||
}
|
||||
return c
|
||||
}, previous : function ( a ) {
|
||||
return!arguments.length || !this._previousAttributes ? null : this._previousAttributes[a]
|
||||
}, previousAttributes : function () {
|
||||
return f.clone( this._previousAttributes )
|
||||
}, isValid : function () {
|
||||
return!this.validate( this.attributes )
|
||||
}, _validate : function ( a, b ) {
|
||||
if ( b.silent || !this.validate ) {
|
||||
return true;
|
||||
}
|
||||
var a = f.extend( {}, this.attributes, a ), c = this.validate( a, b );
|
||||
if ( !c ) {
|
||||
return true;
|
||||
}
|
||||
b && b.error ?
|
||||
b.error( this, c, b ) : this.trigger( "error", this, c, b );
|
||||
return false
|
||||
}} );
|
||||
var r = g.Collection = function ( a, b ) {
|
||||
b || (b = {});
|
||||
if ( b.model ) {
|
||||
this.model = b.model;
|
||||
}
|
||||
if ( b.comparator ) {
|
||||
this.comparator = b.comparator;
|
||||
}
|
||||
this._reset();
|
||||
this.initialize.apply( this, arguments );
|
||||
a && this.reset( a, {silent : true, parse : b.parse} )
|
||||
};
|
||||
f.extend( r.prototype, l, {model : o, initialize : function () {
|
||||
}, toJSON : function ( a ) {
|
||||
return this.map( function ( b ) {
|
||||
return b.toJSON( a )
|
||||
} )
|
||||
}, add : function ( a, b ) {
|
||||
var c, d, e, g, h, j = {}, i = {}, l = [];
|
||||
b || (b = {});
|
||||
a = f.isArray( a ) ? a.slice() : [a];
|
||||
for ( c = 0, d =
|
||||
a.length; c < d; c++ ) {
|
||||
if ( !(e = a[c] = this._prepareModel( a[c], b )) ) {
|
||||
throw Error( "Can't add an invalid model to a collection" );
|
||||
}
|
||||
g = e.cid;
|
||||
h = e.id;
|
||||
j[g] || this._byCid[g] || h != null && (i[h] || this._byId[h]) ? l.push( c ) : j[g] = i[h] = e
|
||||
}
|
||||
for ( c = l.length; c--; ) {
|
||||
a.splice( l[c], 1 );
|
||||
}
|
||||
for ( c = 0, d = a.length; c < d; c++ ) {
|
||||
(e = a[c]).on( "all", this._onModelEvent, this ), this._byCid[e.cid] = e, e.id != null && (this._byId[e.id] = e);
|
||||
}
|
||||
this.length += d;
|
||||
z.apply( this.models, [b.at != null ? b.at : this.models.length, 0].concat( a ) );
|
||||
this.comparator && this.sort( {silent : true} );
|
||||
if ( b.silent ) {
|
||||
return this;
|
||||
}
|
||||
for ( c = 0, d = this.models.length; c < d; c++ ) {
|
||||
if ( j[(e = this.models[c]).cid] ) {
|
||||
b.index = c, e.trigger( "add", e, this, b );
|
||||
}
|
||||
}
|
||||
return this
|
||||
}, remove : function ( a, b ) {
|
||||
var c, d, e, g;
|
||||
b || (b = {});
|
||||
a = f.isArray( a ) ? a.slice() : [a];
|
||||
for ( c = 0, d = a.length; c < d; c++ ) {
|
||||
if ( g = this.getByCid( a[c] ) || this.get( a[c] ) ) {
|
||||
delete this._byId[g.id];
|
||||
delete this._byCid[g.cid];
|
||||
e = this.indexOf( g );
|
||||
this.models.splice( e, 1 );
|
||||
this.length--;
|
||||
if ( !b.silent ) {
|
||||
b.index = e, g.trigger( "remove", g, this, b );
|
||||
}
|
||||
this._removeReference( g )
|
||||
}
|
||||
}
|
||||
return this
|
||||
}, push : function ( a, b ) {
|
||||
a = this._prepareModel( a, b );
|
||||
this.add( a,
|
||||
b );
|
||||
return a
|
||||
}, pop : function ( a ) {
|
||||
var b = this.at( this.length - 1 );
|
||||
this.remove( b, a );
|
||||
return b
|
||||
}, unshift : function ( a, b ) {
|
||||
a = this._prepareModel( a, b );
|
||||
this.add( a, f.extend( {at : 0}, b ) );
|
||||
return a
|
||||
}, shift : function ( a ) {
|
||||
var b = this.at( 0 );
|
||||
this.remove( b, a );
|
||||
return b
|
||||
}, get : function ( a ) {
|
||||
return a == null ? void 0 : this._byId[a.id != null ? a.id : a]
|
||||
}, getByCid : function ( a ) {
|
||||
return a && this._byCid[a.cid || a]
|
||||
}, at : function ( a ) {
|
||||
return this.models[a]
|
||||
}, where : function ( a ) {
|
||||
return f.isEmpty( a ) ? [] : this.filter( function ( b ) {
|
||||
for ( var c in a ) {
|
||||
if ( a[c] !== b.get( c ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true
|
||||
} )
|
||||
}, sort : function ( a ) {
|
||||
a || (a = {});
|
||||
if ( !this.comparator ) {
|
||||
throw Error( "Cannot sort a set without a comparator" );
|
||||
}
|
||||
var b = f.bind( this.comparator, this );
|
||||
this.comparator.length == 1 ? this.models = this.sortBy( b ) : this.models.sort( b );
|
||||
a.silent || this.trigger( "reset", this, a );
|
||||
return this
|
||||
}, pluck : function ( a ) {
|
||||
return f.map( this.models, function ( b ) {
|
||||
return b.get( a )
|
||||
} )
|
||||
}, reset : function ( a, b ) {
|
||||
a || (a = []);
|
||||
b || (b = {});
|
||||
for ( var c = 0, d = this.models.length; c < d; c++ ) {
|
||||
this._removeReference( this.models[c] );
|
||||
}
|
||||
this._reset();
|
||||
this.add( a, f.extend( {silent : true},
|
||||
b ) );
|
||||
b.silent || this.trigger( "reset", this, b );
|
||||
return this
|
||||
}, fetch : function ( a ) {
|
||||
a = a ? f.clone( a ) : {};
|
||||
if ( a.parse === void 0 ) {
|
||||
a.parse = true;
|
||||
}
|
||||
var b = this, c = a.success;
|
||||
a.success = function ( d, e, f ) {
|
||||
b[a.add ? "add" : "reset"]( b.parse( d, f ), a );
|
||||
c && c( b, d )
|
||||
};
|
||||
a.error = g.wrapError( a.error, b, a );
|
||||
return(this.sync || g.sync).call( this, "read", this, a )
|
||||
}, create : function ( a, b ) {
|
||||
var c = this, b = b ? f.clone( b ) : {}, a = this._prepareModel( a, b );
|
||||
if ( !a ) {
|
||||
return false;
|
||||
}
|
||||
b.wait || c.add( a, b );
|
||||
var d = b.success;
|
||||
b.success = function ( e, f ) {
|
||||
b.wait && c.add( e, b );
|
||||
d ? d( e, f ) : e.trigger( "sync",
|
||||
a, f, b )
|
||||
};
|
||||
a.save( null, b );
|
||||
return a
|
||||
}, parse : function ( a ) {
|
||||
return a
|
||||
}, chain : function () {
|
||||
return f( this.models ).chain()
|
||||
}, _reset : function () {
|
||||
this.length = 0;
|
||||
this.models = [];
|
||||
this._byId = {};
|
||||
this._byCid = {}
|
||||
}, _prepareModel : function ( a, b ) {
|
||||
b || (b = {});
|
||||
if ( a instanceof o ) {
|
||||
if ( !a.collection ) {
|
||||
a.collection = this
|
||||
}
|
||||
} else {
|
||||
var c;
|
||||
b.collection = this;
|
||||
a = new this.model( a, b );
|
||||
a._validate( a.attributes, b ) || (a = false)
|
||||
}
|
||||
return a
|
||||
}, _removeReference : function ( a ) {
|
||||
this == a.collection && delete a.collection;
|
||||
a.off( "all", this._onModelEvent, this )
|
||||
}, _onModelEvent : function ( a, b, c, d ) {
|
||||
(a == "add" || a == "remove") && c != this || (a == "destroy" && this.remove( b, d ), b && a === "change:" + b.idAttribute && (delete this._byId[b.previous( b.idAttribute )], this._byId[b.id] = b), this.trigger.apply( this, arguments ))
|
||||
}} );
|
||||
f.each( "forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split( "," ), function ( a ) {
|
||||
r.prototype[a] = function () {
|
||||
return f[a].apply( f,
|
||||
[this.models].concat( f.toArray( arguments ) ) )
|
||||
}
|
||||
} );
|
||||
var u = g.Router = function ( a ) {
|
||||
a || (a = {});
|
||||
if ( a.routes ) {
|
||||
this.routes = a.routes;
|
||||
}
|
||||
this._bindRoutes();
|
||||
this.initialize.apply( this, arguments )
|
||||
}, A = /:\w+/g, B = /\*\w+/g, C = /[-[\]{}()+?.,\\^$|#\s]/g;
|
||||
f.extend( u.prototype, l, {initialize : function () {
|
||||
}, route : function ( a, b, c ) {
|
||||
g.history || (g.history = new m);
|
||||
f.isRegExp( a ) || (a = this._routeToRegExp( a ));
|
||||
c || (c = this[b]);
|
||||
g.history.route( a, f.bind( function ( d ) {
|
||||
d = this._extractParameters( a, d );
|
||||
c && c.apply( this, d );
|
||||
this.trigger.apply( this, ["route:" +
|
||||
b].concat( d ) );
|
||||
g.history.trigger( "route", this, b, d )
|
||||
}, this ) );
|
||||
return this
|
||||
}, navigate : function ( a, b ) {
|
||||
g.history.navigate( a, b )
|
||||
}, _bindRoutes : function () {
|
||||
if ( this.routes ) {
|
||||
var a = [], b;
|
||||
for ( b in this.routes ) {
|
||||
a.unshift( [b, this.routes[b]] );
|
||||
}
|
||||
b = 0;
|
||||
for ( var c = a.length; b < c; b++ ) {
|
||||
this.route( a[b][0], a[b][1], this[a[b][1]] )
|
||||
}
|
||||
}
|
||||
}, _routeToRegExp : function ( a ) {
|
||||
a = a.replace( C, "\\$&" ).replace( A, "([^/]+)" ).replace( B, "(.*?)" );
|
||||
return RegExp( "^" + a + "$" )
|
||||
}, _extractParameters : function ( a, b ) {
|
||||
return a.exec( b ).slice( 1 )
|
||||
}} );
|
||||
var m = g.History = function () {
|
||||
this.handlers =
|
||||
[];
|
||||
f.bindAll( this, "checkUrl" )
|
||||
}, s = /^[#\/]/, D = /msie [\w.]+/;
|
||||
m.started = false;
|
||||
f.extend( m.prototype, l, {interval : 50, getHash : function ( a ) {
|
||||
return(a = (a ? a.location : window.location).href.match( /#(.*)$/ )) ? a[1] : ""
|
||||
}, getFragment : function ( a, b ) {
|
||||
if ( a == null ) {
|
||||
if ( this._hasPushState || b ) {
|
||||
var a = window.location.pathname, c = window.location.search;
|
||||
c && (a += c)
|
||||
} else {
|
||||
a = this.getHash();
|
||||
}
|
||||
}
|
||||
a.indexOf( this.options.root ) || (a = a.substr( this.options.root.length ));
|
||||
return a.replace( s, "" )
|
||||
}, start : function ( a ) {
|
||||
if ( m.started ) {
|
||||
throw Error( "Backbone.history has already been started" );
|
||||
}
|
||||
m.started = true;
|
||||
this.options = f.extend( {}, {root : "/"}, this.options, a );
|
||||
this._wantsHashChange = this.options.hashChange !== false;
|
||||
this._wantsPushState = !!this.options.pushState;
|
||||
this._hasPushState = !(!this.options.pushState || !window.history || !window.history.pushState);
|
||||
var a = this.getFragment(), b = document.documentMode;
|
||||
if ( b = D.exec( navigator.userAgent.toLowerCase() ) && (!b || b <= 7) ) {
|
||||
this.iframe = i( '<iframe src="javascript:0" tabindex="-1" />' ).hide().appendTo( "body" )[0].contentWindow, this.navigate( a );
|
||||
}
|
||||
if ( this._hasPushState ) {
|
||||
i( window ).bind( "popstate",
|
||||
this.checkUrl );
|
||||
} else if ( this._wantsHashChange && "onhashchange"in window && !b ) {
|
||||
i( window ).bind( "hashchange", this.checkUrl );
|
||||
} else if ( this._wantsHashChange ) {
|
||||
this._checkUrlInterval = setInterval( this.checkUrl, this.interval );
|
||||
}
|
||||
this.fragment = a;
|
||||
a = window.location;
|
||||
b = a.pathname == this.options.root;
|
||||
if ( this._wantsHashChange && this._wantsPushState && !this._hasPushState && !b ) {
|
||||
return this.fragment = this.getFragment( null, true ), window.location.replace( this.options.root + "#" + this.fragment ), true;
|
||||
} else if ( this._wantsPushState && this._hasPushState &&
|
||||
b && a.hash ) {
|
||||
this.fragment = this.getHash().replace( s, "" ), window.history.replaceState( {}, document.title, a.protocol + "//" + a.host + this.options.root + this.fragment );
|
||||
}
|
||||
if ( !this.options.silent ) {
|
||||
return this.loadUrl()
|
||||
}
|
||||
}, stop : function () {
|
||||
i( window ).unbind( "popstate", this.checkUrl ).unbind( "hashchange", this.checkUrl );
|
||||
clearInterval( this._checkUrlInterval );
|
||||
m.started = false
|
||||
}, route : function ( a, b ) {
|
||||
this.handlers.unshift( {route : a, callback : b} )
|
||||
}, checkUrl : function () {
|
||||
var a = this.getFragment();
|
||||
a == this.fragment && this.iframe && (a = this.getFragment( this.getHash( this.iframe ) ));
|
||||
if ( a == this.fragment ) {
|
||||
return false;
|
||||
}
|
||||
this.iframe && this.navigate( a );
|
||||
this.loadUrl() || this.loadUrl( this.getHash() )
|
||||
}, loadUrl : function ( a ) {
|
||||
var b = this.fragment = this.getFragment( a );
|
||||
return f.any( this.handlers, function ( a ) {
|
||||
if ( a.route.test( b ) ) {
|
||||
return a.callback( b ), true
|
||||
}
|
||||
} )
|
||||
}, navigate : function ( a, b ) {
|
||||
if ( !m.started ) {
|
||||
return false;
|
||||
}
|
||||
if ( !b || b === true ) {
|
||||
b = {trigger : b};
|
||||
}
|
||||
var c = (a || "").replace( s, "" );
|
||||
if ( this.fragment != c ) {
|
||||
this._hasPushState ? (c.indexOf( this.options.root ) != 0 && (c = this.options.root + c), this.fragment = c, window.history[b.replace ?
|
||||
"replaceState" : "pushState"]( {}, document.title, c )) : this._wantsHashChange ? (this.fragment = c, this._updateHash( window.location, c, b.replace ), this.iframe && c != this.getFragment( this.getHash( this.iframe ) ) && (b.replace || this.iframe.document.open().close(), this._updateHash( this.iframe.location, c, b.replace ))) : window.location.assign( this.options.root + a ), b.trigger && this.loadUrl( a )
|
||||
}
|
||||
}, _updateHash : function ( a, b, c ) {
|
||||
c ? a.replace( a.toString().replace( /(javascript:|#).*$/, "" ) + "#" + b ) : a.hash = b
|
||||
}} );
|
||||
var v = g.View = function ( a ) {
|
||||
this.cid =
|
||||
f.uniqueId( "view" );
|
||||
this._configure( a || {} );
|
||||
this._ensureElement();
|
||||
this.initialize.apply( this, arguments );
|
||||
this.delegateEvents()
|
||||
}, E = /^(\S+)\s*(.*)$/, w = "model,collection,el,id,attributes,className,tagName".split( "," );
|
||||
f.extend( v.prototype, l, {tagName : "div", $ : function ( a ) {
|
||||
return this.$el.find( a )
|
||||
}, initialize : function () {
|
||||
}, render : function () {
|
||||
return this
|
||||
}, remove : function () {
|
||||
this.$el.remove();
|
||||
return this
|
||||
}, make : function ( a, b, c ) {
|
||||
a = document.createElement( a );
|
||||
b && i( a ).attr( b );
|
||||
c != null && i( a ).html( c );
|
||||
return a
|
||||
}, setElement : function ( a, b ) {
|
||||
this.$el && this.undelegateEvents();
|
||||
this.$el = a instanceof i ? a : i( a );
|
||||
this.el = this.$el[0];
|
||||
b !== false && this.delegateEvents();
|
||||
return this
|
||||
}, delegateEvents : function ( a ) {
|
||||
if ( a || (a = n( this, "events" )) ) {
|
||||
this.undelegateEvents();
|
||||
for ( var b in a ) {
|
||||
var c = a[b];
|
||||
f.isFunction( c ) || (c = this[a[b]]);
|
||||
if ( !c ) {
|
||||
throw Error( 'Method "' + a[b] + '" does not exist' );
|
||||
}
|
||||
var d = b.match( E ), e = d[1], d = d[2], c = f.bind( c, this );
|
||||
e += ".delegateEvents" + this.cid;
|
||||
d === "" ? this.$el.bind( e, c ) : this.$el.delegate( d, e, c )
|
||||
}
|
||||
}
|
||||
}, undelegateEvents : function () {
|
||||
this.$el.unbind( ".delegateEvents" +
|
||||
this.cid )
|
||||
}, _configure : function ( a ) {
|
||||
this.options && (a = f.extend( {}, this.options, a ));
|
||||
for ( var b = 0, c = w.length; b < c; b++ ) {
|
||||
var d = w[b];
|
||||
a[d] && (this[d] = a[d])
|
||||
}
|
||||
this.options = a
|
||||
}, _ensureElement : function () {
|
||||
if ( this.el ) {
|
||||
this.setElement( this.el, false );
|
||||
} else {
|
||||
var a = n( this, "attributes" ) || {};
|
||||
if ( this.id ) {
|
||||
a.id = this.id;
|
||||
}
|
||||
if ( this.className ) {
|
||||
a["class"] = this.className;
|
||||
}
|
||||
this.setElement( this.make( this.tagName, a ), false )
|
||||
}
|
||||
}} );
|
||||
o.extend = r.extend = u.extend = v.extend = function ( a, b ) {
|
||||
var c = F( this, a, b );
|
||||
c.extend = this.extend;
|
||||
return c
|
||||
};
|
||||
var G = {create : "POST",
|
||||
update : "PUT", "delete" : "DELETE", read : "GET"};
|
||||
g.sync = function ( a, b, c ) {
|
||||
var d = G[a];
|
||||
c || (c = {});
|
||||
var e = {type : d, dataType : "json"};
|
||||
if ( !c.url ) {
|
||||
e.url = n( b, "url" ) || t();
|
||||
}
|
||||
if ( !c.data && b && (a == "create" || a == "update") ) {
|
||||
e.contentType = "application/json", e.data = JSON.stringify( b.toJSON() );
|
||||
}
|
||||
if ( g.emulateJSON ) {
|
||||
e.contentType = "application/x-www-form-urlencoded", e.data = e.data ? {model : e.data} : {};
|
||||
}
|
||||
if ( g.emulateHTTP && (d === "PUT" || d === "DELETE") ) {
|
||||
if ( g.emulateJSON ) {
|
||||
e.data._method = d;
|
||||
}
|
||||
e.type = "POST";
|
||||
e.beforeSend = function ( a ) {
|
||||
a.setRequestHeader( "X-HTTP-Method-Override",
|
||||
d )
|
||||
}
|
||||
}
|
||||
if ( e.type !== "GET" && !g.emulateJSON ) {
|
||||
e.processData = false;
|
||||
}
|
||||
return i.ajax( f.extend( e, c ) )
|
||||
};
|
||||
g.wrapError = function ( a, b, c ) {
|
||||
return function ( d, e ) {
|
||||
e = d === b ? e : d;
|
||||
a ? a( b, e, c ) : b.trigger( "error", b, e, c )
|
||||
}
|
||||
};
|
||||
var x = function () {
|
||||
}, F = function ( a, b, c ) {
|
||||
var d;
|
||||
d = b && b.hasOwnProperty( "constructor" ) ? b.constructor : function () {
|
||||
a.apply( this, arguments )
|
||||
};
|
||||
f.extend( d, a );
|
||||
x.prototype = a.prototype;
|
||||
d.prototype = new x;
|
||||
b && f.extend( d.prototype, b );
|
||||
c && f.extend( d, c );
|
||||
d.prototype.constructor = d;
|
||||
d.__super__ = a.prototype;
|
||||
return d
|
||||
}, n = function ( a, b ) {
|
||||
return!a ||
|
||||
!a[b] ? null : f.isFunction( a[b] ) ? a[b]() : a[b]
|
||||
}, t = function () {
|
||||
throw Error( 'A "url" property or function must be specified' );
|
||||
};
|
||||
return g
|
||||
} );
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
machina.js
|
||||
Author: Jim Cowart
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.1.0
|
||||
*/
|
||||
|
||||
(function ( root, doc, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root, doc );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root._, root, doc );
|
||||
}
|
||||
}( this, document, function ( _, global, document, undefined ) {
|
||||
|
||||
var slice = [].slice,
|
||||
NEXT_TRANSITION = "transition",
|
||||
NEXT_HANDLER = "handler",
|
||||
transformEventListToObject = function ( eventList ) {
|
||||
var obj = {};
|
||||
_.each( eventList, function ( evntName ) {
|
||||
obj[evntName] = [];
|
||||
} );
|
||||
return obj;
|
||||
},
|
||||
parseEventListeners = function ( evnts ) {
|
||||
var obj = evnts;
|
||||
if ( _.isArray( evnts ) ) {
|
||||
obj = transformEventListToObject( evnts );
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
var utils = {
|
||||
makeFsmNamespace : (function () {
|
||||
var machinaCount = 0;
|
||||
return function () {
|
||||
return "fsm." + machinaCount++;
|
||||
};
|
||||
})(),
|
||||
getDefaultOptions : function () {
|
||||
return {
|
||||
initialState : "uninitialized",
|
||||
eventListeners : {
|
||||
"*" : []
|
||||
},
|
||||
states : {},
|
||||
eventQueue : [],
|
||||
namespace : utils.makeFsmNamespace()
|
||||
};
|
||||
}
|
||||
};
|
||||
var Fsm = function ( options ) {
|
||||
var opt, initialState, defaults = utils.getDefaultOptions();
|
||||
if ( options ) {
|
||||
if ( options.eventListeners ) {
|
||||
options.eventListeners = parseEventListeners( options.eventListeners );
|
||||
}
|
||||
if ( options.messaging ) {
|
||||
options.messaging = _.extend( {}, defaults.messaging, options.messaging );
|
||||
}
|
||||
}
|
||||
opt = _.extend( defaults, options || {} );
|
||||
initialState = opt.initialState;
|
||||
delete opt.initialState;
|
||||
_.extend( this, opt );
|
||||
|
||||
this.state = undefined;
|
||||
this._priorAction = "";
|
||||
this._currentAction = "";
|
||||
if ( initialState ) {
|
||||
this.transition( initialState );
|
||||
}
|
||||
machina.fireEvent( "newFsm", this );
|
||||
};
|
||||
|
||||
Fsm.prototype.fireEvent = function ( eventName ) {
|
||||
var args = arguments;
|
||||
_.each( this.eventListeners["*"], function ( callback ) {
|
||||
try {
|
||||
callback.apply( this, slice.call( args, 0 ) );
|
||||
} catch ( exception ) {
|
||||
if ( console && typeof console.log !== "undefined" ) {
|
||||
console.log( exception.toString() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
_.each( this.eventListeners[eventName], function ( callback ) {
|
||||
try {
|
||||
callback.apply( this, slice.call( args, 1 ) );
|
||||
} catch ( exception ) {
|
||||
if ( console && typeof console.log !== "undefined" ) {
|
||||
console.log( exception.toString() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.handle = function ( msgType ) {
|
||||
// vars to avoid a "this." fest
|
||||
var states = this.states, current = this.state, args = slice.call( arguments, 0 ), handlerName;
|
||||
this.currentActionArgs = args;
|
||||
if ( states[current] && (states[current][msgType] || states[current]["*"]) ) {
|
||||
handlerName = states[current][msgType] ? msgType : "*";
|
||||
this._currentAction = current + "." + handlerName;
|
||||
this.fireEvent.apply( this, ["Handling"].concat( args ) );
|
||||
states[current][handlerName].apply( this, args.slice( 1 ) );
|
||||
this.fireEvent.apply( this, ["Handled"].concat( args ) );
|
||||
this._priorAction = this._currentAction;
|
||||
this._currentAction = "";
|
||||
this.processQueue( NEXT_HANDLER );
|
||||
}
|
||||
else {
|
||||
this.fireEvent.apply( this, ["NoHandler"].concat( args ) );
|
||||
}
|
||||
this.currentActionArgs = undefined;
|
||||
};
|
||||
|
||||
Fsm.prototype.transition = function ( newState ) {
|
||||
if ( this.states[newState] ) {
|
||||
var oldState = this.state;
|
||||
this.state = newState;
|
||||
if ( this.states[newState]._onEnter ) {
|
||||
this.states[newState]._onEnter.call( this );
|
||||
}
|
||||
this.fireEvent.apply( this, ["Transitioned", oldState, this.state ] );
|
||||
this.processQueue( NEXT_TRANSITION );
|
||||
return;
|
||||
}
|
||||
this.fireEvent.apply( this, ["InvalidState", this.state, newState ] );
|
||||
};
|
||||
|
||||
Fsm.prototype.processQueue = function ( type ) {
|
||||
var filterFn = type === NEXT_TRANSITION ?
|
||||
function ( item ) {
|
||||
return item.type === NEXT_TRANSITION && ((!item.untilState) || (item.untilState === this.state));
|
||||
} :
|
||||
function ( item ) {
|
||||
return item.type === NEXT_HANDLER;
|
||||
},
|
||||
toProcess = _.filter( this.eventQueue, filterFn, this );
|
||||
this.eventQueue = _.difference( this.eventQueue, toProcess );
|
||||
_.each( toProcess, function ( item, index ) {
|
||||
this.handle.apply( this, item.args );
|
||||
}, this );
|
||||
};
|
||||
|
||||
Fsm.prototype.deferUntilTransition = function ( stateName ) {
|
||||
if ( this.currentActionArgs ) {
|
||||
var queued = { type : NEXT_TRANSITION, untilState : stateName, args : this.currentActionArgs };
|
||||
this.eventQueue.push( queued );
|
||||
this.fireEvent.apply( this, [ "Deferred", this.state, queued ] );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.deferUntilNextHandler = function () {
|
||||
if ( this.currentActionArgs ) {
|
||||
var queued = { type : NEXT_TRANSITION, args : this.currentActionArgs };
|
||||
this.eventQueue.push( queued );
|
||||
this.fireEvent.apply( this, [ "Deferred", this.state, queued ] );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.on = function ( eventName, callback ) {
|
||||
if ( !this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = [];
|
||||
}
|
||||
this.eventListeners[eventName].push( callback );
|
||||
};
|
||||
|
||||
Fsm.prototype.off = function ( eventName, callback ) {
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
|
||||
}
|
||||
};
|
||||
|
||||
var machina = {
|
||||
Fsm : Fsm,
|
||||
bus : undefined,
|
||||
utils : utils,
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = [];
|
||||
}
|
||||
this.eventListeners[eventName].push( callback );
|
||||
},
|
||||
off : function ( eventName, callback ) {
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
|
||||
}
|
||||
},
|
||||
fireEvent : function ( eventName ) {
|
||||
var i = 0, len, args = arguments, listeners = this.eventListeners[eventName];
|
||||
if ( listeners && listeners.length ) {
|
||||
_.each( listeners, function ( callback ) {
|
||||
callback.apply( null, slice.call( args, 1 ) );
|
||||
} );
|
||||
}
|
||||
},
|
||||
eventListeners : {
|
||||
newFsm : []
|
||||
}
|
||||
};
|
||||
|
||||
global.machina = machina;
|
||||
return machina;
|
||||
} ));
|
||||
|
|
@ -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,527 +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.7.3
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_PRIORITY = 50,
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal",
|
||||
NO_OP = function () {
|
||||
};
|
||||
|
||||
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, defaultTopic ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
};
|
||||
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.priority = DEFAULT_PRIORITY;
|
||||
this.constraints = new Array( 0 );
|
||||
this.maxCalls = DEFAULT_DISPOSEAFTER;
|
||||
this.onHandled = NO_OP;
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
|
||||
postal.configuration.bus.subscribe( this );
|
||||
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( fn, 0, data );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
|
||||
var fn = this.onHandled;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe( this );
|
||||
}, this ) );
|
||||
|
||||
this.onHandled = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once: function() {
|
||||
this.disposeAfter(1);
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withPriority : function ( priority ) {
|
||||
if ( _.isNaN( priority ) ) {
|
||||
throw "Priority must be a number";
|
||||
}
|
||||
this.priority = priority;
|
||||
postal.configuration.bus.changePriority( this );
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
changePriority : function ( subDef ) {
|
||||
var idx, found;
|
||||
if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic] = _.without( this.subscriptions[subDef.channel][subDef.topic], subDef );
|
||||
idx = this.subscriptions[subDef.channel][subDef.topic].length - 1;
|
||||
for ( ; idx >= 0; idx-- ) {
|
||||
if ( this.subscriptions[subDef.channel][subDef.topic][idx].priority <= subDef.priority ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice( idx + 1, 0, subDef );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].unshift( subDef );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call(subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
subDef.onHandled();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : new Array( 0 ),
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
for ( ; idx < len; idx++ ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
|
||||
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
if ( !_.isArray( sources ) ) {
|
||||
sources = [sources];
|
||||
}
|
||||
if ( !_.isArray( destinations ) ) {
|
||||
destinations = [destinations];
|
||||
}
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ],
|
||||
result = [];
|
||||
if ( arguments.length === 1 ) {
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ];
|
||||
}
|
||||
else {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
global.postal = postal;
|
||||
return postal;
|
||||
} ));
|
||||
7
example/node/client/js/lib/postal.min.js
vendored
7
example/node/client/js/lib/postal.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
RequireJS text 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
|
||||
*/
|
||||
(function () {
|
||||
var k = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.4.0"], n = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, o = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im, i = typeof location !== "undefined" && location.href, p = i && location.protocol && location.protocol.replace( /\:/, "" ), q = i && location.hostname, r = i && (location.port || void 0), j = [];
|
||||
define( function () {
|
||||
var g, h, l;
|
||||
typeof window !== "undefined" && window.navigator && window.document ? h = function ( a, c ) {
|
||||
var b = g.createXhr();
|
||||
b.open( "GET", a, !0 );
|
||||
b.onreadystatechange =
|
||||
function () {
|
||||
b.readyState === 4 && c( b.responseText )
|
||||
};
|
||||
b.send( null )
|
||||
} : typeof process !== "undefined" && process.versions && process.versions.node ? (l = require.nodeRequire( "fs" ), h = function ( a, c ) {
|
||||
c( l.readFileSync( a, "utf8" ) )
|
||||
}) : typeof Packages !== "undefined" && (h = function ( a, c ) {
|
||||
var b = new java.io.File( a ), e = java.lang.System.getProperty( "line.separator" ), b = new java.io.BufferedReader( new java.io.InputStreamReader( new java.io.FileInputStream( b ), "utf-8" ) ), d, f, g = "";
|
||||
try {
|
||||
d = new java.lang.StringBuffer;
|
||||
(f = b.readLine()) && f.length() &&
|
||||
f.charAt( 0 ) === 65279 && (f = f.substring( 1 ));
|
||||
for ( d.append( f ); (f = b.readLine()) !== null; ) {
|
||||
d.append( e ), d.append( f );
|
||||
}
|
||||
g = String( d.toString() )
|
||||
} finally {
|
||||
b.close()
|
||||
}
|
||||
c( g )
|
||||
});
|
||||
return g = {version : "1.0.2", strip : function ( a ) {
|
||||
if ( a ) {
|
||||
var a = a.replace( n, "" ), c = a.match( o );
|
||||
c && (a = c[1])
|
||||
} else {
|
||||
a = "";
|
||||
}
|
||||
return a
|
||||
}, jsEscape : function ( a ) {
|
||||
return a.replace( /(['\\])/g, "\\$1" ).replace( /[\f]/g, "\\f" ).replace( /[\b]/g, "\\b" ).replace( /[\n]/g, "\\n" ).replace( /[\t]/g, "\\t" ).replace( /[\r]/g, "\\r" )
|
||||
}, createXhr : function () {
|
||||
var a, c, b;
|
||||
if ( typeof XMLHttpRequest !==
|
||||
"undefined" ) {
|
||||
return new XMLHttpRequest;
|
||||
} else {
|
||||
for ( c = 0; c < 3; c++ ) {
|
||||
b = k[c];
|
||||
try {
|
||||
a = new ActiveXObject( b )
|
||||
} catch ( e ) {
|
||||
}
|
||||
if ( a ) {
|
||||
k = [b];
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !a ) {
|
||||
throw Error( "createXhr(): XMLHttpRequest not available" );
|
||||
}
|
||||
return a
|
||||
}, get : h, parseName : function ( a ) {
|
||||
var c = !1, b = a.indexOf( "." ), e = a.substring( 0, b ), a = a.substring( b + 1, a.length ), b = a.indexOf( "!" );
|
||||
b !== -1 && (c = a.substring( b + 1, a.length ), c = c === "strip", a = a.substring( 0, b ));
|
||||
return{moduleName : e, ext : a, strip : c}
|
||||
}, xdRegExp : /^((\w+)\:)?\/\/([^\/\\]+)/, useXhr : function ( a, c, b, e ) {
|
||||
var d = g.xdRegExp.exec( a ),
|
||||
f;
|
||||
if ( !d ) {
|
||||
return!0;
|
||||
}
|
||||
a = d[2];
|
||||
d = d[3];
|
||||
d = d.split( ":" );
|
||||
f = d[1];
|
||||
d = d[0];
|
||||
return(!a || a === c) && (!d || d === b) && (!f && !d || f === e)
|
||||
}, finishLoad : function ( a, c, b, e, d ) {
|
||||
b = c ? g.strip( b ) : b;
|
||||
d.isBuild && (j[a] = b);
|
||||
e( b )
|
||||
}, load : function ( a, c, b, e ) {
|
||||
if ( e.isBuild && !e.inlineText ) {
|
||||
b();
|
||||
} else {
|
||||
var d = g.parseName( a ), f = d.moduleName + "." + d.ext, m = c.toUrl( f ), h = e && e.text && e.text.useXhr || g.useXhr;
|
||||
!i || h( m, p, q, r ) ? g.get( m, function ( c ) {
|
||||
g.finishLoad( a, d.strip, c, b, e )
|
||||
} ) : c( [f], function ( a ) {
|
||||
g.finishLoad( d.moduleName + "." + d.ext, d.strip, a, b, e )
|
||||
} )
|
||||
}
|
||||
}, write : function ( a, c, b ) {
|
||||
if ( c in
|
||||
j ) {
|
||||
var e = g.jsEscape( j[c] );
|
||||
b.asModule( a + "!" + c, "define(function () { return '" + e + "';});\n" )
|
||||
}
|
||||
}, writeFile : function ( a, c, b, e, d ) {
|
||||
var c = g.parseName( c ), f = c.moduleName + "." + c.ext, h = b.toUrl( c.moduleName + "." + c.ext ) + ".js";
|
||||
g.load( f, b, function () {
|
||||
var b = function ( a ) {
|
||||
return e( h, a )
|
||||
};
|
||||
b.asModule = function ( a, b ) {
|
||||
return e.asModule( a, h, b )
|
||||
};
|
||||
g.write( a, f, b, d )
|
||||
}, d )
|
||||
}}
|
||||
} )
|
||||
})();
|
||||
|
|
@ -1,717 +0,0 @@
|
|||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 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 q( 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 ( a.isEqual && b.isFunction( a.isEqual ) ) {
|
||||
return a.isEqual( c );
|
||||
}
|
||||
if ( c.isEqual && 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 a == String( c );
|
||||
case "[object Number]":
|
||||
return 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 && q( 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 ( b.has( a, h ) && (f++, !(g = b.has( c, h ) && q( a[h], c[h], d ))) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( g ) {
|
||||
for ( h in c ) {
|
||||
if ( b.has( c,
|
||||
h ) && !f-- ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
g = !f
|
||||
}
|
||||
}
|
||||
d.pop();
|
||||
return g
|
||||
}
|
||||
|
||||
var r = this, G = r._, n = {}, k = Array.prototype, o = Object.prototype, i = k.slice, H = k.unshift, l = o.toString, I = o.hasOwnProperty, w = k.forEach, x = k.map, y = k.reduce, z = k.reduceRight, A = k.filter, B = k.every, C = k.some, p = k.indexOf, D = k.lastIndexOf, o = Array.isArray, J = Object.keys, s = Function.prototype.bind, b = function ( a ) {
|
||||
return new m( a )
|
||||
};
|
||||
if ( typeof exports !== "undefined" ) {
|
||||
if ( typeof module !== "undefined" && module.exports ) {
|
||||
exports = module.exports = b;
|
||||
}
|
||||
exports._ = b
|
||||
} else {
|
||||
r._ = b;
|
||||
}
|
||||
b.VERSION = "1.3.1";
|
||||
var j = b.each =
|
||||
b.forEach = function ( a, c, d ) {
|
||||
if ( a != null ) {
|
||||
if ( w && a.forEach === w ) {
|
||||
a.forEach( c, d );
|
||||
} else if ( a.length === +a.length ) {
|
||||
for ( var e = 0, f = a.length; e < f; e++ ) {
|
||||
if ( e in a && c.call( d, a[e], e, a ) === n ) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( e in a ) {
|
||||
if ( b.has( a, e ) && c.call( d, a[e], e, a ) === n ) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
b.map = b.collect = function ( a, c, b ) {
|
||||
var e = [];
|
||||
if ( a == null ) {
|
||||
return e;
|
||||
}
|
||||
if ( x && a.map === x ) {
|
||||
return a.map( c, b );
|
||||
}
|
||||
j( a, function ( a, g, h ) {
|
||||
e[e.length] = c.call( b, a, g, h )
|
||||
} );
|
||||
if ( a.length === +a.length ) {
|
||||
e.length = a.length;
|
||||
}
|
||||
return e
|
||||
};
|
||||
b.reduce = b.foldl = b.inject = function ( a, c, d, e ) {
|
||||
var f = arguments.length > 2;
|
||||
a ==
|
||||
null && (a = []);
|
||||
if ( y && a.reduce === y ) {
|
||||
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 ) {
|
||||
var f = arguments.length > 2;
|
||||
a == null && (a = []);
|
||||
if ( z && a.reduceRight === z ) {
|
||||
return e && (c = b.bind( c, e )), f ? a.reduceRight( c, d ) : a.reduceRight( c );
|
||||
}
|
||||
var g = b.toArray( a ).reverse();
|
||||
e && !f && (c = b.bind( c, e ));
|
||||
return f ? b.reduce( g, c, d, e ) : b.reduce( g, c )
|
||||
};
|
||||
b.find = b.detect =
|
||||
function ( a, c, b ) {
|
||||
var e;
|
||||
E( 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 ( A && a.filter === A ) {
|
||||
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 ( B && a.every === B ) {
|
||||
return a.every( c, b );
|
||||
}
|
||||
j( a, function ( a, g, h ) {
|
||||
if ( !(e =
|
||||
e && c.call( b, a, g, h )) ) {
|
||||
return n
|
||||
}
|
||||
} );
|
||||
return e
|
||||
};
|
||||
var E = b.some = b.any = function ( a, c, d ) {
|
||||
c || (c = b.identity);
|
||||
var e = false;
|
||||
if ( a == null ) {
|
||||
return e;
|
||||
}
|
||||
if ( C && a.some === C ) {
|
||||
return a.some( c, d );
|
||||
}
|
||||
j( a, function ( a, b, h ) {
|
||||
if ( e || (e = c.call( d, a, b, h )) ) {
|
||||
return n
|
||||
}
|
||||
} );
|
||||
return!!e
|
||||
};
|
||||
b.include = b.contains = function ( a, c ) {
|
||||
var b = false;
|
||||
if ( a == null ) {
|
||||
return b;
|
||||
}
|
||||
return p && a.indexOf === p ? a.indexOf( c ) != -1 : b = E( a, function ( a ) {
|
||||
return a === c
|
||||
} )
|
||||
};
|
||||
b.invoke = function ( a, c ) {
|
||||
var d = i.call( arguments, 2 );
|
||||
return b.map( a, function ( a ) {
|
||||
return(b.isFunction( c ) ? 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 b = [], d;
|
||||
j( a, function ( a, f ) {
|
||||
f == 0 ? b[0] = a : (d = Math.floor( Math.random() * (f + 1) ), b[f] = b[d], b[d] = a)
|
||||
} );
|
||||
return b
|
||||
};
|
||||
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, b ) {
|
||||
var c = a.criteria, d = b.criteria;
|
||||
return c < d ? -1 : c > 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, b ) {
|
||||
var c = e( a, b );
|
||||
(d[c] || (d[c] = [])).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 ) {
|
||||
var c = b.flatten( i.call( arguments, 1 ) );
|
||||
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 ( p && a.indexOf === p ) {
|
||||
return a.indexOf( c );
|
||||
}
|
||||
for ( d = 0, e = a.length; d < e; d++ ) {
|
||||
if ( d in a && a[d] === c ) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return-1
|
||||
};
|
||||
b.lastIndexOf = function ( a, b ) {
|
||||
if ( a == null ) {
|
||||
return-1;
|
||||
}
|
||||
if ( D && a.lastIndexOf === D ) {
|
||||
return a.lastIndexOf( b );
|
||||
}
|
||||
for ( var d = a.length; d--; ) {
|
||||
if ( d in a && 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 F = function () {
|
||||
};
|
||||
b.bind = function ( a, c ) {
|
||||
var d, e;
|
||||
if ( a.bind === s && s ) {
|
||||
return s.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 ) ) );
|
||||
}
|
||||
F.prototype = a.prototype;
|
||||
var b = new F, 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 e = c.apply( this, arguments );
|
||||
return b.has( d, e ) ? d[e] : d[e] = 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, 0 ) );
|
||||
return b.apply( this, d )
|
||||
}
|
||||
};
|
||||
b.compose = function () {
|
||||
var a = arguments;
|
||||
return function () {
|
||||
for ( var b = 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 = J || function ( a ) {
|
||||
if ( a !== Object( a ) ) {
|
||||
throw new TypeError( "Invalid object" );
|
||||
}
|
||||
var c = [], d;
|
||||
for ( d in a ) {
|
||||
b.has( a, d ) && (c[c.length] = d);
|
||||
}
|
||||
return c
|
||||
};
|
||||
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 ) {
|
||||
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 q( a, b, [] )
|
||||
};
|
||||
b.isEmpty = function ( a ) {
|
||||
if ( b.isArray( a ) || b.isString( a ) ) {
|
||||
return a.length === 0;
|
||||
}
|
||||
for ( var c in a ) {
|
||||
if ( b.has( a, c ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true
|
||||
};
|
||||
b.isElement = function ( a ) {
|
||||
return!!(a && a.nodeType == 1)
|
||||
};
|
||||
b.isArray = o || function ( a ) {
|
||||
return l.call( a ) == "[object Array]"
|
||||
};
|
||||
b.isObject = function ( a ) {
|
||||
return a === Object( a )
|
||||
};
|
||||
b.isArguments = function ( a ) {
|
||||
return l.call( a ) == "[object Arguments]"
|
||||
};
|
||||
if ( !b.isArguments( arguments ) ) {
|
||||
b.isArguments = function ( a ) {
|
||||
return!(!a || !b.has( 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.has = function ( a, b ) {
|
||||
return I.call( a, b )
|
||||
};
|
||||
b.noConflict = function () {
|
||||
r._ = G;
|
||||
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 ) {
|
||||
K( c, b[c] = a[c] )
|
||||
} )
|
||||
};
|
||||
var L = 0;
|
||||
b.uniqueId = function ( a ) {
|
||||
var b = L++;
|
||||
return a ? a + b : b
|
||||
};
|
||||
b.templateSettings = {evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g};
|
||||
var t = /.^/, u = function ( a ) {
|
||||
return a.replace( /\\\\/g, "\\" ).replace( /\\'/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 || t,
|
||||
function ( a, b ) {
|
||||
return"',_.escape(" +
|
||||
u( b ) + "),'"
|
||||
} ).replace( d.interpolate || t,
|
||||
function ( a, b ) {
|
||||
return"'," + u( b ) + ",'"
|
||||
} ).replace( d.evaluate || t,
|
||||
function ( a, b ) {
|
||||
return"');" + u( b ).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.call( this, a, b )
|
||||
}
|
||||
};
|
||||
b.chain = function ( a ) {
|
||||
return b( a ).chain()
|
||||
};
|
||||
var m = function ( a ) {
|
||||
this._wrapped = a
|
||||
};
|
||||
b.prototype = m.prototype;
|
||||
var v = function ( a, c ) {
|
||||
return c ? b( a ).chain() : a
|
||||
}, K = function ( a, c ) {
|
||||
m.prototype[a] =
|
||||
function () {
|
||||
var a = i.call( arguments );
|
||||
H.call( a, this._wrapped );
|
||||
return v( c.apply( b, a ), this._chain )
|
||||
}
|
||||
};
|
||||
b.mixin( b );
|
||||
j( "pop,push,reverse,shift,sort,splice,unshift".split( "," ), function ( a ) {
|
||||
var b = k[a];
|
||||
m.prototype[a] = function () {
|
||||
var d = this._wrapped;
|
||||
b.apply( d, arguments );
|
||||
var e = d.length;
|
||||
(a == "shift" || a == "splice") && e === 0 && delete d[0];
|
||||
return v( d, this._chain )
|
||||
}
|
||||
} );
|
||||
j( ["concat", "join", "slice"], function ( a ) {
|
||||
var b = k[a];
|
||||
m.prototype[a] = function () {
|
||||
return v( b.apply( this._wrapped, arguments ), this._chain )
|
||||
}
|
||||
} );
|
||||
m.prototype.chain = function () {
|
||||
this._chain =
|
||||
true;
|
||||
return this
|
||||
};
|
||||
m.prototype.value = function () {
|
||||
return this._wrapped
|
||||
};
|
||||
typeof define === "function" && define.amd && define( "underscore", function () {
|
||||
return b
|
||||
} )
|
||||
}).call( this );
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
var socket;
|
||||
|
||||
require.config( {
|
||||
paths : {
|
||||
'text' : 'lib/requirejs-text-1.0.2',
|
||||
'backbone' : 'lib/backbone',
|
||||
'underscore' : 'lib/underscore',
|
||||
'machina' : 'lib/machina',
|
||||
'postal' : 'lib/postal',
|
||||
'amplify' : 'lib/amplify',
|
||||
'bus' : 'infrastructure/bus'
|
||||
},
|
||||
baseUrl : 'js'
|
||||
} );
|
||||
|
||||
require( [ 'backbone', 'jquery', 'underscore', 'amplify', 'machina', 'postal', 'lib/postal.diagnostics', 'infrastructure/postal.socket-client' ],
|
||||
function ( Backbone, $, _, amplify, machina, postal ) {
|
||||
|
||||
// Customizing Postal with some experimental functionality....
|
||||
var sessionInfo = {};
|
||||
|
||||
postal.configuration.getSessionIdAction = function ( callback ) {
|
||||
callback( sessionInfo );
|
||||
};
|
||||
postal.configuration.setSessionIdAction = function ( info, callback ) {
|
||||
sessionInfo = info;
|
||||
callback( sessionInfo );
|
||||
};
|
||||
postal.utils.getSessionId = function ( callback ) {
|
||||
postal.configuration.getSessionIdAction.call( this, callback );
|
||||
};
|
||||
|
||||
postal.utils.setSessionId = function ( value, callback ) {
|
||||
postal.utils.getSessionId( function ( info ) {
|
||||
// get the session info to move id to last id
|
||||
info.lastId = info.id;
|
||||
info.id = value;
|
||||
// invoke the callback the user provided to handle storing session
|
||||
postal.configuration.setSessionIdAction( info, function ( session ) {
|
||||
callback( session );
|
||||
// publish postal event msg about the change
|
||||
postal.publish( {
|
||||
channel : postal.configuration.SYSTEM_CHANNEL,
|
||||
topic : "sessionId.changed",
|
||||
data : session
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
// for debugging purposes ONLY for now:
|
||||
window.postal = postal;
|
||||
|
||||
require( [ 'infrastructure/app' ], function ( app ) {
|
||||
window.app = app;
|
||||
} );
|
||||
|
||||
} );
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
define( [
|
||||
'backbone',
|
||||
'bus'
|
||||
],
|
||||
function ( Backbone, bus ) {
|
||||
"use strict";
|
||||
|
||||
return Backbone.Model.extend( {
|
||||
defaults : {
|
||||
sessionId : "",
|
||||
searchOwnership : "",
|
||||
searchTerm : "",
|
||||
requests : false
|
||||
},
|
||||
|
||||
initialize : function () {
|
||||
this.subscriptions = [
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch ).withContext( this ),
|
||||
bus.app.subscribe( "search.new.ask", this.updateReqCount ).withContext( this ),
|
||||
bus.app.subscribe( "search.requests", this.updateReqCount ).withContext( this )
|
||||
];
|
||||
bus.app.publish( {
|
||||
topic : "get.search.info",
|
||||
data : {}
|
||||
} );
|
||||
},
|
||||
|
||||
dispose : function () {
|
||||
_.each( this.subscriptions, function ( subscription ) {
|
||||
subscription.unsubscribe();
|
||||
} );
|
||||
this.clear( { silent : true } );
|
||||
},
|
||||
|
||||
setCurrentSearch : function ( data, env ) {
|
||||
var self = this;
|
||||
self.set( "searchTerm", data.searchTerm );
|
||||
postal.utils.getSessionId(
|
||||
function ( session ) {
|
||||
self.set( "sessionId", session.id, { silent : true } );
|
||||
self.set( "searchOwnership",
|
||||
(session.id === data.id)
|
||||
? "You own the search."
|
||||
: "You do not own the search."
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
updateReqCount : function ( data, env ) {
|
||||
if ( (_.isArray( data ) && data.length) || data.searchTerm ) {
|
||||
this.set( "requests", true );
|
||||
}
|
||||
else {
|
||||
this.set( "requests", false );
|
||||
}
|
||||
this.change( "requests" );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
define( [
|
||||
'backbone',
|
||||
'bus'
|
||||
],
|
||||
function ( Backbone, bus ) {
|
||||
"use strict";
|
||||
|
||||
return Backbone.Model.extend( {
|
||||
defaults : {
|
||||
requests : [],
|
||||
ownerId : undefined,
|
||||
sessionId : undefined
|
||||
},
|
||||
|
||||
initialize : function () {
|
||||
_.bindAll( this );
|
||||
this.setSessionId();
|
||||
this.subscriptions = [
|
||||
bus.app.subscribe( "search.info", this.setOwner ).withContext( this ),
|
||||
bus.app.subscribe( "search.requests", this.updateRequests ).withContext( this ),
|
||||
bus.app.subscribe( "search.new.ask", this.addRequest ).withContext( this ),
|
||||
bus.app.subscribe( "search.init", this.askForUpdate ).withContext( this )
|
||||
];
|
||||
this.askForUpdate();
|
||||
},
|
||||
|
||||
askForUpdate : function () {
|
||||
bus.app.publish( {
|
||||
topic : "get.search.requests",
|
||||
data : {}
|
||||
} );
|
||||
bus.app.publish( {
|
||||
topic : "get.search.info",
|
||||
data : {}
|
||||
} );
|
||||
},
|
||||
|
||||
dispose : function () {
|
||||
_.each( this.subscriptions, function ( subscription ) {
|
||||
subscription.unsubscribe();
|
||||
} );
|
||||
this.clear( { silent : true } );
|
||||
},
|
||||
|
||||
setOwner : function ( data, env ) {
|
||||
this.set( "ownerId", data.id );
|
||||
this.setSessionId();
|
||||
},
|
||||
|
||||
setSessionId : function () {
|
||||
var self = this;
|
||||
postal.utils.getSessionId(
|
||||
function ( session ) {
|
||||
self.set( "sessionId", session.id );
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
updateRequests : function ( data, env ) {
|
||||
var reqs = _.sortBy( data, function ( item ) {
|
||||
return item.searchTerm;
|
||||
});
|
||||
this.set( "requests", reqs );
|
||||
},
|
||||
|
||||
addRequest : function ( data, env ) {
|
||||
this.askForUpdate();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
define( [
|
||||
'backbone'
|
||||
],
|
||||
function ( Backbone ) {
|
||||
"use strict";
|
||||
|
||||
return function( defaults, subscriptions ) {
|
||||
var model = new (Backbone.Model.extend( {
|
||||
|
||||
initialize : function () {
|
||||
_.bindAll( this );
|
||||
this.subscriptions = [];
|
||||
},
|
||||
|
||||
dispose : function () {
|
||||
if( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( subscription ) {
|
||||
subscription.unsubscribe();
|
||||
} );
|
||||
}
|
||||
this.clear( { silent : true } );
|
||||
}
|
||||
}))( defaults );
|
||||
|
||||
_.each( subscriptions, function( sub ){
|
||||
sub.withContext( model );
|
||||
});
|
||||
|
||||
model.subscriptions = subscriptions ;
|
||||
|
||||
return model;
|
||||
};
|
||||
} );
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
define( [
|
||||
'backbone',
|
||||
'postal'
|
||||
],
|
||||
function ( Backbone, postal ) {
|
||||
"use strict";
|
||||
|
||||
var RawMessage = Backbone.Model.extend({
|
||||
defaults: {
|
||||
text: ""
|
||||
}
|
||||
});
|
||||
|
||||
return Backbone.Collection.extend( {
|
||||
|
||||
model: RawMessage,
|
||||
|
||||
initialize : function () {
|
||||
var self = this;
|
||||
postal.addWireTap(function( data, envelope ){
|
||||
var text = "";
|
||||
try {
|
||||
text = JSON.stringify( envelope );
|
||||
}
|
||||
catch ( exception ) {
|
||||
try {
|
||||
var env = _.extend( {}, envelope );
|
||||
delete env.data;
|
||||
text = JSON.stringify( env ) + "\n\t" + "JSON.stringify Error: " + exception.message;
|
||||
}
|
||||
catch ( ex ) {
|
||||
text = "Unable to parse data to JSON: " + exception;
|
||||
}
|
||||
}
|
||||
self.add({ text: text });
|
||||
});
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/container.html'
|
||||
],
|
||||
function ( $, ManagedView, template ) {
|
||||
// Using ECMAScript 5 strict mode during development. By default r.js will ignore that.
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
el : "body",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
},
|
||||
|
||||
render : function () {
|
||||
this.$el.html( this.template );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/hash-tag-count.html',
|
||||
'models/stat-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, HashTagCountModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend({
|
||||
tagName : "div",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new HashTagCountModel({ hashTags : [] }, [
|
||||
bus.stats.subscribe( "hash-tag-count", function ( data, env ) {
|
||||
if ( data.hashTags && data.hashTags.length ) {
|
||||
this.set( "hashTags", _.sortBy( data.hashTags, function ( item ) {
|
||||
return item.count * -1;
|
||||
} ) );
|
||||
}
|
||||
} ),
|
||||
bus.app.subscribe( "search.init", function () {
|
||||
this.set( "hashTags", [] );
|
||||
} )
|
||||
]);
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch );
|
||||
this.model.bind( "change", this.render );
|
||||
this.inDom = false;
|
||||
bus.stats.publish( { topic : "hash-tag-count.getLatest", data : {} } );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
// TODO: Capture scroll position and restore after render...
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
if ( !this.inDom ) {
|
||||
this.$el.appendTo( "#stats" );
|
||||
this.inDom = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
} );
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
define( [
|
||||
'underscore',
|
||||
'jquery',
|
||||
'backbone'
|
||||
],
|
||||
function ( _, $, Backbone ) {
|
||||
"use strict";
|
||||
|
||||
return Backbone.View.extend( {
|
||||
|
||||
initialize : function ( template ) {
|
||||
_.bindAll( this );
|
||||
if( template ) {
|
||||
this.template = _.template( template );
|
||||
}
|
||||
},
|
||||
|
||||
render : function () {
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
},
|
||||
|
||||
show : function () {
|
||||
this.$el.show();
|
||||
},
|
||||
|
||||
hide : function () {
|
||||
this.$el.hide();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/mention-count.html',
|
||||
'models/stat-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, MentionCountModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
tagName : "div",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new MentionCountModel({ mentions : [] },[
|
||||
bus.stats.subscribe( "mention-count", function ( data, env ) {
|
||||
if ( data.mentions && data.mentions.length ) {
|
||||
this.set( "mentions", _.sortBy( data.mentions, function ( item ) {
|
||||
return item.count * -1;
|
||||
} ) );
|
||||
}
|
||||
} ),
|
||||
bus.app.subscribe( "search.init", function () {
|
||||
this.set( "mentions", [] );
|
||||
} )
|
||||
]);
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch );
|
||||
this.model.bind( "change", this.render );
|
||||
this.inDom = false;
|
||||
bus.stats.publish( { topic : "mention-count.getLatest", data : {} } );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
// TODO: Capture scroll position and restore after render...
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
if ( !this.inDom ) {
|
||||
this.$el.appendTo( "#stats" );
|
||||
this.inDom = true;
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/mentioner-count.html',
|
||||
'models/stat-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, MentionerCountModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
tagName : "div",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new MentionerCountModel({ mentioners: [] }, [
|
||||
bus.stats.subscribe( "mentioner-count", function ( data, env ) {
|
||||
if ( data.mentioners && data.mentioners.length ) {
|
||||
this.set( "mentioners", _.sortBy( data.mentioners, function ( item ) {
|
||||
return item.count * -1;
|
||||
} ) );
|
||||
}
|
||||
} ),
|
||||
bus.app.subscribe( "search.init", function () {
|
||||
this.set( "mentioners", [] );
|
||||
} )
|
||||
]);
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch );
|
||||
this.model.bind( "change", this.render );
|
||||
this.inDom = false;
|
||||
bus.stats.publish( { topic : "mentioner-count.getLatest", data : {} } );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
// TODO: Capture scroll position and restore after render...
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
if ( !this.inDom ) {
|
||||
this.$el.appendTo( "#stats" );
|
||||
this.inDom = true;
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/menu.html',
|
||||
'bus',
|
||||
'models/menu-model'
|
||||
],
|
||||
function ( $, ManagedView, template, bus, MenuModel ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
el : "#menu",
|
||||
|
||||
events : {
|
||||
"click #btnSearch" : "updateSearch"
|
||||
},
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new MenuModel();
|
||||
this.model.bind( "change", this.updateView );
|
||||
},
|
||||
|
||||
updateSearch : function () {
|
||||
var searchTerm = this.$el.find( '#searchTerm' ).val();
|
||||
if ( searchTerm ) {
|
||||
bus.app.publish( {
|
||||
topic : "search.new.request",
|
||||
data : {
|
||||
searchTerm : searchTerm
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
updateView : function () {
|
||||
var $ownership = this.$el.find( "#search-ownership" ),
|
||||
mdlOwnership = this.model.get( "searchOwnership" ),
|
||||
dispOwnership = $ownership.text();
|
||||
if( dispOwnership && mdlOwnership !== dispOwnership ) {
|
||||
$ownership.text( this.model.get( "searchOwnership" ) )
|
||||
.animate({
|
||||
"font-size": "14pt",
|
||||
"margin-left" : "+=15"
|
||||
}, 500, function() {
|
||||
$ownership.stop().animate({
|
||||
"font-size" : "12pt",
|
||||
"margin-left" : "-=15"
|
||||
}, 400);
|
||||
});
|
||||
} else {
|
||||
$ownership.text( this.model.get( "searchOwnership" ) );
|
||||
}
|
||||
this.$el.find( "#currentSearch" ).text( this.model.get( "searchTerm" ) );
|
||||
this.$el.find( "#request-indicator" ).text( this.model.get( "requests" ) ? " *" : "" );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/profanity-percentage.html',
|
||||
'models/stat-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, ProfanityPercentageModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
tagName : "div",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new ProfanityPercentageModel({
|
||||
percentage : "",
|
||||
clean : "",
|
||||
explicit : "",
|
||||
total : ""
|
||||
},[
|
||||
bus.stats.subscribe( "profanity-percentage", function ( data, env ) {
|
||||
this.set( "percentage", data.percentage, { silent : true } );
|
||||
this.set( "clean", data.clean, { silent : true } );
|
||||
this.set( "explicit", data.explicit, { silent : true } );
|
||||
this.set( "total", data.clean + data.explicit, { silent : true } );
|
||||
this.change();
|
||||
} ),
|
||||
bus.app.subscribe( "search.init", function () {
|
||||
this.set( "percentage", "", { silent : true } );
|
||||
this.set( "clean", "", { silent : true } );
|
||||
this.set( "explicit", "", { silent : true } );
|
||||
this.set( "total", "", { silent : true } );
|
||||
this.change();
|
||||
} )
|
||||
]);
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch );
|
||||
this.model.bind( "change", this.render );
|
||||
this.inDom = false;
|
||||
bus.stats.publish( { topic : "profanity-percentage.getLatest", data : {} } );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
// TODO: Capture scroll position and restore after render...
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
if ( !this.inDom ) {
|
||||
this.$el.appendTo( "#stats" );
|
||||
this.inDom = true;
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/search-requests.html',
|
||||
'models/search-requests-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, SearchRequestsModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
el : "#requests",
|
||||
|
||||
events : {
|
||||
"click a.req-allow" : "allowSearch"
|
||||
},
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new SearchRequestsModel();
|
||||
this.model.bind( "change", this.render );
|
||||
},
|
||||
|
||||
allowSearch : function ( e ) {
|
||||
var idx = $( e.currentTarget ).attr( 'href' ),
|
||||
search = this.model.get( "requests" )[idx];
|
||||
if ( search ) {
|
||||
bus.app.publish( {
|
||||
topic : "search.new.confirm",
|
||||
data : search
|
||||
} )
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<div id="menu"></div>
|
||||
|
||||
<div id="requests"></div>
|
||||
|
||||
<div id="stats"></div>
|
||||
|
||||
<div id="wiretap"></div>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<fieldset>
|
||||
<legend>
|
||||
<div class="title">Associated Hash Tags:</div>
|
||||
</legend>
|
||||
<div class="scrollableDiv">
|
||||
<table class="hashTagTable" cellspacing="0">
|
||||
<% _.each( hashTags, function( item ) { %>
|
||||
<tr>
|
||||
<td><span><%= item.hashTag %></span></td>
|
||||
<td><span><%= item.count %></span></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<fieldset>
|
||||
<legend><span>Top Mentioned:</span></legend>
|
||||
<div class="scrollableDiv">
|
||||
<table cellspacing="0">
|
||||
<% _.each( mentions, function( mention ) { %>
|
||||
<tr>
|
||||
<td><img class="images" src="<%= mention.image %>"/></td>
|
||||
<td><span><%= mention.user %></span></td>
|
||||
<td><span><%= mention.count %></span></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<fieldset>
|
||||
<legend><span>Top Mention-ers:</span></legend>
|
||||
<div class="scrollableDiv">
|
||||
<table cellspacing="0">
|
||||
<% _.each( mentioners, function( mentioner ) { %>
|
||||
<tr>
|
||||
<td><img class="images" src="<%= mentioner.image %>"/></td>
|
||||
<td><span><%= mentioner.user %></span></td>
|
||||
<td><span><%= mentioner.count %></span></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<div class="top-bar">
|
||||
<ul class="nav-menu">
|
||||
<li><a class="ps-nav" href="">Home</a></li>
|
||||
<li><a class="ps-nav" href="requests">Requested Searches</a><span id="request-indicator"></span></li>
|
||||
<li><a class="ps-nav" href="wiretap">Show Wiretap</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="search">Searching Twitter for: <span id="currentSearch"><%= searchTerm %></span></div>
|
||||
<div class="search">
|
||||
<input type="text" id="searchTerm">
|
||||
<input id="btnSearch" type="button" value="Search">
|
||||
</div>
|
||||
<div class="search" id="search-ownership"><%= searchOwnership %></div>
|
||||
<div style="clear:both"></div>
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<fieldset>
|
||||
<legend><span>Profanity Rate:</span></legend>
|
||||
<div>
|
||||
<div>
|
||||
<span>Total:</span>
|
||||
<span><%= total %></span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Explicit:</span>
|
||||
<span><%= explicit %></span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Profanity Rate:</span>
|
||||
<span><%= percentage %>%</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<div>
|
||||
<% if( requests.length > 0 ) { %>
|
||||
<div>
|
||||
<h4>Search Requests <% if( sessionId === ownerId ) { %>(click one to give control to the requester)<% } %></h4>
|
||||
</div>
|
||||
<% _.each( requests, function ( request, idx ) { %>
|
||||
<div>(Client ID: <%= request.correlationId %>) -
|
||||
<% if( sessionId === ownerId ) { %>
|
||||
<a class="req-allow" href="<%= idx %>"><%= request.searchTerm %></a>
|
||||
<% } else { %>
|
||||
<%= request.searchTerm %>
|
||||
<% } %>
|
||||
</div>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<div><em>No other searches have been requested at this time.</em></div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<fieldset>
|
||||
<legend><span>Top Tweeters:</span></legend>
|
||||
<div class="scrollableDiv">
|
||||
<table cellspacing="0">
|
||||
<% _.each( tweeters, function( tweeter ) { %>
|
||||
<tr>
|
||||
<td><img class="images" src="<%= tweeter.image %>"/></td>
|
||||
<td><span><%= tweeter.user %></span></td>
|
||||
<td><span><%= tweeter.count %></span></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<h3>A Somewhat Pointless Feed of the postal.js Wiretap:</h3>
|
||||
<div class="scrollableDiv">
|
||||
<% if( messages.length ) { %>
|
||||
<% _.each( messages, function( msg ) { %>
|
||||
<div>
|
||||
<%= msg.text %>
|
||||
</div>
|
||||
<hr>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<em>Waiting on more data to come through the wiretap...</em>
|
||||
<% } %>
|
||||
</div>
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/tweet-count.html',
|
||||
'models/stat-model',
|
||||
'bus'
|
||||
],
|
||||
function ( $, ManagedView, template, TweetCountModel, bus ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
tagName : "div",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new TweetCountModel({
|
||||
tweeters : []
|
||||
}, [
|
||||
bus.stats.subscribe( "tweet-count", function ( data, env ) {
|
||||
if ( data.tweeters && data.tweeters.length ) {
|
||||
this.set( "tweeters", _.sortBy( data.tweeters, function ( item ) {
|
||||
return item.count * -1;
|
||||
} ) );
|
||||
}
|
||||
} ) ,
|
||||
bus.app.subscribe( "search.init", function () {
|
||||
this.set( "tweeters", [] );
|
||||
} )
|
||||
]);
|
||||
bus.app.subscribe( "search.info", this.setCurrentSearch );
|
||||
this.model.bind( "change", this.render );
|
||||
this.inDom = false;
|
||||
bus.stats.publish( { topic : "tweet-count.getLatest", data : {} } );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
// TODO: Capture scroll position and restore after render...
|
||||
this.$el.html( this.template( this.model.toJSON() ) );
|
||||
if ( !this.inDom ) {
|
||||
this.$el.appendTo( "#stats" );
|
||||
this.inDom = true;
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
define( [
|
||||
'jquery',
|
||||
'views/managed-view',
|
||||
'text!views/templates/wiretap.html',
|
||||
'models/wiretap-model'
|
||||
],
|
||||
function ( $, ManagedView, template, WiretapModel ) {
|
||||
"use strict";
|
||||
|
||||
return ManagedView.extend( {
|
||||
el : "#wiretap",
|
||||
|
||||
initialize : function () {
|
||||
ManagedView.prototype.initialize.call(this, template);
|
||||
this.model = new WiretapModel();
|
||||
this.model.bind( "add", this.render );
|
||||
},
|
||||
|
||||
render : function () {
|
||||
this.$el.html( this.template( { messages: this.model.toJSON() } ) );
|
||||
// Yes, I know - quick, dirty and ugly :-)
|
||||
this.$el.find(".scrollableDiv").animate({ scrollTop: this.$el.find(".scrollableDiv")[0].scrollHeight }, "fast");
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
_scanner = function ( regExp, text, callback ) {
|
||||
var match;
|
||||
while ( text.search( regExp ) !== -1 ) {
|
||||
match = regExp.exec( text );
|
||||
if ( match && match[1] ) {
|
||||
callback( match[1].toLowerCase() );
|
||||
}
|
||||
text = text.replace( regExp, "" );
|
||||
}
|
||||
},
|
||||
HashTagCount = function ( namespace ) {
|
||||
this.namespace = namespace;
|
||||
|
||||
this.events = {};
|
||||
|
||||
this.hashTags = { list : [], registry : {} };
|
||||
|
||||
this.lastStats = undefined;
|
||||
|
||||
};
|
||||
|
||||
HashTagCount.prototype = {
|
||||
init : function () {
|
||||
this.hashTags = { list : [], registry : {} };
|
||||
this.lastStats = undefined;
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback( data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
processNewTweets : function ( tweets ) {
|
||||
tweets.forEach( function ( tweet ) {
|
||||
this.processOtherHashTags( tweet.text );
|
||||
}, this );
|
||||
this.lastStats = { type : "HashTagCount", hashTags : this.hashTags.list };
|
||||
this.raiseEvent( this.namespace, this.lastStats );
|
||||
},
|
||||
processOtherHashTags : function ( text ) {
|
||||
_scanner( /#(\w*)/i, text, function ( hash ) {
|
||||
if ( !this.hashTags.registry[ hash ] ) {
|
||||
var obj = { hashTag : hash, count : 0 };
|
||||
this.hashTags.registry[ hash ] = obj;
|
||||
this.hashTags.list.push( obj );
|
||||
}
|
||||
this.hashTags.registry[ hash ].count++;
|
||||
}.bind( this ) );
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = HashTagCount;
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
_scanner = function ( regExp, text, callback ) {
|
||||
var match;
|
||||
while ( text.search( regExp ) !== -1 ) {
|
||||
match = regExp.exec( text );
|
||||
if ( match && match[1] ) {
|
||||
callback( match[1].toLowerCase() );
|
||||
}
|
||||
text = text.replace( regExp, "" );
|
||||
}
|
||||
},
|
||||
MentionCount = function ( namespace ) {
|
||||
this.namespace = namespace;
|
||||
|
||||
this.events = {};
|
||||
|
||||
this.mentions = { list : [], registry : {} };
|
||||
|
||||
this.userImageMap = {};
|
||||
|
||||
this.lastStats = undefined;
|
||||
};
|
||||
|
||||
MentionCount.prototype = {
|
||||
init : function () {
|
||||
this.mentions = { list : [], registry : {} };
|
||||
this.lastStats = undefined;
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback( data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
processNewTweets : function ( tweets ) {
|
||||
tweets.forEach( function ( tweet ) {
|
||||
this.userImageMap[ tweet.from_user ] = tweet.profile_image_url;
|
||||
this.processMentions( tweet );
|
||||
}, this );
|
||||
this.tryToMatchProfileImages();
|
||||
this.lastStats = { type : "MentionCount", mentions : this.mentions.list };
|
||||
this.raiseEvent( this.namespace, this.lastStats );
|
||||
},
|
||||
tryToMatchProfileImages : function () {
|
||||
_.each( this.mentions.registry, function ( v, k ) {
|
||||
if ( this.userImageMap[ k ] ) {
|
||||
v.image = this.userImageMap[ k ];
|
||||
}
|
||||
}, this );
|
||||
},
|
||||
processMentions : function ( tweet ) {
|
||||
_scanner( /@(\w*)/i, tweet.text, function ( mentioned ) {
|
||||
if ( !this.mentions.registry[mentioned] ) {
|
||||
var obj = { user : mentioned, count : 0, image : "images/default_profile_1_normal.png" };
|
||||
this.mentions.registry[mentioned] = obj;
|
||||
this.mentions.list.push( obj );
|
||||
}
|
||||
this.mentions.registry[mentioned].count++;
|
||||
}.bind( this ) );
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = MentionCount;
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
_scanner = function ( regExp, text, callback ) {
|
||||
var match;
|
||||
while ( text.search( regExp ) !== -1 ) {
|
||||
match = regExp.exec( text );
|
||||
if ( match && match[1] ) {
|
||||
callback( match[1].toLowerCase() );
|
||||
}
|
||||
text = text.replace( regExp, "" );
|
||||
}
|
||||
},
|
||||
MentionerCount = function ( namespace ) {
|
||||
this.namespace = namespace;
|
||||
|
||||
this.events = {};
|
||||
|
||||
this.mentioners = { list : [], registry : {} };
|
||||
|
||||
this.lastStats = undefined;
|
||||
};
|
||||
|
||||
MentionerCount.prototype = {
|
||||
init : function () {
|
||||
this.mentioners = { list : [], registry : {} };
|
||||
this.lastStats = undefined;
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback( data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
processNewTweets : function ( tweets ) {
|
||||
tweets.forEach( function ( tweet ) {
|
||||
this.processMentioners( tweet );
|
||||
}, this );
|
||||
this.lastStats = { type : "MentionerCount", mentioners : this.mentioners.list };
|
||||
this.raiseEvent( this.namespace, this.lastStats );
|
||||
},
|
||||
processMentioners : function ( tweet ) {
|
||||
_scanner( /@(\w*)/i, tweet.text, function ( mentioned ) {
|
||||
if ( !this.mentioners.registry[tweet.from_user] ) {
|
||||
var obj = { user : tweet.from_user, count : 0, image : tweet.profile_image_url };
|
||||
this.mentioners.registry[tweet.from_user] = obj;
|
||||
this.mentioners.list.push( obj );
|
||||
}
|
||||
this.mentioners.registry[tweet.from_user].count++;
|
||||
}.bind( this ) );
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = MentionerCount;
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
// quick hack array - not going to go regex crazy for now
|
||||
var _badWords = [
|
||||
/\bfuck\b/i,
|
||||
/\bfucks\b/i,
|
||||
/\bfucking\b/i,
|
||||
/\bfucked\b/i,
|
||||
/\bfucker\b/i,
|
||||
/\bfuckers\b/i,
|
||||
/\bass\b/i,
|
||||
/\basses\b/i,
|
||||
/\basshole\b/i,
|
||||
/\bassholes\b/i,
|
||||
/\bshit\b/i,
|
||||
/\bshitted\b/i,
|
||||
/\bshitter\b/i,
|
||||
/\bshitters\b/i,
|
||||
/\bshitting\b/i,
|
||||
/\bshithead\b/i,
|
||||
/\bcunt\b/i,
|
||||
/\bcunts\b/i,
|
||||
/\bpussy\b/i,
|
||||
/\bdick\b/i,
|
||||
/\bdicks\b/i,
|
||||
/\bdickhead\b/i,
|
||||
/\bdickheads\b/i,
|
||||
/\bdamn\b/i,
|
||||
/\bdamned\b/i,
|
||||
/\bdamning\b/i,
|
||||
/\bcock\b/i,
|
||||
/\bpenis\b/i,
|
||||
/\bfag\b/i,
|
||||
/\bbitch\b/i,
|
||||
/\bfaggot\b/,
|
||||
/\bpiss\b/,
|
||||
/\bpissing\b/
|
||||
],
|
||||
_ = require( 'underscore' ),
|
||||
ProfanityPercentage = function ( namespace ) {
|
||||
this.namespace = namespace;
|
||||
|
||||
this.events = {};
|
||||
|
||||
this.profanityStats = { clean : 0, explicit : 0 };
|
||||
|
||||
this.lastStats = undefined;
|
||||
};
|
||||
|
||||
ProfanityPercentage.prototype = {
|
||||
init : function () {
|
||||
this.profanityStats = { clean : 0, explicit : 0 };
|
||||
this.lastStats = undefined;
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback( data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
hasProfanity : function ( text ) {
|
||||
for ( var i = 0; i < _badWords.length; i++ ) {
|
||||
if ( text.search( _badWords[i] ) !== -1 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getPercentage : function () {
|
||||
var total = (this.profanityStats.clean + this.profanityStats.explicit);
|
||||
if ( total === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return ((this.profanityStats.explicit / total) * 100).toFixed( 2 );
|
||||
}
|
||||
},
|
||||
processNewTweets : function ( tweets ) {
|
||||
tweets.forEach( function ( tweet ) {
|
||||
this.profanitize( tweet );
|
||||
}, this );
|
||||
this.lastStats = {
|
||||
type : "ProfanityPercentage",
|
||||
percentage : this.getPercentage(),
|
||||
clean : this.profanityStats.clean,
|
||||
explicit : this.profanityStats.explicit
|
||||
};
|
||||
this.raiseEvent( this.namespace, this.lastStats );
|
||||
},
|
||||
profanitize : function ( tweet ) {
|
||||
if ( this.hasProfanity( tweet.text ) ) {
|
||||
this.profanityStats.explicit++;
|
||||
}
|
||||
else {
|
||||
this.profanityStats.clean++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ProfanityPercentage;
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
TweetCount = function ( namespace ) {
|
||||
this.namespace = namespace;
|
||||
|
||||
this.events = {};
|
||||
|
||||
this.tweeters = { list : [], registry : {} };
|
||||
|
||||
this.lastStats = undefined;
|
||||
};
|
||||
|
||||
TweetCount.prototype = {
|
||||
init : function () {
|
||||
this.tweeters = { list : [], registry : {} };
|
||||
this.lastStats = undefined;
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback.call( this, data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
processNewTweets : function ( tweets ) {
|
||||
tweets.forEach( function ( tweet ) {
|
||||
this.processTweetCount( tweet );
|
||||
}, this );
|
||||
this.lastStats = { type : "TweetCount", tweeters : this.tweeters.list };
|
||||
this.raiseEvent( this.namespace, this.lastStats );
|
||||
},
|
||||
processTweetCount : function ( tweet ) {
|
||||
if ( tweet.from_user ) {
|
||||
if ( !this.tweeters.registry[tweet.from_user] ) {
|
||||
var obj = {user : tweet.from_user, count : 0, image : tweet.profile_image_url};
|
||||
this.tweeters.registry[tweet.from_user] = obj;
|
||||
this.tweeters.list.push( obj );
|
||||
}
|
||||
this.tweeters.registry[tweet.from_user].count++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TweetCount;
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
var _ = require( "underscore" ),
|
||||
express = require( 'express' ),
|
||||
app = express.createServer(),
|
||||
postal = require( "./messaging/postal.js" ),
|
||||
PostalSocketHost = require( './messaging/postal.socket-host.js' ),
|
||||
sockets = require( './messaging/socket-io-provider.js' ),
|
||||
TwitterSearch = require( './twitter-search.js' ),
|
||||
collectors = require( './stat-collectors.js' ),
|
||||
BusAdapter = require( './messaging/bus-adapter.js' ),
|
||||
machina = require( './machina.js' );
|
||||
|
||||
postal.addWireTap( function ( data, envelope ) {
|
||||
if ( envelope.channel === "stats" /*|| envelope.channel === "twittersearch"*/ ) {
|
||||
var env = _.extend( {}, envelope );
|
||||
delete env.data;
|
||||
console.log( JSON.stringify( env ) );
|
||||
}
|
||||
else if ( _.include( ["postal.socket", "postal", "app", "app.events"], envelope.channel ) ) {
|
||||
console.log( JSON.stringify( envelope ) );
|
||||
}
|
||||
} );
|
||||
|
||||
// wire machina FSMs into postal automagically
|
||||
require( './messaging/machina.postal.js' )( postal, machina );
|
||||
|
||||
var TwitterSocketStats = function ( port, refreshinterval ) {
|
||||
// Stand up our express app
|
||||
app.use( "/", express.static( __dirname + '/client' ) );
|
||||
app.listen( port );
|
||||
var searchChannel = postal.channel( "twittersearch", "#" ),
|
||||
statsChannel = postal.channel( "stats", "#" ),
|
||||
appChannel = postal.channel( "statsApp", "#" ),
|
||||
fsm;
|
||||
|
||||
postal.linkChannels( { channel : "postal.socket", topic : "client.migrated"}, { channel : "statsApp", topic : "client.migrated" } );
|
||||
|
||||
fsm = new machina.Fsm( {
|
||||
|
||||
namespace : "statsApp",
|
||||
|
||||
currentSearch : {
|
||||
id : null,
|
||||
searchTerm : "{ Search Not Active }"
|
||||
},
|
||||
|
||||
searchRequests : {},
|
||||
|
||||
express : app,
|
||||
|
||||
appChannel : appChannel,
|
||||
|
||||
searchChannel : searchChannel,
|
||||
|
||||
statsChannel : statsChannel,
|
||||
|
||||
searchAgent : new BusAdapter( new TwitterSearch( refreshinterval ), searchChannel, searchChannel ),
|
||||
|
||||
requestedSearches : [],
|
||||
|
||||
stats : collectors.load( searchChannel, statsChannel ),
|
||||
|
||||
postal : postal,
|
||||
|
||||
bridge : new PostalSocketHost( postal, sockets.getSocketProvider( postal, app ) ),
|
||||
|
||||
getAvailableStats : function ( clientId ) {
|
||||
this.appChannel.publish( {
|
||||
topic : "available.topics",
|
||||
correlationId : clientId,
|
||||
data : {
|
||||
topics : _.toArray( this.stats ).map( function ( stat ) {
|
||||
return { channel : "stats", topic : stat.namespace };
|
||||
} )
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
setSearch : function ( correlationId, searchTerm ) {
|
||||
this.currentSearch = {
|
||||
id : correlationId,
|
||||
searchTerm : searchTerm
|
||||
};
|
||||
this.searchAgent.search( searchTerm );
|
||||
this.removeSearchRequest( correlationId, searchTerm );
|
||||
this.appChannel.publish( {
|
||||
topic : "search.info",
|
||||
data : this.currentSearch
|
||||
} );
|
||||
},
|
||||
|
||||
getSearchInfo : function ( env ) {
|
||||
this.appChannel.publish( {
|
||||
topic : "search.info",
|
||||
correlationId : env.correlationId,
|
||||
data : this.currentSearch
|
||||
} );
|
||||
},
|
||||
|
||||
getSearchRequests : function ( env ) {
|
||||
env || (env = {});
|
||||
this.appChannel.publish( {
|
||||
topic : "search.requests",
|
||||
correlationId : env.correlationId,
|
||||
data : this.requestedSearches
|
||||
} );
|
||||
},
|
||||
|
||||
addSearchRequest : function ( correlationId, searchTerm ) {
|
||||
if ( !_.any( this.searchRequests, function ( item ) {
|
||||
return item.correlationId === request.correlationId &&
|
||||
item.searchTerm === request.searchTerm;
|
||||
} ) ) {
|
||||
this.requestedSearches.push( { correlationId : correlationId, searchTerm : searchTerm } );
|
||||
}
|
||||
},
|
||||
|
||||
removeSearchRequest : function ( correlationId, searchTerm ) {
|
||||
if ( _.any( this.requestedSearches, function ( item ) {
|
||||
return item.correlationId === correlationId && item.searchTerm === searchTerm;
|
||||
} ) ) {
|
||||
this.requestedSearches = _.filter( this.requestedSearches, function ( item ) {
|
||||
return item.correlationId !== correlationId && item.searchTerm !== searchTerm;
|
||||
} );
|
||||
this.getSearchRequests();
|
||||
}
|
||||
},
|
||||
|
||||
states : {
|
||||
uninitialized : {
|
||||
start : function () {
|
||||
this.transition( "notSearching" );
|
||||
},
|
||||
"*" : function () {
|
||||
this.deferUntilTransition();
|
||||
}
|
||||
},
|
||||
notSearching : {
|
||||
"search.new.request" : function ( data, envelope ) {
|
||||
this.setSearch( envelope.correlationId, data.searchTerm );
|
||||
this.transition( "searching" );
|
||||
},
|
||||
"get.search.info" : function ( data, env ) {
|
||||
this.getSearchInfo( env );
|
||||
},
|
||||
"get.available" : function ( data, envelope ) {
|
||||
this.getAvailableStats( envelope.correlationId );
|
||||
}
|
||||
},
|
||||
searching : {
|
||||
"search.new.request" : function ( data, envelope ) {
|
||||
if ( envelope.correlationId === this.currentSearch.id ) {
|
||||
this.setSearch( envelope.correlationId, data.searchTerm );
|
||||
}
|
||||
else {
|
||||
this.addSearchRequest( envelope.correlationId, data.searchTerm );
|
||||
this.appChannel.publish( {
|
||||
topic : "search.new.ask",
|
||||
data : {
|
||||
correlationId : envelope.correlationId,
|
||||
searchTerm : data.searchTerm
|
||||
}
|
||||
} );
|
||||
}
|
||||
},
|
||||
"search.new.confirm" : function ( data, envelope ) {
|
||||
if ( envelope.correlationId === this.currentSearch.id ) {
|
||||
this.setSearch( data.correlationId, data.searchTerm );
|
||||
}
|
||||
},
|
||||
"get.search.info" : function ( data, env ) {
|
||||
this.getSearchInfo( env );
|
||||
},
|
||||
"get.search.requests" : function ( data, env ) {
|
||||
this.getSearchRequests( env );
|
||||
},
|
||||
"get.available" : function ( data, envelope ) {
|
||||
this.getAvailableStats( envelope.correlationId );
|
||||
},
|
||||
"client.migrated" : function ( data, envelope ) {
|
||||
if ( data.lastSessionId === this.currentSearch.id ) {
|
||||
this.currentSearch.id = data.sessionId;
|
||||
}
|
||||
},
|
||||
"client.disconnect" : function ( data ) {
|
||||
if ( this.currentSearch.id === data.sessionId ) {
|
||||
if( this.requestedSearches.length ) {
|
||||
var newSearch = this.requestedSearches.shift();
|
||||
this.setSearch( newSearch.correlationId, newSearch.searchTerm );
|
||||
} else {
|
||||
this.transition("notSearching");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
postal.subscribe( {
|
||||
channel : "postal.socket",
|
||||
topic : "client.disconnect",
|
||||
callback: function( data, env ) {
|
||||
fsm.handle(env.topic, data );
|
||||
}
|
||||
} );
|
||||
|
||||
return fsm;
|
||||
};
|
||||
|
||||
var x = module.exports = new TwitterSocketStats( 8002, 7000 );
|
||||
|
||||
x.on( "*", function ( evnt, data ) {
|
||||
console.log( "FSM Event: " + evnt + " - " + JSON.stringify( [].slice.call( arguments, 1 ) ) );
|
||||
} );
|
||||
|
||||
x.handle( "start" );
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
var _ = require( 'underscore' );
|
||||
/*
|
||||
machina.js
|
||||
Author: Jim Cowart
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.1.0
|
||||
*/
|
||||
|
||||
var slice = [].slice,
|
||||
NEXT_TRANSITION = "transition",
|
||||
NEXT_HANDLER = "handler",
|
||||
transformEventListToObject = function ( eventList ) {
|
||||
var obj = {};
|
||||
_.each( eventList, function ( evntName ) {
|
||||
obj[evntName] = [];
|
||||
} );
|
||||
return obj;
|
||||
},
|
||||
parseEventListeners = function ( evnts ) {
|
||||
var obj = evnts;
|
||||
if ( _.isArray( evnts ) ) {
|
||||
obj = transformEventListToObject( evnts );
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
utils = {
|
||||
makeFsmNamespace : (function () {
|
||||
var machinaCount = 0;
|
||||
return function () {
|
||||
return "fsm." + machinaCount++;
|
||||
};
|
||||
})(),
|
||||
getDefaultOptions : function () {
|
||||
return {
|
||||
initialState : "uninitialized",
|
||||
eventListeners : {
|
||||
"*" : []
|
||||
},
|
||||
states : {},
|
||||
eventQueue : [],
|
||||
namespace : utils.makeFsmNamespace()
|
||||
};
|
||||
}
|
||||
},
|
||||
Fsm = function ( options ) {
|
||||
var opt, initialState, defaults = utils.getDefaultOptions();
|
||||
if ( options ) {
|
||||
if ( options.eventListeners ) {
|
||||
options.eventListeners = parseEventListeners( options.eventListeners );
|
||||
}
|
||||
if ( options.messaging ) {
|
||||
options.messaging = _.extend( {}, defaults.messaging, options.messaging );
|
||||
}
|
||||
}
|
||||
opt = _.extend( defaults, options || {} );
|
||||
initialState = opt.initialState;
|
||||
delete opt.initialState;
|
||||
_.extend( this, opt );
|
||||
|
||||
this.state = undefined;
|
||||
this._priorAction = "";
|
||||
this._currentAction = "";
|
||||
if ( initialState ) {
|
||||
this.transition( initialState );
|
||||
}
|
||||
machina.fireEvent( "newFsm", this );
|
||||
};
|
||||
|
||||
Fsm.prototype.fireEvent = function ( eventName ) {
|
||||
var args = arguments;
|
||||
_.each( this.eventListeners["*"], function ( callback ) {
|
||||
try {
|
||||
callback.apply( this, slice.call( args, 0 ) );
|
||||
} catch ( exception ) {
|
||||
if ( console && typeof console.log !== "undefined" ) {
|
||||
console.log( exception.toString() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
_.each( this.eventListeners[eventName], function ( callback ) {
|
||||
try {
|
||||
callback.apply( this, slice.call( args, 1 ) );
|
||||
} catch ( exception ) {
|
||||
if ( console && typeof console.log !== "undefined" ) {
|
||||
console.log( exception.toString() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.handle = function ( msgType ) {
|
||||
// vars to avoid a "this." fest
|
||||
var states = this.states, current = this.state, args = slice.call( arguments, 0 ), handlerName;
|
||||
this.currentActionArgs = args;
|
||||
if ( states[current] && (states[current][msgType] || states[current]["*"]) ) {
|
||||
handlerName = states[current][msgType] ? msgType : "*";
|
||||
this._currentAction = current + "." + handlerName;
|
||||
this.fireEvent.apply( this, ["Handling"].concat( args ) );
|
||||
states[current][handlerName].apply( this, args.slice( 1 ) );
|
||||
this.fireEvent.apply( this, ["Handled"].concat( args ) );
|
||||
this._priorAction = this._currentAction;
|
||||
this._currentAction = "";
|
||||
this.processQueue( NEXT_HANDLER );
|
||||
}
|
||||
else {
|
||||
this.fireEvent.apply( this, ["NoHandler"].concat( args ) );
|
||||
}
|
||||
this.currentActionArgs = undefined;
|
||||
};
|
||||
|
||||
Fsm.prototype.transition = function ( newState ) {
|
||||
if ( this.states[newState] ) {
|
||||
var oldState = this.state;
|
||||
this.state = newState;
|
||||
if ( this.states[newState]._onEnter ) {
|
||||
this.states[newState]._onEnter.call( this );
|
||||
}
|
||||
this.fireEvent.apply( this, ["Transitioned", oldState, this.state ] );
|
||||
this.processQueue( NEXT_TRANSITION );
|
||||
return;
|
||||
}
|
||||
this.fireEvent.apply( this, ["InvalidState", this.state, newState ] );
|
||||
};
|
||||
|
||||
Fsm.prototype.processQueue = function ( type ) {
|
||||
var filterFn = type === NEXT_TRANSITION ?
|
||||
function ( item ) {
|
||||
return item.type === NEXT_TRANSITION && ((!item.untilState) || (item.untilState === this.state));
|
||||
} :
|
||||
function ( item ) {
|
||||
return item.type === NEXT_HANDLER;
|
||||
},
|
||||
toProcess = _.filter( this.eventQueue, filterFn, this );
|
||||
this.eventQueue = _.difference( this.eventQueue, toProcess );
|
||||
_.each( toProcess, function ( item, index ) {
|
||||
this.handle.apply( this, item.args );
|
||||
}, this );
|
||||
};
|
||||
|
||||
Fsm.prototype.deferUntilTransition = function ( stateName ) {
|
||||
if ( this.currentActionArgs ) {
|
||||
var queued = { type : NEXT_TRANSITION, untilState : stateName, args : this.currentActionArgs };
|
||||
this.eventQueue.push( queued );
|
||||
this.fireEvent.apply( this, [ "Deferred", this.state, queued ] );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.deferUntilNextHandler = function () {
|
||||
if ( this.currentActionArgs ) {
|
||||
var queued = { type : NEXT_TRANSITION, args : this.currentActionArgs };
|
||||
this.eventQueue.push( queued );
|
||||
this.fireEvent.apply( this, [ "Deferred", this.state, queued ] );
|
||||
}
|
||||
};
|
||||
|
||||
Fsm.prototype.on = function ( eventName, callback ) {
|
||||
if ( !this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = [];
|
||||
}
|
||||
this.eventListeners[eventName].push( callback );
|
||||
};
|
||||
|
||||
Fsm.prototype.off = function ( eventName, callback ) {
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
|
||||
}
|
||||
};
|
||||
|
||||
var machina = {
|
||||
Fsm : Fsm,
|
||||
bus : undefined,
|
||||
utils : utils,
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = [];
|
||||
}
|
||||
this.eventListeners[eventName].push( callback );
|
||||
},
|
||||
off : function ( eventName, callback ) {
|
||||
if ( this.eventListeners[eventName] ) {
|
||||
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
|
||||
}
|
||||
},
|
||||
fireEvent : function ( eventName ) {
|
||||
var i = 0, len, args = arguments, listeners = this.eventListeners[eventName];
|
||||
if ( listeners && listeners.length ) {
|
||||
_.each( listeners, function ( callback ) {
|
||||
callback.apply( null, slice.call( args, 1 ) );
|
||||
} );
|
||||
}
|
||||
},
|
||||
eventListeners : {
|
||||
newFsm : []
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = machina;
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
module.exports = function ( target, searchChannel, statsChannel ) {
|
||||
target.bus = {
|
||||
subscriptions : [],
|
||||
publishers : [
|
||||
target.on( "newTweets", function ( data ) {
|
||||
statsChannel.publish( { topic : "newTweets", data : data } );
|
||||
} ),
|
||||
target.on( "search.current", function ( data ) {
|
||||
searchChannel.publish( { topic : "search.current", data : data } );
|
||||
} ),
|
||||
target.on( "search.nodata", function ( data ) {
|
||||
searchChannel.publish( { topic : "search.nodata", data : data } );
|
||||
} ),
|
||||
target.on( "search.init", function ( data ) {
|
||||
searchChannel.publish( { topic : "search.init", data : data } );
|
||||
} )
|
||||
]
|
||||
};
|
||||
return target;
|
||||
};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
module.exports = function ( target, searchChannel, statChannel ) {
|
||||
target.bus = {
|
||||
subscriptions : [
|
||||
searchChannel.subscribe( "search.init", target.init ).withContext( target ),
|
||||
searchChannel.subscribe( "newTweets", target.processNewTweets ).withContext( target ),
|
||||
statChannel.subscribe( target.namespace + ".getLatest",
|
||||
function ( data, env ) {
|
||||
console.log( "GET LATEST FOR: " + target.namespace );
|
||||
statChannel.publish( {
|
||||
topic : target.namespace,
|
||||
data : target.lastStats,
|
||||
correlationId : env.correlationId
|
||||
} )
|
||||
} ).withContext( target )
|
||||
],
|
||||
publishers : [
|
||||
target.on( target.namespace, function ( data ) {
|
||||
statChannel.publish( { topic : target.namespace, data : data } );
|
||||
} )
|
||||
]
|
||||
};
|
||||
return target;
|
||||
};
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
machina.postal.js
|
||||
Author: Jim Cowart
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.1.0
|
||||
*/
|
||||
|
||||
module.exports = function ( postal, machina ) {
|
||||
var bus = machina.bus = {
|
||||
channels : {},
|
||||
config : {
|
||||
handlerChannelSuffix : "",
|
||||
eventChannelSuffix : ".events"
|
||||
},
|
||||
wireHandlersToBus : function ( fsm, handlerChannel ) {
|
||||
bus.channels[handlerChannel]._subscriptions.push(
|
||||
bus.channels[handlerChannel].subscribe( "#", function ( data, envelope ) {
|
||||
fsm.handle.call( fsm, envelope.topic, data, envelope );
|
||||
} )
|
||||
);
|
||||
},
|
||||
wireEventsToBus : function ( fsm, eventChannel ) {
|
||||
var publisher = bus.channels[eventChannel].eventPublisher = function () {
|
||||
try {
|
||||
bus.channels[eventChannel].publish( { topic : arguments[0], data : arguments[1] || {} } );
|
||||
} catch ( exception ) {
|
||||
if ( console && typeof console.log !== "undefined" ) {
|
||||
console.log( exception.toString() );
|
||||
}
|
||||
}
|
||||
};
|
||||
fsm.on( "*", publisher );
|
||||
},
|
||||
wireUp : function ( fsm ) {
|
||||
var handlerChannel = fsm.namespace + bus.config.handlerChannelSuffix,
|
||||
eventChannel = fsm.namespace + bus.config.eventChannelSuffix;
|
||||
bus.channels[handlerChannel] = postal.channel( { channel : handlerChannel } );
|
||||
bus.channels[eventChannel] = postal.channel( { channel : eventChannel } );
|
||||
bus.channels[handlerChannel]._subscriptions = [];
|
||||
bus.wireHandlersToBus( fsm, handlerChannel );
|
||||
bus.wireEventsToBus( fsm, eventChannel );
|
||||
}
|
||||
};
|
||||
machina.on( "newFsm", bus.wireUp );
|
||||
};
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
module.exports = function ( _, postal ) {
|
||||
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,517 +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.7.3
|
||||
*/
|
||||
// This is the node.js version of postal.js
|
||||
// If you need the web client lib version, go to http://github.com/ifandelse/postal.js
|
||||
var _ = require( 'underscore' );
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_PRIORITY = 50,
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal",
|
||||
NO_OP = function () {
|
||||
};
|
||||
|
||||
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 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 ChannelDefinition = function ( channelName, defaultTopic ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
};
|
||||
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.priority = DEFAULT_PRIORITY;
|
||||
this.constraints = new Array( 0 );
|
||||
this.maxCalls = DEFAULT_DISPOSEAFTER;
|
||||
this.onHandled = NO_OP;
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
|
||||
postal.configuration.bus.subscribe( this );
|
||||
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( fn, 0, data );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
|
||||
var fn = this.onHandled;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe( this );
|
||||
}, this ) );
|
||||
|
||||
this.onHandled = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once: function() {
|
||||
this.disposeAfter(1);
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withPriority : function ( priority ) {
|
||||
if ( _.isNaN( priority ) ) {
|
||||
throw "Priority must be a number";
|
||||
}
|
||||
this.priority = priority;
|
||||
postal.configuration.bus.changePriority( this );
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
changePriority : function ( subDef ) {
|
||||
var idx, found;
|
||||
if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic] = _.without( this.subscriptions[subDef.channel][subDef.topic], subDef );
|
||||
idx = this.subscriptions[subDef.channel][subDef.topic].length - 1;
|
||||
for ( ; idx >= 0; idx-- ) {
|
||||
if ( this.subscriptions[subDef.channel][subDef.topic][idx].priority <= subDef.priority ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice( idx + 1, 0, subDef );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].unshift( subDef );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call(subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
subDef.onHandled();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : new Array( 0 ),
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
for ( ; idx < len; idx++ ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
|
||||
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
if ( !_.isArray( sources ) ) {
|
||||
sources = [sources];
|
||||
}
|
||||
if ( !_.isArray( destinations ) ) {
|
||||
destinations = [destinations];
|
||||
}
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ],
|
||||
result = [];
|
||||
if ( arguments.length === 1 ) {
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ];
|
||||
}
|
||||
else {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = postal;
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
machina = require( '../machina.js' );
|
||||
|
||||
/*
|
||||
A RemoteClientProxy instance is a finite state machine that manages
|
||||
how a socket is used, and how it is (or isn't) allowed to interact
|
||||
with the server-side instance of postal, depending on the state
|
||||
related to the connection and the client on the other end of the socket.
|
||||
*/
|
||||
var RemoteClientProxy = function ( postal, socketClient, bridge ) {
|
||||
// helper function to handle when a socket client wants
|
||||
// to set up a subscription to server-side messages
|
||||
var subscribe = function ( data ) {
|
||||
var self = this;
|
||||
if ( !self.subscriptions[ data.channel ] ) {
|
||||
self.subscriptions[ data.channel ] = {};
|
||||
}
|
||||
if ( !self.subscriptions[ data.channel ][ data.topic ] ) {
|
||||
self.subscriptions[ data.channel ][ data.topic ] =
|
||||
postal.subscribe( {
|
||||
channel : data.channel,
|
||||
topic : data.topic,
|
||||
callback : function ( d, e ) {
|
||||
self.handle( "socketTransmit", d, e );
|
||||
}
|
||||
} ).withConstraint( function ( data, env ) {
|
||||
if ( !env.correlationId ) {
|
||||
return true;
|
||||
}
|
||||
return env.correlationId === self.sessionId;
|
||||
} );
|
||||
}
|
||||
},
|
||||
// The actual FSM managing the socketClient
|
||||
fsm = new machina.Fsm( {
|
||||
// handle to the socket client wrapper
|
||||
socket : socketClient,
|
||||
|
||||
// object used to store (and lookup) subscriptions owned by this connection/client
|
||||
subscriptions : {},
|
||||
|
||||
// method to allow a RemoteClientProxy to hand off state to another RemoteClientProxy
|
||||
// as part of a migration
|
||||
getClientState : function () {
|
||||
return {
|
||||
queuedMsgs : _.filter( this.eventQueue, function ( item ) {
|
||||
return item.args[0] === "socketTransmit";
|
||||
} )
|
||||
};
|
||||
},
|
||||
|
||||
sessionId : null,
|
||||
|
||||
states : {
|
||||
// we start here after an explicit transition from uninitialized
|
||||
// the only message we are interested in is "clientId" - in which
|
||||
// the client tells us who they are, and we determine if we need
|
||||
// to move through a migration first, or go directly to 'connected'
|
||||
connecting : {
|
||||
"*" : function () {
|
||||
this.deferUntilTransition();
|
||||
},
|
||||
clientId : function ( session ) {
|
||||
console.log( "SESSION STUFF: " + JSON.stringify( session ) );
|
||||
this.sessionId = session.id;
|
||||
if ( bridge.enableMigration && session.lastId && session.lastId !== session.id ) {
|
||||
this.lastSessionId = session.lastId;
|
||||
this.transition( "migrating" );
|
||||
}
|
||||
else {
|
||||
this.transition( "connected" );
|
||||
}
|
||||
}
|
||||
},
|
||||
// we're going to get any relevant state from the old RemoteClientProxy
|
||||
// and apply it to this instance. This includes the publishing of
|
||||
// messages that got queued up at the old RemoteClientProxy.
|
||||
migrating : {
|
||||
// We tell the client to handle it's migration work (if any) first.
|
||||
_onEnter : function () {
|
||||
console.log( "TELLING CLIENT TO MIGRATE" )
|
||||
this.socket.migrateClientSubscriptions();
|
||||
},
|
||||
// Once the client has completed its migration tasks, it will
|
||||
// publish a message that triggers this handler. This is where we
|
||||
// transfer queued messages from the old RemoteClientProxy, etc.
|
||||
migrationComplete : function ( data ) {
|
||||
if ( bridge.clientMap[ this.lastSessionId ] ) {
|
||||
console.log( "LAST SESSION ID EXISTS" )
|
||||
var priorState = bridge.clientMap[ this.lastSessionId ].getClientState();
|
||||
_.each( priorState.queuedMsgs, function ( item ) {
|
||||
this.eventQueue.unshift( item );
|
||||
}, this );
|
||||
}
|
||||
postal.publish( {
|
||||
channel : "postal.socket",
|
||||
topic : "client.migrated",
|
||||
data : {
|
||||
sessionId : this.socket.id,
|
||||
lastSessionId : this.lastSessionId
|
||||
}
|
||||
} );
|
||||
this.transition( "connected" );
|
||||
},
|
||||
subscribe : subscribe,
|
||||
"*" : function () {
|
||||
this.deferUntilTransition();
|
||||
}
|
||||
},
|
||||
// We're online and this is how to handle it....
|
||||
connected : {
|
||||
_onEnter : function () {
|
||||
bridge.clientMap[ this.sessionId ] = this;
|
||||
this.socket.confirmClientIdentified( this.sessionId );
|
||||
},
|
||||
disconnect : function () {
|
||||
this.transition( "disconnected" );
|
||||
},
|
||||
subscribe : subscribe,
|
||||
unsubscribe : function ( data ) {
|
||||
if ( this.subscriptions[ data.channel ] && this.subscriptions[ data.channel ][ data.topic ] ) {
|
||||
this.subscriptions[ data.channel ] && this.subscriptions[ data.channel ][ data.topic ].unsubscribe();
|
||||
}
|
||||
},
|
||||
publish : function ( envelope ) {
|
||||
postal.publish( envelope );
|
||||
},
|
||||
socketTransmit : function ( data, envelope ) {
|
||||
this.socket.publish.call( this.socket, data, envelope );
|
||||
}
|
||||
},
|
||||
disconnected : {
|
||||
_onEnter : function () {
|
||||
postal.publish( {
|
||||
channel : "postal.socket",
|
||||
topic : "client.disconnect",
|
||||
data : {
|
||||
sessionId : this.sessionId
|
||||
}
|
||||
} );
|
||||
},
|
||||
"*" : function () {
|
||||
this.deferUntilTransition();
|
||||
},
|
||||
socketTransmit : function () {
|
||||
this.deferUntilTransition( "connected" );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
// These are all the events from our Socket Client wrapper to which we need to listen.
|
||||
_.each( [ "disconnect", "subscribe", "unsubscribe", "publish", "clientId", "migrationComplete" ], function ( evnt ) {
|
||||
socketClient.on( evnt, function ( data ) {
|
||||
fsm.handle( evnt, data );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This is currently here for debugging purposes only
|
||||
fsm.on( "*", function ( evnt, data ) {
|
||||
var args = [].slice.call( arguments, 1 );
|
||||
if ( evnt === "Deferred" || args[0] === "socketTransmit" ) {
|
||||
console.log( "Socket FSM: " + evnt + " - " + JSON.stringify( args[0] ) );
|
||||
}
|
||||
else {
|
||||
console.log( "Socket FSM: " + evnt + " - " + JSON.stringify( args ) );
|
||||
}
|
||||
} );
|
||||
|
||||
// every RemoteClientProxy gets a private subscription that can talk to the client
|
||||
// apart from any application-level channels. This is a "utility" conduit.
|
||||
fsm.subscriptions._direct = {
|
||||
_private : postal.subscribe( {
|
||||
channel : socketClient.id,
|
||||
topic : "#",
|
||||
callback : function ( d, e ) {
|
||||
fsm.handle( "socketTransmit", d, e );
|
||||
}
|
||||
} )
|
||||
};
|
||||
|
||||
// we explicitly start the FSM now that we've wired everything up, etc.
|
||||
fsm.transition( "connecting" );
|
||||
return fsm;
|
||||
};
|
||||
|
||||
var PostalSocketHost = function ( postal, socketProvider ) {
|
||||
var self = this;
|
||||
|
||||
// if enableMigration === true, then clients are allowed to migrate
|
||||
// from an older session id to a new one. This can happen when the
|
||||
// client's connection is lost, but the old session id is kept around
|
||||
// in memory or local storage, etc. When the client reconnects, it
|
||||
// will transmit the current and previous (if applicable) session ids.
|
||||
// If this is set to true, then the migration will take place - which
|
||||
// includes transferring existing subscriptions, as well as the delivery
|
||||
// of messages sent to the old session id proxy after the client lost
|
||||
// connection.
|
||||
self.enableMigration = true;
|
||||
|
||||
|
||||
self.channel = postal.channel( "postal.socket", "client.connect" );
|
||||
|
||||
// array of clients - confirmed or not
|
||||
self.clients = [];
|
||||
|
||||
// once a client has been marked as "identified", they are added to
|
||||
// this object, with the session id as the key.
|
||||
self.clientMap = {};
|
||||
|
||||
// how to clean up a client
|
||||
self.removeClient = function ( id ) {
|
||||
if ( self.clientMap[ id ] ) {
|
||||
_.each( self.clientMap[ id ].subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( sub ) {
|
||||
console.log( "unsubscribing: " + sub.channel + " - " + sub.topic );
|
||||
sub.unsubscribe();
|
||||
} );
|
||||
} );
|
||||
delete self.clientMap[ id ];
|
||||
}
|
||||
self.clients = _.filter( self.clients, function ( item ) {
|
||||
return item.sessionId !== id;
|
||||
} );
|
||||
};
|
||||
|
||||
|
||||
self.provider = socketProvider;
|
||||
|
||||
// when our provider indicates a new socket has connected, we pass
|
||||
// the socket wrapper to an fsm (RemoteClientProxy) to be managed by it.
|
||||
self.provider.on( "connect", function ( socket ) {
|
||||
self.clients.push( new RemoteClientProxy( postal, socket, self ) );
|
||||
} );
|
||||
|
||||
// when a client disconnects, if enableMigration is true, we don't delete
|
||||
// the old session until it has been used in a migration
|
||||
self.channel.subscribe( "client.disconnect", function ( data, envelope ) {
|
||||
if ( !self.enableMigration ) {
|
||||
self.removeClient( data.sessionId );
|
||||
}
|
||||
} );
|
||||
|
||||
// if a session has been used in a migration and is no longer needed, remove it
|
||||
self.channel.subscribe( "client.migrated", function ( data, envelope ) {
|
||||
self.removeClient( data.lastSessionId );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = PostalSocketHost;
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
var _ = require( 'underscore' ),
|
||||
EventEmitter = require( 'events' ).EventEmitter,
|
||||
util = require( 'util' );
|
||||
|
||||
module.exports = {
|
||||
getSocketProvider : function ( postal, app, options ) {
|
||||
/*
|
||||
Socket client wrappers must provide the following:
|
||||
1.) an "id" property which returns the session identifier for the connection
|
||||
2.) EventEmitter's "on" method call (to subscribe callbacks to events)
|
||||
3.) publish the following events (consumed by the postal socket host:
|
||||
a.) disconnect - triggered when the socket disconnects.
|
||||
b.) publish - triggered when the client at the other end of the socket
|
||||
publishes a message. The callback(s) for this event should receive
|
||||
a valid postal.js message envelope, and should ideally include a
|
||||
correlationId that matches the client wrapper id.
|
||||
{
|
||||
channel: "someChannel",
|
||||
topic: "some.topic",
|
||||
data: {
|
||||
greeting: "Oh, hai!"
|
||||
},
|
||||
timeStamp: "2012-04-17T07:11:14.731Z",
|
||||
correlationId: "195371348624696856"
|
||||
}
|
||||
c.) unsubscribe - triggered when the client at the other end of the socket
|
||||
specified that they want to unsubscribe a server-side subscription tied
|
||||
to the socket. The callback(s) for this event should receive an object
|
||||
that contains the channel name and topic to unsubscribe:
|
||||
{ channel: "someChannel", topic: "some.topic" }
|
||||
d.) subscribe - triggered when the client at the other end of the socket
|
||||
specifies that they want to subscribe on the server-side, and have messages
|
||||
forwarded over the socket down the client. The callback(s) for this event
|
||||
should receive an object that contains the channel name and topic to which
|
||||
the client wants to subscribe:
|
||||
{ channel: "someChannel", topic: "some.topic" }
|
||||
e.) clientId - triggered when the client sends a message containing the current
|
||||
and previous (if one exists) session id. The callback(s) for this event
|
||||
should receive an object similar to the following:
|
||||
{ sessionId: "5072013211555684761", lastSessionId: "15075244651115973710" }
|
||||
f.) migrationComplete - triggered when the client is done migrating any state from
|
||||
an old session id to the current one. The callbacks do not need an arg provided.
|
||||
|
||||
*/
|
||||
var SocketIoClient = function ( socket ) {
|
||||
var self = this;
|
||||
EventEmitter.call( self );
|
||||
|
||||
self.socket = socket;
|
||||
Object.defineProperty( self, "id", {
|
||||
get : function () {
|
||||
return self.socket.id;
|
||||
},
|
||||
enumerable : true
|
||||
} );
|
||||
|
||||
// wire up what to do when this socket disconnects
|
||||
self.socket.on( 'disconnect', function () {
|
||||
self.emit( "disconnect", self );
|
||||
} );
|
||||
|
||||
// set up event hooks for postal specific events
|
||||
_.each( [ "publish", "unsubscribe", "subscribe", "clientId", "migrationComplete" ], function ( evnt ) {
|
||||
self.socket.on( "postal." + evnt, function ( data ) {
|
||||
self.emit( evnt, data );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
util.inherits( SocketIoClient, EventEmitter );
|
||||
|
||||
SocketIoClient.prototype.publish = function ( data, envelope ) {
|
||||
this.socket.emit( "postal.socket.remote", envelope );
|
||||
};
|
||||
SocketIoClient.prototype.confirmClientIdentified = function ( uid ) {
|
||||
this.socket.emit( "postal.socket.identified", { uid : uid } );
|
||||
};
|
||||
SocketIoClient.prototype.migrateClientSubscriptions = function () {
|
||||
this.socket.emit( "postal.socket.migration", {} );
|
||||
};
|
||||
|
||||
/*
|
||||
The Socket Provider is a wrapper around the framework being
|
||||
used to provide websocket functionality to postal.socket.
|
||||
The implementation below wraps socket.io.
|
||||
*/
|
||||
var SocketIoProvider = function ( webApp, opt ) {
|
||||
var opt = opt || {},
|
||||
io = require( 'socket.io' ).listen( webApp ),
|
||||
self = this;
|
||||
io.set( 'log level', opt.ioLogLevel || 1 );
|
||||
|
||||
EventEmitter.call( self );
|
||||
|
||||
self.events = {
|
||||
"connect" : []
|
||||
};
|
||||
|
||||
// We listen for when socket.io detects a new socket connection.
|
||||
// Then we take that socket instance and wrap it in our SocketIoClient
|
||||
// wrapper. The PostalSocketHost receives an instance of this
|
||||
// SocketIoProvider object and subscribes to the "connect" event.
|
||||
io.sockets.on( "connection", function ( socket ) {
|
||||
var _socket = new SocketIoClient( socket );
|
||||
self.emit( "connect", _socket );
|
||||
} );
|
||||
};
|
||||
|
||||
util.inherits( SocketIoProvider, EventEmitter );
|
||||
|
||||
return new SocketIoProvider( app, options );
|
||||
}
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"author": "Jim Cowart <jim@ifandelse.com>",
|
||||
"name": "postal.js-node.js-example",
|
||||
"version": "0.1.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/ifandelse/postal.js.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express" : "2.5.11",
|
||||
"socket.io" : "0.9.10",
|
||||
"underscore" : "1.3.3"
|
||||
},
|
||||
"optionalDependencies": {},
|
||||
"engines": {
|
||||
"node": "0.6.14"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
var fs = require( 'fs' ),
|
||||
path = require( 'path' ),
|
||||
Adapter = require( './messaging/collector-adapter.js' ),
|
||||
collectorPath = './collectors';
|
||||
|
||||
module.exports = {
|
||||
load : function ( searchChannel, statsChannel ) {
|
||||
return fs.readdirSync( collectorPath ).reduce( function ( accum, collector ) {
|
||||
try {
|
||||
var instance = new ( require( path.join( path.resolve( collectorPath ), collector ) ) )( collector.replace( /.js\b/, "" ) );
|
||||
accum[ collector ] = new Adapter( instance, searchChannel, statsChannel );
|
||||
}
|
||||
catch ( ex ) {
|
||||
console.log( "Unable to load '" + collector + "' - " + ex );
|
||||
}
|
||||
return accum;
|
||||
}, {} );
|
||||
}
|
||||
};
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
var query = require( 'querystring' ),
|
||||
http = require( 'http' );
|
||||
|
||||
var client = http.createClient( 80, "search.twitter.com" );
|
||||
|
||||
var TwitterSearch = function ( refreshInterval ) {
|
||||
this.events = {};
|
||||
|
||||
this.timeoutFn = undefined;
|
||||
|
||||
this.searchPath = "/search.json";
|
||||
|
||||
this.refreshInterval = refreshInterval || 5000;
|
||||
|
||||
this.searchRegistry = {};
|
||||
|
||||
this.tweetRegistry = {};
|
||||
|
||||
this.defaultSearch = {};
|
||||
|
||||
this.nextSearch = undefined;
|
||||
};
|
||||
|
||||
TwitterSearch.prototype = {
|
||||
search : function ( searchTerm ) {
|
||||
clearTimeout( this.timeoutFn );
|
||||
this.raiseEvent( "search.init", searchTerm );
|
||||
this.defaultSearch = {
|
||||
q : searchTerm,
|
||||
result_type : "recent",
|
||||
rpp : 100
|
||||
};
|
||||
this.nextSearch = undefined;
|
||||
this.searchRegistry = {};
|
||||
this.tweetRegistry = {};
|
||||
this.runSearch();
|
||||
},
|
||||
on : function ( eventName, callback ) {
|
||||
if ( !this.events[ eventName ] ) {
|
||||
this.events[ eventName ] = [];
|
||||
}
|
||||
this.events[ eventName ].push( callback );
|
||||
return function () {
|
||||
this.events[ eventName ] = _.without( this.events[ eventName ], callback );
|
||||
}.bind( this );
|
||||
},
|
||||
raiseEvent : function ( eventName, data ) {
|
||||
if ( this.events[ eventName ] ) {
|
||||
this.events[ eventName ].forEach( function ( callback ) {
|
||||
callback( data );
|
||||
} );
|
||||
}
|
||||
},
|
||||
buildUrl : function () {
|
||||
var srchQry;
|
||||
if ( this.nextSearch ) {
|
||||
srchQry = this.nextSearch;
|
||||
}
|
||||
else {
|
||||
if ( !this.defaultSearch.since_id ) {
|
||||
delete this.defaultSearch.since_id;
|
||||
}
|
||||
srchQry = "?" + query.stringify( this.defaultSearch );
|
||||
}
|
||||
return this.searchPath + srchQry;
|
||||
},
|
||||
|
||||
runSearch : function () {
|
||||
var url = this.buildUrl();
|
||||
if ( !this.searchRegistry[url] ) {
|
||||
this.searchRegistry[url] = true;
|
||||
this.raiseEvent( "search.current", { url : url } );
|
||||
var request = client.request( "GET", url, {"host" : "search.twitter.com"} );
|
||||
request.addListener( "response", function ( response ) {
|
||||
var body = "";
|
||||
response.addListener( "data", function ( data ) {
|
||||
body += data;
|
||||
} );
|
||||
|
||||
response.addListener( "end", function () {
|
||||
this.onSearched( JSON.parse( body ) );
|
||||
}.bind( this ) );
|
||||
this.timeoutFn = setTimeout( this.runSearch.bind( this ), this.refreshInterval );
|
||||
}.bind( this ) );
|
||||
request.end();
|
||||
}
|
||||
else {
|
||||
this.raiseEvent( "search.nodata", { url : url } );
|
||||
this.timeoutFn = setTimeout( this.runSearch.bind( this ), this.refreshInterval );
|
||||
}
|
||||
},
|
||||
onSearched : function ( payload ) {
|
||||
// sift out errors
|
||||
if ( !payload.error ) {
|
||||
if ( payload.results.length > 0 ) {
|
||||
this.processTweets( payload.results );
|
||||
}
|
||||
// do we have a next page option? If so, let's use it
|
||||
if ( payload.next_page ) {
|
||||
this.nextSearch = payload.next_page;
|
||||
}
|
||||
if ( payload.max_id ) {
|
||||
this.defaultSearch.since_id = payload.max_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.nextSearch = undefined;
|
||||
}
|
||||
},
|
||||
processTweets : function ( tweets ) {
|
||||
var newTweets = tweets.filter( function ( t ) {
|
||||
return !this.tweetRegistry[t];
|
||||
}, this );
|
||||
newTweets.forEach( function ( tweet ) {
|
||||
if ( !this.tweetRegistry[tweet.id] ) {
|
||||
this.tweetRegistry[tweet.id] = true;
|
||||
}
|
||||
// deal with the images that consistently fail from twitter...
|
||||
if ( tweet.profile_image_url === "http://twitter.com/images/default_profile_normal.png" ||
|
||||
tweet.profile_image_url === "http://static.twitter.com/images/default_profile_normal.png" ) {
|
||||
tweet.profile_image_url = "templates/images/default_profile_1_normal.png";
|
||||
}
|
||||
}, this );
|
||||
this.raiseEvent( "newTweets", newTweets );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = TwitterSearch;
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
$( function () {
|
||||
// The world's simplest subscription
|
||||
var channel = postal.channel( "Name.Changed" );
|
||||
// This gets you a handle to the default postal channel...
|
||||
var channel = postal.channel();
|
||||
// subscribe
|
||||
var subscription = channel.subscribe( function ( data ) {
|
||||
var subscription = channel.subscribe( "Name.Changed", function ( data ) {
|
||||
$( "#example1" ).html( "Name: " + data.name );
|
||||
} );
|
||||
// And someone publishes a first name change:
|
||||
channel.publish( { name : "Dr. Who" } );
|
||||
channel.publish( "Name.Changed", { name : "Dr. Who" } );
|
||||
subscription.unsubscribe();
|
||||
|
||||
|
||||
|
|
@ -14,14 +14,11 @@ $( function () {
|
|||
// The # symbol represents "one word" in a topic (i.e - the text between two periods of a topic).
|
||||
// By subscribing to "#.Changed", the binding will match
|
||||
// Name.Changed & Location.Changed but *not* for Changed.Companion
|
||||
var hashChannel = postal.channel( "#.Changed" ),
|
||||
chgSubscription = hashChannel.subscribe( function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example2" );
|
||||
} );
|
||||
postal.channel( "Name.Changed" )
|
||||
.publish( { type : "Name", value : "John Smith" } );
|
||||
postal.channel( "Location.Changed" )
|
||||
.publish( { type : "Location", value : "Early 20th Century England" } );
|
||||
var chgSubscription = channel.subscribe( "#.Changed", function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example2" );
|
||||
} );
|
||||
channel.publish( "Name.Changed", { type : "Name", value : "John Smith" } );
|
||||
channel.publish( "Location.Changed", { type : "Location", value : "Early 20th Century England" } );
|
||||
chgSubscription.unsubscribe();
|
||||
|
||||
|
||||
|
|
@ -29,120 +26,85 @@ $( function () {
|
|||
// The * symbol represents any number of characters/words in a topic string.
|
||||
// By subscribing to "DrWho.*.Changed", the binding will match
|
||||
// DrWho.NinthDoctor.Companion.Changed & DrWho.Location.Changed but *not* Changed
|
||||
var starChannel = postal.channel( "DrWho.*.Changed" ),
|
||||
starSubscription = starChannel.subscribe( function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example3" );
|
||||
} );
|
||||
postal.channel( "DrWho.NinthDoctor.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Rose" } );
|
||||
postal.channel( "DrWho.TenthDoctor.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Martha" } );
|
||||
postal.channel( "DrWho.Eleventh.Companion.Changed" )
|
||||
.publish( { type : "Companion Name", value : "Amy" } );
|
||||
postal.channel( "DrWho.Location.Changed" )
|
||||
.publish( { type : "Location", value : "The Library" } );
|
||||
postal.channel( "TheMaster.DrumBeat.Changed" )
|
||||
.publish( { type : "DrumBeat", value : "This won't trigger any subscriptions" } );
|
||||
postal.channel( "Changed" )
|
||||
.publish( { type : "Useless", value : "This won't trigger any subscriptions either" } );
|
||||
var starSubscription = channel.subscribe( "DrWho.*.Changed", function ( data ) {
|
||||
$( '<li>' + data.type + " Changed: " + data.value + '</li>' ).appendTo( "#example3" );
|
||||
} );
|
||||
channel.publish( "DrWho.NinthDoctor.Companion.Changed", { type : "Companion Name", value : "Rose" } );
|
||||
channel.publish( "DrWho.TenthDoctor.Companion.Changed", { type : "Companion Name", value : "Martha" } );
|
||||
channel.publish( "DrWho.Eleventh.Companion.Changed", { type : "Companion Name", value : "Amy" } );
|
||||
channel.publish( "DrWho.Location.Changed", { type : "Location", value : "The Library" } );
|
||||
channel.publish( "TheMaster.DrumBeat.Changed", { type : "DrumBeat", value : "This won't trigger any subscriptions" } );
|
||||
channel.publish( "Changed", { type : "Useless", value : "This won't trigger any subscriptions either" } );
|
||||
starSubscription.unsubscribe();
|
||||
|
||||
// Applying distinctUntilChanged to a subscription
|
||||
var dupChannel = postal.channel( "WeepingAngel.*" ),
|
||||
dupSubscription = dupChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
|
||||
} ).distinctUntilChanged();
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontEvenBlink" )
|
||||
.publish( { value : "Don't Even Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Close Your Eyes" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
postal.channel( "WeepingAngel.DontBlink" )
|
||||
.publish( { value : "Don't Blink" } );
|
||||
var dupSubscription = channel.subscribe( "WeepingAngel.*",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
|
||||
} ).distinctUntilChanged();
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontEvenBlink", { value : "Don't Even Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Close Your Eyes" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
channel.publish( "WeepingAngel.DontBlink", { value : "Don't Blink" } );
|
||||
dupSubscription.unsubscribe();
|
||||
|
||||
// Using disposeAfter(X) to remove subscription automagically after X number of receives
|
||||
var daChannel = postal.channel( "Donna.Noble.*" ),
|
||||
daSubscription = daChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example5" );
|
||||
} ).disposeAfter( 2 );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
postal.channel( "Donna.Noble.ScreamingAgain" )
|
||||
.publish( { value : "Donna Noble has left the library." } );
|
||||
var daSubscription = channel.subscribe( "Donna.Noble.*",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example5" );
|
||||
} ).disposeAfter( 2 );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
channel.publish( "Donna.Noble.ScreamingAgain", { value : "Donna Noble has left the library." } );
|
||||
daSubscription.unsubscribe();
|
||||
|
||||
// Using withConstraint to apply a predicate to the subscription
|
||||
var drIsInTheTardis = false,
|
||||
wcChannel = postal.channel( "Tardis.Depart" ),
|
||||
wcSubscription = wcChannel.subscribe(
|
||||
wcSubscription = channel.subscribe( "Tardis.Depart",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( "#example6" );
|
||||
} ).withConstraint( function () {
|
||||
return drIsInTheTardis;
|
||||
} );
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
drIsInTheTardis = true;
|
||||
postal.channel( "Tardis.Depart" )
|
||||
.publish( { value : "Time for time travel....fantastic!" } );
|
||||
channel.publish( "Tardis.Depart", { value : "Time for time travel....fantastic!" } );
|
||||
wcSubscription.unsubscribe();
|
||||
|
||||
// Using withContext to set the "this" context
|
||||
var ctxChannel = postal.channel( "Dalek.Meet.CyberMen" ),
|
||||
ctxSubscription = ctxChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( this );
|
||||
} ).withContext( $( "#example7" ) );
|
||||
postal.channel( "Dalek.Meet.CyberMen" )
|
||||
.publish( { value : "Exterminate!" } );
|
||||
postal.channel( "Dalek.Meet.CyberMen" )
|
||||
.publish( { value : "Delete!" } );
|
||||
var ctxSubscription = channel.subscribe( "Dalek.Meet.CyberMen",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( this );
|
||||
} ).withContext( $( "#example7" ) );
|
||||
channel.publish( "Dalek.Meet.CyberMen", { value : "Exterminate!" } );
|
||||
channel.publish( "Dalek.Meet.CyberMen", { value : "Delete!" } );
|
||||
ctxSubscription.unsubscribe();
|
||||
|
||||
// Using withDelay() to delay the subscription evaluation
|
||||
var wdChannel = postal.channel( "He.Will.Knock.Four.Times" ),
|
||||
wdSubscription = wdChannel.subscribe(
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( $( "#example8" ) );
|
||||
} ).withDelay( 5000 );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
postal.channel( "He.Will.Knock.Four.Times" )
|
||||
.publish( { value : "Knock!" } );
|
||||
var wdSubscription = channel.subscribe( "He.Will.Knock.Four.Times",
|
||||
function ( data ) {
|
||||
$( '<li>' + data.value + '</li>' ).appendTo( $( "#example8" ) );
|
||||
} ).withDelay( 5000 );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
channel.publish( "He.Will.Knock.Four.Times", { value : "Knock!" } );
|
||||
wdSubscription.unsubscribe();
|
||||
|
||||
// Using distinct() to ignore duplicate messages
|
||||
var revealChannel = postal.channel('detect.cylon'),
|
||||
revealSubscription = revealChannel.subscribe(function (who) {
|
||||
$('<li></li>').text(who.name).appendTo($('#example9'));
|
||||
}).distinct();
|
||||
postal.channel('detect.cylon').publish({name: 'Boomer'});
|
||||
postal.channel('detect.cylon').publish({name: 'Saul Tigh'});
|
||||
postal.channel('detect.cylon').publish({name: 'William Adama'});
|
||||
postal.channel('detect.cylon').publish({name: 'Helo'});
|
||||
//ignored!
|
||||
postal.channel('detect.cylon').publish({name: 'Boomer'});
|
||||
postal.channel('detect.cylon').publish({name: 'Felix Gaeta'});
|
||||
//ignored!
|
||||
postal.channel('detect.cylon').publish({name: 'William Adama'});
|
||||
var revealSubscription = channel.subscribe( 'detect.cylon',function ( who ) {
|
||||
$( '<li></li>' ).text( who.name ).appendTo( $( '#example9' ) );
|
||||
} ).distinct();
|
||||
channel.publish( 'detect.cylon', {name : 'Boomer'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Saul Tigh'} );
|
||||
channel.publish( 'detect.cylon', {name : 'William Adama'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Helo'} );
|
||||
channel.publish( 'detect.cylon', {name : 'Boomer'} ); //ignored!
|
||||
channel.publish( 'detect.cylon', {name : 'Felix Gaeta'} );
|
||||
channel.publish( 'detect.cylon', {name : 'William Adama'} );//ignored!
|
||||
} );
|
||||
|
|
@ -54,44 +54,22 @@
|
|||
return isDistinct;
|
||||
};
|
||||
};
|
||||
var ChannelDefinition = function ( channelName, defaultTopic ) {
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
publish : function () {
|
||||
var envelope = arguments.length === 1 ? arguments[0] : { topic: arguments[0], data: arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -302,6 +280,7 @@
|
|||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
|
@ -371,55 +350,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
var sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
|
@ -434,29 +365,22 @@
|
|||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
|
|
|
|||
2
example/standard/js/postal.min.js
vendored
2
example/standard/js/postal.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,53 +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.7.3
|
||||
*/
|
||||
/*
|
||||
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.7.3
|
||||
*/
|
||||
(function ( root, doc, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["postal"], function ( postal ) {
|
||||
return factory( postal, root, doc );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root.postal, root, doc );
|
||||
}
|
||||
}( this, document, function ( postal, global, document, undefined ) {
|
||||
|
||||
var classicBindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
// binding.replace(/\./g,"\\.") // escape actual periods
|
||||
// .replace(/\*/g, ".*") // asterisks match any value
|
||||
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
|
||||
var rgx = new RegExp( "^" + binding.replace( /\./g, "\\." ).replace( /\*/g, ".*" ).replace( /#/g, "[A-Z,a-z,0-9]*" ) + "$" ),
|
||||
result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
postal.configuration.resolver = classicBindingsResolver;
|
||||
|
||||
} ));
|
||||
7
lib/browser/classic-resolver.min.js
vendored
7
lib/browser/classic-resolver.min.js
vendored
|
|
@ -1,7 +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.7.3
|
||||
*/
|
||||
(function(e,t,n){typeof define=="function"&&define.amd?define(["postal"],function(r){return n(r,e,t)}):n(e.postal,e,t)})(this,document,function(e,t,n,r){var i={cache:{},compare:function(e,t){if(this.cache[t]&&this.cache[t][e])return!0;var n=new RegExp("^"+e.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/#/g,"[A-Z,a-z,0-9]*")+"$"),r=n.test(t);return r&&(this.cache[t]||(this.cache[t]={}),this.cache[t][e]=!0),r},reset:function(){this.cache={}}};e.configuration.resolver=i})
|
||||
7
lib/browser/postal.min.js
vendored
7
lib/browser/postal.min.js
vendored
File diff suppressed because one or more lines are too long
6
lib/classic-resolver.js
Normal file
6
lib/classic-resolver.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.0
|
||||
*/
|
||||
6
lib/classic-resolver.min.js
vendored
Normal file
6
lib/classic-resolver.min.js
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
postal
|
||||
Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
|
||||
License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
|
||||
Version 0.8.0
|
||||
*/
|
||||
|
|
@ -1,45 +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.7.3
|
||||
*/
|
||||
/*
|
||||
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.7.3
|
||||
*/
|
||||
var classicBindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
// binding.replace(/\./g,"\\.") // escape actual periods
|
||||
// .replace(/\*/g, ".*") // asterisks match any value
|
||||
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
|
||||
var rgx = new RegExp( "^" + binding.replace( /\./g, "\\." ).replace( /\*/g, ".*" ).replace( /#/g, "[A-Z,a-z,0-9]*" ) + "$" ),
|
||||
result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
configure: function(postal) {
|
||||
postal.configuration.resolver = classicBindingsResolver;
|
||||
return postal;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,517 +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.7.3
|
||||
*/
|
||||
// This is the node.js version of postal.js
|
||||
// If you need the web client lib version, go to http://github.com/ifandelse/postal.js
|
||||
var _ = require( 'underscore' );
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_PRIORITY = 50,
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal",
|
||||
NO_OP = function () {
|
||||
};
|
||||
|
||||
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 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 ChannelDefinition = function ( channelName, defaultTopic ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
};
|
||||
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.priority = DEFAULT_PRIORITY;
|
||||
this.constraints = new Array( 0 );
|
||||
this.maxCalls = DEFAULT_DISPOSEAFTER;
|
||||
this.onHandled = NO_OP;
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
|
||||
postal.configuration.bus.subscribe( this );
|
||||
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
unsubscribe : function () {
|
||||
postal.configuration.bus.unsubscribe( this );
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
topic : this.topic
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
defer : function () {
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( fn, 0, data );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
disposeAfter : function ( maxCalls ) {
|
||||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
|
||||
var fn = this.onHandled;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe( this );
|
||||
}, this ) );
|
||||
|
||||
this.onHandled = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
distinct : function () {
|
||||
this.withConstraint( new DistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
||||
once: function() {
|
||||
this.disposeAfter(1);
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
throw "Predicate constraint must be a function";
|
||||
}
|
||||
this.constraints.push( predicate );
|
||||
return this;
|
||||
},
|
||||
|
||||
withConstraints : function ( predicates ) {
|
||||
var self = this;
|
||||
if ( _.isArray( predicates ) ) {
|
||||
_.each( predicates, function ( predicate ) {
|
||||
self.withConstraint( predicate );
|
||||
} );
|
||||
}
|
||||
return self;
|
||||
},
|
||||
|
||||
withContext : function ( context ) {
|
||||
this.context = context;
|
||||
return this;
|
||||
},
|
||||
|
||||
withDebounce : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.debounce( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
withDelay : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = function ( data ) {
|
||||
setTimeout( function () {
|
||||
fn( data );
|
||||
}, milliseconds );
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
withPriority : function ( priority ) {
|
||||
if ( _.isNaN( priority ) ) {
|
||||
throw "Priority must be a number";
|
||||
}
|
||||
this.priority = priority;
|
||||
postal.configuration.bus.changePriority( this );
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
}
|
||||
var fn = this.callback;
|
||||
this.callback = _.throttle( fn, milliseconds );
|
||||
return this;
|
||||
},
|
||||
|
||||
subscribe : function ( callback ) {
|
||||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
return function () {
|
||||
var idx = self.wireTaps.indexOf( callback );
|
||||
if ( idx !== -1 ) {
|
||||
self.wireTaps.splice( idx, 1 );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
changePriority : function ( subDef ) {
|
||||
var idx, found;
|
||||
if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic] = _.without( this.subscriptions[subDef.channel][subDef.topic], subDef );
|
||||
idx = this.subscriptions[subDef.channel][subDef.topic].length - 1;
|
||||
for ( ; idx >= 0; idx-- ) {
|
||||
if ( this.subscriptions[subDef.channel][subDef.topic][idx].priority <= subDef.priority ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice( idx + 1, 0, subDef );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].unshift( subDef );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call(subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
subDef.onHandled();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
if ( this.subscriptions ) {
|
||||
_.each( this.subscriptions, function ( channel ) {
|
||||
_.each( channel, function ( topic ) {
|
||||
while ( topic.length ) {
|
||||
topic.pop().unsubscribe();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
this.subscriptions = {};
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
},
|
||||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : new Array( 0 ),
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
var len = this.subscriptions[config.channel][config.topic].length,
|
||||
idx = 0;
|
||||
for ( ; idx < len; idx++ ) {
|
||||
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
|
||||
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
||||
var postal = {
|
||||
configuration : {
|
||||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
|
||||
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
return this.configuration.bus.addWireTap( callback );
|
||||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
if ( !_.isArray( sources ) ) {
|
||||
sources = [sources];
|
||||
}
|
||||
if ( !_.isArray( destinations ) ) {
|
||||
destinations = [destinations];
|
||||
}
|
||||
_.each( sources, function ( source ) {
|
||||
var sourceTopic = source.topic || "#";
|
||||
_.each( destinations, function ( destination ) {
|
||||
var destChannel = destination.channel || DEFAULT_CHANNEL;
|
||||
result.push(
|
||||
postal.subscribe( {
|
||||
channel : source.channel || DEFAULT_CHANNEL,
|
||||
topic : source.topic || "#",
|
||||
callback : function ( data, env ) {
|
||||
var newEnv = _.clone( env );
|
||||
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
|
||||
newEnv.channel = destChannel;
|
||||
newEnv.data = data;
|
||||
postal.publish( newEnv );
|
||||
}
|
||||
} )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
return result;
|
||||
},
|
||||
|
||||
utils : {
|
||||
getSubscribersFor : function () {
|
||||
var channel = arguments[ 0 ],
|
||||
tpc = arguments[ 1 ],
|
||||
result = [];
|
||||
if ( arguments.length === 1 ) {
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ];
|
||||
}
|
||||
else {
|
||||
channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
|
||||
tpc = arguments[ 0 ].topic;
|
||||
}
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
postal.configuration.bus.reset();
|
||||
postal.configuration.resolver.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = postal;
|
||||
|
|
@ -2,27 +2,29 @@
|
|||
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.7.3
|
||||
Version 0.8.0
|
||||
*/
|
||||
(function ( root, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
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 ( _ ) {
|
||||
define( ["."], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root._, root );
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_PRIORITY = 50,
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal",
|
||||
NO_OP = function () {
|
||||
};
|
||||
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
var ConsecutiveDistinctPredicate = function () {
|
||||
var previous;
|
||||
return function ( data ) {
|
||||
|
|
@ -39,84 +41,52 @@
|
|||
};
|
||||
};
|
||||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
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;
|
||||
};
|
||||
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, defaultTopic ) {
|
||||
var ChannelDefinition = function ( channelName ) {
|
||||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
this._topic = defaultTopic || "";
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
var len = arguments.length;
|
||||
if ( len === 1 ) {
|
||||
return new SubscriptionDefinition( this.channel, this._topic, arguments[0] );
|
||||
}
|
||||
else if ( len === 2 ) {
|
||||
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( obj ) {
|
||||
var _obj = obj || {};
|
||||
var envelope = {
|
||||
channel : this.channel,
|
||||
topic : this._topic,
|
||||
data : _obj
|
||||
};
|
||||
// If this is an envelope....
|
||||
if ( _obj.topic && _obj.data ) {
|
||||
envelope = _obj;
|
||||
envelope.channel = envelope.channel || this.channel;
|
||||
}
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
|
||||
topic : function ( topic ) {
|
||||
if ( topic === this._topic ) {
|
||||
return this;
|
||||
}
|
||||
return new ChannelDefinition( this.channel, topic );
|
||||
}
|
||||
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 ? arguments[0] : { topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
var SubscriptionDefinition = function ( channel, topic, callback ) {
|
||||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.priority = DEFAULT_PRIORITY;
|
||||
this.constraints = new Array( 0 );
|
||||
this.maxCalls = DEFAULT_DISPOSEAFTER;
|
||||
this.onHandled = NO_OP;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
|
||||
postal.configuration.bus.subscribe( this );
|
||||
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
|
|
@ -125,7 +95,6 @@
|
|||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
|
|
@ -146,20 +115,19 @@
|
|||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
|
||||
var fn = this.onHandled;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe( this );
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.onHandled = function () {
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
|
@ -169,9 +137,9 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
once: function() {
|
||||
this.disposeAfter(1);
|
||||
},
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
|
|
@ -218,15 +186,6 @@
|
|||
return this;
|
||||
},
|
||||
|
||||
withPriority : function ( priority ) {
|
||||
if ( _.isNaN( priority ) ) {
|
||||
throw "Priority must be a number";
|
||||
}
|
||||
this.priority = priority;
|
||||
postal.configuration.bus.changePriority( this );
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
|
|
@ -241,7 +200,6 @@
|
|||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
var bindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
|
|
@ -250,10 +208,10 @@
|
|||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
|
|
@ -269,9 +227,7 @@
|
|||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
||||
var localBus = {
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
|
|
@ -283,47 +239,28 @@
|
|||
};
|
||||
},
|
||||
|
||||
changePriority : function ( subDef ) {
|
||||
var idx, found;
|
||||
if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic] = _.without( this.subscriptions[subDef.channel][subDef.topic], subDef );
|
||||
idx = this.subscriptions[subDef.channel][subDef.topic].length - 1;
|
||||
for ( ; idx >= 0; idx-- ) {
|
||||
if ( this.subscriptions[subDef.channel][subDef.topic][idx].priority <= subDef.priority ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice( idx + 1, 0, subDef );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].unshift( subDef );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call(subDef.context, envelope.data, envelope );
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
subDef.onHandled();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
|
|
@ -341,13 +278,12 @@
|
|||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
|
|
@ -355,7 +291,7 @@
|
|||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : new Array( 0 ),
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
|
|
@ -370,57 +306,6 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
var publishPicker = {
|
||||
"1" : function ( envelope ) {
|
||||
if ( !envelope ) {
|
||||
throw new Error( "publishing from the 'global' postal.publish call requires a valid envelope." );
|
||||
}
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
envelope.timeStamp = new Date();
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"2" : function ( topic, data ) {
|
||||
var envelope = { channel : DEFAULT_CHANNEL, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
},
|
||||
"3" : function ( channel, topic, data ) {
|
||||
var envelope = { channel : channel, topic : topic, timeStamp : new Date(), data : data };
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
},
|
||||
channelPicker = {
|
||||
"1" : function ( chn ) {
|
||||
var channel = chn, topic, options = {};
|
||||
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
}
|
||||
else {
|
||||
channel = chn.channel || DEFAULT_CHANNEL;
|
||||
topic = chn.topic;
|
||||
options = chn.options || options;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"2" : function ( chn, tpc ) {
|
||||
var channel = chn, topic = tpc, options = {};
|
||||
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
|
||||
channel = DEFAULT_CHANNEL;
|
||||
topic = chn;
|
||||
options = tpc;
|
||||
}
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
},
|
||||
"3" : function ( channel, topic, options ) {
|
||||
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
|
||||
}
|
||||
},
|
||||
sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
||||
|
|
@ -429,34 +314,24 @@
|
|||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
|
||||
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
channelTypes : {
|
||||
local : ChannelDefinition
|
||||
},
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
|
||||
channel : function () {
|
||||
var len = arguments.length;
|
||||
if ( channelPicker[len] ) {
|
||||
return channelPicker[len].apply( this, arguments );
|
||||
}
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
var callback = options.callback,
|
||||
topic = options.topic,
|
||||
channel = options.channel || DEFAULT_CHANNEL;
|
||||
return new SubscriptionDefinition( channel, topic, callback );
|
||||
return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var len = arguments.length;
|
||||
if ( publishPicker[len] ) {
|
||||
return publishPicker[len].apply( this, arguments );
|
||||
}
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
|
|
@ -464,13 +339,9 @@
|
|||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
if ( !_.isArray( sources ) ) {
|
||||
sources = [sources];
|
||||
}
|
||||
if ( !_.isArray( destinations ) ) {
|
||||
destinations = [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 ) {
|
||||
|
|
@ -509,7 +380,7 @@
|
|||
}
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return result;
|
||||
|
|
@ -522,6 +393,5 @@
|
|||
}
|
||||
};
|
||||
|
||||
global.postal = postal;
|
||||
return postal;
|
||||
} ));
|
||||
7
lib/postal.min.js
vendored
Normal file
7
lib/postal.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name" : "postal",
|
||||
"description" : "Pub/Sub library providing wildcard subscriptions, complex message handling, etc. Works server and client-side.",
|
||||
"version" : "0.7.3",
|
||||
"version" : "0.8.0",
|
||||
"homepage" : "http://github.com/ifandelse/postal.js",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
|
|
|
|||
|
|
@ -1,29 +1,16 @@
|
|||
describe( "ChannelDefinition", function () {
|
||||
describe( "When initializing a channel definition", function () {
|
||||
var chDef = new ChannelDefinition( "TestChannel", "TestTopic" );
|
||||
var chDef = new ChannelDefinition( "TestChannel" );
|
||||
it( "should set channel to TestChannel", function () {
|
||||
expect( chDef.channel ).to.be( "TestChannel" );
|
||||
} );
|
||||
it( "should set topic to TestTopic", function () {
|
||||
expect( chDef._topic ).to.be( "TestTopic" );
|
||||
} );
|
||||
} );
|
||||
describe( "When calling subscribe", function () {
|
||||
var ch = new ChannelDefinition( "TestChannel", "TestTopic" ),
|
||||
sub = ch.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 calling topic", function () {
|
||||
var ch = new ChannelDefinition( "TestChannel", "TestTopic" ),
|
||||
ch2 = ch.topic( "TestTopic2" );
|
||||
it( "new channel should be of type ChannelDefinition", function () {
|
||||
expect( ch2 instanceof ChannelDefinition ).to.be.ok();
|
||||
} );
|
||||
it( "new channel should have topic of TestTopic2", function () {
|
||||
expect( ch2._topic ).to.be( "TestTopic2" );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
@ -20,9 +20,7 @@ describe( "Postal", function () {
|
|||
}
|
||||
}
|
||||
} );
|
||||
subscription = postal.channel( { channel : "MyChannel", topic : "MyTopic" } )
|
||||
.subscribe( function () {
|
||||
} );
|
||||
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic" , function () {} );
|
||||
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
|
||||
} );
|
||||
after( function () {
|
||||
|
|
@ -41,15 +39,9 @@ describe( "Postal", function () {
|
|||
it( "should have set subscription topic value", function () {
|
||||
expect( sub.topic ).to.be( "MyTopic" );
|
||||
} );
|
||||
it( "should have set subscription priority value", function () {
|
||||
expect( sub.priority ).to.be( 50 );
|
||||
} );
|
||||
it( "should have defaulted the subscription constraints array", function () {
|
||||
expect( sub.constraints.length ).to.be( 0 );
|
||||
} );
|
||||
it( "should have defaulted the subscription disposeAfter value", function () {
|
||||
expect( sub.maxCalls ).to.be( 0 );
|
||||
} );
|
||||
it( "should have defaulted the subscription context value", function () {
|
||||
expect( sub.context ).to.be(null);
|
||||
} );
|
||||
|
|
@ -75,9 +67,7 @@ describe( "Postal", function () {
|
|||
;
|
||||
}
|
||||
} );
|
||||
subscription = postal.channel( { channel : "MyChannel", topic : "MyTopic" } )
|
||||
.subscribe( function () {
|
||||
} );
|
||||
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;
|
||||
|
|
@ -100,14 +90,14 @@ describe( "Postal", function () {
|
|||
var msgReceivedCnt = 0,
|
||||
msgData;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
msgReceivedCnt++;
|
||||
msgData = data;
|
||||
} );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
subscription.unsubscribe();
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -119,35 +109,20 @@ describe( "Postal", function () {
|
|||
expect( msgData ).to.be( "Testing123" );
|
||||
} );
|
||||
} );
|
||||
describe( "When subscribing multiple subscribers with different priority", function () {
|
||||
var s1, s2, r1 = [];
|
||||
before( function () {
|
||||
s1 = postal.subscribe( { channel : "MyChannel", topic : "MyTopic", callback: function() { r1.push("lower"); } } ).withPriority(200);
|
||||
s2 = postal.subscribe( { channel : "MyChannel", topic : "MyTopic", callback: function() { r1.push("higher"); } } ).withPriority(1);
|
||||
postal.publish( { channel: "MyChannel", topic: "MyTopic", data: "Oh, Hai!" } );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
} );
|
||||
it( "should invoke higher priority subscription first", function () {
|
||||
expect(r1[0] ).to.be("higher");
|
||||
expect(r1[1] ).to.be("lower");
|
||||
} );
|
||||
} );
|
||||
describe( "When subscribing with a disposeAfter of 5", function () {
|
||||
var msgReceivedCnt = 0;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
msgReceivedCnt++;
|
||||
} )
|
||||
.disposeAfter( 5 );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
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();
|
||||
|
|
@ -159,16 +134,16 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with once()", function () {
|
||||
var msgReceivedCnt = 0;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
msgReceivedCnt++;
|
||||
} ).once();
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
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();
|
||||
|
|
@ -180,17 +155,17 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing and ignoring duplicates", function () {
|
||||
var subInvokedCnt = 0;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
subInvokedCnt++;
|
||||
} )
|
||||
.distinctUntilChanged();
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "Testing123" );
|
||||
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();
|
||||
|
|
@ -206,14 +181,14 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with one constraint returning true", function () {
|
||||
var recvd = false;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
recvd = true;
|
||||
} )
|
||||
.withConstraint( function () {
|
||||
return true;
|
||||
} );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -229,14 +204,14 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with one constraint returning false", function () {
|
||||
var recvd = false;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
recvd = true;
|
||||
} )
|
||||
.withConstraint( function () {
|
||||
return false;
|
||||
} );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -252,8 +227,8 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with multiple constraints returning true", function () {
|
||||
var recvd = false;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
recvd = true;
|
||||
} )
|
||||
.withConstraints( [function () {
|
||||
|
|
@ -265,7 +240,7 @@ describe( "Postal", function () {
|
|||
function () {
|
||||
return true;
|
||||
}] );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -281,8 +256,8 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with multiple constraints and one returning false", function () {
|
||||
var recvd = false;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
recvd = true;
|
||||
} )
|
||||
.withConstraints( [function () {
|
||||
|
|
@ -294,7 +269,7 @@ describe( "Postal", function () {
|
|||
function () {
|
||||
return true;
|
||||
}] );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -315,12 +290,12 @@ describe( "Postal", function () {
|
|||
}
|
||||
};
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic", function ( data ) {
|
||||
this.increment();
|
||||
} )
|
||||
.withContext( obj );
|
||||
channel.publish( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -332,49 +307,49 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with defer", function () {
|
||||
var results = [];
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
channel = postal.channel( "MyChannel" );
|
||||
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
} );
|
||||
it( "should have met expected results", function (done) {
|
||||
subscription = channel.subscribe(
|
||||
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( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
results.push( "first" );
|
||||
} );
|
||||
} );
|
||||
describe( "When subscribing with delay", function () {
|
||||
var results = [];
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic" } );
|
||||
channel = postal.channel( "MyChannel" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
} );
|
||||
it( "should have met expected results", function (done) {
|
||||
subscription = channel.subscribe(
|
||||
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( "Testing123" );
|
||||
channel.publish( "MyTopic", "Testing123" );
|
||||
results.push( "first" );
|
||||
} );
|
||||
} );
|
||||
describe( "When subscribing with debounce", function () {
|
||||
var results = [], debouncedChannel;
|
||||
before( function () {
|
||||
debouncedChannel = postal.channel( { channel : "DebouncedChannel", topic : "MyTopic" } );
|
||||
subscription = debouncedChannel.subscribe(
|
||||
debouncedChannel = postal.channel( "MyChannel" );
|
||||
subscription = debouncedChannel.subscribe( "MyTopic",
|
||||
function ( data ) {
|
||||
results.push( data );
|
||||
} ).withDebounce( 800 );
|
||||
|
|
@ -383,21 +358,21 @@ describe( "Postal", function () {
|
|||
postal.utils.reset();
|
||||
} );
|
||||
it( "should have only invoked debounced callback once", function (done) {
|
||||
debouncedChannel.publish( 1 ); // starts the two second clock on debounce
|
||||
debouncedChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
|
||||
setTimeout( function () {
|
||||
debouncedChannel.publish( 2 );
|
||||
debouncedChannel.publish( "MyTopic", 2 );
|
||||
}, 20 ); // should not invoke callback
|
||||
setTimeout( function () {
|
||||
debouncedChannel.publish( 3 );
|
||||
debouncedChannel.publish( "MyTopic", 3 );
|
||||
}, 80 ); // should not invoke callback
|
||||
setTimeout( function () {
|
||||
debouncedChannel.publish( 4 );
|
||||
debouncedChannel.publish( "MyTopic", 4 );
|
||||
}, 250 ); // should not invoke callback
|
||||
setTimeout( function () {
|
||||
debouncedChannel.publish( 5 );
|
||||
debouncedChannel.publish( "MyTopic", 5 );
|
||||
}, 500 ); // should not invoke callback
|
||||
setTimeout( function () {
|
||||
debouncedChannel.publish( 6 );
|
||||
debouncedChannel.publish( "MyTopic", 6 );
|
||||
}, 1000 ); // should invoke callback
|
||||
setTimeout( function () {
|
||||
expect( results[0] ).to.be( 6 );
|
||||
|
|
@ -409,8 +384,8 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with throttle", function () {
|
||||
var results = [], throttledChannel;
|
||||
before( function () {
|
||||
throttledChannel = postal.channel( { channel : "ThrottledChannel", topic : "MyTopic" } );
|
||||
subscription = throttledChannel.subscribe(
|
||||
throttledChannel = postal.channel( "MyChannel" );
|
||||
subscription = throttledChannel.subscribe( "MyTopic",
|
||||
function ( data ) {
|
||||
results.push( data );
|
||||
} ).withThrottle( 500 );
|
||||
|
|
@ -419,13 +394,13 @@ describe( "Postal", function () {
|
|||
postal.utils.reset();
|
||||
} );
|
||||
it( "should have only invoked throttled callback twice", function (done) {
|
||||
throttledChannel.publish( 1 ); // starts the two second clock on debounce
|
||||
throttledChannel.publish( "MyTopic", 1 ); // starts the two second clock on debounce
|
||||
setTimeout( function () {
|
||||
throttledChannel.publish( 800 );
|
||||
throttledChannel.publish( "MyTopic", 800 );
|
||||
}, 800 ); // should invoke callback
|
||||
for ( var i = 0; i < 20; i++ ) {
|
||||
(function ( x ) {
|
||||
throttledChannel.publish( x );
|
||||
throttledChannel.publish( "MyTopic", x );
|
||||
})( i );
|
||||
}
|
||||
setTimeout( function () {
|
||||
|
|
@ -439,15 +414,13 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with a hierarchical binding, no wildcards", function () {
|
||||
var count = 0, channelB, channelC;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic" } );
|
||||
channelB = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic" } );
|
||||
channelC = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic.MiddleTopic", function ( data ) {
|
||||
count++;
|
||||
} );
|
||||
channel.publish( "Testing123" );
|
||||
channelB.publish( "Testing123" );
|
||||
channelC.publish( "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -461,19 +434,14 @@ describe( "Postal", function () {
|
|||
var count, channelB, channelC, channelD, channelE;
|
||||
before( function () {
|
||||
count = 0;
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic.#.SubTopic" } );
|
||||
channelB = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic" } );
|
||||
channelC = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic" } );
|
||||
channelD = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubMiddle.SubTopic" } );
|
||||
channelE = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother" } );
|
||||
subscription = channel.subscribe( function ( data, env ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic.#.SubTopic", function ( data, env ) {
|
||||
count++;
|
||||
console.log("TOPIC: " + env.topic );
|
||||
} );
|
||||
channelC.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic", data : "Testing123"} );
|
||||
channelB.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic", data : "Testing123"} );
|
||||
channelD.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubMiddle.SubTopic", data : "Testing123"} );
|
||||
channelD.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother", data : "Testing123"} );
|
||||
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();
|
||||
|
|
@ -486,17 +454,14 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with a hierarchical binding, using *", function () {
|
||||
var count = 0, channelB, channelC, channelD;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.*" } );
|
||||
channelB = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic" } );
|
||||
channelC = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic" } );
|
||||
channelD = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic.MiddleTopic.*", function ( data ) {
|
||||
count++;
|
||||
} );
|
||||
|
||||
channelC.publish( "Testing123" );
|
||||
channelB.publish( "Testing123" );
|
||||
channelD.publish( "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic", "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic.SubTopic", "Testing123" );
|
||||
channel.publish( "MyTopic.MiddleTopic.SubTopic.YetAnother", "Testing123" );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -509,19 +474,15 @@ describe( "Postal", function () {
|
|||
describe( "When subscribing with a hierarchical binding, using # and *", function () {
|
||||
var count = 0, channelB, channelC, channelD, channelE;
|
||||
before( function () {
|
||||
channel = postal.channel( { channel : "MyChannel", topic : "MyTopic.#.*" } );
|
||||
channelB = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic" } );
|
||||
channelC = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic" } );
|
||||
channelD = postal.channel( { channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother" } );
|
||||
channelE = postal.channel( { channel : "MyChannel", topic : "OtherTopic.MiddleTopic.SubTopic.YetAnother" } );
|
||||
subscription = channel.subscribe( function ( data ) {
|
||||
channel = postal.channel( "MyChannel" );
|
||||
subscription = channel.subscribe( "MyTopic.#.*", function ( data ) {
|
||||
count++;
|
||||
} );
|
||||
|
||||
channelC.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic", data : "Testing123"} );
|
||||
channelB.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic", data : "Testing123"} );
|
||||
channelD.publish( {channel : "MyChannel", topic : "MyTopic.MiddleTopic.SubTopic.YetAnother", data : "Testing123"} );
|
||||
channelE.publish( {channel : "MyChannel", topic : "OtherTopic.MiddleTopic.SubTopic.YetAnother", data : "Testing123"} );
|
||||
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();
|
||||
|
|
@ -540,9 +501,9 @@ describe( "Postal", function () {
|
|||
msgReceivedCnt++;
|
||||
msgData = data;
|
||||
} );
|
||||
postal.publish( "MyGlobalChannel", "MyTopic", "Testing123" );
|
||||
postal.publish( { channel: "MyGlobalChannel", topic: "MyTopic", data: "Testing123" } );
|
||||
subscription.unsubscribe();
|
||||
postal.publish( "MyGlobalChannel", "MyTopic", "Testing123" );
|
||||
postal.publish( { channel: "MyGlobalChannel", topic: "MyTopic", data: "Testing123" } );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -586,15 +547,9 @@ describe( "Postal", function () {
|
|||
it( "should have set subscription topic value", function () {
|
||||
expect( sub.topic ).to.be( "MyTopic" );
|
||||
} );
|
||||
it( "should have set subscription priority value", function () {
|
||||
expect( sub.priority ).to.be( 50 );
|
||||
} );
|
||||
it( "should have defaulted the subscription constraints array", function () {
|
||||
expect( sub.constraints.length ).to.be( 0 );
|
||||
} );
|
||||
it( "should have defaulted the subscription disposeAfter value", function () {
|
||||
expect( sub.maxCalls ).to.be( 0 );
|
||||
} );
|
||||
it( "should have defaulted the subscription context value", function () {
|
||||
expect( sub.context ).to.be(null);
|
||||
} );
|
||||
|
|
@ -653,9 +608,9 @@ describe( "Postal", function () {
|
|||
var resolver;
|
||||
before( function () {
|
||||
postal.utils.reset();
|
||||
subscription = postal.channel( { channel : "MyChannel", topic : "MyTopic" } ).subscribe( function () {
|
||||
subscription = postal.channel( "MyChannel" ).subscribe( "MyTopic", function () {
|
||||
} );
|
||||
postal.channel( { channel : "MyChannel", topic : "MyTopic" } ).publish( "Oh Hai!" );
|
||||
postal.channel( "MyChannel" ).publish( "MyTopic", "Oh Hai!" );
|
||||
sub = postal.configuration.bus.subscriptions.MyChannel.MyTopic[0];
|
||||
resolver = postal.configuration.resolver.cache["MyTopic"];
|
||||
postal.utils.reset();
|
||||
|
|
@ -665,9 +620,7 @@ describe( "Postal", function () {
|
|||
it( "should have created a subscription definition", function () {
|
||||
expect( sub.channel ).to.be( "MyChannel" );
|
||||
expect( sub.topic ).to.be( "MyTopic" );
|
||||
expect( sub.priority ).to.be( 50 );
|
||||
expect( sub.constraints.length ).to.be( 0 );
|
||||
expect( sub.maxCalls ).to.be( 0 );
|
||||
expect( sub.context ).to.be(null);
|
||||
} );
|
||||
it( "should have created a resolver cache entry", function () {
|
||||
|
|
@ -685,12 +638,12 @@ describe( "Postal", function () {
|
|||
var subs = [], i;
|
||||
before( function () {
|
||||
i = 10;
|
||||
var ch1 = postal.channel( { channel : "MyChannel", topic : "MyTopic" } ),
|
||||
ch2 = postal.channel( { channel : "MyChannel2", topic : "MyTopic2" } );
|
||||
var ch1 = postal.channel( "MyChannel" ),
|
||||
ch2 = postal.channel( "MyChannel2" );
|
||||
while ( i ) {
|
||||
subs.push( ch1.subscribe( function () {
|
||||
subs.push( ch1.subscribe( "MyTopic", function () {
|
||||
} ) );
|
||||
subs.push( ch2.subscribe( function () {
|
||||
subs.push( ch2.subscribe( "MyTopic2", function () {
|
||||
} ) );
|
||||
i--;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
var NO_OP = function() {};
|
||||
describe( "SubscriptionDefinition", function () {
|
||||
describe( "When initializing SubscriptionDefinition", function () {
|
||||
var sDef,
|
||||
|
|
@ -32,18 +33,9 @@ describe( "SubscriptionDefinition", function () {
|
|||
it( "should set the callback", function () {
|
||||
expect( sDef.callback ).to.be( NO_OP );
|
||||
} );
|
||||
it( "should default the priority", function () {
|
||||
expect( sDef.priority ).to.be( 50 );
|
||||
} );
|
||||
it( "should default the constraints", function () {
|
||||
expect( sDef.constraints.length ).to.be( 0 );
|
||||
} );
|
||||
it( "should default the maxCalls", function () {
|
||||
expect( sDef.maxCalls ).to.be( 0 );
|
||||
} );
|
||||
it( "should default the onHandled callback", function () {
|
||||
expect( sDef.onHandled ).to.be( NO_OP );
|
||||
} );
|
||||
it( "should default the context", function () {
|
||||
expect( sDef.context ).to.be( null );
|
||||
} );
|
||||
|
|
@ -100,14 +92,6 @@ describe( "SubscriptionDefinition", function () {
|
|||
} );
|
||||
} );
|
||||
|
||||
describe( "When setting priority", function () {
|
||||
var sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ).withPriority( 10 );
|
||||
|
||||
it( "Should set priority", function () {
|
||||
expect( sDefe.priority ).to.be( 10 );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( "When calling subscribe to set the callback", function () {
|
||||
var sDefe = new SubscriptionDefinition( "TestChannel", "TestTopic", NO_OP ),
|
||||
fn = function () {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ describe("Linked Channels", function(){
|
|||
destData.push( data );
|
||||
destEnv.push( env );
|
||||
}} );
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data: "I'm in yer bus, linkin' to yer subscriptionz..." } );
|
||||
linkages[0].unsubscribe();
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -22,7 +22,7 @@ describe("Linked Channels", function(){
|
|||
expect( destEnv.length ).to.be( 1 );
|
||||
} );
|
||||
it( "linked subscription data should match expected results", function () {
|
||||
expect( destData[0].data ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
|
||||
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" );
|
||||
|
|
@ -39,9 +39,9 @@ describe("Linked Channels", function(){
|
|||
destData.push( data );
|
||||
destEnv.push( env );
|
||||
}} );
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
linkages[0].unsubscribe();
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -51,7 +51,7 @@ describe("Linked Channels", function(){
|
|||
expect( destEnv.length ).to.be( 1 );
|
||||
} );
|
||||
it( "linked subscription data should match expected results", function () {
|
||||
expect( destData[0].data ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
|
||||
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" );
|
||||
|
|
@ -70,9 +70,9 @@ describe("Linked Channels", function(){
|
|||
destData.push( data );
|
||||
destEnv.push( env );
|
||||
}} );
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
linkages[0].unsubscribe();
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
@ -82,7 +82,7 @@ describe("Linked Channels", function(){
|
|||
expect( destEnv.length ).to.be( 1 );
|
||||
} );
|
||||
it( "linked subscription data should match expected results", function () {
|
||||
expect( destData[0].data ).to.be( "I'm in yer bus, linkin' to yer subscriptionz..." );
|
||||
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" );
|
||||
|
|
@ -107,7 +107,7 @@ describe("Linked Channels", function(){
|
|||
]);
|
||||
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai", callback : callback} );
|
||||
postal.subscribe( { channel : "destinationChannel", topic : "NewTopic.Oh.Hai.There", callback : callback } );
|
||||
postal.publish( "sourceChannel", "Oh.Hai.There", { data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
postal.publish( { channel: "sourceChannel", topic: "Oh.Hai.There", data : "I'm in yer bus, linkin' to yer subscriptionz..."} );
|
||||
} );
|
||||
after( function () {
|
||||
postal.utils.reset();
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ var bindingsResolver = {
|
|||
return true;
|
||||
}
|
||||
var pattern = ("^" + binding.replace( /\./g, "\\." ) // escape actual periods
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
.replace( /\*/g, "[A-Z,a-z,0-9]*" ) // asterisks match any alpha-numeric 'word'
|
||||
.replace( /#/g, ".*" ) + "$") // hash matches 'n' # of words (+ optional on start/end of topic)
|
||||
.replace( "\\..*$", "(\\..*)*$" ) // fix end of topic matching on hash wildcards
|
||||
.replace( "^.*\\.", "^(.*\\.)*" ); // fix beginning of topic matching on hash wildcards
|
||||
var rgx = new RegExp( pattern );
|
||||
var result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
|
|
@ -24,4 +24,4 @@ var bindingsResolver = {
|
|||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
};
|
||||
27
src/Api.js
27
src/Api.js
|
|
@ -1,5 +1,3 @@
|
|||
var sessionInfo = {};
|
||||
|
||||
// save some setup time, albeit tiny
|
||||
localBus.subscriptions[SYSTEM_CHANNEL] = {};
|
||||
|
||||
|
|
@ -8,17 +6,15 @@ var postal = {
|
|||
bus : localBus,
|
||||
resolver : bindingsResolver,
|
||||
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
|
||||
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
|
||||
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
|
||||
SYSTEM_CHANNEL : SYSTEM_CHANNEL
|
||||
},
|
||||
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
ChannelDefinition : ChannelDefinition,
|
||||
|
||||
SubscriptionDefinition: SubscriptionDefinition,
|
||||
SubscriptionDefinition : SubscriptionDefinition,
|
||||
|
||||
channel : function ( channelName ) {
|
||||
return new ChannelDefinition( channelName );
|
||||
return new ChannelDefinition( channelName );
|
||||
},
|
||||
|
||||
subscribe : function ( options ) {
|
||||
|
|
@ -26,9 +22,8 @@ var postal = {
|
|||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
envelope.channel = envelope.channel || DEFAULT_CHANNEL;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
},
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
|
|
@ -36,13 +31,9 @@ var postal = {
|
|||
},
|
||||
|
||||
linkChannels : function ( sources, destinations ) {
|
||||
var result = [];
|
||||
if ( !_.isArray( sources ) ) {
|
||||
sources = [sources];
|
||||
}
|
||||
if ( !_.isArray( destinations ) ) {
|
||||
destinations = [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 ) {
|
||||
|
|
@ -81,7 +72,7 @@ var postal = {
|
|||
}
|
||||
}
|
||||
if ( postal.configuration.bus.subscriptions[ channel ] &&
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
|
||||
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
var classicBindingsResolver = {
|
||||
cache : { },
|
||||
|
||||
compare : function ( binding, topic ) {
|
||||
if ( this.cache[topic] && this.cache[topic][binding] ) {
|
||||
return true;
|
||||
}
|
||||
// binding.replace(/\./g,"\\.") // escape actual periods
|
||||
// .replace(/\*/g, ".*") // asterisks match any value
|
||||
// .replace(/#/g, "[A-Z,a-z,0-9]*"); // hash matches any alpha-numeric 'word'
|
||||
var rgx = new RegExp( "^" + binding.replace( /\./g, "\\." ).replace( /\*/g, ".*" ).replace( /#/g, "[A-Z,a-z,0-9]*" ) + "$" ),
|
||||
result = rgx.test( topic );
|
||||
if ( result ) {
|
||||
if ( !this.cache[topic] ) {
|
||||
this.cache[topic] = {};
|
||||
}
|
||||
this.cache[topic][binding] = true;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
this.cache = {};
|
||||
}
|
||||
};
|
||||
|
|
@ -2,17 +2,14 @@ var ChannelDefinition = function ( channelName ) {
|
|||
this.channel = channelName || DEFAULT_CHANNEL;
|
||||
};
|
||||
|
||||
ChannelDefinition.prototype = {
|
||||
subscribe : function () {
|
||||
return arguments.length === 1 ?
|
||||
new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
|
||||
new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
|
||||
},
|
||||
|
||||
publish : function () {
|
||||
var envelope = arguments.length === 1 ? arguments[0] : { topic: arguments[0], data: arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
postal.configuration.bus.publish( envelope );
|
||||
return envelope;
|
||||
}
|
||||
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 ? arguments[0] : { topic : arguments[0], data : arguments[1] };
|
||||
envelope.channel = this.channel;
|
||||
return postal.configuration.bus.publish( envelope );
|
||||
};
|
||||
|
|
@ -1,6 +1,3 @@
|
|||
var DEFAULT_CHANNEL = "/",
|
||||
DEFAULT_PRIORITY = 50,
|
||||
DEFAULT_DISPOSEAFTER = 0,
|
||||
SYSTEM_CHANNEL = "postal",
|
||||
NO_OP = function () {
|
||||
};
|
||||
SYSTEM_CHANNEL = "postal";
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
var DistinctPredicate = function () {
|
||||
var previous = [];
|
||||
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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
var localBus = {
|
||||
|
||||
addWireTap : function ( callback ) {
|
||||
var self = this;
|
||||
self.wireTaps.push( callback );
|
||||
|
|
@ -11,48 +10,28 @@ var localBus = {
|
|||
};
|
||||
},
|
||||
|
||||
changePriority : function ( subDef ) {
|
||||
var idx, found;
|
||||
if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic] = _.without( this.subscriptions[subDef.channel][subDef.topic], subDef );
|
||||
idx = this.subscriptions[subDef.channel][subDef.topic].length - 1;
|
||||
for ( ; idx >= 0; idx-- ) {
|
||||
if ( this.subscriptions[subDef.channel][subDef.topic][idx].priority <= subDef.priority ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].splice( idx + 1, 0, subDef );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
this.subscriptions[subDef.channel][subDef.topic].unshift( subDef );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
publish : function ( envelope ) {
|
||||
envelope.timeStamp = new Date();
|
||||
envelope.timeStamp = new Date();
|
||||
_.each( this.wireTaps, function ( tap ) {
|
||||
tap( envelope.data, envelope );
|
||||
} );
|
||||
|
||||
if ( this.subscriptions[envelope.channel] ) {
|
||||
_.each( this.subscriptions[envelope.channel], function ( topic ) {
|
||||
// TODO: research faster ways to handle this than _.clone
|
||||
_.each( _.clone( topic ), function ( subDef ) {
|
||||
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
|
||||
if ( _.all( subDef.constraints, function ( constraint ) {
|
||||
return constraint.call(subDef.context, envelope.data, envelope );
|
||||
return constraint.call( subDef.context, envelope.data, envelope );
|
||||
} ) ) {
|
||||
if ( typeof subDef.callback === 'function' ) {
|
||||
subDef.callback.call( subDef.context, envelope.data, envelope );
|
||||
subDef.onHandled();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
return envelope;
|
||||
},
|
||||
|
||||
reset : function () {
|
||||
|
|
@ -70,13 +49,12 @@ var localBus = {
|
|||
|
||||
subscribe : function ( subDef ) {
|
||||
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
|
||||
|
||||
if ( !channel ) {
|
||||
channel = this.subscriptions[subDef.channel] = {};
|
||||
}
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic];
|
||||
if ( !subs ) {
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
|
||||
subs = this.subscriptions[subDef.channel][subDef.topic] = [];
|
||||
}
|
||||
subs.push( subDef );
|
||||
return subDef;
|
||||
|
|
@ -84,7 +62,7 @@ var localBus = {
|
|||
|
||||
subscriptions : {},
|
||||
|
||||
wireTaps : new Array( 0 ),
|
||||
wireTaps : [],
|
||||
|
||||
unsubscribe : function ( config ) {
|
||||
if ( this.subscriptions[config.channel][config.topic] ) {
|
||||
|
|
@ -98,4 +76,4 @@ var localBus = {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -2,24 +2,18 @@ var SubscriptionDefinition = function ( channel, topic, callback ) {
|
|||
this.channel = channel;
|
||||
this.topic = topic;
|
||||
this.callback = callback;
|
||||
this.priority = DEFAULT_PRIORITY;
|
||||
this.constraints = new Array( 0 );
|
||||
this.maxCalls = DEFAULT_DISPOSEAFTER;
|
||||
this.onHandled = NO_OP;
|
||||
this.constraints = [];
|
||||
this.context = null;
|
||||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.created",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.created",
|
||||
channel : channel,
|
||||
topic : topic
|
||||
}
|
||||
} );
|
||||
|
||||
postal.configuration.bus.subscribe( this );
|
||||
|
||||
};
|
||||
|
||||
SubscriptionDefinition.prototype = {
|
||||
|
|
@ -28,7 +22,6 @@ SubscriptionDefinition.prototype = {
|
|||
postal.configuration.bus.publish( {
|
||||
channel : SYSTEM_CHANNEL,
|
||||
topic : "subscription.removed",
|
||||
timeStamp : new Date(),
|
||||
data : {
|
||||
event : "subscription.removed",
|
||||
channel : this.channel,
|
||||
|
|
@ -49,20 +42,19 @@ SubscriptionDefinition.prototype = {
|
|||
if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
|
||||
throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
|
||||
}
|
||||
|
||||
var fn = this.onHandled;
|
||||
var fn = this.callback;
|
||||
var dispose = _.after( maxCalls, _.bind( function () {
|
||||
this.unsubscribe( this );
|
||||
this.unsubscribe();
|
||||
}, this ) );
|
||||
|
||||
this.onHandled = function () {
|
||||
this.callback = function () {
|
||||
fn.apply( this.context, arguments );
|
||||
dispose();
|
||||
};
|
||||
return this;
|
||||
},
|
||||
|
||||
distinctUntilChanged : function () {
|
||||
distinctUntilChanged : function () {
|
||||
this.withConstraint( new ConsecutiveDistinctPredicate() );
|
||||
return this;
|
||||
},
|
||||
|
|
@ -72,9 +64,9 @@ SubscriptionDefinition.prototype = {
|
|||
return this;
|
||||
},
|
||||
|
||||
once: function() {
|
||||
this.disposeAfter(1);
|
||||
},
|
||||
once : function () {
|
||||
this.disposeAfter( 1 );
|
||||
},
|
||||
|
||||
withConstraint : function ( predicate ) {
|
||||
if ( !_.isFunction( predicate ) ) {
|
||||
|
|
@ -121,15 +113,6 @@ SubscriptionDefinition.prototype = {
|
|||
return this;
|
||||
},
|
||||
|
||||
withPriority : function ( priority ) {
|
||||
if ( _.isNaN( priority ) ) {
|
||||
throw "Priority must be a number";
|
||||
}
|
||||
this.priority = priority;
|
||||
postal.configuration.bus.changePriority( this );
|
||||
return this;
|
||||
},
|
||||
|
||||
withThrottle : function ( milliseconds ) {
|
||||
if ( _.isNaN( milliseconds ) ) {
|
||||
throw "Milliseconds must be a number";
|
||||
|
|
@ -143,4 +126,4 @@ SubscriptionDefinition.prototype = {
|
|||
this.callback = callback;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
(function ( root, doc, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["postal"], function ( postal ) {
|
||||
return factory( postal, root, doc );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root.postal, root, doc );
|
||||
}
|
||||
}( this, document, function ( postal, global, document, undefined ) {
|
||||
|
||||
//import("../BindingsResolver.js");
|
||||
postal.configuration.resolver = classicBindingsResolver;
|
||||
|
||||
} ));
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
(function ( root, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( ["underscore"], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
//import("../Constants.js");
|
||||
//import("../ConsecutiveDistinctPredicate.js");
|
||||
//import("../DistinctPredicate.js");
|
||||
//import("../ChannelDefinition.js");
|
||||
//import("../SubscriptionDefinition.js");
|
||||
//import("../AmqpBindingsResolver.js");
|
||||
//import("../LocalBus.js");
|
||||
//import("../Api.js");
|
||||
|
||||
global.postal = postal;
|
||||
return postal;
|
||||
} ));
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
//import("../BindingsResolver.js");
|
||||
|
||||
module.exports = {
|
||||
configure: function(postal) {
|
||||
postal.configuration.resolver = classicBindingsResolver;
|
||||
return postal;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// This is the node.js version of postal.js
|
||||
// If you need the web client lib version, go to http://github.com/ifandelse/postal.js
|
||||
var _ = require( 'underscore' );
|
||||
|
||||
//import("../Constants.js");
|
||||
//import("../DistinctPredicate.js");
|
||||
//import("../ConsecutiveDistinctPredicate.js");
|
||||
//import("../ChannelDefinition.js");
|
||||
//import("../SubscriptionDefinition.js");
|
||||
//import("../AmqpBindingsResolver.js");
|
||||
//import("../LocalBus.js");
|
||||
//import("../Api.js");
|
||||
|
||||
module.exports = postal;
|
||||
29
src/postal.js
Normal file
29
src/postal.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
(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( ["."], function ( _ ) {
|
||||
return factory( _, root );
|
||||
} );
|
||||
} else {
|
||||
// Browser globals
|
||||
root.postal = factory( root._, root );
|
||||
}
|
||||
}( this, function ( _, global, undefined ) {
|
||||
|
||||
//import("Constants.js");
|
||||
//import("ConsecutiveDistinctPredicate.js");
|
||||
//import("DistinctPredicate.js");
|
||||
//import("ChannelDefinition.js");
|
||||
//import("SubscriptionDefinition.js");
|
||||
//import("AmqpBindingsResolver.js");
|
||||
//import("LocalBus.js");
|
||||
//import("Api.js");
|
||||
|
||||
return postal;
|
||||
} ));
|
||||
Loading…
Reference in a new issue