Reformatted code to more closely (but not exactly) line up with jQuery core formatting standards. Updated postal.socket dependencies in the node example, etc.

This commit is contained in:
Jim Cowart 2012-04-21 00:56:40 -04:00
parent e25d9196ec
commit 951738d56b
715 changed files with 144577 additions and 120084 deletions

View file

@ -1,7 +1,9 @@
{
"source": "src/diags",
"output": "lib/standard",
"lint": {},
"uglify": {},
"extensions": { "uglify": "min" }
"source" : "src/diags",
"output" : "lib/standard",
"lint" : {},
"uglify" : {},
"extensions" : {
"uglify" : "min"
}
}

View file

@ -1,10 +1,12 @@
{
"source": "src/main",
"output": "lib/standard",
"lint": {},
"uglify": {},
"extensions": { "uglify": "min" },
"hosts": {
"source" : "src/main",
"output" : "lib/standard",
"lint" : {},
"uglify" : {},
"extensions" : {
"uglify" : "min"
},
"hosts" : {
"/" : "./"
},
"port" : 8080

View file

@ -1,50 +1,50 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Postal Examples (AMD/require.js Lib Format)</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script data-main="js/main" src="js/libs/require/require.js"></script>
<title>Postal Examples (AMD/require.js Lib Format)</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script data-main="js/main" src="js/libs/require/require.js"></script>
</head>
<body>
<div>
Example 1 - The World's Simplest Subscription
<div class="results" id="example1"></div>
</div>
<div>
Example 1 - The World's Simplest Subscription
<div class="results" id="example1"></div>
</div>
<div>
Example 2 - Subscribing with the "#" wildcard character
<ul class="results" id="example2"></ul>
</div>
<div>
Example 2 - Subscribing with the "#" wildcard character
<ul class="results" id="example2"></ul>
</div>
<div>
Example 3 - Subscribing with the "*" wildcard character
<ul class="results" id="example3"></ul>
</div>
<div>
Example 3 - Subscribing with the "*" wildcard character
<ul class="results" id="example3"></ul>
</div>
<div>
Example 4 - using ignoreDuplicates()
<ul class="results" id="example4"></ul>
</div>
<div>
Example 4 - using ignoreDuplicates()
<ul class="results" id="example4"></ul>
</div>
<div>
Example 5 - using disposeAfter(X)
<ul class="results" id="example5"></ul>
</div>
<div>
Example 5 - using disposeAfter(X)
<ul class="results" id="example5"></ul>
</div>
<div>
Example 6 - using withConstraint() to apply a predicate to subscription callback
<ul class="results" id="example6"></ul>
</div>
<div>
Example 6 - using withConstraint() to apply a predicate to subscription callback
<ul class="results" id="example6"></ul>
</div>
<div>
Example 7 - using withContext to set the "this" context
<ul class="results" id="example7"></ul>
</div>
<div>
Example 7 - using withContext to set the "this" context
<ul class="results" id="example7"></ul>
</div>
<div>
Example 8 - using withDelay to delay evaluation of subscription
<ul class="results" id="example8"></ul>
</div>
<div>
Example 8 - using withDelay to delay evaluation of subscription
<ul class="results" id="example8"></ul>
</div>
</body>
</html>

View file

@ -1,11 +1,13 @@
define(['postal', 'postaldiags'], function(postal, diags){
define( ['postal', 'postaldiags'], function ( postal, diags ) {
// The world's simplest subscription
var channel = postal.channel("Name.Changed");
var channel = postal.channel( "Name.Changed" );
// subscribe
var subscription = channel.subscribe(function(data) { $("#example1").html("Name: " + data.name); });
var subscription = channel.subscribe( function ( data ) {
$( "#example1" ).html( "Name: " + data.name );
} );
// And someone publishes a first name change:
channel.publish({ name: "Dr. Who" });
channel.publish( { name : "Dr. Who" } );
subscription.unsubscribe();
@ -13,14 +15,14 @@ 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 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" } );
chgSubscription.unsubscribe();
@ -28,94 +30,101 @@ 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 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" } );
starSubscription.unsubscribe();
// Applying ignoreDuplicates to a subscription
var dupChannel = postal.channel("WeepingAngel.*"),
dupSubscription = dupChannel.subscribe(function(data) {
$('<li>' + data.value + '</li>').appendTo("#example4");
}).ignoreDuplicates();
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" });
var dupChannel = postal.channel( "WeepingAngel.*" ),
dupSubscription = dupChannel.subscribe(
function ( data ) {
$( '<li>' + data.value + '</li>' ).appendTo( "#example4" );
} ).ignoreDuplicates();
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" } );
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 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." } );
daSubscription.unsubscribe();
// Using withConstraint to apply a predicate to the subscription
var drIsInTheTardis = false,
wcChannel = postal.channel("Tardis.Depart"),
wcSubscription = wcChannel.subscribe(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!" });
wcChannel = postal.channel( "Tardis.Depart" ),
wcSubscription = wcChannel.subscribe(
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!" } );
drIsInTheTardis = true;
postal.channel("Tardis.Depart")
.publish({ value:"Time for time travel....fantastic!" });
postal.channel( "Tardis.Depart" )
.publish( { 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 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!" } );
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 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!" } );
wdSubscription.unsubscribe();
});
} );

File diff suppressed because one or more lines are too long

View file

@ -47,7 +47,9 @@ define( [ "postal", "underscore" ], function ( postal, _, undefined ) {
}
} );
postal.diagnostics = {
postal.diagnostics = postal.diagnostics || {};
postal.diagnostics.console = {
clearFilters : function () {
filters = [];
},
@ -60,13 +62,13 @@ define( [ "postal", "underscore" ], function ( postal, _, undefined ) {
if ( !_.isArray( constraint ) ) {
constraint = [ constraint ];
}
_.each( constraint, function( item ){
_.each( constraint, function ( item ) {
if ( filters.length === 0 || !_.any( filters, function ( filter ) {
return _.isEqual( filter, item );
} ) ) {
filters.push( item );
}
});
} );
},
getCurrentFilters : function () {

View file

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

View file

@ -1,225 +1,228 @@
/*
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.6.0
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.6.0
*/
// This is the amd-module version of postal.js
// If you need the standard lib style version, go to http://github.com/ifandelse/postal.js
define(["underscore"], function(_, undefined) {
define( ["underscore"], function ( _, undefined ) {
var DEFAULT_CHANNEL = "/",
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };
NO_OP = function () {
};
var DistinctPredicate = function() {
var DistinctPredicate = function () {
var previous;
return function(data) {
return function ( data ) {
var eq = false;
if(_.isString(data)) {
if ( _.isString( data ) ) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
eq = _.isEqual( data, previous );
previous = _.clone( data );
}
return !eq;
};
};
var ChannelDefinition = function(channelName, defaultTopic) {
var ChannelDefinition = function ( channelName, defaultTopic ) {
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
};
ChannelDefinition.prototype = {
subscribe: function() {
subscribe : function () {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
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]);
else if ( len === 2 ) {
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
}
},
publish: function(obj) {
publish : function ( obj ) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj || {}
channel : this.channel,
topic : this._topic,
data : obj || {}
};
// If this is an envelope....
if( obj.topic && obj.data ) {
if ( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
postal.configuration.bus.publish( envelope );
return envelope;
},
topic: function(topic) {
if(topic === this._topic) {
topic : function ( topic ) {
if ( topic === this._topic ) {
return this;
}
return new ChannelDefinition(this.channel, topic);
return new ChannelDefinition( this.channel, topic );
}
};
var SubscriptionDefinition = function(channel, topic, callback) {
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.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.publish( {
channel : SYSTEM_CHANNEL,
topic : "subscription.created",
timeStamp : new Date(),
data : {
event : "subscription.created",
channel : channel,
topic : topic
}
});
} );
postal.configuration.bus.subscribe(this);
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
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() {
defer : function () {
var fn = this.callback;
this.callback = function(data) {
setTimeout(fn,0,data);
this.callback = function ( data ) {
setTimeout( fn, 0, data );
};
return this;
},
disposeAfter: function(maxCalls) {
if(_.isNaN(maxCalls) || maxCalls <= 0) {
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));
var dispose = _.after( maxCalls, _.bind( function () {
this.unsubscribe( this );
}, this ) );
this.onHandled = function() {
fn.apply(this.context, arguments);
this.onHandled = function () {
fn.apply( this.context, arguments );
dispose();
};
return this;
},
ignoreDuplicates: function() {
this.withConstraint(new DistinctPredicate());
ignoreDuplicates : function () {
this.withConstraint( new DistinctPredicate() );
return this;
},
withConstraint: function(predicate) {
if(! _.isFunction(predicate)) {
withConstraint : function ( predicate ) {
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
this.constraints.push(predicate);
this.constraints.push( predicate );
return this;
},
withConstraints: function(predicates) {
withConstraints : function ( predicates ) {
var self = this;
if(_.isArray(predicates)) {
_.each(predicates, function(predicate) { self.withConstraint(predicate); } );
if ( _.isArray( predicates ) ) {
_.each( predicates, function ( predicate ) {
self.withConstraint( predicate );
} );
}
return self;
},
withContext: function(context) {
withContext : function ( context ) {
this.context = context;
return this;
},
withDebounce: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withDebounce : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.debounce(fn, milliseconds);
this.callback = _.debounce( fn, milliseconds );
return this;
},
withDelay: function(milliseconds) {
if(_.isNaN(milliseconds)) {
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);
this.callback = function ( data ) {
setTimeout( function () {
fn( data );
}, milliseconds );
};
return this;
},
withPriority: function(priority) {
if(_.isNaN(priority)) {
withPriority : function ( priority ) {
if ( _.isNaN( priority ) ) {
throw "Priority must be a number";
}
this.priority = priority;
return this;
},
withThrottle: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withThrottle : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle(fn, milliseconds);
this.callback = _.throttle( fn, milliseconds );
return this;
},
subscribe: function(callback) {
subscribe : function ( callback ) {
this.callback = callback;
return this;
}
};
var bindingsResolver = {
cache: { },
cache : { },
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
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]) {
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;
@ -227,91 +230,93 @@ var bindingsResolver = {
return result;
},
reset: function() {
reset : function () {
this.cache = {};
}
};
var localBus = {
addWireTap: function(callback) {
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);
self.wireTaps.push( callback );
return function () {
var idx = self.wireTaps.indexOf( callback );
if ( idx !== -1 ) {
self.wireTaps.splice( idx, 1 );
}
};
},
publish: function(envelope) {
_.each(this.wireTaps,function(tap) {
tap(envelope.data, envelope);
});
publish : function ( envelope ) {
_.each( this.wireTaps, function ( tap ) {
tap( envelope.data, envelope );
} );
_.each(this.subscriptions[envelope.channel], function(topic) {
_.each(topic, function(subDef){
if(postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
if(_.all(subDef.constraints, function(constraint) { return constraint(envelope.data,envelope); })) {
if(typeof subDef.callback === 'function') {
subDef.callback.apply(subDef.context, [envelope.data, envelope]);
_.each( this.subscriptions[envelope.channel], function ( topic ) {
_.each( topic, function ( subDef ) {
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
if ( _.all( subDef.constraints, function ( constraint ) {
return constraint( envelope.data, envelope );
} ) ) {
if ( typeof subDef.callback === 'function' ) {
subDef.callback.apply( subDef.context, [envelope.data, envelope] );
subDef.onHandled();
}
}
}
});
});
} );
} );
},
reset: function() {
if( this.subscriptions ) {
_.each( this.subscriptions , function( channel ){
_.each( channel, function( topic ){
while( topic.length ) {
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) {
subscribe : function ( subDef ) {
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
if(!channel) {
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);
if ( !subs ) {
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
}
idx = subs.length - 1;
for(; idx >= 0; idx--) {
if(subs[idx].priority <= subDef.priority) {
subs.splice(idx + 1, 0, subDef);
for ( ; idx >= 0; idx-- ) {
if ( subs[idx].priority <= subDef.priority ) {
subs.splice( idx + 1, 0, subDef );
found = true;
break;
}
}
if(!found) {
subs.unshift(subDef);
if ( !found ) {
subs.unshift( subDef );
}
return subDef;
},
subscriptions: {},
subscriptions : {},
wireTaps: new Array(0),
wireTaps : new Array( 0 ),
unsubscribe: function(config) {
if(this.subscriptions[config.channel][config.topic]) {
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) {
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
break;
}
@ -321,50 +326,50 @@ var localBus = {
};
var publishPicker = {
"1" : function(envelope) {
if(!envelope) {
throw new Error("publishing from the 'global' postal.publish call requires a valid envelope.");
"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 };
"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 ) {
"1" : function ( chn ) {
var channel = chn, topic, options = {};
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
}
else {
channel = chn.channel || DEFAULT_CHANNEL;
topic = chn.topic;
topic = chn.topic;
options = chn.options || options;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"2" : function( chn, tpc ) {
"2" : function ( chn, tpc ) {
var channel = chn, topic = tpc, options = {};
if( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
options = tpc;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"3" : function( channel, topic, options ) {
"3" : function ( channel, topic, options ) {
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
}
},
@ -374,110 +379,110 @@ var publishPicker = {
localBus.subscriptions[SYSTEM_CHANNEL] = {};
var postal = {
configuration: {
bus: localBus,
resolver: bindingsResolver,
getSessionIdAction: function( callback ) {
configuration : {
bus : localBus,
resolver : bindingsResolver,
getSessionIdAction : function ( callback ) {
callback( sessionInfo );
},
setSessionIdAction: function( info, callback ) {
setSessionIdAction : function ( info, callback ) {
sessionInfo = info;
callback( sessionInfo );
},
DEFAULT_CHANNEL: DEFAULT_CHANNEL,
DEFAULT_PRIORITY: DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER: DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL: SYSTEM_CHANNEL
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL : SYSTEM_CHANNEL
},
channelTypes: {
local: ChannelDefinition
channelTypes : {
local : ChannelDefinition
},
channel: function() {
channel : function () {
var len = arguments.length;
if(channelPicker[len]) {
return channelPicker[len].apply(this, arguments);
if ( channelPicker[len] ) {
return channelPicker[len].apply( this, arguments );
}
},
subscribe: function(options) {
subscribe : function ( options ) {
var callback = options.callback,
topic = options.topic,
channel = options.channel || DEFAULT_CHANNEL;
return new SubscriptionDefinition(channel, topic, callback);
return new SubscriptionDefinition( channel, topic, callback );
},
publish: function() {
publish : function () {
var len = arguments.length;
if(publishPicker[len]) {
return publishPicker[len].apply(this, arguments);
if ( publishPicker[len] ) {
return publishPicker[len].apply( this, arguments );
}
},
addWireTap: function(callback) {
return this.configuration.bus.addWireTap(callback);
addWireTap : function ( callback ) {
return this.configuration.bus.addWireTap( callback );
},
linkChannels: function(sources, destinations) {
linkChannels : function ( sources, destinations ) {
var result = [];
if(!_.isArray(sources)) {
if ( !_.isArray( sources ) ) {
sources = [sources];
}
if(!_.isArray(destinations)) {
if ( !_.isArray( destinations ) ) {
destinations = [destinations];
}
_.each(sources, function(source){
_.each( sources, function ( source ) {
var sourceTopic = source.topic || "*";
_.each(destinations, function(destination) {
_.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) {
postal.subscribe( {
channel : source.channel || DEFAULT_CHANNEL,
topic : source.topic || "*",
callback : function ( data, env ) {
var newEnv = env;
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
newEnv.channel = destChannel;
newEnv.data = data;
postal.publish(newEnv);
postal.publish( newEnv );
}
})
} )
);
});
});
} );
} );
return result;
},
utils: {
getSessionId: function( callback ) {
utils : {
getSessionId : function ( callback ) {
postal.configuration.getSessionIdAction.call( this, callback );
},
setSessionId: function( value, callback ) {
postal.utils.getSessionId( function( info ) {
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 ) {
postal.configuration.setSessionIdAction( info, function ( session ) {
callback( session );
// publish postal event msg about the change
postal.publish({
channel: SYSTEM_CHANNEL,
topic: "sessionId.changed",
data: session
});
postal.publish( {
channel : SYSTEM_CHANNEL,
topic : "sessionId.changed",
data : session
} );
} );
});
} );
},
getSubscribersFor: function() {
getSubscribersFor : function () {
var channel = arguments[ 0 ],
tpc = arguments[ 1 ],
result = [];
if( arguments.length === 1 ) {
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( arguments.length === 1 ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = postal.configuration.DEFAULT_CHANNEL;
tpc = arguments[ 0 ];
}
@ -486,14 +491,14 @@ var postal = {
tpc = arguments[ 0 ].topic;
}
}
if( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc )) {
if ( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
}
return result;
},
reset: function() {
reset : function () {
postal.configuration.bus.reset();
postal.configuration.resolver.reset();
}
@ -501,4 +506,4 @@ var postal = {
};
return postal;
});
} );

View file

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

View file

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

View file

@ -1,14 +1,14 @@
require.config({
paths: {
underscore: 'libs/underscore/underscore-min',
postal: 'libs/postal/postal',
postaldiags:'libs/postal/postal.diagnostics',
jquery: 'libs/jquery/jquery-min'
require.config( {
paths : {
underscore : 'libs/underscore/underscore-min',
postal : 'libs/postal/postal',
postaldiags : 'libs/postal/postal.diagnostics',
jquery : 'libs/jquery/jquery-min'
}
});
} );
require( [ 'jquery' ], function( $ ){
$(function(){
require( [ 'jquery' ], function ( $ ) {
$( function () {
require( [ 'examples' ] );
});
});
} );
} );

View file

@ -4,7 +4,7 @@ div {
}
.results {
margin-bottom:20px;
margin-bottom: 20px;
padding: 10px;
border-top: 1pt solid lightsteelblue;
border-bottom: 1pt solid lightsteelblue;

View file

@ -2,8 +2,8 @@
<html>
<head>
<base href="/">
<title>Twitter Hash Tag Stats Demo</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<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>

View file

@ -1,4 +1,4 @@
define([
define( [
'jquery',
'backbone',
'bus',
@ -12,41 +12,40 @@ define([
'views/hash-tag-count',
'views/profanity-percentage',
'views/search-requests'
], function( $, Backbone, bus, Router, ViewManager, ContainerView, MenuView, TweetCountView, MentionCountView,
MentionerCountView, HashTagCountView, ProfanityPercentage, SearchRequestView ) {
], function ( $, Backbone, bus, Router, ViewManager, ContainerView, MenuView, TweetCountView, MentionCountView, MentionerCountView, HashTagCountView, ProfanityPercentage, SearchRequestView ) {
var app = {
bus: bus,
router: new Router()
bus : bus,
router : new Router()
};
// Set up UI concerns
app.viewManager = new ViewManager();
app.bus.viewManager.subscribe( "ui.show", function( data, env ) {
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 }
]);
app.viewManager.defineUIs([
{ name: "homeUI", dependencies: [ "container", "menu", "tweetCount", "mentionCount", "mentionerCount", "hashTagCount", "profanityPercentage" ] },
{ name: "searchRequestUI", dependencies: [ "container", "menu", "searchRequests" ] },
{ name: "wireTapLogUI", dependencies: [ "menu" ], options: { noHide: true } }
]);
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 }
] );
app.viewManager.defineUIs( [
{ name : "homeUI", dependencies : [ "container", "menu", "tweetCount", "mentionCount", "mentionerCount", "hashTagCount", "profanityPercentage" ] },
{ name : "searchRequestUI", dependencies : [ "container", "menu", "searchRequests" ] },
{ name : "wireTapLogUI", dependencies : [ "menu" ], options : { noHide : true } }
] );
$(function() {
Backbone.history.start({
pushState: true,
root: $( "base" ).attr( "href" )
});
});
$( function () {
Backbone.history.start( {
pushState : true,
root : $( "base" ).attr( "href" )
} );
} );
return app;
});
} );

View file

@ -1,11 +1,11 @@
define([
define( [
'postal'
], function ( postal ) {
return {
router : postal.channel( "router", "*" ),
router : postal.channel( "router", "*" ),
viewManager : postal.channel( "viewmanager", "*" ),
data : postal.channel( "data", "*" ),
app : postal.channel( "statsApp", "*", { type: "websocket" } ),
stats : postal.channel( "stats", "*", { type: "websocket" } )
data : postal.channel( "data", "*" ),
app : postal.channel( "statsApp", "*", { type : "websocket" } ),
stats : postal.channel( "stats", "*", { type : "websocket" } )
}
});
} );

View file

@ -1,315 +1,298 @@
/*
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
*/
/*
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 ) {
(function ( root, doc, factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [ "underscore", "machina", "postal" ], function( _, machina, postal ) {
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 ) {
}( 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.
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 postalSocket = postal.connections.socket = (function () {
var socketNamespace,
fsm = new machina.Fsm({
retryFn: undefined,
session: undefined,
wireUpSocketEvents: function() {
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 ) {
_.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");
states : {
uninitialized : {
tryConnect : function () {
this.transition( "initializing" );
}
},
initializing: {
_onEnter: function() {
socketNamespace = io.connect(postalSocket.config.url, { "auto connect": false });
initializing : {
_onEnter : function () {
socketNamespace = io.connect( postalSocket.config.url, { "auto connect" : false } );
this.wireUpSocketEvents();
this.transition("probing")
this.transition( "probing" )
},
socketTransmit: function() {
this.deferUntilTransition("online");
socketTransmit : function () {
this.deferUntilTransition( "online" );
}
},
probing: {
_onEnter: function() {
clearTimeout(this.retryFn);
if(!socketNamespace.socket.connecting && !socketNamespace.socket.reconnecting) {
probing : {
_onEnter : function () {
clearTimeout( this.retryFn );
if ( !socketNamespace.socket.connecting && !socketNamespace.socket.reconnecting ) {
socketNamespace.socket.connect();
}
else {
this.transition("settingSessionInfo");
this.transition( "settingSessionInfo" );
}
},
connect: function(){
this.transition("settingSessionInfo");
connect : function () {
this.transition( "settingSessionInfo" );
},
connect_failed: function() {
this.transition("disconnected");
connect_failed : function () {
this.transition( "disconnected" );
},
maxAttempts: function() {
this.transition("offline");
maxAttempts : function () {
this.transition( "offline" );
},
"postal.socket.remote" : function() {
this.deferUntilTransition("online");
"postal.socket.remote" : function () {
this.deferUntilTransition( "online" );
},
reconnect: function(){
this.transition("settingSessionInfo");
reconnect : function () {
this.transition( "settingSessionInfo" );
},
reconnect_failed: function() {
this.transition("disconnected");
reconnect_failed : function () {
this.transition( "disconnected" );
},
socketTransmit: function() {
this.deferUntilTransition("online");
socketTransmit : function () {
this.deferUntilTransition( "online" );
}
},
settingSessionInfo: {
_onEnter: function() {
settingSessionInfo : {
_onEnter : function () {
var self = this;
postal.utils.getSessionId( function( session ) {
if( !session || !session.id ) {
self.handle("useFallbackSessionId" );
} else {
self.session = session;
self.transition("identifying");
}
});
},
useFallbackSessionId : function () {
var self = this;
postal.utils.setSessionId( socketNamespace.socket.sessionid , function( session ) {
postal.utils.setSessionId( socketNamespace.socket.sessionid, function ( session ) {
self.session = session;
self.transition("identifying");
});
self.transition( "identifying" );
} );
}
},
identifying: {
_onEnter: function() {
identifying : {
_onEnter : function () {
var self = this;
self.retryFn = setTimeout(function() {
self.retryFn = setTimeout( function () {
self.handle( "timeout.identifying" );
},postalSocket.config.reconnectInterval );
}, postalSocket.config.reconnectInterval );
self.handle( "client.identifier" );
},
"client.identifier" : function() {
"client.identifier" : function () {
clearTimeout( this.retryFn );
socketNamespace.emit( "postal.clientId", { sessionId: this.session.id, lastSessionId: this.session.lastId } );
socketNamespace.emit( "postal.clientId", { id : this.session.id, lastId : this.session.lastId } );
},
"postal.session.changed" : function() {
socketNamespace.socket.disconnect();
connect_failed : function () {
this.transition( "disconnected" );
},
connect_failed: function() {
this.transition("disconnected");
disconnect : function () {
this.transition( "probing" );
},
disconnect: function() {
this.transition("probing");
"postal.socket.identified" : function ( data ) {
this.transition( "online" );
},
"postal.socket.identified" : function( data ) {
this.transition("online");
},
"postal.socket.migration" : function() {
_.each(postal.socket.manifest, function( sub ) {
"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");
"postal.socket.remote" : function () {
this.deferUntilTransition( "online" );
},
reconnect_failed: function() {
this.transition("disconnected");
reconnect_failed : function () {
this.transition( "disconnected" );
},
socketTransmit: function( evntName, envelope ) {
if( evntName === "postal.subscribe" ){
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);
socketNamespace.emit( evntName, socketEnv );
}
else {
this.deferUntilTransition("online");
this.deferUntilTransition( "online" );
}
},
"timeout.identifying" : function() {
this.transition("probing");
"timeout.identifying" : function () {
this.transition( "probing" );
}
},
online: {
disconnect: function() {
this.transition("probing");
online : {
disconnect : function () {
this.transition( "probing" );
},
"postal.session.changed" : function() {
socketNamespace.socket.disconnect();
goOffline : function () {
this.transition( "offline" );
},
goOffline: function() {
this.transition("offline");
},
"postal.socket.remote" : function( envelope ) {
"postal.socket.remote" : function ( envelope ) {
postal.publish( envelope );
},
socketTransmit: function( evntName, 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);
socketNamespace.emit( evntName, socketEnv );
}
},
offline: {
_onEnter: function() {
offline : {
_onEnter : function () {
socketNamespace.socket.disconnect();
},
socketTransmit: function() {
this.deferUntilTransition("online");
socketTransmit : function () {
this.deferUntilTransition( "online" );
},
"tryConnect": function() {
this.transition("probing");
"tryConnect" : function () {
this.transition( "probing" );
}
},
disconnected: {
_onEnter: function() {
disconnected : {
_onEnter : function () {
var self = this;
self.retryFn = setTimeout(function() {
self.transition("probing");
},postalSocket.config.reconnectInterval);
self.retryFn = setTimeout( function () {
self.transition( "probing" );
}, postalSocket.config.reconnectInterval );
},
connecting: function() {
this.transition("probing");
connecting : function () {
this.transition( "probing" );
},
reconnecting: function() {
this.transition("probing");
reconnecting : function () {
this.transition( "probing" );
},
socketTransmit: function() {
this.deferUntilTransition("online");
socketTransmit : function () {
this.deferUntilTransition( "online" );
}
}
}
});
postal.subscribe({
channel: "postal",
topic: "sessionId.changed",
callback: function() {
fsm.handle("postal.session.changed");
} );
postal.subscribe( {
channel : "postal",
topic : "sessionId.changed",
callback : function () {
fsm.handle( "postal.session.changed" );
}
});
} );
return {
config : {
url: window.location.origin,
reconnectInterval: 4000
url : window.location.origin,
reconnectInterval : 4000
},
goOffline: function() {
goOffline : function () {
fsm.handle( "goOffline" );
},
goOnline: function() {
goOnline : function () {
fsm.handle( "tryConnect" );
},
manifest: [],
publish: function( envelope ) {
manifest : [],
publish : function ( envelope ) {
fsm.handle( "socketTransmit", "postal.publish", envelope );
},
subscribe: function( options ) {
subscribe : function ( options ) {
options.channel = options.channel || postal.configuration.DEFAULT_CHANNEL;
options.topic = options.topic || "*";
if( !_.any( this.manifest, function( item ){
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 ) {
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);
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 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.publish = function () {
postalSocket.publish( localPublish.apply( channel, arguments ) );
};
channel.subscribe = function() {
var sub = localSubscribe.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 });
sub.unsubscribe = function () {
origUnsubscribe.call( sub );
postalSocket.unsubscribe( { channel : sub.channel, topic : sub.topic } );
};
postalSocket.subscribe({ channel: sub.channel, topic: sub.topic });
postalSocket.subscribe( { channel : sub.channel, topic : sub.topic } );
return sub;
};
channel.topic = function( topic ) {
if(topic === channel._topic) {
channel.topic = function ( topic ) {
if ( topic === channel._topic ) {
return this;
}
return new SocketChannel(this.channel, topic);
return new SocketChannel( this.channel, topic );
};
return channel;
};
}));
} ));

View file

@ -1,52 +1,52 @@
define([
define( [
'jquery',
'backbone',
'bus'
], function( $, Backbone, bus ){
], function ( $, Backbone, bus ) {
return Backbone.Router.extend({
routes: {
"" : "home",
"requests" : "requests",
"wiretap" : "wiretap",
return Backbone.Router.extend( {
routes : {
"" : "home",
"requests" : "requests",
"wiretap" : "wiretap",
"*anything" : "redirect"
},
initialize: function() {
initialize : function () {
var self = this;
_.bindAll( self );
$( document ).delegate( "a.ps-nav", "click", function( e ){
$( document ).delegate( "a.ps-nav", "click", function ( e ) {
e.preventDefault();
self.navigate( $( this ).attr( 'href' ), { trigger: true });
});
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
activateUI : function ( uiName, context ) {
bus.viewManager.publish( {
topic : "ui.show",
data : {
name : uiName,
context : context
}
});
} );
},
home: function() {
home : function () {
this.activateUI( "homeUI" );
},
requests: function() {
requests : function () {
this.activateUI( "searchRequestUI" );
},
wiretap: function() {
wiretap : function () {
this.activateUI( "wireTapLogUI" );
},
redirect: function() {
this.navigate( "/", { trigger: true });
redirect : function () {
this.navigate( "/", { trigger : true } );
}
});
});
} );
} );

View file

@ -1,8 +1,8 @@
define([
define( [
'underscore',
'bus'
], function( _, bus ){
var ViewManager = function() {
], 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
@ -17,16 +17,16 @@ define([
// 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) {
ViewManager.prototype.registerView = function ( name, viewCtor ) {
this.views[name] = {
rendered: false,
visible: false,
getInstance: (function(){
rendered : false,
visible : false,
getInstance : (function () {
var _instance;
return function(options){
return function ( options ) {
var _options = options || {};
if(!_instance || _options.forceNew) {
_instance = new viewCtor(_options.args || {});
if ( !_instance || _options.forceNew ) {
_instance = new viewCtor( _options.args || {} );
}
return _instance;
}
@ -34,8 +34,8 @@ define([
}
};
ViewManager.prototype.registerViews = function(views) {
_.each( views, function( view ){
ViewManager.prototype.registerViews = function ( views ) {
_.each( views, function ( view ) {
this.registerView( view.name, view.ctor );
}, this );
};
@ -46,76 +46,76 @@ define([
// 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 ) {
ViewManager.prototype.defineUI = function ( name, dependencies, options ) {
var self = this;
self.UI[ name ] = {
options: options || {},
dependencies: dependencies,
activate: function( data ) {
options : options || {},
dependencies : dependencies,
activate : function ( data ) {
data = data || {};
data.priorContext = self.priorContext;
data.targetContext = name;
if( !this.options.noHide ) {
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 ) ){
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 ){
_.each( shouldHide, function ( viewName ) {
var instance = self.views[ viewName ].getInstance();
if( instance.hide ) {
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);
_.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);
if ( !self.views[viewName].visible ) {
if ( instance.show ) {
instance.show( data );
}
self.views[viewName].visible = true;
}
if(instance.update) {
instance.update(data);
if ( instance.update ) {
instance.update( data );
}
});
} );
self.priorContext = name;
}
};
};
ViewManager.prototype.defineUIs = function( uis ) {
_.each( uis, function( ui ){
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 ) {
ViewManager.prototype.addViewToUI = function ( uiName, viewName, viewCtor ) {
var uis = _.isArray( uiName ) ? uiName : [ uiName ];
if( !this.views[ viewName ] ) {
if ( !this.views[ viewName ] ) {
this.registerView( viewName, viewCtor );
}
_.each( uis, function( ui ) {
if( this.UI[ ui ] ) {
_.each( uis, function ( ui ) {
if ( this.UI[ ui ] ) {
this.UI[ ui ].dependencies.push( viewName );
}
}, this );
};
return ViewManager;
});
} );

File diff suppressed because one or more lines are too long

View file

@ -4,36 +4,707 @@
// 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});
(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
} );

View file

@ -1,212 +1,212 @@
/*
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
*/
/*
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) {
(function ( root, doc, factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define(["underscore"], function(_) {
return factory(_, root, doc);
});
define( ["underscore"], function ( _ ) {
return factory( _, root, doc );
} );
} else {
// Browser globals
factory(root._, root, doc);
factory( root._, root, doc );
}
}(this, document, function(_, global, document, undefined) {
}( 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;
};
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);
makeFsmNamespace : (function () {
var machinaCount = 0;
return function () {
return "fsm." + machinaCount++;
};
})(),
getDefaultOptions : function () {
return {
initialState : "uninitialized",
eventListeners : {
"*" : []
},
states : {},
eventQueue : [],
namespace : utils.makeFsmNamespace()
};
}
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());
};
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 );
}
}
});
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());
}
}
});
}
};
opt = _.extend( defaults, options || {} );
initialState = opt.initialState;
delete opt.initialState;
_.extend( this, opt );
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;
};
this.state = undefined;
this._priorAction = "";
this._currentAction = "";
if ( initialState ) {
this.transition( initialState );
}
machina.fireEvent( "newFsm", this );
};
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.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.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.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.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.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.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.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.on = function(eventName, callback) {
if(!this.eventListeners[eventName]) {
this.eventListeners[eventName] = [];
}
this.eventListeners[eventName].push(callback);
};
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.off = function(eventName, callback) {
if(this.eventListeners[eventName]){
this.eventListeners[eventName] = _.without(this.eventListeners[eventName], callback);
}
};
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]) {
Fsm : Fsm,
bus : undefined,
utils : utils,
on : function ( eventName, callback ) {
if ( !this.eventListeners[eventName] ) {
this.eventListeners[eventName] = [];
}
this.eventListeners[eventName].push(callback);
this.eventListeners[eventName].push( callback );
},
off: function(eventName, callback) {
if(this.eventListeners[eventName]){
this.eventListeners[eventName] = _.without(this.eventListeners[eventName], callback);
off : function ( eventName, callback ) {
if ( this.eventListeners[eventName] ) {
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
}
},
fireEvent: function(eventName) {
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));
});
if ( listeners && listeners.length ) {
_.each( listeners, function ( callback ) {
callback.apply( null, slice.call( args, 1 ) );
} );
}
},
eventListeners: {
eventListeners : {
newFsm : []
}
};
global.machina = machina;
return machina;
}));
} ));

View file

@ -47,7 +47,9 @@ define( [ "postal", "underscore" ], function ( postal, _, undefined ) {
}
} );
postal.diagnostics = {
postal.diagnostics = postal.diagnostics || {};
postal.diagnostics.console = {
clearFilters : function () {
filters = [];
},
@ -60,13 +62,13 @@ define( [ "postal", "underscore" ], function ( postal, _, undefined ) {
if ( !_.isArray( constraint ) ) {
constraint = [ constraint ];
}
_.each( constraint, function( item ){
_.each( constraint, function ( item ) {
if ( filters.length === 0 || !_.any( filters, function ( filter ) {
return _.isEqual( filter, item );
} ) ) {
filters.push( item );
}
});
} );
},
getCurrentFilters : function () {

View file

@ -1,225 +1,228 @@
/*
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.6.0
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.6.0
*/
// This is the amd-module version of postal.js
// If you need the standard lib style version, go to http://github.com/ifandelse/postal.js
define(["underscore"], function(_, undefined) {
define( ["underscore"], function ( _, undefined ) {
var DEFAULT_CHANNEL = "/",
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };
NO_OP = function () {
};
var DistinctPredicate = function() {
var DistinctPredicate = function () {
var previous;
return function(data) {
return function ( data ) {
var eq = false;
if(_.isString(data)) {
if ( _.isString( data ) ) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
eq = _.isEqual( data, previous );
previous = _.clone( data );
}
return !eq;
};
};
var ChannelDefinition = function(channelName, defaultTopic) {
var ChannelDefinition = function ( channelName, defaultTopic ) {
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
};
ChannelDefinition.prototype = {
subscribe: function() {
subscribe : function () {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
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]);
else if ( len === 2 ) {
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
}
},
publish: function(obj) {
publish : function ( obj ) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj || {}
channel : this.channel,
topic : this._topic,
data : obj || {}
};
// If this is an envelope....
if( obj.topic && obj.data ) {
if ( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
postal.configuration.bus.publish( envelope );
return envelope;
},
topic: function(topic) {
if(topic === this._topic) {
topic : function ( topic ) {
if ( topic === this._topic ) {
return this;
}
return new ChannelDefinition(this.channel, topic);
return new ChannelDefinition( this.channel, topic );
}
};
var SubscriptionDefinition = function(channel, topic, callback) {
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.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.publish( {
channel : SYSTEM_CHANNEL,
topic : "subscription.created",
timeStamp : new Date(),
data : {
event : "subscription.created",
channel : channel,
topic : topic
}
});
} );
postal.configuration.bus.subscribe(this);
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
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() {
defer : function () {
var fn = this.callback;
this.callback = function(data) {
setTimeout(fn,0,data);
this.callback = function ( data ) {
setTimeout( fn, 0, data );
};
return this;
},
disposeAfter: function(maxCalls) {
if(_.isNaN(maxCalls) || maxCalls <= 0) {
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));
var dispose = _.after( maxCalls, _.bind( function () {
this.unsubscribe( this );
}, this ) );
this.onHandled = function() {
fn.apply(this.context, arguments);
this.onHandled = function () {
fn.apply( this.context, arguments );
dispose();
};
return this;
},
ignoreDuplicates: function() {
this.withConstraint(new DistinctPredicate());
ignoreDuplicates : function () {
this.withConstraint( new DistinctPredicate() );
return this;
},
withConstraint: function(predicate) {
if(! _.isFunction(predicate)) {
withConstraint : function ( predicate ) {
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
this.constraints.push(predicate);
this.constraints.push( predicate );
return this;
},
withConstraints: function(predicates) {
withConstraints : function ( predicates ) {
var self = this;
if(_.isArray(predicates)) {
_.each(predicates, function(predicate) { self.withConstraint(predicate); } );
if ( _.isArray( predicates ) ) {
_.each( predicates, function ( predicate ) {
self.withConstraint( predicate );
} );
}
return self;
},
withContext: function(context) {
withContext : function ( context ) {
this.context = context;
return this;
},
withDebounce: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withDebounce : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.debounce(fn, milliseconds);
this.callback = _.debounce( fn, milliseconds );
return this;
},
withDelay: function(milliseconds) {
if(_.isNaN(milliseconds)) {
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);
this.callback = function ( data ) {
setTimeout( function () {
fn( data );
}, milliseconds );
};
return this;
},
withPriority: function(priority) {
if(_.isNaN(priority)) {
withPriority : function ( priority ) {
if ( _.isNaN( priority ) ) {
throw "Priority must be a number";
}
this.priority = priority;
return this;
},
withThrottle: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withThrottle : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle(fn, milliseconds);
this.callback = _.throttle( fn, milliseconds );
return this;
},
subscribe: function(callback) {
subscribe : function ( callback ) {
this.callback = callback;
return this;
}
};
var bindingsResolver = {
cache: { },
cache : { },
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
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]) {
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;
@ -227,91 +230,93 @@ var bindingsResolver = {
return result;
},
reset: function() {
reset : function () {
this.cache = {};
}
};
var localBus = {
addWireTap: function(callback) {
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);
self.wireTaps.push( callback );
return function () {
var idx = self.wireTaps.indexOf( callback );
if ( idx !== -1 ) {
self.wireTaps.splice( idx, 1 );
}
};
},
publish: function(envelope) {
_.each(this.wireTaps,function(tap) {
tap(envelope.data, envelope);
});
publish : function ( envelope ) {
_.each( this.wireTaps, function ( tap ) {
tap( envelope.data, envelope );
} );
_.each(this.subscriptions[envelope.channel], function(topic) {
_.each(topic, function(subDef){
if(postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
if(_.all(subDef.constraints, function(constraint) { return constraint(envelope.data,envelope); })) {
if(typeof subDef.callback === 'function') {
subDef.callback.apply(subDef.context, [envelope.data, envelope]);
_.each( this.subscriptions[envelope.channel], function ( topic ) {
_.each( topic, function ( subDef ) {
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
if ( _.all( subDef.constraints, function ( constraint ) {
return constraint( envelope.data, envelope );
} ) ) {
if ( typeof subDef.callback === 'function' ) {
subDef.callback.apply( subDef.context, [envelope.data, envelope] );
subDef.onHandled();
}
}
}
});
});
} );
} );
},
reset: function() {
if( this.subscriptions ) {
_.each( this.subscriptions , function( channel ){
_.each( channel, function( topic ){
while( topic.length ) {
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) {
subscribe : function ( subDef ) {
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
if(!channel) {
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);
if ( !subs ) {
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
}
idx = subs.length - 1;
for(; idx >= 0; idx--) {
if(subs[idx].priority <= subDef.priority) {
subs.splice(idx + 1, 0, subDef);
for ( ; idx >= 0; idx-- ) {
if ( subs[idx].priority <= subDef.priority ) {
subs.splice( idx + 1, 0, subDef );
found = true;
break;
}
}
if(!found) {
subs.unshift(subDef);
if ( !found ) {
subs.unshift( subDef );
}
return subDef;
},
subscriptions: {},
subscriptions : {},
wireTaps: new Array(0),
wireTaps : new Array( 0 ),
unsubscribe: function(config) {
if(this.subscriptions[config.channel][config.topic]) {
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) {
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
break;
}
@ -321,50 +326,50 @@ var localBus = {
};
var publishPicker = {
"1" : function(envelope) {
if(!envelope) {
throw new Error("publishing from the 'global' postal.publish call requires a valid envelope.");
"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 };
"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 ) {
"1" : function ( chn ) {
var channel = chn, topic, options = {};
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
}
else {
channel = chn.channel || DEFAULT_CHANNEL;
topic = chn.topic;
topic = chn.topic;
options = chn.options || options;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"2" : function( chn, tpc ) {
"2" : function ( chn, tpc ) {
var channel = chn, topic = tpc, options = {};
if( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
options = tpc;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"3" : function( channel, topic, options ) {
"3" : function ( channel, topic, options ) {
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
}
},
@ -374,110 +379,110 @@ var publishPicker = {
localBus.subscriptions[SYSTEM_CHANNEL] = {};
var postal = {
configuration: {
bus: localBus,
resolver: bindingsResolver,
getSessionIdAction: function( callback ) {
configuration : {
bus : localBus,
resolver : bindingsResolver,
getSessionIdAction : function ( callback ) {
callback( sessionInfo );
},
setSessionIdAction: function( info, callback ) {
setSessionIdAction : function ( info, callback ) {
sessionInfo = info;
callback( sessionInfo );
},
DEFAULT_CHANNEL: DEFAULT_CHANNEL,
DEFAULT_PRIORITY: DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER: DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL: SYSTEM_CHANNEL
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL : SYSTEM_CHANNEL
},
channelTypes: {
local: ChannelDefinition
channelTypes : {
local : ChannelDefinition
},
channel: function() {
channel : function () {
var len = arguments.length;
if(channelPicker[len]) {
return channelPicker[len].apply(this, arguments);
if ( channelPicker[len] ) {
return channelPicker[len].apply( this, arguments );
}
},
subscribe: function(options) {
subscribe : function ( options ) {
var callback = options.callback,
topic = options.topic,
channel = options.channel || DEFAULT_CHANNEL;
return new SubscriptionDefinition(channel, topic, callback);
return new SubscriptionDefinition( channel, topic, callback );
},
publish: function() {
publish : function () {
var len = arguments.length;
if(publishPicker[len]) {
return publishPicker[len].apply(this, arguments);
if ( publishPicker[len] ) {
return publishPicker[len].apply( this, arguments );
}
},
addWireTap: function(callback) {
return this.configuration.bus.addWireTap(callback);
addWireTap : function ( callback ) {
return this.configuration.bus.addWireTap( callback );
},
linkChannels: function(sources, destinations) {
linkChannels : function ( sources, destinations ) {
var result = [];
if(!_.isArray(sources)) {
if ( !_.isArray( sources ) ) {
sources = [sources];
}
if(!_.isArray(destinations)) {
if ( !_.isArray( destinations ) ) {
destinations = [destinations];
}
_.each(sources, function(source){
_.each( sources, function ( source ) {
var sourceTopic = source.topic || "*";
_.each(destinations, function(destination) {
_.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) {
postal.subscribe( {
channel : source.channel || DEFAULT_CHANNEL,
topic : source.topic || "*",
callback : function ( data, env ) {
var newEnv = env;
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
newEnv.channel = destChannel;
newEnv.data = data;
postal.publish(newEnv);
postal.publish( newEnv );
}
})
} )
);
});
});
} );
} );
return result;
},
utils: {
getSessionId: function( callback ) {
utils : {
getSessionId : function ( callback ) {
postal.configuration.getSessionIdAction.call( this, callback );
},
setSessionId: function( value, callback ) {
postal.utils.getSessionId( function( info ) {
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 ) {
postal.configuration.setSessionIdAction( info, function ( session ) {
callback( session );
// publish postal event msg about the change
postal.publish({
channel: SYSTEM_CHANNEL,
topic: "sessionId.changed",
data: session
});
postal.publish( {
channel : SYSTEM_CHANNEL,
topic : "sessionId.changed",
data : session
} );
} );
});
} );
},
getSubscribersFor: function() {
getSubscribersFor : function () {
var channel = arguments[ 0 ],
tpc = arguments[ 1 ],
result = [];
if( arguments.length === 1 ) {
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( arguments.length === 1 ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = postal.configuration.DEFAULT_CHANNEL;
tpc = arguments[ 0 ];
}
@ -486,14 +491,14 @@ var postal = {
tpc = arguments[ 0 ].topic;
}
}
if( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc )) {
if ( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
}
return result;
},
reset: function() {
reset : function () {
postal.configuration.bus.reset();
postal.configuration.resolver.reset();
}
@ -501,4 +506,4 @@ var postal = {
};
return postal;
});
} );

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,116 @@
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)}}})})();
*/
(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 )
}}
} )
})();

View file

@ -5,27 +5,713 @@
// 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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
function(c){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);
(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, "&amp;" ).replace( /</g, "&lt;" ).replace( />/g, "&gt;" ).replace( /"/g, "&quot;" ).replace( /'/g, "&#x27;" ).replace( /\//g, "&#x2F;" )
};
b.mixin = function ( a ) {
j( b.functions( a ),
function ( c ) {
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 );

View file

@ -1,42 +1,35 @@
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'
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'
baseUrl : 'js'
} );
require( [ 'backbone', 'jquery', 'underscore', 'machina', 'postal', 'lib/postal.diagnostics', 'infrastructure/postal.socket-client' ],
function( Backbone, $, _, machina, postal ){
require( [ 'backbone', 'jquery', 'underscore', 'amplify', 'machina', 'postal', 'lib/postal.diagnostics', 'infrastructure/postal.socket-client' ],
function ( Backbone, $, _, amplify, machina, postal ) {
// for debugging purposes ONLY for now:
window.postal = postal;
/*postal.addWireTap( function( d, e ){
if( /search/.test(e.topic) ) {
console.log( JSON.stringify( e ) );
}
});
*/
postal.connections.socket.socketMgr.on( "*", function( evnt, data ){
var args = [].slice.call( arguments,1 );
if( args[0] === "postal.remote" ) {
//console.log( "FSM Event: " + evnt + " - " + JSON.stringify( args[0] ) );
}
else {
//console.log( "FSM Event: " + evnt + " - " + JSON.stringify( args ) );
}
});
postal.configuration.getSessionIdAction = function ( callback ) {
callback( amplify.store( "postal.session" ) || {} );
};
require([ 'infrastructure/app' ], function( app ) {
postal.configuration.setSessionIdAction = function ( info, callback ) {
amplify.store( "postal.session", info );
callback( amplify.store( "postal.session", info ) );
};
require( [ 'infrastructure/app' ], function ( app ) {
window.app = app;
});
} );
});
} );

View file

@ -2,30 +2,35 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
hashTags: []
return Backbone.Model.extend( {
defaults : {
hashTags : []
},
initialize: function() {
initialize : function () {
var self = this;
this.subscriptions = [
bus.stats.subscribe( "hash-tag-count", function( data, env ){
if( data.hashTags && data.hashTags.length ) {
self.set( "hashTags", _.sortBy( data.hashTags, function( item ) { return item.count * -1; } ) );
bus.stats.subscribe( "hash-tag-count", function ( data, env ) {
if ( data.hashTags && data.hashTags.length ) {
self.set( "hashTags", _.sortBy( data.hashTags, function ( item ) {
return item.count * -1;
} ) );
}
})
} ),
bus.app.subscribe( "search.init", function () {
self.set( "hashTags", [] );
} )
];
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
}
});
});
} );
} );

View file

@ -2,30 +2,35 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
mentions: []
return Backbone.Model.extend( {
defaults : {
mentions : []
},
initialize: function() {
initialize : function () {
var self = this;
this.subscriptions = [
bus.stats.subscribe( "mention-count", function( data, env ){
if( data.mentions && data.mentions.length ) {
self.set( "mentions", _.sortBy( data.mentions, function( item ) { return item.count * -1; } ) );
bus.stats.subscribe( "mention-count", function ( data, env ) {
if ( data.mentions && data.mentions.length ) {
self.set( "mentions", _.sortBy( data.mentions, function ( item ) {
return item.count * -1;
} ) );
}
})
} ),
bus.app.subscribe( "search.init", function () {
self.set( "mentions", [] );
} )
];
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
}
});
});
} );
} );

View file

@ -2,30 +2,35 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
mentioners: []
return Backbone.Model.extend( {
defaults : {
mentioners : []
},
initialize: function() {
initialize : function () {
var self = this;
this.subscriptions = [
bus.stats.subscribe( "mentioner-count", function( data, env ){
if( data.mentioners && data.mentioners.length ) {
self.set( "mentioners", _.sortBy( data.mentioners, function( item ) { return item.count * -1; } ) );
bus.stats.subscribe( "mentioner-count", function ( data, env ) {
if ( data.mentioners && data.mentioners.length ) {
self.set( "mentioners", _.sortBy( data.mentioners, function ( item ) {
return item.count * -1;
} ) );
}
})
} ),
bus.app.subscribe( "search.init", function () {
self.set( "mentioners", [] );
} )
];
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
}
});
});
} );
} );

View file

@ -2,43 +2,43 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
sessionId: "",
searchOwnership: "",
searchTerm: "",
requests: false
return Backbone.Model.extend( {
defaults : {
sessionId : "",
searchOwnership : "",
searchTerm : "",
requests : false
},
initialize: function() {
initialize : function () {
_.bindAll( this );
this.subscriptions = [
bus.app.subscribe( "search.info", this.setCurrentSearch ).defer(),
bus.app.subscribe( "search.new.ask", this.updateReqCount).defer(),
bus.app.subscribe( "search.new.ask", this.updateReqCount ).defer(),
bus.app.subscribe( "search.requests", this.updateReqCount ).defer()
];
bus.app.publish({
topic: "get.search.info",
data: {}
});
bus.app.publish( {
topic : "get.search.info",
data : {}
} );
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
},
setCurrentSearch: function( data, env ) {
setCurrentSearch : function ( data, env ) {
var self = this;
self.set( "searchTerm", data.searchTerm );
postal.utils.getSessionId(
function( session ) {
self.set( "sessionId", session.id, { silent: true } );
function ( session ) {
self.set( "sessionId", session.id, { silent : true } );
self.set( "searchOwnership",
(session.id === data.id)
? "You own the search."
@ -48,14 +48,14 @@ define( [
);
},
updateReqCount: function( data, env ) {
if((_.isArray(data) && data.length) || data.searchTerm) {
this.set("requests", true);
updateReqCount : function ( data, env ) {
if ( (_.isArray( data ) && data.length) || data.searchTerm ) {
this.set( "requests", true );
}
else {
this.set("requests", false);
this.set( "requests", false );
}
this.change("requests");
this.change( "requests" );
}
});
});
} );
} );

View file

@ -2,35 +2,43 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
percentage: "",
clean: "",
explicit: "",
total: ""
return Backbone.Model.extend( {
defaults : {
percentage : "",
clean : "",
explicit : "",
total : ""
},
initialize: function() {
initialize : function () {
var self = this;
this.subscriptions = [
bus.stats.subscribe( "profanity-percentage", function( data, env ){
self.set("percentage", data.percentage, { silent: true });
self.set("clean", data.clean, { silent: true });
self.set("explicit", data.explicit, { silent: true });
self.set("total", data.clean + data.explicit, { silent: true });
bus.stats.subscribe( "profanity-percentage", function ( data, env ) {
self.set( "percentage", data.percentage, { silent : true } );
self.set( "clean", data.clean, { silent : true } );
self.set( "explicit", data.explicit, { silent : true } );
self.set( "total", data.clean + data.explicit, { silent : true } );
self.change();
})
} ),
bus.app.subscribe( "search.init", function () {
console.log( "PROF PERF - SEARCH INIT!" );
self.set( "percentage", "", { silent : true } );
self.set( "clean", "", { silent : true } );
self.set( "explicit", "", { silent : true } );
self.set( "total", "", { silent : true } );
self.change();
} )
];
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
}
});
});
} );
} );

View file

@ -2,66 +2,85 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
requests: [],
ownerId: undefined,
sessionId: undefined
return Backbone.Model.extend( {
defaults : {
requests : [],
ownerId : undefined,
sessionId : undefined
},
initialize: function() {
initialize : function () {
_.bindAll( this );
this.setSessionId();
this.subscriptions = [
bus.app.subscribe( "search.info", this.setOwner ),
bus.app.subscribe( "search.requests", this.updateRequests ),
bus.app.subscribe( "search.new.ask", this.addRequest )
bus.app.subscribe( "search.new.ask", this.addRequest ),
bus.app.subscribe( "search.init", function () {
self.set( "requests", [] );
} )
];
bus.app.publish({
topic: "get.search.requests",
data: {}
});
this.askForUpdate();
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
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 } );
} );
this.clear( { silent : true } );
},
setOwner: function( data, env ) {
this.set("ownerId", data.id);
setOwner : function ( data, env ) {
this.set( "ownerId", data.id );
this.change();
this.setSessionId();
},
setSessionId: function() {
setSessionId : function () {
var self = this;
postal.utils.getSessionId(
function( session ) {
self.set( "sessionId", session.id, { silent: true } );
function ( session ) {
self.set( "sessionId", session.id );
self.change();
}
);
},
updateRequests: function( data, env ) {
this.set( "requests", _.sortBy( data, function( item ) { return item.searchTerm; } ) );
updateRequests : function ( data, env ) {
console.log( "Got an update" );
this.set( "requests", _.sortBy( data, function ( item ) {
return item.searchTerm;
} ) );
this.change();
},
addRequest: function( data, env ) {
var reqs = this.get("requests");
if( _.any( reqs, function( item ){
addRequest : function ( data, env ) {
var reqs = this.get( "requests" );
if ( _.any( reqs, function ( item ) {
return item.searchTerm === data.searchTerm &&
item.correlationId === data.correlationId
})) {
item.correlationId === data.correlationId
} ) ) {
return;
}
reqs.push( data );
this.set( "requests", _.sortBy( reqs, function( item ) { return item.searchTerm; } ), { silent: true } );
this.change("change:requests");
this.set( "requests", _.sortBy( reqs, function ( item ) {
return item.searchTerm;
} ) );
this.change();
}
});
});
} );
} );

View file

@ -2,30 +2,35 @@ define( [
'backbone',
'bus'
],
function( Backbone, bus ) {
function ( Backbone, bus ) {
"use strict";
return Backbone.Model.extend({
defaults: {
tweeters: []
return Backbone.Model.extend( {
defaults : {
tweeters : []
},
initialize: function() {
initialize : function () {
var self = this;
this.subscriptions = [
bus.stats.subscribe( "tweet-count", function( data, env ){
if( data.tweeters && data.tweeters.length ) {
self.set( "tweeters", _.sortBy( data.tweeters, function( item ) { return item.count * -1; } ) );
bus.stats.subscribe( "tweet-count", function ( data, env ) {
if ( data.tweeters && data.tweeters.length ) {
self.set( "tweeters", _.sortBy( data.tweeters, function ( item ) {
return item.count * -1;
} ) );
}
})
} ) ,
bus.app.subscribe( "search.init", function () {
self.set( "tweeters", [] );
} )
];
},
dispose: function(){
_.each( this.subscriptions, function( subscription ){
dispose : function () {
_.each( this.subscriptions, function ( subscription ) {
subscription.unsubscribe();
});
this.clear( { silent: true } );
} );
this.clear( { silent : true } );
}
});
});
} );
} );

View file

@ -3,27 +3,27 @@ define( [
'backbone',
'text!views/templates/container.html'
],
function( $, Backbone, template ) {
function ( $, Backbone, template ) {
// Using ECMAScript 5 strict mode during development. By default r.js will ignore that.
"use strict";
return Backbone.View.extend( {
el: "body",
el : "body",
initialize: function() {
_.bindAll(this, "render");
initialize : function () {
_.bindAll( this, "render" );
this.template = template;
},
render: function() {
this.$el.html(this.template);
render : function () {
this.$el.html( this.template );
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
update: function( data ) {
update : function ( data ) {
}
} );

View file

@ -5,36 +5,36 @@ define( [
'models/hash-tag-count-model',
'bus'
],
function( $, Backbone, template, HashTagCountModel, bus ) {
function ( $, Backbone, template, HashTagCountModel, bus ) {
"use strict";
return Backbone.View.extend( {
tagName: "div",
tagName : "div",
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new HashTagCountModel();
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: {} });
bus.stats.publish( { topic : "hash-tag-count.getLatest", data : {} } );
},
render: function() {
render : function () {
// TODO: Capture scroll position and restore after render...
this.$el.html( this.template( this.model.toJSON() ) );
if( !this.inDom ) {
if ( !this.inDom ) {
this.$el.appendTo( "#stats" );
this.inDom = true;
}
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
}
} );

View file

@ -5,36 +5,36 @@ define( [
'models/mention-count-model',
'bus'
],
function( $, Backbone, template, MentionCountModel, bus ) {
function ( $, Backbone, template, MentionCountModel, bus ) {
"use strict";
return Backbone.View.extend( {
tagName: "div",
tagName : "div",
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new MentionCountModel();
bus.app.subscribe( "search.info", this.setCurrentSearch );
this.model.bind( "change", this.render );
this.inDom = false;
bus.stats.publish({ topic: "mention-count.getLatest", data: {} });
bus.stats.publish( { topic : "mention-count.getLatest", data : {} } );
},
render: function() {
render : function () {
// TODO: Capture scroll position and restore after render...
this.$el.html( this.template( this.model.toJSON() ) );
if( !this.inDom ) {
if ( !this.inDom ) {
this.$el.appendTo( "#stats" );
this.inDom = true;
}
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
}
} );

View file

@ -5,36 +5,36 @@ define( [
'models/mentioner-count-model',
'bus'
],
function( $, Backbone, template, MentionerCountModel, bus ) {
function ( $, Backbone, template, MentionerCountModel, bus ) {
"use strict";
return Backbone.View.extend( {
tagName: "div",
tagName : "div",
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new MentionerCountModel();
bus.app.subscribe( "search.info", this.setCurrentSearch );
this.model.bind( "change", this.render );
this.inDom = false;
bus.stats.publish({ topic: "mentioner-count.getLatest", data: {} });
bus.stats.publish( { topic : "mentioner-count.getLatest", data : {} } );
},
render: function() {
render : function () {
// TODO: Capture scroll position and restore after render...
this.$el.html( this.template( this.model.toJSON() ) );
if( !this.inDom ) {
if ( !this.inDom ) {
this.$el.appendTo( "#stats" );
this.inDom = true;
}
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
}
} );

View file

@ -5,47 +5,47 @@ define( [
'bus',
'models/menu-model'
],
function( $, Backbone, template, bus, MenuModel ) {
function ( $, Backbone, template, bus, MenuModel ) {
"use strict";
return Backbone.View.extend( {
el: "#menu",
el : "#menu",
events: {
events : {
"click #btnSearch" : "updateSearch"
},
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new MenuModel();
this.model.bind( "change", this.updateView );
},
render: function() {
render : function () {
this.$el.html( this.template( this.model.toJSON() ) );
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
updateSearch: function() {
var searchTerm = this.$el.find('#searchTerm').val();
if( searchTerm ) {
bus.app.publish({
topic: "search.new.request",
data: {
searchTerm: searchTerm
updateSearch : function () {
var searchTerm = this.$el.find( '#searchTerm' ).val();
if ( searchTerm ) {
bus.app.publish( {
topic : "search.new.request",
data : {
searchTerm : searchTerm
}
});
} );
}
},
updateView: function() {
this.$el.find( "#currentSearch" ).text( this.model.get("searchTerm") );
this.$el.find( "#search-ownership").text( this.model.get("searchOwnership" ));
this.$el.find( "#request-indicator").text( this.model.get("requests") ? " *" : "" );
updateView : function () {
this.$el.find( "#currentSearch" ).text( this.model.get( "searchTerm" ) );
this.$el.find( "#search-ownership" ).text( this.model.get( "searchOwnership" ) );
this.$el.find( "#request-indicator" ).text( this.model.get( "requests" ) ? " *" : "" );
}
} );
} );

View file

@ -5,36 +5,36 @@ define( [
'models/profanity-percentage-model',
'bus'
],
function( $, Backbone, template, ProfanityPercentageModel, bus ) {
function ( $, Backbone, template, ProfanityPercentageModel, bus ) {
"use strict";
return Backbone.View.extend( {
tagName: "div",
tagName : "div",
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new ProfanityPercentageModel();
bus.app.subscribe( "search.info", this.setCurrentSearch );
this.model.bind( "change", this.render );
this.inDom = false;
bus.stats.publish({ topic: "profanity-percentage.getLatest", data: {} });
bus.stats.publish( { topic : "profanity-percentage.getLatest", data : {} } );
},
render: function() {
render : function () {
// TODO: Capture scroll position and restore after render...
this.$el.html( this.template( this.model.toJSON() ) );
if( !this.inDom ) {
if ( !this.inDom ) {
this.$el.appendTo( "#stats" );
this.inDom = true;
}
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
}
} );

View file

@ -5,43 +5,44 @@ define( [
'models/search-requests-model',
'bus'
],
function( $, Backbone, template, SearchRequestsModel, bus ) {
function ( $, Backbone, template, SearchRequestsModel, bus ) {
"use strict";
return Backbone.View.extend( {
el: "#requests",
el : "#requests",
events: {
"click a.req-allow": "allowSearch"
events : {
"click a.req-allow" : "allowSearch"
},
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new SearchRequestsModel();
this.model.bind( "change", this.render );
},
render: function() {
render : function () {
this.$el.html( this.template( this.model.toJSON() ) );
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
this.model.askForUpdate();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
},
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
})
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();
}

View file

@ -1,5 +1,7 @@
<fieldset>
<legend><div class="title">Associated Hash Tags:</div></legend>
<legend>
<div class="title">Associated Hash Tags:</div>
</legend>
<div class="scrollableDiv">
<table class="hashTagTable" cellspacing="0">
<% _.each( hashTags, function( item ) { %>

View file

@ -4,7 +4,7 @@
<table cellspacing="0">
<% _.each( mentions, function( mention ) { %>
<tr>
<td><img class="images" src="<%= mention.image %>" /></td>
<td><img class="images" src="<%= mention.image %>"/></td>
<td><span><%= mention.user %></span></td>
<td><span><%= mention.count %></span></td>
</tr>

View file

@ -4,7 +4,7 @@
<table cellspacing="0">
<% _.each( mentioners, function( mentioner ) { %>
<tr>
<td><img class="images" src="<%= mentioner.image %>" /></td>
<td><img class="images" src="<%= mentioner.image %>"/></td>
<td><span><%= mentioner.user %></span></td>
<td><span><%= mentioner.count %></span></td>
</tr>

View file

@ -1,18 +1,18 @@
<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>
<% }) %>
<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><em>No other searches have been requested at this time.</em></div>
<% } %>
</div>

View file

@ -4,7 +4,7 @@
<table cellspacing="0">
<% _.each( tweeters, function( tweeter ) { %>
<tr>
<td><img class="images" src="<%= tweeter.image %>" /></td>
<td><img class="images" src="<%= tweeter.image %>"/></td>
<td><span><%= tweeter.user %></span></td>
<td><span><%= tweeter.count %></span></td>
</tr>

View file

@ -5,36 +5,36 @@ define( [
'models/tweet-count-model',
'bus'
],
function( $, Backbone, template, TweetCountModel, bus ) {
function ( $, Backbone, template, TweetCountModel, bus ) {
"use strict";
return Backbone.View.extend( {
tagName: "div",
tagName : "div",
initialize: function() {
initialize : function () {
_.bindAll( this );
this.template = _.template( template );
this.model = new TweetCountModel();
bus.app.subscribe( "search.info", this.setCurrentSearch );
this.model.bind( "change", this.render );
this.inDom = false;
bus.stats.publish({ topic: "tweet-count.getLatest", data: {} });
bus.stats.publish( { topic : "tweet-count.getLatest", data : {} } );
},
render: function() {
render : function () {
// TODO: Capture scroll position and restore after render...
this.$el.html( this.template( this.model.toJSON() ) );
if( !this.inDom ) {
if ( !this.inDom ) {
this.$el.appendTo( "#stats" );
this.inDom = true;
}
},
show: function( data ) {
show : function ( data ) {
this.$el.show();
},
hide: function( data ) {
hide : function ( data ) {
this.$el.hide();
}
} );

View file

@ -1,62 +1,62 @@
var _ = require('underscore'),
_scanner = function(regExp, text, callback) {
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());
while ( text.search( regExp ) !== -1 ) {
match = regExp.exec( text );
if ( match && match[1] ) {
callback( match[1].toLowerCase() );
}
text = text.replace(regExp, "");
text = text.replace( regExp, "" );
}
},
HashTagCount = function(namespace) {
HashTagCount = function ( namespace ) {
this.namespace = namespace;
this.events = {};
this.hashTags = { list: [], registry: {} };
this.hashTags = { list : [], registry : {} };
this.lastStats = undefined;
this.lastStats = undefined;
};
HashTagCount.prototype = {
init: function() {
this.hashTags = { list: [], registry: {} };
init : function () {
this.hashTags = { list : [], registry : {} };
this.lastStats = undefined;
},
on: function( eventName, callback ) {
if( !this.events[ eventName ] ) {
on : function ( eventName, callback ) {
if ( !this.events[ eventName ] ) {
this.events[ eventName ] = [];
}
this.events[ eventName ].push( callback );
return function() {
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 ){
raiseEvent : function ( eventName, data ) {
if ( this.events[ eventName ] ) {
this.events[ eventName ].forEach( function ( callback ) {
callback( data );
});
} );
}
},
processNewTweets: function( tweets ) {
tweets.forEach( function( tweet ){
processNewTweets : function ( tweets ) {
tweets.forEach( function ( tweet ) {
this.processOtherHashTags( tweet.text );
}, this );
this.lastStats = { type: "HashTagCount", hashTags: this.hashTags.list };
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 };
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 ));
}.bind( this ) );
}
};

View file

@ -1,72 +1,72 @@
var _ = require('underscore'),
_scanner = function(regExp, text, callback) {
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());
while ( text.search( regExp ) !== -1 ) {
match = regExp.exec( text );
if ( match && match[1] ) {
callback( match[1].toLowerCase() );
}
text = text.replace(regExp, "");
text = text.replace( regExp, "" );
}
},
MentionCount = function(namespace) {
MentionCount = function ( namespace ) {
this.namespace = namespace;
this.events = {};
this.mentions = { list: [], registry: {} };
this.mentions = { list : [], registry : {} };
this.userImageMap = {};
this.lastStats = undefined;
this.lastStats = undefined;
};
MentionCount.prototype = {
init: function() {
this.mentions = { list: [], registry: {} };
init : function () {
this.mentions = { list : [], registry : {} };
this.lastStats = undefined;
},
on: function( eventName, callback ) {
if( !this.events[ eventName ] ) {
on : function ( eventName, callback ) {
if ( !this.events[ eventName ] ) {
this.events[ eventName ] = [];
}
this.events[ eventName ].push( callback );
return function() {
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 ){
raiseEvent : function ( eventName, data ) {
if ( this.events[ eventName ] ) {
this.events[ eventName ].forEach( function ( callback ) {
callback( data );
});
} );
}
},
processNewTweets: function(tweets) {
tweets.forEach(function(tweet){
processNewTweets : function ( tweets ) {
tweets.forEach( function ( tweet ) {
this.userImageMap[ tweet.from_user ] = tweet.profile_image_url;
this.processMentions(tweet);
}, this);
this.processMentions( tweet );
}, this );
this.tryToMatchProfileImages();
this.lastStats = { type: "MentionCount", mentions: this.mentions.list };
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 ] ) {
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" };
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.list.push( obj );
}
this.mentions.registry[mentioned].count++;
}.bind(this));
}.bind( this ) );
}
};

View file

@ -1,61 +1,61 @@
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());
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, "");
text = text.replace( regExp, "" );
}
},
MentionerCount = function(namespace) {
MentionerCount = function ( namespace ) {
this.namespace = namespace;
this.events = {};
this.mentioners = { list: [], registry: {} };
this.mentioners = { list : [], registry : {} };
this.lastStats = undefined;
this.lastStats = undefined;
};
MentionerCount.prototype = {
init: function() {
this.mentioners = { list: [], registry: {} };
init : function () {
this.mentioners = { list : [], registry : {} };
this.lastStats = undefined;
},
on: function( eventName, callback ) {
if( !this.events[ eventName ] ) {
on : function ( eventName, callback ) {
if ( !this.events[ eventName ] ) {
this.events[ eventName ] = [];
}
this.events[ eventName ].push( callback );
return function() {
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 ){
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 };
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 };
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.list.push( obj );
}
this.mentioners.registry[tweet.from_user].count++;
}.bind(this));
}.bind( this ) );
}
};

View file

@ -34,70 +34,69 @@ var _badWords = [
/\bpiss\b/,
/\bpissing\b/
],
_ = require('underscore'),
ProfanityPercentage = function(namespace) {
_ = require( 'underscore' ),
ProfanityPercentage = function ( namespace ) {
this.namespace = namespace;
this.events = {};
this.profanityStats = { clean: 0, explicit: 0 };
this.profanityStats = { clean : 0, explicit : 0 };
this.lastStats = undefined;
this.lastStats = undefined;
};
ProfanityPercentage.prototype = {
init: function() {
this.profanityStats = { clean: 0, explicit: 0 };
init : function () {
this.profanityStats = { clean : 0, explicit : 0 };
this.lastStats = undefined;
},
on: function( eventName, callback ) {
if( !this.events[ eventName ] ) {
on : function ( eventName, callback ) {
if ( !this.events[ eventName ] ) {
this.events[ eventName ] = [];
}
this.events[ eventName ].push( callback );
return function() {
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 ){
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)
{
hasProfanity : function ( text ) {
for ( var i = 0; i < _badWords.length; i++ ) {
if ( text.search( _badWords[i] ) !== -1 ) {
return true;
}
}
return false;
},
getPercentage: function() {
getPercentage : function () {
var total = (this.profanityStats.clean + this.profanityStats.explicit);
if(total === 0) {
if ( total === 0 ) {
return 0;
}
else {
return ((this.profanityStats.explicit / total) * 100).toFixed(2);
return ((this.profanityStats.explicit / total) * 100).toFixed( 2 );
}
},
processNewTweets: function(tweets) {
tweets.forEach(function(tweet){
this.profanitize(tweet);
}, this);
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
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)) {
profanitize : function ( tweet ) {
if ( this.hasProfanity( tweet.text ) ) {
this.profanityStats.explicit++;
}
else {

View file

@ -1,48 +1,48 @@
var _ = require('underscore'),
TweetCount = function(namespace) {
var _ = require( 'underscore' ),
TweetCount = function ( namespace ) {
this.namespace = namespace;
this.events = {};
this.tweeters = { list: [], registry: {} };
this.tweeters = { list : [], registry : {} };
this.lastStats = undefined;
this.lastStats = undefined;
};
TweetCount.prototype = {
init: function() {
this.tweeters = { list: [], registry: {} };
init : function () {
this.tweeters = { list : [], registry : {} };
this.lastStats = undefined;
},
on: function( eventName, callback ) {
if( !this.events[ eventName ] ) {
on : function ( eventName, callback ) {
if ( !this.events[ eventName ] ) {
this.events[ eventName ] = [];
}
this.events[ eventName ].push( callback );
return function() {
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 ){
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 };
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};
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.list.push( obj );
}
this.tweeters.registry[tweet.from_user].count++;
}

View file

@ -1,4 +1,4 @@
var _ = require("underscore"),
var _ = require( "underscore" ),
express = require( 'express' ),
app = express.createServer(),
postal = require( "./messaging/postal.js" ),
@ -9,21 +9,21 @@ var _ = require("underscore"),
BusAdapter = require( './messaging/bus-adapter.js' ),
machina = require( './machina.js' );
postal.addWireTap( function( data, envelope ){
if( envelope.channel === "stats" /*|| envelope.channel === "twittersearch"*/ ) {
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) ) {
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 ) {
var TwitterSocketStats = function ( port, refreshinterval ) {
// Stand up our express app
app.use( "/", express.static( __dirname + '/client' ) );
app.listen( port );
@ -31,162 +31,164 @@ var TwitterSocketStats = function( port, refreshinterval ) {
statsChannel = postal.channel( "stats", "*" ),
appChannel = postal.channel( "statsApp", "*" );
postal.linkChannels( { channel: "postal.socket", topic: "client.migrated"}, { channel: "statsApp", topic: "client.migrated" } );
postal.linkChannels( { channel : "postal.socket", topic : "client.migrated"}, { channel : "statsApp", topic : "client.migrated" } );
return new machina.Fsm({
return new machina.Fsm( {
namespace: "statsApp",
namespace : "statsApp",
currentSearch: {
id: null,
searchTerm: "{ Search Not Active }"
currentSearch : {
id : null,
searchTerm : "{ Search Not Active }"
},
searchRequests: {},
searchRequests : {},
express: app,
express : app,
appChannel: appChannel,
appChannel : appChannel,
searchChannel: searchChannel,
searchChannel : searchChannel,
statsChannel: statsChannel,
statsChannel : statsChannel,
searchAgent: new BusAdapter(new TwitterSearch( refreshinterval), searchChannel, searchChannel ),
searchAgent : new BusAdapter( new TwitterSearch( refreshinterval ), searchChannel, searchChannel ),
requestedSearches: [],
requestedSearches : [],
stats: collectors.load( searchChannel, statsChannel ),
stats : collectors.load( searchChannel, statsChannel ),
postal: postal,
postal : postal,
bridge: new PostalSocketHost( postal, sockets.getSocketProvider( postal, app ) ),
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 }; })
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 ) {
setSearch : function ( correlationId, searchTerm ) {
this.currentSearch = {
id: correlationId,
searchTerm: searchTerm
id : correlationId,
searchTerm : searchTerm
};
this.searchAgent.search( searchTerm );
this.removeSearchRequest( correlationId, searchTerm );
this.appChannel.publish({
topic: "search.info",
data: this.currentSearch
});
this.appChannel.publish( {
topic : "search.info",
data : this.currentSearch
} );
},
getSearchInfo: function( env ) {
this.appChannel.publish({
topic: "search.info",
correlationId: env.correlationId,
data: this.currentSearch
});
getSearchInfo : function ( env ) {
this.appChannel.publish( {
topic : "search.info",
correlationId : env.correlationId,
data : this.currentSearch
} );
},
getSearchRequests: function ( env ) {
getSearchRequests : function ( env ) {
env || (env = {});
this.appChannel.publish({
topic: "search.requests",
correlationId: env.correlationId,
data: this.requestedSearches
});
this.appChannel.publish( {
topic : "search.requests",
correlationId : env.correlationId,
data : this.requestedSearches
} );
},
addSearchRequest: function( correlationId, searchTerm ) {
if( !_.any( this.searchRequests, function( item ) {
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 } );
item.searchTerm === request.searchTerm;
} ) ) {
this.requestedSearches.push( { correlationId : correlationId, searchTerm : searchTerm } );
}
},
removeSearchRequest: function ( correlationId, searchTerm ) {
if(_.any( this.requestedSearches, function ( item ) {
removeSearchRequest : function ( correlationId, searchTerm ) {
if ( _.any( this.requestedSearches, function ( item ) {
return item.correlationId = correlationId && item.searchTerm === searchTerm;
})) {
this.requestedSearches = _.filter( this.requestedSearches, function ( item ){
} ) ) {
this.requestedSearches = _.filter( this.requestedSearches, function ( item ) {
return item.correlationId !== correlationId && item.searchTerm !== searchTerm;
});
} );
this.getSearchRequests();
}
},
states: {
uninitialized: {
start: function() {
this.transition("notSearching");
states : {
uninitialized : {
start : function () {
this.transition( "notSearching" );
},
"*" : function() {
"*" : function () {
this.deferUntilTransition();
}
},
notSearching: {
"search.new.request" : function( data, envelope ) {
notSearching : {
"search.new.request" : function ( data, envelope ) {
this.setSearch( envelope.correlationId, data.searchTerm );
this.transition("searching");
this.transition( "searching" );
},
"get.search.info": function( data, env ) {
"get.search.info" : function ( data, env ) {
this.getSearchInfo( env );
},
"get.available" : function( data, envelope ) {
"get.available" : function ( data, envelope ) {
this.getAvailableStats( envelope.correlationId );
}
},
searching: {
"search.new.request" : function( data, envelope ) {
if( envelope.correlationId === this.currentSearch.id ) {
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
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 ) {
"search.new.confirm" : function ( data, envelope ) {
if ( envelope.correlationId === this.currentSearch.id ) {
this.setSearch( data.correlationId, data.searchTerm );
}
},
"get.search.info": function( data, env ) {
"get.search.info" : function ( data, env ) {
this.getSearchInfo( env );
},
"get.search.requests": function( data, env ) {
"get.search.requests" : function ( data, env ) {
this.getSearchRequests( env );
},
"get.available" : function( data, envelope ) {
"get.available" : function ( data, envelope ) {
this.getAvailableStats( envelope.correlationId );
},
"client.migrated" : function( data, envelope ) {
if( data.lastSessionId === this.currentSearch.id ) {
"client.migrated" : function ( data, envelope ) {
if ( data.lastSessionId === this.currentSearch.id ) {
this.currentSearch.id = data.sessionId;
}
}
}
}
});
} );
};
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.on( "*", function ( evnt, data ) {
console.log( "FSM Event: " + evnt + " - " + JSON.stringify( [].slice.call( arguments, 1 ) ) );
} );
x.handle("start");
x.handle( "start" );

View file

@ -1,197 +1,197 @@
var _ = require('underscore');
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
*/
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){
transformEventListToObject = function ( eventList ) {
var obj = {};
_.each(eventList, function(evntName) {
_.each( eventList, function ( evntName ) {
obj[evntName] = [];
});
} );
return obj;
},
parseEventListeners = function(evnts) {
parseEventListeners = function ( evnts ) {
var obj = evnts;
if(_.isArray(evnts)) {
obj = transformEventListToObject(evnts);
if ( _.isArray( evnts ) ) {
obj = transformEventListToObject( evnts );
}
return obj;
},
utils = {
makeFsmNamespace: (function(){
makeFsmNamespace : (function () {
var machinaCount = 0;
return function() {
return function () {
return "fsm." + machinaCount++;
};
})(),
getDefaultOptions: function() {
getDefaultOptions : function () {
return {
initialState: "uninitialized",
eventListeners: {
initialState : "uninitialized",
eventListeners : {
"*" : []
},
states: {},
eventQueue: [],
namespace: utils.makeFsmNamespace()
states : {},
eventQueue : [],
namespace : utils.makeFsmNamespace()
};
}
},
Fsm = function(options) {
var opt, initialState, defaults = utils.getDefaultOptions();
if(options) {
if(options.eventListeners) {
options.eventListeners = parseEventListeners(options.eventListeners);
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);
if ( options.messaging ) {
options.messaging = _.extend( {}, defaults.messaging, options.messaging );
}
}
opt = _.extend(defaults , options || {});
opt = _.extend( defaults, options || {} );
initialState = opt.initialState;
delete opt.initialState;
_.extend(this,opt);
_.extend( this, opt );
this.state = undefined;
this._priorAction = "";
this._currentAction = "";
if(initialState) {
this.transition(initialState);
if ( initialState ) {
this.transition( initialState );
}
machina.fireEvent("newFsm", this);
machina.fireEvent( "newFsm", this );
};
Fsm.prototype.fireEvent = function(eventName) {
var args = arguments;
_.each(this.eventListeners["*"], function(callback) {
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());
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());
}
}
});
}
} );
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) {
Fsm.prototype.handle = function ( msgType ) {
// vars to avoid a "this." fest
var states = this.states, current = this.state, args = slice.call(arguments,0), handlerName;
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));
}
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.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) {
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);
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.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.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.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);
}
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]) {
Fsm : Fsm,
bus : undefined,
utils : utils,
on : function ( eventName, callback ) {
if ( !this.eventListeners[eventName] ) {
this.eventListeners[eventName] = [];
}
this.eventListeners[eventName].push(callback);
this.eventListeners[eventName].push( callback );
},
off: function(eventName, callback) {
if(this.eventListeners[eventName]){
this.eventListeners[eventName] = _.without(this.eventListeners[eventName], callback);
off : function ( eventName, callback ) {
if ( this.eventListeners[eventName] ) {
this.eventListeners[eventName] = _.without( this.eventListeners[eventName], callback );
}
},
fireEvent: function(eventName) {
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));
});
if ( listeners && listeners.length ) {
_.each( listeners, function ( callback ) {
callback.apply( null, slice.call( args, 1 ) );
} );
}
},
eventListeners: {
eventListeners : {
newFsm : []
}
};

View file

@ -1,15 +1,18 @@
module.exports = function( target, searchChannel, statsChannel ) {
module.exports = function ( target, searchChannel, statsChannel ) {
target.bus = {
subscriptions: [],
publishers: [
target.on( "newTweets", function( data ) {
statsChannel.publish( { topic: "newTweets", data: data } );
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.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.nodata", function ( data ) {
searchChannel.publish( { topic : "search.nodata", data : data } );
} ),
target.on( "search.init", function ( data ) {
searchChannel.publish( { topic : "search.init", data : data } );
} )
]
};

View file

@ -1,20 +1,21 @@
module.exports = function( target, searchChannel, statChannel ) {
module.exports = function ( target, searchChannel, statChannel ) {
target.bus = {
subscriptions: [
searchChannel.subscribe( "init", target.init ).withContext( target ),
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 )
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 } );
publishers : [
target.on( target.namespace, function ( data ) {
statChannel.publish( { topic : target.namespace, data : data } );
} )
]
};

View file

@ -1,45 +1,45 @@
/*
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
*/
/*
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) {
module.exports = function ( postal, machina ) {
var bus = machina.bus = {
channels: {},
config: {
handlerChannelSuffix: "",
eventChannelSuffix: ".events"
channels : {},
config : {
handlerChannelSuffix : "",
eventChannelSuffix : ".events"
},
wireHandlersToBus: function(fsm, handlerChannel) {
wireHandlersToBus : function ( fsm, handlerChannel ) {
bus.channels[handlerChannel]._subscriptions.push(
bus.channels[handlerChannel].subscribe("*", function(data, envelope){
fsm.handle.call(fsm, envelope.topic, data, envelope);
})
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(){
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());
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);
fsm.on( "*", publisher );
},
wireUp: function(fsm) {
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 });
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);
bus.wireHandlersToBus( fsm, handlerChannel );
bus.wireEventsToBus( fsm, eventChannel );
}
};
machina.on("newFsm", bus.wireUp);
machina.on( "newFsm", bus.wireUp );
};

View file

@ -1,4 +1,4 @@
module.exports = function( _, postal ) {
module.exports = function ( _, postal ) {
var filters = [],
applyFilter = function ( filter, env ) {
var match = 0, possible = 0;
@ -44,7 +44,9 @@ module.exports = function( _, postal ) {
}
} );
postal.diagnostics = {
postal.diagnostics = postal.diagnostics || {};
postal.diagnostics.console = {
clearFilters : function () {
filters = [];
},
@ -57,13 +59,13 @@ module.exports = function( _, postal ) {
if ( !_.isArray( constraint ) ) {
constraint = [ constraint ];
}
_.each( constraint, function( item ){
_.each( constraint, function ( item ) {
if ( filters.length === 0 || !_.any( filters, function ( filter ) {
return _.isEqual( filter, item );
} ) ) {
filters.push( item );
}
});
} );
},
getCurrentFilters : function () {

View file

@ -1,225 +1,228 @@
/*
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.6.0
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.6.0
*/
// This is the node.js version of postal.js
// If you need the standard or amd client lib version, go to http://github.com/ifandelse/postal.js
var _ = require('underscore');
var _ = require( 'underscore' );
var DEFAULT_CHANNEL = "/",
DEFAULT_PRIORITY = 50,
DEFAULT_DISPOSEAFTER = 0,
SYSTEM_CHANNEL = "postal",
NO_OP = function() { };
NO_OP = function () {
};
var DistinctPredicate = function() {
var DistinctPredicate = function () {
var previous;
return function(data) {
return function ( data ) {
var eq = false;
if(_.isString(data)) {
if ( _.isString( data ) ) {
eq = data === previous;
previous = data;
}
else {
eq = _.isEqual(data, previous);
previous = _.clone(data);
eq = _.isEqual( data, previous );
previous = _.clone( data );
}
return !eq;
};
};
var ChannelDefinition = function(channelName, defaultTopic) {
var ChannelDefinition = function ( channelName, defaultTopic ) {
this.channel = channelName || DEFAULT_CHANNEL;
this._topic = defaultTopic || "";
};
ChannelDefinition.prototype = {
subscribe: function() {
subscribe : function () {
var len = arguments.length;
if(len === 1) {
return new SubscriptionDefinition(this.channel, this._topic, arguments[0]);
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]);
else if ( len === 2 ) {
return new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
}
},
publish: function(obj) {
publish : function ( obj ) {
var envelope = {
channel: this.channel,
topic: this._topic,
data: obj || {}
channel : this.channel,
topic : this._topic,
data : obj || {}
};
// If this is an envelope....
if( obj.topic && obj.data ) {
if ( obj.topic && obj.data ) {
envelope = obj;
envelope.channel = envelope.channel || this.channel;
}
envelope.timeStamp = new Date();
postal.configuration.bus.publish(envelope);
postal.configuration.bus.publish( envelope );
return envelope;
},
topic: function(topic) {
if(topic === this._topic) {
topic : function ( topic ) {
if ( topic === this._topic ) {
return this;
}
return new ChannelDefinition(this.channel, topic);
return new ChannelDefinition( this.channel, topic );
}
};
var SubscriptionDefinition = function(channel, topic, callback) {
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.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.publish( {
channel : SYSTEM_CHANNEL,
topic : "subscription.created",
timeStamp : new Date(),
data : {
event : "subscription.created",
channel : channel,
topic : topic
}
});
} );
postal.configuration.bus.subscribe(this);
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
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() {
defer : function () {
var fn = this.callback;
this.callback = function(data) {
setTimeout(fn,0,data);
this.callback = function ( data ) {
setTimeout( fn, 0, data );
};
return this;
},
disposeAfter: function(maxCalls) {
if(_.isNaN(maxCalls) || maxCalls <= 0) {
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));
var dispose = _.after( maxCalls, _.bind( function () {
this.unsubscribe( this );
}, this ) );
this.onHandled = function() {
fn.apply(this.context, arguments);
this.onHandled = function () {
fn.apply( this.context, arguments );
dispose();
};
return this;
},
ignoreDuplicates: function() {
this.withConstraint(new DistinctPredicate());
ignoreDuplicates : function () {
this.withConstraint( new DistinctPredicate() );
return this;
},
withConstraint: function(predicate) {
if(! _.isFunction(predicate)) {
withConstraint : function ( predicate ) {
if ( !_.isFunction( predicate ) ) {
throw "Predicate constraint must be a function";
}
this.constraints.push(predicate);
this.constraints.push( predicate );
return this;
},
withConstraints: function(predicates) {
withConstraints : function ( predicates ) {
var self = this;
if(_.isArray(predicates)) {
_.each(predicates, function(predicate) { self.withConstraint(predicate); } );
if ( _.isArray( predicates ) ) {
_.each( predicates, function ( predicate ) {
self.withConstraint( predicate );
} );
}
return self;
},
withContext: function(context) {
withContext : function ( context ) {
this.context = context;
return this;
},
withDebounce: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withDebounce : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.debounce(fn, milliseconds);
this.callback = _.debounce( fn, milliseconds );
return this;
},
withDelay: function(milliseconds) {
if(_.isNaN(milliseconds)) {
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);
this.callback = function ( data ) {
setTimeout( function () {
fn( data );
}, milliseconds );
};
return this;
},
withPriority: function(priority) {
if(_.isNaN(priority)) {
withPriority : function ( priority ) {
if ( _.isNaN( priority ) ) {
throw "Priority must be a number";
}
this.priority = priority;
return this;
},
withThrottle: function(milliseconds) {
if(_.isNaN(milliseconds)) {
withThrottle : function ( milliseconds ) {
if ( _.isNaN( milliseconds ) ) {
throw "Milliseconds must be a number";
}
var fn = this.callback;
this.callback = _.throttle(fn, milliseconds);
this.callback = _.throttle( fn, milliseconds );
return this;
},
subscribe: function(callback) {
subscribe : function ( callback ) {
this.callback = callback;
return this;
}
};
var bindingsResolver = {
cache: { },
cache : { },
compare: function(binding, topic) {
if(this.cache[topic] && this.cache[topic][binding]) {
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]) {
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;
@ -227,91 +230,93 @@ var bindingsResolver = {
return result;
},
reset: function() {
reset : function () {
this.cache = {};
}
};
var localBus = {
addWireTap: function(callback) {
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);
self.wireTaps.push( callback );
return function () {
var idx = self.wireTaps.indexOf( callback );
if ( idx !== -1 ) {
self.wireTaps.splice( idx, 1 );
}
};
},
publish: function(envelope) {
_.each(this.wireTaps,function(tap) {
tap(envelope.data, envelope);
});
publish : function ( envelope ) {
_.each( this.wireTaps, function ( tap ) {
tap( envelope.data, envelope );
} );
_.each(this.subscriptions[envelope.channel], function(topic) {
_.each(topic, function(subDef){
if(postal.configuration.resolver.compare(subDef.topic, envelope.topic)) {
if(_.all(subDef.constraints, function(constraint) { return constraint(envelope.data,envelope); })) {
if(typeof subDef.callback === 'function') {
subDef.callback.apply(subDef.context, [envelope.data, envelope]);
_.each( this.subscriptions[envelope.channel], function ( topic ) {
_.each( topic, function ( subDef ) {
if ( postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
if ( _.all( subDef.constraints, function ( constraint ) {
return constraint( envelope.data, envelope );
} ) ) {
if ( typeof subDef.callback === 'function' ) {
subDef.callback.apply( subDef.context, [envelope.data, envelope] );
subDef.onHandled();
}
}
}
});
});
} );
} );
},
reset: function() {
if( this.subscriptions ) {
_.each( this.subscriptions , function( channel ){
_.each( channel, function( topic ){
while( topic.length ) {
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) {
subscribe : function ( subDef ) {
var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
if(!channel) {
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);
if ( !subs ) {
subs = this.subscriptions[subDef.channel][subDef.topic] = new Array( 0 );
}
idx = subs.length - 1;
for(; idx >= 0; idx--) {
if(subs[idx].priority <= subDef.priority) {
subs.splice(idx + 1, 0, subDef);
for ( ; idx >= 0; idx-- ) {
if ( subs[idx].priority <= subDef.priority ) {
subs.splice( idx + 1, 0, subDef );
found = true;
break;
}
}
if(!found) {
subs.unshift(subDef);
if ( !found ) {
subs.unshift( subDef );
}
return subDef;
},
subscriptions: {},
subscriptions : {},
wireTaps: new Array(0),
wireTaps : new Array( 0 ),
unsubscribe: function(config) {
if(this.subscriptions[config.channel][config.topic]) {
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) {
if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
this.subscriptions[config.channel][config.topic].splice( idx, 1 );
break;
}
@ -321,50 +326,50 @@ var localBus = {
};
var publishPicker = {
"1" : function(envelope) {
if(!envelope) {
throw new Error("publishing from the 'global' postal.publish call requires a valid envelope.");
"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 };
"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 ) {
"1" : function ( chn ) {
var channel = chn, topic, options = {};
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
}
else {
channel = chn.channel || DEFAULT_CHANNEL;
topic = chn.topic;
topic = chn.topic;
options = chn.options || options;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"2" : function( chn, tpc ) {
"2" : function ( chn, tpc ) {
var channel = chn, topic = tpc, options = {};
if( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
if ( Object.prototype.toString.call( tpc ) === "[object Object]" ) {
channel = DEFAULT_CHANNEL;
topic = chn;
topic = chn;
options = tpc;
}
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
},
"3" : function( channel, topic, options ) {
"3" : function ( channel, topic, options ) {
return new postal.channelTypes[ options.type || "local" ]( channel, topic );
}
},
@ -374,110 +379,110 @@ var publishPicker = {
localBus.subscriptions[SYSTEM_CHANNEL] = {};
var postal = {
configuration: {
bus: localBus,
resolver: bindingsResolver,
getSessionIdAction: function( callback ) {
configuration : {
bus : localBus,
resolver : bindingsResolver,
getSessionIdAction : function ( callback ) {
callback( sessionInfo );
},
setSessionIdAction: function( info, callback ) {
setSessionIdAction : function ( info, callback ) {
sessionInfo = info;
callback( sessionInfo );
},
DEFAULT_CHANNEL: DEFAULT_CHANNEL,
DEFAULT_PRIORITY: DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER: DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL: SYSTEM_CHANNEL
DEFAULT_CHANNEL : DEFAULT_CHANNEL,
DEFAULT_PRIORITY : DEFAULT_PRIORITY,
DEFAULT_DISPOSEAFTER : DEFAULT_DISPOSEAFTER,
SYSTEM_CHANNEL : SYSTEM_CHANNEL
},
channelTypes: {
local: ChannelDefinition
channelTypes : {
local : ChannelDefinition
},
channel: function() {
channel : function () {
var len = arguments.length;
if(channelPicker[len]) {
return channelPicker[len].apply(this, arguments);
if ( channelPicker[len] ) {
return channelPicker[len].apply( this, arguments );
}
},
subscribe: function(options) {
subscribe : function ( options ) {
var callback = options.callback,
topic = options.topic,
channel = options.channel || DEFAULT_CHANNEL;
return new SubscriptionDefinition(channel, topic, callback);
return new SubscriptionDefinition( channel, topic, callback );
},
publish: function() {
publish : function () {
var len = arguments.length;
if(publishPicker[len]) {
return publishPicker[len].apply(this, arguments);
if ( publishPicker[len] ) {
return publishPicker[len].apply( this, arguments );
}
},
addWireTap: function(callback) {
return this.configuration.bus.addWireTap(callback);
addWireTap : function ( callback ) {
return this.configuration.bus.addWireTap( callback );
},
linkChannels: function(sources, destinations) {
linkChannels : function ( sources, destinations ) {
var result = [];
if(!_.isArray(sources)) {
if ( !_.isArray( sources ) ) {
sources = [sources];
}
if(!_.isArray(destinations)) {
if ( !_.isArray( destinations ) ) {
destinations = [destinations];
}
_.each(sources, function(source){
_.each( sources, function ( source ) {
var sourceTopic = source.topic || "*";
_.each(destinations, function(destination) {
_.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) {
postal.subscribe( {
channel : source.channel || DEFAULT_CHANNEL,
topic : source.topic || "*",
callback : function ( data, env ) {
var newEnv = env;
newEnv.topic = _.isFunction(destination.topic) ? destination.topic(env.topic) : destination.topic || env.topic;
newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
newEnv.channel = destChannel;
newEnv.data = data;
postal.publish(newEnv);
postal.publish( newEnv );
}
})
} )
);
});
});
} );
} );
return result;
},
utils: {
getSessionId: function( callback ) {
utils : {
getSessionId : function ( callback ) {
postal.configuration.getSessionIdAction.call( this, callback );
},
setSessionId: function( value, callback ) {
postal.utils.getSessionId( function( info ) {
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 ) {
postal.configuration.setSessionIdAction( info, function ( session ) {
callback( session );
// publish postal event msg about the change
postal.publish({
channel: SYSTEM_CHANNEL,
topic: "sessionId.changed",
data: session
});
postal.publish( {
channel : SYSTEM_CHANNEL,
topic : "sessionId.changed",
data : session
} );
} );
});
} );
},
getSubscribersFor: function() {
getSubscribersFor : function () {
var channel = arguments[ 0 ],
tpc = arguments[ 1 ],
result = [];
if( arguments.length === 1 ) {
if( Object.prototype.toString.call( channel ) === "[object String]" ) {
if ( arguments.length === 1 ) {
if ( Object.prototype.toString.call( channel ) === "[object String]" ) {
channel = postal.configuration.DEFAULT_CHANNEL;
tpc = arguments[ 0 ];
}
@ -486,14 +491,14 @@ var postal = {
tpc = arguments[ 0 ].topic;
}
}
if( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc )) {
if ( postal.configuration.bus.subscriptions[ channel ] &&
postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
result = postal.configuration.bus.subscriptions[ channel ][ tpc ];
}
return result;
},
reset: function() {
reset : function () {
postal.configuration.bus.reset();
postal.configuration.resolver.reset();
}

View file

@ -1,68 +1,69 @@
var _ = require('underscore'),
machina = require('../machina.js');
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.
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 ) {
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 subscribe = function ( data ) {
var self = this;
if( !self.subscriptions[ data.channel ] ) {
if ( !self.subscriptions[ data.channel ] ) {
self.subscriptions[ data.channel ] = {};
}
if( !self.subscriptions[ data.channel ][ data.topic ] ) {
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 );
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;
}
}).withConstraint(function( data, env ) {
if( !env.correlationId ) {
return true;
}
return env.correlationId === self.sessionId;
});
return env.correlationId === self.sessionId;
} );
}
},
// The actual FSM managing the socketClient
fsm = new machina.Fsm({
// The actual FSM managing the socketClient
fsm = new machina.Fsm( {
// handle to the socket client wrapper
socket: socketClient,
socket : socketClient,
// object used to store (and lookup) subscriptions owned by this connection/client
subscriptions: {},
subscriptions : {},
// method to allow a RemoteClientProxy to hand off state to another RemoteClientProxy
// as part of a migration
getClientState: function() {
getClientState : function () {
return {
queuedMsgs: _.filter( this.eventQueue, function( item ) {
queuedMsgs : _.filter( this.eventQueue, function ( item ) {
return item.args[0] === "socketTransmit";
})
} )
};
},
sessionId: null,
sessionId : null,
states: {
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() {
connecting : {
"*" : function () {
this.deferUntilTransition();
},
clientId : function( session ) {
clientId : function ( session ) {
console.log( "SESSION STUFF: " + JSON.stringify( session ) );
this.sessionId = session.id;
if( bridge.enableMigration && session.lastId && session.lastId !== session.id ) {
if ( bridge.enableMigration && session.lastId && session.lastId !== session.id ) {
this.lastSessionId = session.lastId;
this.transition( "migrating" );
}
@ -74,105 +75,107 @@ var RemoteClientProxy = function( postal, socketClient, bridge ) {
// 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: {
migrating : {
// We tell the client to handle it's migration work (if any) first.
_onEnter: function() {
_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 ] ){
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 ) {
_.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
channel : "postal.socket",
topic : "client.migrated",
data : {
sessionId : this.socket.id,
lastSessionId : this.lastSessionId
}
} );
this.transition( "connected" );
},
subscribe : subscribe,
"*": function() {
"*" : function () {
this.deferUntilTransition();
}
},
// We're online and this is how to handle it....
connected: {
_onEnter: function() {
connected : {
_onEnter : function () {
bridge.clientMap[ this.sessionId ] = this;
this.socket.confirmClientIdentified( this.sessionId );
},
disconnect: function() {
disconnect : function () {
this.transition( "disconnected" );
},
subscribe : subscribe,
unsubscribe: function( data ) {
if( this.subscriptions[ data.channel ] && this.subscriptions[ data.channel ][ data.topic ] ) {
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 ) {
publish : function ( envelope ) {
postal.publish( envelope );
},
socketTransmit: function( data, envelope ) {
socketTransmit : function ( data, envelope ) {
this.socket.publish.call( this.socket, data, envelope );
}
},
disconnected: {
_onEnter: function() {
disconnected : {
_onEnter : function () {
postal.publish( {
channel: "postal.socket",
topic: "client.disconnect",
data: {
sessionId: this.sessionId
channel : "postal.socket",
topic : "client.disconnect",
data : {
sessionId : this.sessionId
}
} );
},
"*" : function() {
"*" : function () {
this.deferUntilTransition();
},
socketTransmit: function() {
this.deferUntilTransition("connected");
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 ) {
_.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]));
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));
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({
_private : postal.subscribe( {
channel : socketClient.id,
topic : "*",
callback: function(d, e) {
fsm.handle("socketTransmit", d, e);
topic : "*",
callback : function ( d, e ) {
fsm.handle( "socketTransmit", d, e );
}
})
} )
};
// we explicitly start the FSM now that we've wired everything up, etc.
@ -180,7 +183,7 @@ var RemoteClientProxy = function( postal, socketClient, bridge ) {
return fsm;
};
var PostalSocketHost = function( postal, socketProvider ) {
var PostalSocketHost = function ( postal, socketProvider ) {
var self = this;
// if enableMigration === true, then clients are allowed to migrate
@ -195,7 +198,7 @@ var PostalSocketHost = function( postal, socketProvider ) {
self.enableMigration = true;
self.channel = postal.channel("postal.socket", "client.connect");
self.channel = postal.channel( "postal.socket", "client.connect" );
// array of clients - confirmed or not
self.clients = [];
@ -205,19 +208,19 @@ var PostalSocketHost = function( postal, socketProvider ) {
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);
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 ) {
self.clients = _.filter( self.clients, function ( item ) {
return item.sessionId !== id;
});
} );
};
@ -225,22 +228,22 @@ var PostalSocketHost = function( postal, 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.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.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.channel.subscribe( "client.migrated", function ( data, envelope ) {
self.removeClient( data.lastSessionId );
});
} );
};
module.exports = PostalSocketHost;

View file

@ -1,96 +1,96 @@
var _ = require('underscore'),
EventEmitter = require('events').EventEmitter,
util = require('util');
var _ = require( 'underscore' ),
EventEmitter = require( 'events' ).EventEmitter,
util = require( 'util' );
module.exports = {
getSocketProvider:function( postal, app, options ) {
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.
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 SocketIoClient = function ( socket ) {
var self = this;
EventEmitter.call(self);
EventEmitter.call( self );
self.socket = socket;
Object.defineProperty( self, "id", {
get: function() {
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 ){
_.each( [ "publish", "unsubscribe", "subscribe", "clientId", "migrationComplete" ], function ( evnt ) {
self.socket.on( "postal." + evnt, function ( data ) {
self.emit( evnt, data );
} );
});
} );
};
util.inherits(SocketIoClient, EventEmitter);
util.inherits( SocketIoClient, EventEmitter );
SocketIoClient.prototype.publish = function( data, envelope ) {
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.confirmClientIdentified = function ( uid ) {
this.socket.emit( "postal.socket.identified", { uid : uid } );
};
SocketIoClient.prototype.migrateClientSubscriptions = function() {
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.
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 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);
EventEmitter.call( self );
self.events = {
"connect" : []
@ -100,13 +100,13 @@ module.exports = {
// 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 ) {
io.sockets.on( "connection", function ( socket ) {
var _socket = new SocketIoClient( socket );
self.emit("connect", _socket);
});
self.emit( "connect", _socket );
} );
};
util.inherits(SocketIoProvider, EventEmitter);
util.inherits( SocketIoProvider, EventEmitter );
return new SocketIoProvider( app, options );
}

View file

@ -1,2 +1 @@
module.exports = require('./lib/express');
module.exports = require( './lib/express' );

View file

@ -1,4 +1,3 @@
/*!
* Express
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,14 +8,14 @@
* Module dependencies.
*/
var connect = require('connect')
, HTTPSServer = require('./https')
, HTTPServer = require('./http')
, Route = require('./router/route')
var connect = require( 'connect' )
, HTTPSServer = require( './https' )
, HTTPServer = require( './http' )
, Route = require( './router/route' )
/**
* Re-export connect auto-loaders.
*
*
* This prevents the need to `require('connect')` in order
* to access core middleware, so for example `express.logger()` instead
* of `require('connect').logger()`.
@ -38,12 +37,12 @@ exports.version = '2.5.8';
* @api public
*/
exports.createServer = function(options){
if ('object' == typeof options) {
return new HTTPSServer(options, Array.prototype.slice.call(arguments, 1));
} else {
return new HTTPServer(Array.prototype.slice.call(arguments));
}
exports.createServer = function ( options ) {
if ( 'object' == typeof options ) {
return new HTTPSServer( options, Array.prototype.slice.call( arguments, 1 ) );
} else {
return new HTTPServer( Array.prototype.slice.call( arguments ) );
}
};
/**
@ -59,19 +58,19 @@ exports.Route = Route;
*/
exports.View =
exports.view = require('./view');
exports.view = require( './view' );
/**
* Response extensions.
*/
require('./response');
require( './response' );
/**
* Request extensions.
*/
require('./request');
require( './request' );
// Error handler title

View file

@ -8,15 +8,15 @@
* Module dependencies.
*/
var qs = require('qs')
, connect = require('connect')
, router = require('./router')
, Router = require('./router')
, view = require('./view')
, toArray = require('./utils').toArray
, methods = router.methods.concat('del', 'all')
, url = require('url')
, utils = connect.utils;
var qs = require( 'qs' )
, connect = require( 'connect' )
, router = require( './router' )
, Router = require( './router' )
, view = require( './view' )
, toArray = require( './utils' ).toArray
, methods = router.methods.concat( 'del', 'all' )
, url = require( 'url' )
, utils = connect.utils;
/**
* Expose `HTTPServer`.
@ -37,10 +37,11 @@ var app = HTTPServer.prototype;
* @api public
*/
function HTTPServer(middleware){
connect.HTTPServer.call(this, []);
this.init(middleware);
};
function HTTPServer( middleware ) {
connect.HTTPServer.call( this, [] );
this.init( middleware );
}
;
/**
* Inherit from `connect.HTTPServer`.
@ -55,83 +56,84 @@ app.__proto__ = connect.HTTPServer.prototype;
* @api private
*/
app.init = function(middleware){
var self = this;
this.cache = {};
this.settings = {};
this.redirects = {};
this.isCallbacks = {};
this._locals = {};
this.dynamicViewHelpers = {};
this.errorHandlers = [];
app.init = function ( middleware ) {
var self = this;
this.cache = {};
this.settings = {};
this.redirects = {};
this.isCallbacks = {};
this._locals = {};
this.dynamicViewHelpers = {};
this.errorHandlers = [];
this.set('env', process.env.NODE_ENV || 'development');
this.set( 'env', process.env.NODE_ENV || 'development' );
// expose objects to each other
this.use(function(req, res, next){
req.query = req.query || {};
res.setHeader('X-Powered-By', 'Express');
req.app = res.app = self;
req.res = res;
res.req = req;
req.next = next;
// assign req.query
if (req.url.indexOf('?') > 0) {
var query = url.parse(req.url).query;
req.query = qs.parse(query);
}
next();
});
// expose objects to each other
this.use( function ( req, res, next ) {
req.query = req.query || {};
res.setHeader( 'X-Powered-By', 'Express' );
req.app = res.app = self;
req.res = res;
res.req = req;
req.next = next;
// assign req.query
if ( req.url.indexOf( '?' ) > 0 ) {
var query = url.parse( req.url ).query;
req.query = qs.parse( query );
}
next();
} );
// apply middleware
if (middleware) middleware.forEach(self.use.bind(self));
// apply middleware
if ( middleware ) {
middleware.forEach( self.use.bind( self ) );
}
// router
this.routes = new Router(this);
this.__defineGetter__('router', function(){
this.__usedRouter = true;
return self.routes.middleware;
});
// router
this.routes = new Router( this );
this.__defineGetter__( 'router', function () {
this.__usedRouter = true;
return self.routes.middleware;
} );
// default locals
this.locals({
settings: this.settings
, app: this
});
// default locals
this.locals( {
settings : this.settings, app : this
} );
// default development configuration
this.configure('development', function(){
this.enable('hints');
});
// default development configuration
this.configure( 'development', function () {
this.enable( 'hints' );
} );
// default production configuration
this.configure('production', function(){
this.enable('view cache');
});
// default production configuration
this.configure( 'production', function () {
this.enable( 'view cache' );
} );
// register error handlers on "listening"
// so that they disregard definition position.
this.on('listening', this.registerErrorHandlers.bind(this));
// register error handlers on "listening"
// so that they disregard definition position.
this.on( 'listening', this.registerErrorHandlers.bind( this ) );
// route manipulation methods
methods.forEach(function(method){
self.lookup[method] = function(path){
return self.routes.lookup(method, path);
};
// route manipulation methods
methods.forEach( function ( method ) {
self.lookup[method] = function ( path ) {
return self.routes.lookup( method, path );
};
self.match[method] = function(path){
return self.routes.match(method, path);
};
self.match[method] = function ( path ) {
return self.routes.match( method, path );
};
self.remove[method] = function(path){
return self.routes.lookup(method, path).remove();
};
});
self.remove[method] = function ( path ) {
return self.routes.lookup( method, path ).remove();
};
} );
// del -> delete
self.lookup.del = self.lookup.delete;
self.match.del = self.match.delete;
self.remove.del = self.remove.delete;
// del -> delete
self.lookup.del = self.lookup.delete;
self.match.del = self.match.delete;
self.remove.del = self.remove.delete;
};
/**
@ -142,8 +144,8 @@ app.init = function(middleware){
* @api public
*/
app.remove = function(path){
return this.routes.lookup('all', path).remove();
app.remove = function ( path ) {
return this.routes.lookup( 'all', path ).remove();
};
/**
@ -155,8 +157,8 @@ app.remove = function(path){
* @api public
*/
app.lookup = function(path){
return this.routes.lookup('all', path);
app.lookup = function ( path ) {
return this.routes.lookup( 'all', path );
};
/**
@ -167,16 +169,16 @@ app.lookup = function(path){
* @api public
*/
app.match = function(url){
return this.routes.match('all', url);
app.match = function ( url ) {
return this.routes.match( 'all', url );
};
/**
* When using the vhost() middleware register error handlers.
*/
app.onvhost = function(){
this.registerErrorHandlers();
app.onvhost = function () {
this.registerErrorHandlers();
};
/**
@ -186,13 +188,13 @@ app.onvhost = function(){
* @api public
*/
app.registerErrorHandlers = function(){
this.errorHandlers.forEach(function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
});
}, this);
return this;
app.registerErrorHandlers = function () {
this.errorHandlers.forEach( function ( fn ) {
this.use( function ( err, req, res, next ) {
fn.apply( this, arguments );
} );
}, this );
return this;
};
/**
@ -205,42 +207,48 @@ app.registerErrorHandlers = function(){
* @api public
*/
app.use = function(route, middleware){
var app, base, handle;
app.use = function ( route, middleware ) {
var app, base, handle;
if ('string' != typeof route) {
middleware = route, route = '/';
}
if ( 'string' != typeof route ) {
middleware = route, route = '/';
}
// express app
if (middleware.handle && middleware.set) app = middleware;
// express app
if ( middleware.handle && middleware.set ) {
app = middleware;
}
// restore .app property on req and res
if (app) {
app.route = route;
middleware = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
req.app = res.app = orig;
next(err);
});
};
}
// restore .app property on req and res
if ( app ) {
app.route = route;
middleware = function ( req, res, next ) {
var orig = req.app;
app.handle( req, res, function ( err ) {
req.app = res.app = orig;
next( err );
} );
};
}
connect.HTTPServer.prototype.use.call(this, route, middleware);
connect.HTTPServer.prototype.use.call( this, route, middleware );
// mounted an app, invoke the hook
// and adjust some settings
if (app) {
base = this.set('basepath') || this.route;
if ('/' == base) base = '';
base = base + (app.set('basepath') || app.route);
app.set('basepath', base);
app.parent = this;
if (app.__mounted) app.__mounted.call(app, this);
}
// mounted an app, invoke the hook
// and adjust some settings
if ( app ) {
base = this.set( 'basepath' ) || this.route;
if ( '/' == base ) {
base = '';
}
base = base + (app.set( 'basepath' ) || app.route);
app.set( 'basepath', base );
app.parent = this;
if ( app.__mounted ) {
app.__mounted.call( app, this );
}
}
return this;
return this;
};
/**
@ -264,9 +272,9 @@ app.use = function(route, middleware){
* @api public
*/
app.mounted = function(fn){
this.__mounted = fn;
return this;
app.mounted = function ( fn ) {
this.__mounted = fn;
return this;
};
/**
@ -276,9 +284,9 @@ app.mounted = function(fn){
* @api public
*/
app.register = function(){
view.register.apply(this, arguments);
return this;
app.register = function () {
view.register.apply( this, arguments );
return this;
};
/**
@ -291,9 +299,9 @@ app.register = function(){
*/
app.helpers =
app.locals = function(obj){
utils.merge(this._locals, obj);
return this;
app.locals = function ( obj ) {
utils.merge( this._locals, obj );
return this;
};
/**
@ -305,9 +313,9 @@ app.locals = function(obj){
* @api public
*/
app.dynamicHelpers = function(obj){
utils.merge(this.dynamicViewHelpers, obj);
return this;
app.dynamicHelpers = function ( obj ) {
utils.merge( this.dynamicViewHelpers, obj );
return this;
};
/**
@ -369,29 +377,31 @@ app.dynamicHelpers = function(obj){
* @api public
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
app.param = function ( name, fn ) {
var self = this
, fns = [].slice.call( arguments, 1 );
// array
if (Array.isArray(name)) {
name.forEach(function(name){
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this.routes.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
fns.forEach(function(fn){
self.routes.param(name, fn);
});
}
// array
if ( Array.isArray( name ) ) {
name.forEach( function ( name ) {
fns.forEach( function ( fn ) {
self.param( name, fn );
} );
} );
// param logic
} else if ( 'function' == typeof name ) {
this.routes.param( name );
// single
} else {
if ( ':' == name[0] ) {
name = name.substr( 1 );
}
fns.forEach( function ( fn ) {
self.routes.param( name, fn );
} );
}
return this;
return this;
};
/**
@ -403,9 +413,9 @@ app.param = function(name, fn){
* @api public
*/
app.error = function(fn){
this.errorHandlers.push(fn);
return this;
app.error = function ( fn ) {
this.errorHandlers.push( fn );
return this;
};
/**
@ -417,10 +427,12 @@ app.error = function(fn){
* @api public
*/
app.is = function(type, fn){
if (!fn) return this.isCallbacks[type];
this.isCallbacks[type] = fn;
return this;
app.is = function ( type, fn ) {
if ( !fn ) {
return this.isCallbacks[type];
}
this.isCallbacks[type] = fn;
return this;
};
/**
@ -433,17 +445,17 @@ app.is = function(type, fn){
* @api public
*/
app.set = function(setting, val){
if (val === undefined) {
if (this.settings.hasOwnProperty(setting)) {
return this.settings[setting];
} else if (this.parent) {
return this.parent.set(setting);
}
} else {
this.settings[setting] = val;
return this;
}
app.set = function ( setting, val ) {
if ( val === undefined ) {
if ( this.settings.hasOwnProperty( setting ) ) {
return this.settings[setting];
} else if ( this.parent ) {
return this.parent.set( setting );
}
} else {
this.settings[setting] = val;
return this;
}
};
/**
@ -454,8 +466,8 @@ app.set = function(setting, val){
* @api public
*/
app.enabled = function(setting){
return !!this.set(setting);
app.enabled = function ( setting ) {
return !!this.set( setting );
};
/**
@ -466,8 +478,8 @@ app.enabled = function(setting){
* @api public
*/
app.disabled = function(setting){
return !this.set(setting);
app.disabled = function ( setting ) {
return !this.set( setting );
};
/**
@ -478,8 +490,8 @@ app.disabled = function(setting){
* @api public
*/
app.enable = function(setting){
return this.set(setting, true);
app.enable = function ( setting ) {
return this.set( setting, true );
};
/**
@ -490,8 +502,8 @@ app.enable = function(setting){
* @api public
*/
app.disable = function(setting){
return this.set(setting, false);
app.disable = function ( setting ) {
return this.set( setting, false );
};
/**
@ -503,9 +515,9 @@ app.disable = function(setting){
* @api public
*/
app.redirect = function(key, url){
this.redirects[key] = url;
return this;
app.redirect = function ( key, url ) {
this.redirects[key] = url;
return this;
};
/**
@ -534,27 +546,35 @@ app.redirect = function(key, url){
* @api public
*/
app.configure = function(env, fn){
var envs = 'all'
, args = toArray(arguments);
fn = args.pop();
if (args.length) envs = args;
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
return this;
app.configure = function ( env, fn ) {
var envs = 'all'
, args = toArray( arguments );
fn = args.pop();
if ( args.length ) {
envs = args;
}
if ( 'all' == envs || ~envs.indexOf( this.settings.env ) ) {
fn.call( this );
}
return this;
};
/**
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
if (1 == arguments.length) return this.routes.lookup(method, path);
var args = [method].concat(toArray(arguments));
if (!this.__usedRouter) this.use(this.router);
return this.routes._route.apply(this.routes, args);
}
});
methods.forEach( function ( method ) {
app[method] = function ( path ) {
if ( 1 == arguments.length ) {
return this.routes.lookup( method, path );
}
var args = [method].concat( toArray( arguments ) );
if ( !this.__usedRouter ) {
this.use( this.router );
}
return this.routes._route.apply( this.routes, args );
}
} );
/**
* Special-cased "all" method, applying the given route `path`,
@ -566,14 +586,18 @@ methods.forEach(function(method){
* @api public
*/
app.all = function(path){
var args = arguments;
if (1 == args.length) return this.routes.lookup('all', path);
methods.forEach(function(method){
if ('all' == method || 'del' == method) return;
app[method].apply(this, args);
}, this);
return this;
app.all = function ( path ) {
var args = arguments;
if ( 1 == args.length ) {
return this.routes.lookup( 'all', path );
}
methods.forEach( function ( method ) {
if ( 'all' == method || 'del' == method ) {
return;
}
app[method].apply( this, args );
}, this );
return this;
};
// del -> delete alias

View file

@ -1,4 +1,3 @@
/*!
* Express - HTTPSServer
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,9 +8,9 @@
* Module dependencies.
*/
var connect = require('connect')
, HTTPServer = require('./http')
, https = require('https');
var connect = require( 'connect' )
, HTTPServer = require( './http' )
, https = require( 'https' );
/**
* Expose `HTTPSServer`.
@ -26,7 +25,7 @@ exports = module.exports = HTTPSServer;
var app = HTTPSServer.prototype;
/**
* Initialize a new `HTTPSServer` with the
* Initialize a new `HTTPSServer` with the
* given `options`, and optional `middleware`.
*
* @param {Object} options
@ -34,10 +33,11 @@ var app = HTTPSServer.prototype;
* @api public
*/
function HTTPSServer(options, middleware){
connect.HTTPSServer.call(this, options, []);
this.init(middleware);
};
function HTTPSServer( options, middleware ) {
connect.HTTPSServer.call( this, options, [] );
this.init( middleware );
}
;
/**
* Inherit from `connect.HTTPSServer`.
@ -47,6 +47,6 @@ app.__proto__ = connect.HTTPSServer.prototype;
// mixin HTTPServer methods
Object.keys(HTTPServer.prototype).forEach(function(method){
app[method] = HTTPServer.prototype[method];
});
Object.keys( HTTPServer.prototype ).forEach( function ( method ) {
app[method] = HTTPServer.prototype[method];
} );

View file

@ -1,4 +1,3 @@
/*!
* Express - request
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,11 +8,11 @@
* Module dependencies.
*/
var http = require('http')
, req = http.IncomingMessage.prototype
, utils = require('./utils')
, parse = require('url').parse
, mime = require('mime');
var http = require( 'http' )
, req = http.IncomingMessage.prototype
, utils = require( './utils' )
, parse = require( 'url' ).parse
, mime = require( 'mime' );
/**
* Default flash formatters.
@ -22,9 +21,9 @@ var http = require('http')
*/
var flashFormatters = exports.flashFormatters = {
s: function(val){
return String(val);
}
s : function ( val ) {
return String( val );
}
};
/**
@ -38,32 +37,32 @@ var flashFormatters = exports.flashFormatters = {
*
* req.header('Content-Type');
* // => "text/plain"
*
*
* req.header('content-type');
* // => "text/plain"
*
*
* req.header('Accept');
* // => undefined
*
*
* req.header('Accept', 'text/html');
* // => "text/html"
*
* @param {String} name
* @param {String} defaultValue
* @return {String}
* @return {String}
* @api public
*/
req.header = function(name, defaultValue){
switch (name = name.toLowerCase()) {
case 'referer':
case 'referrer':
return this.headers.referrer
|| this.headers.referer
|| defaultValue;
default:
return this.headers[name] || defaultValue;
}
req.header = function ( name, defaultValue ) {
switch ( name = name.toLowerCase() ) {
case 'referer':
case 'referrer':
return this.headers.referrer
|| this.headers.referer
|| defaultValue;
default:
return this.headers[name] || defaultValue;
}
};
/**
@ -80,12 +79,16 @@ req.header = function(name, defaultValue){
* @api public
*/
req.get = function(field, param){
var val = this.header(field);
if (!val) return '';
var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i');
if (!regexp.exec(val)) return '';
return RegExp.$1 || RegExp.$2;
req.get = function ( field, param ) {
var val = this.header( field );
if ( !val ) {
return '';
}
var regexp = new RegExp( param + ' *= *(?:"([^"]+)"|([^;]+))', 'i' );
if ( !regexp.exec( val ) ) {
return '';
}
return RegExp.$1 || RegExp.$2;
};
/**
@ -95,9 +98,9 @@ req.get = function(field, param){
* @api public
*/
req.__defineGetter__('path', function(){
return parse(this.url).pathname;
});
req.__defineGetter__( 'path', function () {
return parse( this.url ).pathname;
} );
/**
* Check if the _Accept_ header is present, and includes the given `type`.
@ -108,7 +111,7 @@ req.__defineGetter__('path', function(){
* to "text/html" using the mime lookup table.
*
* Examples:
*
*
* // Accept: text/html
* req.accepts('html');
* // => true
@ -129,28 +132,34 @@ req.__defineGetter__('path', function(){
* @api public
*/
req.accepts = function(type){
var accept = this.header('Accept');
req.accepts = function ( type ) {
var accept = this.header( 'Accept' );
// normalize extensions ".json" -> "json"
if (type && '.' == type[0]) type = type.substr(1);
// normalize extensions ".json" -> "json"
if ( type && '.' == type[0] ) {
type = type.substr( 1 );
}
// when Accept does not exist, or is '*/*' return true
if (!accept || '*/*' == accept) {
return true;
} else if (type) {
// allow "html" vs "text/html" etc
if (!~type.indexOf('/')) type = mime.lookup(type);
// when Accept does not exist, or is '*/*' return true
if ( !accept || '*/*' == accept ) {
return true;
} else if ( type ) {
// allow "html" vs "text/html" etc
if ( !~type.indexOf( '/' ) ) {
type = mime.lookup( type );
}
// check if we have a direct match
if (~accept.indexOf(type)) return true;
// check if we have a direct match
if ( ~accept.indexOf( type ) ) {
return true;
}
// check if we have type/*
type = type.split('/')[0] + '/*';
return !!~accept.indexOf(type);
} else {
return false;
}
// check if we have type/*
type = type.split( '/' )[0] + '/*';
return !!~accept.indexOf( type );
} else {
return false;
}
};
/**
@ -170,20 +179,20 @@ req.accepts = function(type){
* @api public
*/
req.param = function(name, defaultValue){
// route params like /user/:id
if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) {
return this.params[name];
}
// query string params
if (undefined !== this.query[name]) {
return this.query[name];
}
// request body params via connect.bodyParser
if (this.body && undefined !== this.body[name]) {
return this.body[name];
}
return defaultValue;
req.param = function ( name, defaultValue ) {
// route params like /user/:id
if ( this.params && this.params.hasOwnProperty( name ) && undefined !== this.params[name] ) {
return this.params[name];
}
// query string params
if ( undefined !== this.query[name] ) {
return this.query[name];
}
// request body params via connect.bodyParser
if ( this.body && undefined !== this.body[name] ) {
return this.body[name];
}
return defaultValue;
};
/**
@ -221,32 +230,36 @@ req.param = function(name, defaultValue){
* @api public
*/
req.flash = function(type, msg){
if (this.session === undefined) throw Error('req.flash() requires sessions');
var msgs = this.session.flash = this.session.flash || {};
if (type && msg) {
var i = 2
, args = arguments
, formatters = this.app.flashFormatters || {};
formatters.__proto__ = flashFormatters;
msg = utils.miniMarkdown(msg);
msg = msg.replace(/%([a-zA-Z])/g, function(_, format){
var formatter = formatters[format];
if (formatter) return formatter(utils.escape(args[i++]));
});
return (msgs[type] = msgs[type] || []).push(msg);
} else if (type) {
var arr = msgs[type];
delete msgs[type];
return arr || [];
} else {
this.session.flash = {};
return msgs;
}
req.flash = function ( type, msg ) {
if ( this.session === undefined ) {
throw Error( 'req.flash() requires sessions' );
}
var msgs = this.session.flash = this.session.flash || {};
if ( type && msg ) {
var i = 2
, args = arguments
, formatters = this.app.flashFormatters || {};
formatters.__proto__ = flashFormatters;
msg = utils.miniMarkdown( msg );
msg = msg.replace( /%([a-zA-Z])/g, function ( _, format ) {
var formatter = formatters[format];
if ( formatter ) {
return formatter( utils.escape( args[i++] ) );
}
} );
return (msgs[type] = msgs[type] || []).push( msg );
} else if ( type ) {
var arr = msgs[type];
delete msgs[type];
return arr || [];
} else {
this.session.flash = {};
return msgs;
}
};
/**
* Check if the incoming request contains the "Content-Type"
* Check if the incoming request contains the "Content-Type"
* header field, and it contains the give mime `type`.
*
* Examples:
@ -255,27 +268,27 @@ req.flash = function(type, msg){
* req.is('html');
* req.is('text/html');
* // => true
*
*
* // When Content-Type is application/json
* req.is('json');
* req.is('application/json');
* // => true
*
*
* req.is('html');
* // => false
*
*
* Ad-hoc callbacks can also be registered with Express, to perform
* assertions again the request, for example if we need an expressive
* way to check if our incoming request is an image, we can register "an image"
* callback:
*
*
* app.is('an image', function(req){
* return 0 == req.headers['content-type'].indexOf('image');
* });
*
*
* Now within our route callbacks, we can use to to assert content types
* such as "image/jpeg", "image/png", etc.
*
*
* app.post('/image/upload', function(req, res, next){
* if (req.is('an image')) {
* // do something
@ -283,33 +296,43 @@ req.flash = function(type, msg){
* next();
* }
* });
*
*
* @param {String} type
* @return {Boolean}
* @api public
*/
req.is = function(type){
var fn = this.app.is(type);
if (fn) return fn(this);
var ct = this.headers['content-type'];
if (!ct) return false;
ct = ct.split(';')[0];
if (!~type.indexOf('/')) type = mime.lookup(type);
if (~type.indexOf('*')) {
type = type.split('/');
ct = ct.split('/');
if ('*' == type[0] && type[1] == ct[1]) return true;
if ('*' == type[1] && type[0] == ct[0]) return true;
return false;
}
return !! ~ct.indexOf(type);
req.is = function ( type ) {
var fn = this.app.is( type );
if ( fn ) {
return fn( this );
}
var ct = this.headers['content-type'];
if ( !ct ) {
return false;
}
ct = ct.split( ';' )[0];
if ( !~type.indexOf( '/' ) ) {
type = mime.lookup( type );
}
if ( ~type.indexOf( '*' ) ) {
type = type.split( '/' );
ct = ct.split( '/' );
if ( '*' == type[0] && type[1] == ct[1] ) {
return true;
}
if ( '*' == type[1] && type[0] == ct[0] ) {
return true;
}
return false;
}
return !!~ct.indexOf( type );
};
// Callback for isXMLHttpRequest / xhr
function isxhr() {
return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest';
return this.header( 'X-Requested-With', '' ).toLowerCase() === 'xmlhttprequest';
}
/**
@ -319,5 +342,5 @@ function isxhr() {
* @api public
*/
req.__defineGetter__('isXMLHttpRequest', isxhr);
req.__defineGetter__('xhr', isxhr);
req.__defineGetter__( 'isXMLHttpRequest', isxhr );
req.__defineGetter__( 'xhr', isxhr );

View file

@ -1,4 +1,3 @@
/*!
* Express - response
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,17 +8,17 @@
* Module dependencies.
*/
var fs = require('fs')
, http = require('http')
, path = require('path')
, connect = require('connect')
, utils = connect.utils
, parseRange = require('./utils').parseRange
, res = http.ServerResponse.prototype
, send = connect.static.send
, mime = require('mime')
, basename = path.basename
, join = path.join;
var fs = require( 'fs' )
, http = require( 'http' )
, path = require( 'path' )
, connect = require( 'connect' )
, utils = connect.utils
, parseRange = require( './utils' ).parseRange
, res = http.ServerResponse.prototype
, send = connect.static.send
, mime = require( 'mime' )
, basename = path.basename
, join = path.join;
/**
* Send a response with the given `body` and optional `headers` and `status` code.
@ -41,72 +40,74 @@ var fs = require('fs')
* @api public
*/
res.send = function(body, headers, status){
// allow status as second arg
if ('number' == typeof headers) {
status = headers,
headers = null;
}
res.send = function ( body, headers, status ) {
// allow status as second arg
if ( 'number' == typeof headers ) {
status = headers,
headers = null;
}
// default status
status = status || this.statusCode;
// default status
status = status || this.statusCode;
// allow 0 args as 204
if (!arguments.length || undefined === body) status = 204;
// allow 0 args as 204
if ( !arguments.length || undefined === body ) {
status = 204;
}
// determine content type
switch (typeof body) {
case 'number':
if (!this.header('Content-Type')) {
this.contentType('.txt');
}
body = http.STATUS_CODES[status = body];
break;
case 'string':
if (!this.header('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.contentType('.html');
}
break;
case 'boolean':
case 'object':
if (Buffer.isBuffer(body)) {
if (!this.header('Content-Type')) {
this.contentType('.bin');
}
} else {
return this.json(body, headers, status);
}
break;
}
// determine content type
switch ( typeof body ) {
case 'number':
if ( !this.header( 'Content-Type' ) ) {
this.contentType( '.txt' );
}
body = http.STATUS_CODES[status = body];
break;
case 'string':
if ( !this.header( 'Content-Type' ) ) {
this.charset = this.charset || 'utf-8';
this.contentType( '.html' );
}
break;
case 'boolean':
case 'object':
if ( Buffer.isBuffer( body ) ) {
if ( !this.header( 'Content-Type' ) ) {
this.contentType( '.bin' );
}
} else {
return this.json( body, headers, status );
}
break;
}
// populate Content-Length
if (undefined !== body && !this.header('Content-Length')) {
this.header('Content-Length', Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
}
// populate Content-Length
if ( undefined !== body && !this.header( 'Content-Length' ) ) {
this.header( 'Content-Length', Buffer.isBuffer( body )
? body.length
: Buffer.byteLength( body ) );
}
// merge headers passed
if (headers) {
var fields = Object.keys(headers);
for (var i = 0, len = fields.length; i < len; ++i) {
var field = fields[i];
this.header(field, headers[field]);
}
}
// merge headers passed
if ( headers ) {
var fields = Object.keys( headers );
for ( var i = 0, len = fields.length; i < len; ++i ) {
var field = fields[i];
this.header( field, headers[field] );
}
}
// strip irrelevant headers
if (204 == status || 304 == status) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
body = '';
}
// strip irrelevant headers
if ( 204 == status || 304 == status ) {
this.removeHeader( 'Content-Type' );
this.removeHeader( 'Content-Length' );
body = '';
}
// respond
this.statusCode = status;
this.end('HEAD' == this.req.method ? null : body);
return this;
// respond
this.statusCode = status;
this.end( 'HEAD' == this.req.method ? null : body );
return this;
};
/**
@ -126,20 +127,20 @@ res.send = function(body, headers, status){
* @api public
*/
res.json = function(obj, headers, status){
var body = JSON.stringify(obj)
, callback = this.req.query.callback
, jsonp = this.app.enabled('jsonp callback');
res.json = function ( obj, headers, status ) {
var body = JSON.stringify( obj )
, callback = this.req.query.callback
, jsonp = this.app.enabled( 'jsonp callback' );
this.charset = this.charset || 'utf-8';
this.header('Content-Type', 'application/json');
this.charset = this.charset || 'utf-8';
this.header( 'Content-Type', 'application/json' );
if (callback && jsonp) {
this.header('Content-Type', 'text/javascript');
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
}
if ( callback && jsonp ) {
this.header( 'Content-Type', 'text/javascript' );
body = callback.replace( /[^\w$.]/g, '' ) + '(' + body + ');';
}
return this.send(body, headers, status);
return this.send( body, headers, status );
};
/**
@ -150,13 +151,13 @@ res.json = function(obj, headers, status){
* @api public
*/
res.status = function(code){
this.statusCode = code;
return this;
res.status = function ( code ) {
this.statusCode = code;
return this;
};
/**
* Transfer the file at the given `path`. Automatically sets
* Transfer the file at the given `path`. Automatically sets
* the _Content-Type_ response header field. `next()` is called
* when `path` is a directory, or when an error occurs.
*
@ -171,19 +172,19 @@ res.status = function(code){
* @api public
*/
res.sendfile = function(path, options, fn){
var next = this.req.next;
options = options || {};
res.sendfile = function ( path, options, fn ) {
var next = this.req.next;
options = options || {};
// support function as second arg
if ('function' == typeof options) {
fn = options;
options = {};
}
// support function as second arg
if ( 'function' == typeof options ) {
fn = options;
options = {};
}
options.path = encodeURIComponent(path);
options.callback = fn;
send(this.req, this, next, options);
options.path = encodeURIComponent( path );
options.callback = fn;
send( this.req, this, next, options );
};
/**
@ -205,8 +206,8 @@ res.sendfile = function(path, options, fn){
* @api public
*/
res.contentType = function(type){
return this.header('Content-Type', mime.lookup(type));
res.contentType = function ( type ) {
return this.header( 'Content-Type', mime.lookup( type ) );
};
/**
@ -217,16 +218,18 @@ res.contentType = function(type){
* @api public
*/
res.attachment = function(filename){
if (filename) this.contentType(filename);
this.header('Content-Disposition', filename
? 'attachment; filename="' + basename(filename) + '"'
: 'attachment');
return this;
res.attachment = function ( filename ) {
if ( filename ) {
this.contentType( filename );
}
this.header( 'Content-Disposition', filename
? 'attachment; filename="' + basename( filename ) + '"'
: 'attachment' );
return this;
};
/**
* Transfer the file at the given `path`, with optional
* Transfer the file at the given `path`, with optional
* `filename` as an attachment and optional callback `fn(err)`,
* and optional `fn2(err)` which is invoked when an error has
* occurred after header has been sent.
@ -238,32 +241,34 @@ res.attachment = function(filename){
* @api public
*/
res.download = function(path, filename, fn, fn2){
var self = this;
res.download = function ( path, filename, fn, fn2 ) {
var self = this;
// support callback as second arg
if ('function' == typeof filename) {
fn2 = fn;
fn = filename;
filename = null;
}
// support callback as second arg
if ( 'function' == typeof filename ) {
fn2 = fn;
fn = filename;
filename = null;
}
// transfer the file
this.attachment(filename || path).sendfile(path, function(err){
var sentHeader = self._header;
if (err) {
if (!sentHeader) self.removeHeader('Content-Disposition');
if (sentHeader) {
fn2 && fn2(err);
} else if (fn) {
fn(err);
} else {
self.req.next(err);
}
} else if (fn) {
fn();
}
});
// transfer the file
this.attachment( filename || path ).sendfile( path, function ( err ) {
var sentHeader = self._header;
if ( err ) {
if ( !sentHeader ) {
self.removeHeader( 'Content-Disposition' );
}
if ( sentHeader ) {
fn2 && fn2( err );
} else if ( fn ) {
fn( err );
} else {
self.req.next( err );
}
} else if ( fn ) {
fn();
}
} );
};
/**
@ -275,10 +280,12 @@ res.download = function(path, filename, fn, fn2){
* @api public
*/
res.header = function(name, val){
if (1 == arguments.length) return this.getHeader(name);
this.setHeader(name, val);
return this;
res.header = function ( name, val ) {
if ( 1 == arguments.length ) {
return this.getHeader( name );
}
this.setHeader( name, val );
return this;
};
/**
@ -289,11 +296,11 @@ res.header = function(name, val){
* @api public
*/
res.clearCookie = function(name, options){
var opts = { expires: new Date(1) };
this.cookie(name, '', options
? utils.merge(options, opts)
: opts);
res.clearCookie = function ( name, options ) {
var opts = { expires : new Date( 1 ) };
this.cookie( name, '', options
? utils.merge( options, opts )
: opts );
};
/**
@ -318,12 +325,16 @@ res.clearCookie = function(name, options){
* @api public
*/
res.cookie = function(name, val, options){
options = options || {};
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
if (undefined === options.path) options.path = this.app.set('basepath');
var cookie = utils.serializeCookie(name, val, options);
this.header('Set-Cookie', cookie);
res.cookie = function ( name, val, options ) {
options = options || {};
if ( 'maxAge' in options ) {
options.expires = new Date( Date.now() + options.maxAge );
}
if ( undefined === options.path ) {
options.path = this.app.set( 'basepath' );
}
var cookie = utils.serializeCookie( name, val, options );
this.header( 'Set-Cookie', cookie );
};
/**
@ -338,12 +349,12 @@ res.cookie = function(name, val, options){
* to '/'.
*
* Redirect Mapping:
*
*
* To extend the redirect mapping capabilities that Express provides,
* we may use the `app.redirect()` method:
*
*
* app.redirect('google', 'http://google.com');
*
*
* Now in a route we may call:
*
* res.redirect('google');
@ -371,55 +382,56 @@ res.cookie = function(name, val, options){
* @api public
*/
res.redirect = function(url, status){
var app = this.app
, req = this.req
, base = app.set('basepath') || app.route
, status = status || 302
, head = 'HEAD' == req.method
, body;
res.redirect = function ( url, status ) {
var app = this.app
, req = this.req
, base = app.set( 'basepath' ) || app.route
, status = status || 302
, head = 'HEAD' == req.method
, body;
// Setup redirect map
var map = {
back: req.header('Referrer', base)
, home: base
};
// Setup redirect map
var map = {
back : req.header( 'Referrer', base ), home : base
};
// Support custom redirect map
map.__proto__ = app.redirects;
// Support custom redirect map
map.__proto__ = app.redirects;
// Attempt mapped redirect
var mapped = 'function' == typeof map[url]
? map[url](req, this)
: map[url];
// Attempt mapped redirect
var mapped = 'function' == typeof map[url]
? map[url]( req, this )
: map[url];
// Perform redirect
url = mapped || url;
// Perform redirect
url = mapped || url;
// Relative
if (!~url.indexOf('://')) {
// Respect mount-point
if ('/' != base && 0 != url.indexOf(base)) url = base + url;
// Relative
if ( !~url.indexOf( '://' ) ) {
// Respect mount-point
if ( '/' != base && 0 != url.indexOf( base ) ) {
url = base + url;
}
// Absolute
var host = req.headers.host
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}
// Absolute
var host = req.headers.host
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}
// Support text/{plain,html} by default
if (req.accepts('html')) {
body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
this.header('Content-Type', 'text/html');
} else {
body = http.STATUS_CODES[status] + '. Redirecting to ' + url;
this.header('Content-Type', 'text/plain');
}
// Support text/{plain,html} by default
if ( req.accepts( 'html' ) ) {
body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
this.header( 'Content-Type', 'text/html' );
} else {
body = http.STATUS_CODES[status] + '. Redirecting to ' + url;
this.header( 'Content-Type', 'text/plain' );
}
// Respond
this.statusCode = status;
this.header('Location', url);
this.end(head ? null : body);
// Respond
this.statusCode = status;
this.header( 'Location', url );
this.end( head ? null : body );
};
/**
@ -432,11 +444,11 @@ res.redirect = function(url, status){
* @api public
*/
res.local = function(name, val){
this._locals = this._locals || {};
return undefined === val
? this._locals[name]
: this._locals[name] = val;
res.local = function ( name, val ) {
this._locals = this._locals || {};
return undefined === val
? this._locals[name]
: this._locals[name] = val;
};
/**
@ -449,12 +461,12 @@ res.local = function(name, val){
*/
res.locals =
res.helpers = function(obj){
if (obj) {
for (var key in obj) {
this.local(key, obj[key]);
}
} else {
return this._locals;
}
res.helpers = function ( obj ) {
if ( obj ) {
for ( var key in obj ) {
this.local( key, obj[key] );
}
} else {
return this._locals;
}
};

View file

@ -1,4 +1,3 @@
/*!
* Express - router - Collection
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -14,14 +13,14 @@ module.exports = Collection;
/**
* Initialize a new route `Collection`
* with the given `router`.
*
*
* @param {Router} router
* @api private
*/
function Collection(router) {
Array.apply(this, arguments);
this.router = router;
function Collection( router ) {
Array.apply( this, arguments );
this.router = router;
}
/**
@ -37,17 +36,17 @@ Collection.prototype.__proto__ = Array.prototype;
* @api public
*/
Collection.prototype.remove = function(){
var router = this.router
, len = this.length
, ret = new Collection(this.router);
Collection.prototype.remove = function () {
var router = this.router
, len = this.length
, ret = new Collection( this.router );
for (var i = 0; i < len; ++i) {
if (router.remove(this[i])) {
ret.push(this[i]);
}
}
for ( var i = 0; i < len; ++i ) {
if ( router.remove( this[i] ) ) {
ret.push( this[i] );
}
}
return ret;
return ret;
};

View file

@ -1,4 +1,3 @@
/*!
* Express - Router
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,11 +8,11 @@
* Module dependencies.
*/
var Route = require('./route')
, Collection = require('./collection')
, utils = require('../utils')
, parse = require('url').parse
, toArray = utils.toArray;
var Route = require( './route' )
, Collection = require( './collection' )
, utils = require( '../utils' )
, parse = require( 'url' ).parse
, toArray = utils.toArray;
/**
* Expose `Router` constructor.
@ -25,25 +24,25 @@ exports = module.exports = Router;
* Expose HTTP methods.
*/
var methods = exports.methods = require('./methods');
var methods = exports.methods = require( './methods' );
/**
* Initialize a new `Router` with the given `app`.
*
*
* @param {express.HTTPServer} app
* @api private
*/
function Router(app) {
var self = this;
this.app = app;
this.routes = {};
this.params = {};
this._params = [];
function Router( app ) {
var self = this;
this.app = app;
this.routes = {};
this.params = {};
this._params = [];
this.middleware = function(req, res, next){
self._dispatch(req, res, next);
};
this.middleware = function ( req, res, next ) {
self._dispatch( req, res, next );
};
}
/**
@ -55,32 +54,32 @@ function Router(app) {
* @api public
*/
Router.prototype.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
return;
}
Router.prototype.param = function ( name, fn ) {
// param logic
if ( 'function' == typeof name ) {
this._params.push( name );
return;
}
// apply param functions
var params = this._params
, len = params.length
, ret;
// apply param functions
var params = this._params
, len = params.length
, ret;
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
}
}
for ( var i = 0; i < len; ++i ) {
if ( ret = params[i]( name, fn ) ) {
fn = ret;
}
}
// ensure we end up with a
// middleware function
if ('function' != typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
// ensure we end up with a
// middleware function
if ( 'function' != typeof fn ) {
throw new Error( 'invalid param() call for ' + name + ', got ' + fn );
}
(this.params[name] = this.params[name] || []).push(fn);
return this;
(this.params[name] = this.params[name] || []).push( fn );
return this;
};
/**
@ -90,10 +89,10 @@ Router.prototype.param = function(name, fn){
* @api public
*/
Router.prototype.all = function(){
return this.find(function(){
return true;
});
Router.prototype.all = function () {
return this.find( function () {
return true;
} );
};
/**
@ -106,16 +105,16 @@ Router.prototype.all = function(){
* @api public
*/
Router.prototype.remove = function(route){
var routes = this.routes[route.method]
, len = routes.length;
Router.prototype.remove = function ( route ) {
var routes = this.routes[route.method]
, len = routes.length;
for (var i = 0; i < len; ++i) {
if (route == routes[i]) {
routes.splice(i, 1);
return true;
}
}
for ( var i = 0; i < len; ++i ) {
if ( route == routes[i] ) {
routes.splice( i, 1 );
return true;
}
}
};
/**
@ -127,12 +126,12 @@ Router.prototype.remove = function(route){
* @api public
*/
Router.prototype.lookup = function(method, path){
return this.find(function(route){
return path == route.path
&& (route.method == method
|| method == 'all');
});
Router.prototype.lookup = function ( method, path ) {
return this.find( function ( route ) {
return path == route.path
&& (route.method == method
|| method == 'all');
} );
};
/**
@ -144,12 +143,12 @@ Router.prototype.lookup = function(method, path){
* @api public
*/
Router.prototype.match = function(method, url){
return this.find(function(route){
return route.match(url)
&& (route.method == method
|| method == 'all');
});
Router.prototype.match = function ( method, url ) {
return this.find( function ( route ) {
return route.match( url )
&& (route.method == method
|| method == 'all');
} );
};
/**
@ -161,24 +160,28 @@ Router.prototype.match = function(method, url){
* @api public
*/
Router.prototype.find = function(fn){
var len = methods.length
, ret = new Collection(this)
, method
, routes
, route;
Router.prototype.find = function ( fn ) {
var len = methods.length
, ret = new Collection( this )
, method
, routes
, route;
for (var i = 0; i < len; ++i) {
method = methods[i];
routes = this.routes[method];
if (!routes) continue;
for (var j = 0, jlen = routes.length; j < jlen; ++j) {
route = routes[j];
if (fn(route)) ret.push(route);
}
}
for ( var i = 0; i < len; ++i ) {
method = methods[i];
routes = this.routes[method];
if ( !routes ) {
continue;
}
for ( var j = 0, jlen = routes.length; j < jlen; ++j ) {
route = routes[j];
if ( fn( route ) ) {
ret.push( route );
}
}
}
return ret;
return ret;
};
/**
@ -190,94 +193,104 @@ Router.prototype.find = function(fn){
* @api private
*/
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
Router.prototype._dispatch = function ( req, res, next ) {
var params = this.params
, self = this;
// route dispatch
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key
, ret;
// route dispatch
(function pass( i, err ) {
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key
, ret;
// match next route
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// match next route
function nextRoute( err ) {
pass( req._route_index + 1, err );
}
// match route
req.route = route = self._match(req, i);
// match route
req.route = route = self._match( req, i );
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// implied OPTIONS
if ( !route && 'OPTIONS' == req.method ) {
return self._options( req, res );
}
// no route
if (!route) return next(err);
// no route
if ( !route ) {
return next( err );
}
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
// param callbacks
function param( err ) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
callbacks();
}
} catch (err) {
param(err);
}
};
try {
if ( 'route' == err ) {
nextRoute();
} else if ( err ) {
i = 0;
callbacks( err );
} else if ( paramCallbacks && undefined !== paramVal ) {
paramCallback();
} else if ( key ) {
param();
} else {
i = 0;
callbacks();
}
} catch ( err ) {
param( err );
}
}
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
;
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
fn(req, res, callbacks);
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
param( err );
// single param callbacks
function paramCallback( err ) {
var fn = paramCallbacks[paramIndex++];
if ( err || !fn ) {
return param( err );
}
fn( req, res, paramCallback, paramVal, key.name );
}
// invoke route callbacks
function callbacks( err ) {
var fn = route.callbacks[i++];
try {
if ( 'route' == err ) {
nextRoute();
} else if ( err && fn ) {
if ( fn.length < 4 ) {
return callbacks( err );
}
fn( err, req, res, callbacks );
} else if ( fn ) {
fn( req, res, callbacks );
} else {
nextRoute( err );
}
} catch ( err ) {
callbacks( err );
}
}
})( 0 );
};
/**
@ -288,10 +301,10 @@ Router.prototype._dispatch = function(req, res, next){
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req.url).pathname
, body = this._optionsFor(path).join(',');
res.send(body, { Allow: body });
Router.prototype._options = function ( req, res ) {
var path = parse( req.url ).pathname
, body = this._optionsFor( path ).join( ',' );
res.send( body, { Allow : body } );
};
/**
@ -302,17 +315,22 @@ Router.prototype._options = function(req, res){
* @api private
*/
Router.prototype._optionsFor = function(path){
var self = this;
return methods.filter(function(method){
var routes = self.routes[method];
if (!routes || 'options' == method) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
Router.prototype._optionsFor = function ( path ) {
var self = this;
return methods.filter(
function ( method ) {
var routes = self.routes[method];
if ( !routes || 'options' == method ) {
return;
}
for ( var i = 0, len = routes.length; i < len; ++i ) {
if ( routes[i].match( path ) ) {
return true;
}
}
} ).map( function ( method ) {
return method.toUpperCase();
} );
};
/**
@ -325,47 +343,49 @@ Router.prototype._optionsFor = function(path){
* @api private
*/
Router.prototype._match = function(req, i){
var method = req.method.toLowerCase()
, url = parse(req.url)
, path = url.pathname
, routes = this.routes
, captures
, route
, keys;
Router.prototype._match = function ( req, i ) {
var method = req.method.toLowerCase()
, url = parse( req.url )
, path = url.pathname
, routes = this.routes
, captures
, route
, keys;
// pass HEAD to GET routes
if ('head' == method) method = 'get';
// pass HEAD to GET routes
if ( 'head' == method ) {
method = 'get';
}
// routes for this method
if (routes = routes[method]) {
// routes for this method
if ( routes = routes[method] ) {
// matching routes
for (var len = routes.length; i < len; ++i) {
route = routes[i];
if (captures = route.match(path)) {
keys = route.keys;
route.params = [];
// matching routes
for ( var len = routes.length; i < len; ++i ) {
route = routes[i];
if ( captures = route.match( path ) ) {
keys = route.keys;
route.params = [];
// params from capture groups
for (var j = 1, jlen = captures.length; j < jlen; ++j) {
var key = keys[j-1]
, val = 'string' == typeof captures[j]
? decodeURIComponent(captures[j])
: captures[j];
if (key) {
route.params[key.name] = val;
} else {
route.params.push(val);
}
}
// params from capture groups
for ( var j = 1, jlen = captures.length; j < jlen; ++j ) {
var key = keys[j - 1]
, val = 'string' == typeof captures[j]
? decodeURIComponent( captures[j] )
: captures[j];
if ( key ) {
route.params[key.name] = val;
} else {
route.params.push( val );
}
}
// all done
req._route_index = i;
return route;
}
}
}
// all done
req._route_index = i;
return route;
}
}
}
};
/**
@ -378,21 +398,22 @@ Router.prototype._match = function(req, i){
* @api private
*/
Router.prototype._route = function(method, path, callbacks){
var app = this.app
, callbacks = utils.flatten(toArray(arguments, 2));
Router.prototype._route = function ( method, path, callbacks ) {
var app = this.app
, callbacks = utils.flatten( toArray( arguments, 2 ) );
// ensure path was given
if (!path) throw new Error('app.' + method + '() requires a path');
// ensure path was given
if ( !path ) {
throw new Error( 'app.' + method + '() requires a path' );
}
// create the route
var route = new Route(method, path, callbacks, {
sensitive: app.enabled('case sensitive routes')
, strict: app.enabled('strict routing')
});
// create the route
var route = new Route( method, path, callbacks, {
sensitive : app.enabled( 'case sensitive routes' ), strict : app.enabled( 'strict routing' )
} );
// add it
(this.routes[method] = this.routes[method] || [])
.push(route);
return this;
// add it
(this.routes[method] = this.routes[method] || [])
.push( route );
return this;
};

View file

@ -1,4 +1,3 @@
/*!
* Express - router - methods
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -6,7 +5,7 @@
*/
/**
* Hypertext Transfer Protocol -- HTTP/1.1
* Hypertext Transfer Protocol -- HTTP/1.1
* http://www.ietf.org/rfc/rfc2616.txt
*/
@ -20,21 +19,21 @@ var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'];
var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'];
/**
* Versioning Extensions to WebDAV
* Versioning Extensions to WebDAV
* http://www.ietf.org/rfc/rfc3253.txt
*/
var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY'];
/**
* Ordered Collections Protocol (WebDAV)
* Ordered Collections Protocol (WebDAV)
* http://www.ietf.org/rfc/rfc3648.txt
*/
var RFC3648 = ['ORDERPATCH'];
/**
* Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
* Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
* http://www.ietf.org/rfc/rfc3744.txt
*/
@ -48,7 +47,7 @@ var RFC3744 = ['ACL'];
var RFC5323 = ['SEARCH'];
/**
* PATCH Method for HTTP
* PATCH Method for HTTP
* http://www.ietf.org/rfc/rfc5789.txt
*/
@ -59,12 +58,12 @@ var RFC5789 = ['PATCH'];
*/
module.exports = [].concat(
RFC2616
, RFC2518
, RFC3253
, RFC3648
, RFC3744
, RFC5323
, RFC5789).map(function(method){
return method.toLowerCase();
});
RFC2616
, RFC2518
, RFC3253
, RFC3648
, RFC3744
, RFC5323
, RFC5789 ).map( function ( method ) {
return method.toLowerCase();
} );

View file

@ -1,4 +1,3 @@
/*!
* Express - router - Route
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -27,15 +26,15 @@ module.exports = Route;
* @api private
*/
function Route(method, path, callbacks, options) {
options = options || {};
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.regexp = normalize(path
, this.keys = []
, options.sensitive
, options.strict);
function Route( method, path, callbacks, options ) {
options = options || {};
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.regexp = normalize( path
, this.keys = []
, options.sensitive
, options.strict );
}
/**
@ -46,8 +45,8 @@ function Route(method, path, callbacks, options) {
* @api private
*/
Route.prototype.match = function(path){
return this.regexp.exec(path);
Route.prototype.match = function ( path ) {
return this.regexp.exec( path );
};
/**
@ -67,22 +66,24 @@ Route.prototype.match = function(path){
* @api private
*/
function normalize(path, keys, sensitive, strict) {
if (path instanceof RegExp) return path;
path = path
.concat(strict ? '' : '/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
keys.push({ name: key, optional: !! optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
function normalize( path, keys, sensitive, strict ) {
if ( path instanceof RegExp ) {
return path;
}
path = path
.concat( strict ? '' : '/?' )
.replace( /\/\(/g, '(?:/' )
.replace( /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function ( _, slash, format, key, capture, optional ) {
keys.push( { name : key, optional : !!optional } );
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
} )
.replace( /([\/.])/g, '\\$1' )
.replace( /\*/g, '(.*)' );
return new RegExp( '^' + path + '$', sensitive ? '' : 'i' );
}

View file

@ -1,4 +1,3 @@
/*!
* Express - Utils
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -13,9 +12,13 @@
* @api private
*/
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
exports.isAbsolute = function ( path ) {
if ( '/' == path[0] ) {
return true;
}
if ( ':' == path[1] && '\\' == path[2] ) {
return true;
}
};
/**
@ -28,19 +31,19 @@ exports.isAbsolute = function(path){
* @api private
*/
exports.union = function(a, b){
if (a && b) {
var keys = Object.keys(b)
, len = keys.length
, key;
for (var i = 0; i < len; ++i) {
key = keys[i];
if (!a.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
exports.union = function ( a, b ) {
if ( a && b ) {
var keys = Object.keys( b )
, len = keys.length
, key;
for ( var i = 0; i < len; ++i ) {
key = keys[i];
if ( !a.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
}
return a;
};
/**
@ -51,17 +54,17 @@ exports.union = function(a, b){
* @api private
*/
exports.flatten = function(arr, ret){
var ret = ret || []
, len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
} else {
ret.push(arr[i]);
}
}
return ret;
exports.flatten = function ( arr, ret ) {
var ret = ret || []
, len = arr.length;
for ( var i = 0; i < len; ++i ) {
if ( Array.isArray( arr[i] ) ) {
exports.flatten( arr[i], ret );
} else {
ret.push( arr[i] );
}
}
return ret;
};
/**
@ -78,11 +81,11 @@ exports.flatten = function(arr, ret){
* @api private
*/
exports.miniMarkdown = function(str){
return String(str)
.replace(/(__|\*\*)(.*?)\1/g, '<strong>$2</strong>')
.replace(/(_|\*)(.*?)\1/g, '<em>$2</em>')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
exports.miniMarkdown = function ( str ) {
return String( str )
.replace( /(__|\*\*)(.*?)\1/g, '<strong>$2</strong>' )
.replace( /(_|\*)(.*?)\1/g, '<em>$2</em>' )
.replace( /\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>' );
};
/**
@ -93,12 +96,12 @@ exports.miniMarkdown = function(str){
* @api private
*/
exports.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
exports.escape = function ( html ) {
return String( html )
.replace( /&/g, '&amp;' )
.replace( /"/g, '&quot;' )
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' );
};
/**
@ -110,28 +113,30 @@ exports.escape = function(html) {
* @api private
*/
exports.parseRange = function(size, str){
var valid = true;
var arr = str.substr(6).split(',').map(function(range){
var range = range.split('-')
, start = parseInt(range[0], 10)
, end = parseInt(range[1], 10);
exports.parseRange = function ( size, str ) {
var valid = true;
var arr = str.substr( 6 ).split( ',' ).map( function ( range ) {
var range = range.split( '-' )
, start = parseInt( range[0], 10 )
, end = parseInt( range[1], 10 );
// -500
if (isNaN(start)) {
start = size - end;
end = size - 1;
// 500-
} else if (isNaN(end)) {
end = size - 1;
}
// -500
if ( isNaN( start ) ) {
start = size - end;
end = size - 1;
// 500-
} else if ( isNaN( end ) ) {
end = size - 1;
}
// Invalid
if (isNaN(start) || isNaN(end) || start > end) valid = false;
// Invalid
if ( isNaN( start ) || isNaN( end ) || start > end ) {
valid = false;
}
return { start: start, end: end };
});
return valid ? arr : undefined;
return { start : start, end : end };
} );
return valid ? arr : undefined;
};
/**
@ -143,10 +148,12 @@ exports.parseRange = function(size, str){
* @api public
*/
exports.toArray = function(args, i){
var arr = []
, len = args.length
, i = i || 0;
for (; i < len; ++i) arr.push(args[i]);
return arr;
exports.toArray = function ( args, i ) {
var arr = []
, len = args.length
, i = i || 0;
for ( ; i < len; ++i ) {
arr.push( args[i] );
}
return arr;
};

View file

@ -1,4 +1,3 @@
/*!
* Express - view
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,17 +8,17 @@
* Module dependencies.
*/
var path = require('path')
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
, utils = require('connect').utils
, View = require('./view/view')
, partial = require('./view/partial')
, union = require('./utils').union
, merge = utils.merge
, http = require('http')
, res = http.ServerResponse.prototype;
var path = require( 'path' )
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
, utils = require( 'connect' ).utils
, View = require( './view/view' )
, partial = require( './view/partial' )
, union = require( './utils' ).union
, merge = utils.merge
, http = require( 'http' )
, res = http.ServerResponse.prototype;
/**
* Expose constructors.
@ -46,29 +45,31 @@ exports.register = View.register;
* @api private
*/
exports.compile = function(view, cache, cid, options){
if (cache && cid && cache[cid]){
options.filename = cache[cid].path;
return cache[cid];
}
exports.compile = function ( view, cache, cid, options ) {
if ( cache && cid && cache[cid] ) {
options.filename = cache[cid].path;
return cache[cid];
}
// lookup
view = exports.lookup(view, options);
// lookup
view = exports.lookup( view, options );
// hints
if (!view.exists) {
if (options.hint) hintAtViewPaths(view.original, options);
var err = new Error('failed to locate view "' + view.original.view + '"');
err.view = view.original;
throw err;
}
// hints
if ( !view.exists ) {
if ( options.hint ) {
hintAtViewPaths( view.original, options );
}
var err = new Error( 'failed to locate view "' + view.original.view + '"' );
err.view = view.original;
throw err;
}
// compile
options.filename = view.path;
view.fn = view.templateEngine.compile(view.contents, options);
cache[cid] = view;
// compile
options.filename = view.path;
view.fn = view.templateEngine.compile( view.contents, options );
cache[cid] = view;
return view;
return view;
};
/**
@ -96,33 +97,43 @@ exports.compile = function(view, cache, cid, options){
* @api private
*/
exports.lookup = function(view, options){
var orig = view = new View(view, options)
, partial = options.isPartial
, layout = options.isLayout;
exports.lookup = function ( view, options ) {
var orig = view = new View( view, options )
, partial = options.isPartial
, layout = options.isLayout;
// Try _ prefix ex: ./views/_<name>.jade
// taking precedence over the direct path
if (partial) {
view = new View(orig.prefixPath, options);
if (!view.exists) view = orig;
}
// Try _ prefix ex: ./views/_<name>.jade
// taking precedence over the direct path
if ( partial ) {
view = new View( orig.prefixPath, options );
if ( !view.exists ) {
view = orig;
}
}
// Try index ex: ./views/user/index.jade
if (!layout && !view.exists) view = new View(orig.indexPath, options);
// Try index ex: ./views/user/index.jade
if ( !layout && !view.exists ) {
view = new View( orig.indexPath, options );
}
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if (!layout && !view.exists) view = new View(orig.upIndexPath, options);
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if ( !layout && !view.exists ) {
view = new View( orig.upIndexPath, options );
}
// Try root ex: <root>/user.jade
if (!view.exists) view = new View(orig.rootPath, options);
// Try root ex: <root>/user.jade
if ( !view.exists ) {
view = new View( orig.rootPath, options );
}
// Try root _ prefix ex: <root>/_user.jade
if (!view.exists && partial) view = new View(view.prefixPath, options);
// Try root _ prefix ex: <root>/_user.jade
if ( !view.exists && partial ) {
view = new View( view.prefixPath, options );
}
view.original = orig;
return view;
view.original = orig;
return view;
};
/**
@ -131,103 +142,106 @@ exports.lookup = function(view, options){
* @api private
*/
function renderPartial(res, view, options, parentLocals, parent){
var collection, object, locals;
function renderPartial( res, view, options, parentLocals, parent ) {
var collection, object, locals;
if (options) {
// collection
if (options.collection) {
collection = options.collection;
delete options.collection;
} else if ('length' in options) {
collection = options;
options = {};
}
if ( options ) {
// collection
if ( options.collection ) {
collection = options.collection;
delete options.collection;
} else if ( 'length' in options ) {
collection = options;
options = {};
}
// locals
if (options.locals) {
locals = options.locals;
delete options.locals;
}
// locals
if ( options.locals ) {
locals = options.locals;
delete options.locals;
}
// object
if ('Object' != options.constructor.name) {
object = options;
options = {};
} else if (undefined != options.object) {
object = options.object;
delete options.object;
}
} else {
options = {};
}
// object
if ( 'Object' != options.constructor.name ) {
object = options;
options = {};
} else if ( undefined != options.object ) {
object = options.object;
delete options.object;
}
} else {
options = {};
}
// Inherit locals from parent
union(options, parentLocals);
// Inherit locals from parent
union( options, parentLocals );
// Merge locals
if (locals) merge(options, locals);
// Merge locals
if ( locals ) {
merge( options, locals );
}
// Partials dont need layouts
options.isPartial = true;
options.layout = false;
// Partials dont need layouts
options.isPartial = true;
options.layout = false;
// Deduce name from view path
var name = options.as || partial.resolveObjectName(view);
// Deduce name from view path
var name = options.as || partial.resolveObjectName( view );
// Render partial
function render(){
if (object) {
if ('string' == typeof name) {
options[name] = object;
} else if (name === global) {
merge(options, object);
}
}
return res.render(view, options, null, parent, true);
}
// Render partial
function render() {
if ( object ) {
if ( 'string' == typeof name ) {
options[name] = object;
} else if ( name === global ) {
merge( options, object );
}
}
return res.render( view, options, null, parent, true );
}
// Collection support
if (collection) {
var len = collection.length
, buf = ''
, keys
, key
, val;
// Collection support
if ( collection ) {
var len = collection.length
, buf = ''
, keys
, key
, val;
options.collectionLength = len;
options.collectionLength = len;
if ('number' == typeof len || Array.isArray(collection)) {
for (var i = 0; i < len; ++i) {
val = collection[i];
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
} else {
keys = Object.keys(collection);
len = keys.length;
options.collectionLength = len;
options.collectionKeys = keys;
for (var i = 0; i < len; ++i) {
key = keys[i];
val = collection[key];
options.keyInCollection = key;
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
}
if ( 'number' == typeof len || Array.isArray( collection ) ) {
for ( var i = 0; i < len; ++i ) {
val = collection[i];
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
} else {
keys = Object.keys( collection );
len = keys.length;
options.collectionLength = len;
options.collectionKeys = keys;
for ( var i = 0; i < len; ++i ) {
key = keys[i];
val = collection[key];
options.keyInCollection = key;
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
}
return buf;
} else {
return render();
}
};
return buf;
} else {
return render();
}
}
;
/**
* Render `view` partial with the given `options`. Optionally a
@ -253,42 +267,44 @@ function renderPartial(res, view, options, parentLocals, parent){
* @api public
*/
res.partial = function(view, options, fn){
var app = this.app
, options = options || {}
, viewEngine = app.set('view engine')
, parent = {};
res.partial = function ( view, options, fn ) {
var app = this.app
, options = options || {}
, viewEngine = app.set( 'view engine' )
, parent = {};
// accept callback as second argument
if ('function' == typeof options) {
fn = options;
options = {};
}
// accept callback as second argument
if ( 'function' == typeof options ) {
fn = options;
options = {};
}
// root "views" option
parent.dirname = app.set('views') || process.cwd() + '/views';
// root "views" option
parent.dirname = app.set( 'views' ) || process.cwd() + '/views';
// utilize "view engine" option
if (viewEngine) parent.engine = viewEngine;
// utilize "view engine" option
if ( viewEngine ) {
parent.engine = viewEngine;
}
// render the partial
try {
var str = renderPartial(this, view, options, null, parent);
} catch (err) {
if (fn) {
fn(err);
} else {
this.req.next(err);
}
return;
}
// render the partial
try {
var str = renderPartial( this, view, options, null, parent );
} catch ( err ) {
if ( fn ) {
fn( err );
} else {
this.req.next( err );
}
return;
}
// callback or transfer
if (fn) {
fn(null, str);
} else {
this.send(str);
}
// callback or transfer
if ( fn ) {
fn( null, str );
} else {
this.send( str );
}
};
/**
@ -308,139 +324,151 @@ res.partial = function(view, options, fn){
* @api public
*/
res.render = function(view, opts, fn, parent, sub){
// support callback function as second arg
if ('function' == typeof opts) {
fn = opts, opts = null;
}
res.render = function ( view, opts, fn, parent, sub ) {
// support callback function as second arg
if ( 'function' == typeof opts ) {
fn = opts, opts = null;
}
try {
return this._render(view, opts, fn, parent, sub);
} catch (err) {
// callback given
if (fn) {
fn(err);
// unwind to root call to prevent multiple callbacks
} else if (sub) {
throw err;
// root template, next(err)
} else {
this.req.next(err);
}
}
try {
return this._render( view, opts, fn, parent, sub );
} catch ( err ) {
// callback given
if ( fn ) {
fn( err );
// unwind to root call to prevent multiple callbacks
} else if ( sub ) {
throw err;
// root template, next(err)
} else {
this.req.next( err );
}
}
};
// private render()
res._render = function(view, opts, fn, parent, sub){
var options = {}
, self = this
, app = this.app
, helpers = app._locals
, dynamicHelpers = app.dynamicViewHelpers
, viewOptions = app.set('view options')
, root = app.set('views') || process.cwd() + '/views';
res._render = function ( view, opts, fn, parent, sub ) {
var options = {}
, self = this
, app = this.app
, helpers = app._locals
, dynamicHelpers = app.dynamicViewHelpers
, viewOptions = app.set( 'view options' )
, root = app.set( 'views' ) || process.cwd() + '/views';
// cache id
var cid = app.enabled('view cache')
? view + (parent ? ':' + parent.path : '')
: false;
// cache id
var cid = app.enabled( 'view cache' )
? view + (parent ? ':' + parent.path : '')
: false;
// merge "view options"
if (viewOptions) merge(options, viewOptions);
// merge "view options"
if ( viewOptions ) {
merge( options, viewOptions );
}
// merge res._locals
if (this._locals) merge(options, this._locals);
// merge res._locals
if ( this._locals ) {
merge( options, this._locals );
}
// merge render() options
if (opts) merge(options, opts);
// merge render() options
if ( opts ) {
merge( options, opts );
}
// merge render() .locals
if (opts && opts.locals) merge(options, opts.locals);
// merge render() .locals
if ( opts && opts.locals ) {
merge( options, opts.locals );
}
// status support
if (options.status) this.statusCode = options.status;
// status support
if ( options.status ) {
this.statusCode = options.status;
}
// capture attempts
options.attempts = [];
// capture attempts
options.attempts = [];
var partial = options.isPartial
, layout = options.layout;
var partial = options.isPartial
, layout = options.layout;
// Layout support
if (true === layout || undefined === layout) {
layout = 'layout';
}
// Layout support
if ( true === layout || undefined === layout ) {
layout = 'layout';
}
// Default execution scope to a plain object
options.scope = options.scope || {};
// Default execution scope to a plain object
options.scope = options.scope || {};
// Populate view
options.parentView = parent;
// Populate view
options.parentView = parent;
// "views" setting
options.root = root;
// "views" setting
options.root = root;
// "view engine" setting
options.defaultEngine = app.set('view engine');
// "view engine" setting
options.defaultEngine = app.set( 'view engine' );
// charset option
if (options.charset) this.charset = options.charset;
// charset option
if ( options.charset ) {
this.charset = options.charset;
}
// Dynamic helper support
if (false !== options.dynamicHelpers) {
// cache
if (!this.__dynamicHelpers) {
this.__dynamicHelpers = {};
for (var key in dynamicHelpers) {
this.__dynamicHelpers[key] = dynamicHelpers[key].call(
this.app
, this.req
, this);
}
}
// Dynamic helper support
if ( false !== options.dynamicHelpers ) {
// cache
if ( !this.__dynamicHelpers ) {
this.__dynamicHelpers = {};
for ( var key in dynamicHelpers ) {
this.__dynamicHelpers[key] = dynamicHelpers[key].call(
this.app
, this.req
, this );
}
}
// apply
merge(options, this.__dynamicHelpers);
}
// apply
merge( options, this.__dynamicHelpers );
}
// Merge view helpers
union(options, helpers);
// Merge view helpers
union( options, helpers );
// Always expose partial() as a local
options.partial = function(path, opts){
return renderPartial(self, path, opts, options, view);
};
// Always expose partial() as a local
options.partial = function ( path, opts ) {
return renderPartial( self, path, opts, options, view );
};
// View lookup
options.hint = app.enabled('hints');
view = exports.compile(view, app.cache, cid, options);
// View lookup
options.hint = app.enabled( 'hints' );
view = exports.compile( view, app.cache, cid, options );
// layout helper
options.layout = function(path){
layout = path;
};
// layout helper
options.layout = function ( path ) {
layout = path;
};
// render
var str = view.fn.call(options.scope, options);
// render
var str = view.fn.call( options.scope, options );
// layout expected
if (layout) {
options.isLayout = true;
options.layout = false;
options.body = str;
this.render(layout, options, fn, view, true);
// partial return
} else if (partial) {
return str;
// render complete, and
// callback given
} else if (fn) {
fn(null, str);
// respond
} else {
this.send(str);
}
// layout expected
if ( layout ) {
options.isLayout = true;
options.layout = false;
options.body = str;
this.render( layout, options, fn, view, true );
// partial return
} else if ( partial ) {
return str;
// render complete, and
// callback given
} else if ( fn ) {
fn( null, str );
// respond
} else {
this.send( str );
}
}
/**
@ -450,11 +478,11 @@ res._render = function(view, opts, fn, parent, sub){
* @api private
*/
function hintAtViewPaths(view, options) {
console.error();
console.error('failed to locate view "' + view.view + '", tried:');
options.attempts.forEach(function(path){
console.error(' - %s', path);
});
console.error();
function hintAtViewPaths( view, options ) {
console.error();
console.error( 'failed to locate view "' + view.view + '", tried:' );
options.attempts.forEach( function ( path ) {
console.error( ' - %s', path );
} );
console.error();
}

View file

@ -1,4 +1,3 @@
/*!
* Express - view - Partial
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -25,16 +24,17 @@ var cache = {};
* @api private
*/
exports.resolveObjectName = function(view){
return cache[view] || (cache[view] = view
.split('/')
.slice(-1)[0]
.split('.')[0]
.replace(/^_/, '')
.replace(/[^a-zA-Z0-9 ]+/g, ' ')
.split(/ +/).map(function(word, i){
return i
? word[0].toUpperCase() + word.substr(1)
: word;
}).join(''));
exports.resolveObjectName = function ( view ) {
return cache[view] || (cache[view] = view
.split( '/' )
.slice( -1 )[0]
.split( '.' )[0]
.replace( /^_/, '' )
.replace( /[^a-zA-Z0-9 ]+/g, ' ' )
.split( / +/ ).map(
function ( word, i ) {
return i
? word[0].toUpperCase() + word.substr( 1 )
: word;
} ).join( '' ));
};

View file

@ -1,4 +1,3 @@
/*!
* Express - View
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@ -9,13 +8,13 @@
* Module dependencies.
*/
var path = require('path')
, utils = require('../utils')
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
, fs = require('fs')
, stat = fs.statSync;
var path = require( 'path' )
, utils = require( '../utils' )
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
, fs = require( 'fs' )
, stat = fs.statSync;
/**
* Expose `View`.
@ -37,24 +36,26 @@ var cache = {};
* @api private
*/
function View(view, options) {
options = options || {};
this.view = view;
this.root = options.root;
this.relative = false !== options.relative;
this.defaultEngine = options.defaultEngine;
this.parent = options.parentView;
this.basename = basename(view);
this.engine = this.resolveEngine();
this.extension = '.' + this.engine;
this.name = this.basename.replace(this.extension, '');
this.path = this.resolvePath();
this.dirname = dirname(this.path);
if (options.attempts) {
if (!~options.attempts.indexOf(this.path))
options.attempts.push(this.path);
}
};
function View( view, options ) {
options = options || {};
this.view = view;
this.root = options.root;
this.relative = false !== options.relative;
this.defaultEngine = options.defaultEngine;
this.parent = options.parentView;
this.basename = basename( view );
this.engine = this.resolveEngine();
this.extension = '.' + this.engine;
this.name = this.basename.replace( this.extension, '' );
this.path = this.resolvePath();
this.dirname = dirname( this.path );
if ( options.attempts ) {
if ( !~options.attempts.indexOf( this.path ) ) {
options.attempts.push( this.path );
}
}
}
;
/**
* Check if the view path exists.
@ -63,14 +64,14 @@ function View(view, options) {
* @api public
*/
View.prototype.__defineGetter__('exists', function(){
try {
stat(this.path);
return true;
} catch (err) {
return false;
}
});
View.prototype.__defineGetter__( 'exists', function () {
try {
stat( this.path );
return true;
} catch ( err ) {
return false;
}
} );
/**
* Resolve view engine.
@ -79,13 +80,17 @@ View.prototype.__defineGetter__('exists', function(){
* @api private
*/
View.prototype.resolveEngine = function(){
// Explicit
if (~this.basename.indexOf('.')) return extname(this.basename).substr(1);
// Inherit from parent
if (this.parent) return this.parent.engine;
// Default
return this.defaultEngine;
View.prototype.resolveEngine = function () {
// Explicit
if ( ~this.basename.indexOf( '.' ) ) {
return extname( this.basename ).substr( 1 );
}
// Inherit from parent
if ( this.parent ) {
return this.parent.engine;
}
// Default
return this.defaultEngine;
};
/**
@ -95,18 +100,24 @@ View.prototype.resolveEngine = function(){
* @api private
*/
View.prototype.resolvePath = function(){
var path = this.view;
// Implicit engine
if (!~this.basename.indexOf('.')) path += this.extension;
// Absolute
if (utils.isAbsolute(path)) return path;
// Relative to parent
if (this.relative && this.parent) return this.parent.dirname + '/' + path;
// Relative to root
return this.root
? this.root + '/' + path
: path;
View.prototype.resolvePath = function () {
var path = this.view;
// Implicit engine
if ( !~this.basename.indexOf( '.' ) ) {
path += this.extension;
}
// Absolute
if ( utils.isAbsolute( path ) ) {
return path;
}
// Relative to parent
if ( this.relative && this.parent ) {
return this.parent.dirname + '/' + path;
}
// Relative to root
return this.root
? this.root + '/' + path
: path;
};
/**
@ -117,9 +128,9 @@ View.prototype.resolvePath = function(){
* @api public
*/
View.prototype.__defineGetter__('contents', function(){
return fs.readFileSync(this.path, 'utf8');
});
View.prototype.__defineGetter__( 'contents', function () {
return fs.readFileSync( this.path, 'utf8' );
} );
/**
* Get template engine api, cache exports to reduce
@ -129,10 +140,10 @@ View.prototype.__defineGetter__('contents', function(){
* @api public
*/
View.prototype.__defineGetter__('templateEngine', function(){
var ext = this.extension;
return cache[ext] || (cache[ext] = require(this.engine));
});
View.prototype.__defineGetter__( 'templateEngine', function () {
var ext = this.extension;
return cache[ext] || (cache[ext] = require( this.engine ));
} );
/**
* Return root path alternative.
@ -141,10 +152,10 @@ View.prototype.__defineGetter__('templateEngine', function(){
* @api public
*/
View.prototype.__defineGetter__('rootPath', function(){
this.relative = false;
return this.resolvePath();
});
View.prototype.__defineGetter__( 'rootPath', function () {
this.relative = false;
return this.resolvePath();
} );
/**
* Return index path alternative.
@ -153,11 +164,11 @@ View.prototype.__defineGetter__('rootPath', function(){
* @api public
*/
View.prototype.__defineGetter__('indexPath', function(){
return this.dirname
+ '/' + this.basename.replace(this.extension, '')
+ '/index' + this.extension;
});
View.prototype.__defineGetter__( 'indexPath', function () {
return this.dirname
+ '/' + this.basename.replace( this.extension, '' )
+ '/index' + this.extension;
} );
/**
* Return ../<name>/index path alternative.
@ -166,9 +177,9 @@ View.prototype.__defineGetter__('indexPath', function(){
* @api public
*/
View.prototype.__defineGetter__('upIndexPath', function(){
return this.dirname + '/../' + this.name + '/index' + this.extension;
});
View.prototype.__defineGetter__( 'upIndexPath', function () {
return this.dirname + '/../' + this.name + '/index' + this.extension;
} );
/**
* Return _ prefix path alternative
@ -177,9 +188,9 @@ View.prototype.__defineGetter__('upIndexPath', function(){
* @api public
*/
View.prototype.__defineGetter__('prefixPath', function(){
return this.dirname + '/_' + this.basename;
});
View.prototype.__defineGetter__( 'prefixPath', function () {
return this.dirname + '/_' + this.basename;
} );
/**
* Register the given template engine `exports`
@ -204,7 +215,9 @@ View.prototype.__defineGetter__('prefixPath', function(){
* @api public
*/
exports.register = function(ext, exports) {
if ('.' != ext[0]) ext = '.' + ext;
cache[ext] = exports;
exports.register = function ( ext, exports ) {
if ( '.' != ext[0] ) {
ext = '.' + ext;
}
cache[ext] = exports;
};

View file

@ -1,2 +1 @@
module.exports = require('./lib/connect');
module.exports = require( './lib/connect' );

View file

@ -1,4 +1,3 @@
/*!
* Connect - Cache
* Copyright(c) 2011 Sencha Inc.
@ -18,10 +17,10 @@ module.exports = Cache;
* @api private
*/
function Cache(limit) {
this.store = {};
this.keys = [];
this.limit = limit;
function Cache( limit ) {
this.store = {};
this.keys = [];
this.limit = limit;
}
/**
@ -32,9 +31,9 @@ function Cache(limit) {
* @api private
*/
Cache.prototype.touch = function(key, i){
this.keys.splice(i,1);
this.keys.push(key);
Cache.prototype.touch = function ( key, i ) {
this.keys.splice( i, 1 );
this.keys.push( key );
};
/**
@ -44,8 +43,8 @@ Cache.prototype.touch = function(key, i){
* @api private
*/
Cache.prototype.remove = function(key){
delete this.store[key];
Cache.prototype.remove = function ( key ) {
delete this.store[key];
};
/**
@ -56,8 +55,8 @@ Cache.prototype.remove = function(key){
* @api private
*/
Cache.prototype.get = function(key){
return this.store[key];
Cache.prototype.get = function ( key ) {
return this.store[key];
};
/**
@ -68,14 +67,16 @@ Cache.prototype.get = function(key){
* @api private
*/
Cache.prototype.add = function(key){
// initialize store
var len = this.keys.push(key);
Cache.prototype.add = function ( key ) {
// initialize store
var len = this.keys.push( key );
// limit reached, invalid LRU
if (len > this.limit) this.remove(this.keys.shift());
// limit reached, invalid LRU
if ( len > this.limit ) {
this.remove( this.keys.shift() );
}
var arr = this.store[key] = [];
arr.createdAt = new Date;
return arr;
var arr = this.store[key] = [];
arr.createdAt = new Date;
return arr;
};

View file

@ -1,4 +1,3 @@
/*!
* Connect
* Copyright(c) 2010 Sencha Inc.
@ -10,13 +9,13 @@
* Module dependencies.
*/
var HTTPServer = require('./http').Server
, HTTPSServer = require('./https').Server
, fs = require('fs');
var HTTPServer = require( './http' ).Server
, HTTPSServer = require( './https' ).Server
, fs = require( 'fs' );
// node patches
require('./patch');
require( './patch' );
// expose createServer() as the module
@ -63,12 +62,13 @@ exports.version = '1.8.6';
*/
function createServer() {
if ('object' == typeof arguments[0]) {
return new HTTPSServer(arguments[0], Array.prototype.slice.call(arguments, 1));
} else {
return new HTTPServer(Array.prototype.slice.call(arguments));
}
};
if ( 'object' == typeof arguments[0] ) {
return new HTTPSServer( arguments[0], Array.prototype.slice.call( arguments, 1 ) );
} else {
return new HTTPServer( Array.prototype.slice.call( arguments ) );
}
}
;
// support connect.createServer()
@ -82,22 +82,22 @@ exports.middleware = {};
* Auto-load bundled middleware with getters.
*/
fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
if (/\.js$/.test(filename)) {
var name = filename.substr(0, filename.lastIndexOf('.'));
exports.middleware.__defineGetter__(name, function(){
return require('./middleware/' + name);
});
}
});
fs.readdirSync( __dirname + '/middleware' ).forEach( function ( filename ) {
if ( /\.js$/.test( filename ) ) {
var name = filename.substr( 0, filename.lastIndexOf( '.' ) );
exports.middleware.__defineGetter__( name, function () {
return require( './middleware/' + name );
} );
}
} );
// expose utils
exports.utils = require('./utils');
exports.utils = require( './utils' );
// expose getters as first-class exports
exports.utils.merge(exports, exports.middleware);
exports.utils.merge( exports, exports.middleware );
// expose constructors

View file

@ -1,4 +1,3 @@
/*!
* Connect - HTTPServer
* Copyright(c) 2010 Sencha Inc.
@ -10,9 +9,9 @@
* Module dependencies.
*/
var http = require('http')
, parse = require('url').parse
, assert = require('assert');
var http = require( 'http' )
, parse = require( 'url' ).parse
, assert = require( 'assert' );
// environment
@ -29,17 +28,17 @@ var env = process.env.NODE_ENV || 'development';
* , connect.static(__dirname + '/public')
* );
*
* @params {Array} middleware
* @params {Array} middleware
* @return {Server}
* @api public
*/
var Server = exports.Server = function HTTPServer(middleware) {
this.stack = [];
middleware.forEach(function(fn){
this.use(fn);
}, this);
http.Server.call(this, this.handle);
var Server = exports.Server = function HTTPServer( middleware ) {
this.stack = [];
middleware.forEach( function ( fn ) {
this.use( fn );
}, this );
http.Server.call( this, this.handle );
};
/**
@ -88,39 +87,39 @@ Server.prototype.__proto__ = http.Server.prototype;
* @api public
*/
Server.prototype.use = function(route, handle){
this.route = '/';
Server.prototype.use = function ( route, handle ) {
this.route = '/';
// default route to '/'
if ('string' != typeof route) {
handle = route;
route = '/';
}
// default route to '/'
if ( 'string' != typeof route ) {
handle = route;
route = '/';
}
// wrap sub-apps
if ('function' == typeof handle.handle) {
var server = handle;
server.route = route;
handle = function(req, res, next) {
server.handle(req, res, next);
};
}
// wrap sub-apps
if ( 'function' == typeof handle.handle ) {
var server = handle;
server.route = route;
handle = function ( req, res, next ) {
server.handle( req, res, next );
};
}
// wrap vanilla http.Servers
if (handle instanceof http.Server) {
handle = handle.listeners('request')[0];
}
// wrap vanilla http.Servers
if ( handle instanceof http.Server ) {
handle = handle.listeners( 'request' )[0];
}
// normalize route to not trail with slash
if ('/' == route[route.length - 1]) {
route = route.substr(0, route.length - 1);
}
// normalize route to not trail with slash
if ( '/' == route[route.length - 1] ) {
route = route.substr( 0, route.length - 1 );
}
// add the middleware
this.stack.push({ route: route, handle: handle });
// add the middleware
this.stack.push( { route : route, handle : handle } );
// allow chaining
return this;
// allow chaining
return this;
};
/**
@ -130,88 +129,107 @@ Server.prototype.use = function(route, handle){
* @api private
*/
Server.prototype.handle = function(req, res, out) {
var writeHead = res.writeHead
, stack = this.stack
, removed = ''
, index = 0;
Server.prototype.handle = function ( req, res, out ) {
var writeHead = res.writeHead
, stack = this.stack
, removed = ''
, index = 0;
function next(err) {
var layer, path, c;
req.url = removed + req.url;
req.originalUrl = req.originalUrl || req.url;
removed = '';
function next( err ) {
var layer, path, c;
req.url = removed + req.url;
req.originalUrl = req.originalUrl || req.url;
removed = '';
layer = stack[index++];
layer = stack[index++];
// all done
if (!layer || res.headerSent) {
// but wait! we have a parent
if (out) return out(err);
// all done
if ( !layer || res.headerSent ) {
// but wait! we have a parent
if ( out ) {
return out( err );
}
// error
if (err) {
var msg = 'production' == env
? 'Internal Server Error'
: err.stack || err.toString();
// error
if ( err ) {
var msg = 'production' == env
? 'Internal Server Error'
: err.stack || err.toString();
// output to stderr in a non-test env
if ('test' != env) console.error(err.stack || err.toString());
// output to stderr in a non-test env
if ( 'test' != env ) {
console.error( err.stack || err.toString() );
}
// unable to respond
if (res.headerSent) return req.socket.destroy();
// unable to respond
if ( res.headerSent ) {
return req.socket.destroy();
}
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain');
if ('HEAD' == req.method) return res.end();
res.end(msg);
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
if ('HEAD' == req.method) return res.end();
res.end('Cannot ' + req.method + ' ' + req.url);
}
return;
}
res.statusCode = 500;
res.setHeader( 'Content-Type', 'text/plain' );
if ( 'HEAD' == req.method ) {
return res.end();
}
res.end( msg );
} else {
res.statusCode = 404;
res.setHeader( 'Content-Type', 'text/plain' );
if ( 'HEAD' == req.method ) {
return res.end();
}
res.end( 'Cannot ' + req.method + ' ' + req.url );
}
return;
}
try {
path = parse(req.url).pathname;
if (undefined == path) path = '/';
try {
path = parse( req.url ).pathname;
if ( undefined == path ) {
path = '/';
}
// skip this layer if the route doesn't match.
if (0 != path.indexOf(layer.route)) return next(err);
// skip this layer if the route doesn't match.
if ( 0 != path.indexOf( layer.route ) ) {
return next( err );
}
c = path[layer.route.length];
if (c && '/' != c && '.' != c) return next(err);
c = path[layer.route.length];
if ( c && '/' != c && '.' != c ) {
return next( err );
}
// Call the layer handler
// Trim off the part of the url that matches the route
removed = layer.route;
req.url = req.url.substr(removed.length);
// Call the layer handler
// Trim off the part of the url that matches the route
removed = layer.route;
req.url = req.url.substr( removed.length );
// Ensure leading slash
if ('/' != req.url[0]) req.url = '/' + req.url;
// Ensure leading slash
if ( '/' != req.url[0] ) {
req.url = '/' + req.url;
}
var arity = layer.handle.length;
if (err) {
if (arity === 4) {
layer.handle(err, req, res, next);
} else {
next(err);
}
} else if (arity < 4) {
layer.handle(req, res, next);
} else {
next();
}
} catch (e) {
if (e instanceof assert.AssertionError) {
console.error(e.stack + '\n');
next(e);
} else {
next(e);
}
}
}
next();
var arity = layer.handle.length;
if ( err ) {
if ( arity === 4 ) {
layer.handle( err, req, res, next );
} else {
next( err );
}
} else if ( arity < 4 ) {
layer.handle( req, res, next );
} else {
next();
}
} catch ( e ) {
if ( e instanceof assert.AssertionError ) {
console.error( e.stack + '\n' );
next( e );
} else {
next( e );
}
}
}
next();
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - HTTPServer
* Copyright(c) 2010 Sencha Inc.
@ -10,8 +9,8 @@
* Module dependencies.
*/
var HTTPServer = require('./http').Server
, https = require('https');
var HTTPServer = require( './http' ).Server
, https = require( 'https' );
/**
* Initialize a new `Server` with the given
@ -26,12 +25,12 @@ var HTTPServer = require('./http').Server
* @api public
*/
var Server = exports.Server = function HTTPSServer(options, middleware) {
this.stack = [];
middleware.forEach(function(fn){
this.use(fn);
}, this);
https.Server.call(this, options, this.handle);
var Server = exports.Server = function HTTPSServer( options, middleware ) {
this.stack = [];
middleware.forEach( function ( fn ) {
this.use( fn );
}, this );
https.Server.call( this, options, this.handle );
};
/**
@ -42,6 +41,6 @@ Server.prototype.__proto__ = https.Server.prototype;
// mixin HTTPServer methods
Object.keys(HTTPServer.prototype).forEach(function(method){
Server.prototype[method] = HTTPServer.prototype[method];
});
Object.keys( HTTPServer.prototype ).forEach( function ( method ) {
Server.prototype[method] = HTTPServer.prototype[method];
} );

View file

@ -1,4 +1,3 @@
/**
* # Connect
*
@ -7,9 +6,9 @@
* [3rd-party middleware](https://github.com/senchalabs/connect/wiki).
*
* Installation:
*
*
* $ npm install connect
*
*
* API:
*
* - [connect](connect.html) general

View file

@ -1,4 +1,3 @@
/*!
* Connect - basicAuth
* Copyright(c) 2010 Sencha Inc.
@ -10,9 +9,9 @@
* Module dependencies.
*/
var utils = require('../utils')
, unauthorized = utils.unauthorized
, badRequest = utils.badRequest;
var utils = require( '../utils' )
, unauthorized = utils.unauthorized
, badRequest = utils.badRequest;
/**
* Enfore basic authentication by providing a `callback(user, pass)`,
@ -42,52 +41,62 @@ var utils = require('../utils')
* @api public
*/
module.exports = function basicAuth(callback, realm) {
var username, password;
module.exports = function basicAuth( callback, realm ) {
var username, password;
// user / pass strings
if ('string' == typeof callback) {
username = callback;
password = realm;
if ('string' != typeof password) throw new Error('password argument required');
realm = arguments[2];
callback = function(user, pass){
return user == username && pass == password;
}
}
// user / pass strings
if ( 'string' == typeof callback ) {
username = callback;
password = realm;
if ( 'string' != typeof password ) {
throw new Error( 'password argument required' );
}
realm = arguments[2];
callback = function ( user, pass ) {
return user == username && pass == password;
}
}
realm = realm || 'Authorization Required';
realm = realm || 'Authorization Required';
return function(req, res, next) {
var authorization = req.headers.authorization;
return function ( req, res, next ) {
var authorization = req.headers.authorization;
if (req.remoteUser) return next();
if (!authorization) return unauthorized(res, realm);
if ( req.remoteUser ) {
return next();
}
if ( !authorization ) {
return unauthorized( res, realm );
}
var parts = authorization.split(' ')
, scheme = parts[0]
, credentials = new Buffer(parts[1], 'base64').toString().split(':');
var parts = authorization.split( ' ' )
, scheme = parts[0]
, credentials = new Buffer( parts[1], 'base64' ).toString().split( ':' );
if ('Basic' != scheme) return badRequest(res);
if ( 'Basic' != scheme ) {
return badRequest( res );
}
// async
if (callback.length >= 3) {
var pause = utils.pause(req);
callback(credentials[0], credentials[1], function(err, user){
if (err || !user) return unauthorized(res, realm);
req.remoteUser = user;
next();
pause.resume();
});
// sync
} else {
if (callback(credentials[0], credentials[1])) {
req.remoteUser = credentials[0];
next();
} else {
unauthorized(res, realm);
}
}
}
// async
if ( callback.length >= 3 ) {
var pause = utils.pause( req );
callback( credentials[0], credentials[1], function ( err, user ) {
if ( err || !user ) {
return unauthorized( res, realm );
}
req.remoteUser = user;
next();
pause.resume();
} );
// sync
} else {
if ( callback( credentials[0], credentials[1] ) ) {
req.remoteUser = credentials[0];
next();
} else {
unauthorized( res, realm );
}
}
}
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - bodyParser
* Copyright(c) 2010 Sencha Inc.
@ -10,8 +9,8 @@
* Module dependencies.
*/
var qs = require('qs')
, formidable = require('formidable');
var qs = require( 'qs' )
, formidable = require( 'formidable' );
/**
* Extract the mime type from the given request's
@ -22,9 +21,9 @@ var qs = require('qs')
* @api private
*/
function mime(req) {
var str = req.headers['content-type'] || '';
return str.split(';')[0];
function mime( req ) {
var str = req.headers['content-type'] || '';
return str.split( ';' )[0];
}
/**
@ -66,7 +65,7 @@ function mime(req) {
*
* If you wish to use formidable directly within your app, and do not
* desire this behaviour for multipart requests simply remove the
* parser:
* parser:
*
* delete connect.bodyParser.parse['multipart/form-data'];
*
@ -79,20 +78,24 @@ function mime(req) {
* @api public
*/
exports = module.exports = function bodyParser(options){
options = options || {};
return function bodyParser(req, res, next) {
if (req.body) return next();
req.body = {};
exports = module.exports = function bodyParser( options ) {
options = options || {};
return function bodyParser( req, res, next ) {
if ( req.body ) {
return next();
}
req.body = {};
if ('GET' == req.method || 'HEAD' == req.method) return next();
var parser = exports.parse[mime(req)];
if (parser) {
parser(req, options, next);
} else {
next();
}
}
if ( 'GET' == req.method || 'HEAD' == req.method ) {
return next();
}
var parser = exports.parse[mime( req )];
if ( parser ) {
parser( req, options, next );
} else {
next();
}
}
};
/**
@ -105,40 +108,44 @@ exports.parse = {};
* Parse application/x-www-form-urlencoded.
*/
exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
try {
req.body = buf.length
? qs.parse(buf)
: {};
fn();
} catch (err){
fn(err);
}
});
exports.parse['application/x-www-form-urlencoded'] = function ( req, options, fn ) {
var buf = '';
req.setEncoding( 'utf8' );
req.on( 'data', function ( chunk ) {
buf += chunk
} );
req.on( 'end', function () {
try {
req.body = buf.length
? qs.parse( buf )
: {};
fn();
} catch ( err ) {
fn( err );
}
} );
};
/**
* Parse application/json.
*/
exports.parse['application/json'] = function(req, options, fn){
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
try {
req.body = buf.length
? JSON.parse(buf)
: {};
fn();
} catch (err){
fn(err);
}
});
exports.parse['application/json'] = function ( req, options, fn ) {
var buf = '';
req.setEncoding( 'utf8' );
req.on( 'data', function ( chunk ) {
buf += chunk
} );
req.on( 'end', function () {
try {
req.body = buf.length
? JSON.parse( buf )
: {};
fn();
} catch ( err ) {
fn( err );
}
} );
};
/**
@ -148,49 +155,51 @@ exports.parse['application/json'] = function(req, options, fn){
* TODO: revisit "error" flag if it's a formidable bug
*/
exports.parse['multipart/form-data'] = function(req, options, fn){
var form = new formidable.IncomingForm
, data = {}
, files = {}
, done;
exports.parse['multipart/form-data'] = function ( req, options, fn ) {
var form = new formidable.IncomingForm
, data = {}
, files = {}
, done;
Object.keys(options).forEach(function(key){
form[key] = options[key];
});
Object.keys( options ).forEach( function ( key ) {
form[key] = options[key];
} );
function ondata(name, val, data){
if (Array.isArray(data[name])) {
data[name].push(val);
} else if (data[name]) {
data[name] = [data[name], val];
} else {
data[name] = val;
}
}
function ondata( name, val, data ) {
if ( Array.isArray( data[name] ) ) {
data[name].push( val );
} else if ( data[name] ) {
data[name] = [data[name], val];
} else {
data[name] = val;
}
}
form.on('field', function(name, val){
ondata(name, val, data);
});
form.on( 'field', function ( name, val ) {
ondata( name, val, data );
} );
form.on('file', function(name, val){
ondata(name, val, files);
});
form.on( 'file', function ( name, val ) {
ondata( name, val, files );
} );
form.on('error', function(err){
fn(err);
done = true;
});
form.on( 'error', function ( err ) {
fn( err );
done = true;
} );
form.on('end', function(){
if (done) return;
try {
req.body = qs.parse(data);
req.files = qs.parse(files);
fn();
} catch (err) {
fn(err);
}
});
form.on( 'end', function () {
if ( done ) {
return;
}
try {
req.body = qs.parse( data );
req.files = qs.parse( files );
fn();
} catch ( err ) {
fn( err );
}
} );
form.parse(req);
form.parse( req );
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - compiler
* Copyright(c) 2010 Sencha Inc.
@ -10,9 +9,9 @@
* Module dependencies.
*/
var fs = require('fs')
, path = require('path')
, parse = require('url').parse;
var fs = require( 'fs' )
, path = require( 'path' )
, parse = require( 'url' ).parse;
/**
* Require cache.
@ -39,80 +38,83 @@ var cache = {};
* @api public
*/
exports = module.exports = function compiler(options){
options = options || {};
exports = module.exports = function compiler( options ) {
options = options || {};
var srcDir = options.src || process.cwd()
, destDir = options.dest || srcDir
, enable = options.enable;
var srcDir = options.src || process.cwd()
, destDir = options.dest || srcDir
, enable = options.enable;
if (!enable || enable.length === 0) {
throw new Error('compiler\'s "enable" option is not set, nothing will be compiled.');
}
if ( !enable || enable.length === 0 ) {
throw new Error( 'compiler\'s "enable" option is not set, nothing will be compiled.' );
}
return function compiler(req, res, next){
if ('GET' != req.method) return next();
var pathname = parse(req.url).pathname;
for (var i = 0, len = enable.length; i < len; ++i) {
var name = enable[i]
, compiler = compilers[name];
if (compiler.match.test(pathname)) {
var src = (srcDir + pathname).replace(compiler.match, compiler.ext)
, dest = destDir + pathname;
return function compiler( req, res, next ) {
if ( 'GET' != req.method ) {
return next();
}
var pathname = parse( req.url ).pathname;
for ( var i = 0, len = enable.length; i < len; ++i ) {
var name = enable[i]
, compiler = compilers[name];
if ( compiler.match.test( pathname ) ) {
var src = (srcDir + pathname).replace( compiler.match, compiler.ext )
, dest = destDir + pathname;
// Compare mtimes
fs.stat(src, function(err, srcStats){
if (err) {
if ('ENOENT' == err.code) {
next();
} else {
next(err);
}
} else {
fs.stat(dest, function(err, destStats){
if (err) {
// Oh snap! it does not exist, compile it
if ('ENOENT' == err.code) {
compile();
} else {
next(err);
}
} else {
// Source has changed, compile it
if (srcStats.mtime > destStats.mtime) {
compile();
} else {
// Defer file serving
next();
}
}
});
}
});
// Compare mtimes
fs.stat( src, function ( err, srcStats ) {
if ( err ) {
if ( 'ENOENT' == err.code ) {
next();
} else {
next( err );
}
} else {
fs.stat( dest, function ( err, destStats ) {
if ( err ) {
// Oh snap! it does not exist, compile it
if ( 'ENOENT' == err.code ) {
compile();
} else {
next( err );
}
} else {
// Source has changed, compile it
if ( srcStats.mtime > destStats.mtime ) {
compile();
} else {
// Defer file serving
next();
}
}
} );
}
} );
// Compile to the destination
function compile() {
fs.readFile(src, 'utf8', function(err, str){
if (err) {
next(err);
} else {
compiler.compile(str, function(err, str){
if (err) {
next(err);
} else {
fs.writeFile(dest, str, 'utf8', function(err){
next(err);
});
}
});
}
});
}
return;
}
}
next();
};
// Compile to the destination
function compile() {
fs.readFile( src, 'utf8', function ( err, str ) {
if ( err ) {
next( err );
} else {
compiler.compile( str, function ( err, str ) {
if ( err ) {
next( err );
} else {
fs.writeFile( dest, str, 'utf8', function ( err ) {
next( err );
} );
}
} );
}
} );
}
return;
}
}
next();
};
};
/**
@ -124,40 +126,40 @@ exports = module.exports = function compiler(options){
*/
var compilers = exports.compilers = {
sass: {
match: /\.css$/,
ext: '.sass',
compile: function(str, fn){
var sass = cache.sass || (cache.sass = require('sass'));
try {
fn(null, sass.render(str));
} catch (err) {
fn(err);
}
}
},
less: {
match: /\.css$/,
ext: '.less',
compile: function(str, fn){
var less = cache.less || (cache.less = require('less'));
try {
less.render(str, fn);
} catch (err) {
fn(err);
}
}
},
coffeescript: {
match: /\.js$/,
ext: '.coffee',
compile: function(str, fn){
var coffee = cache.coffee || (cache.coffee = require('coffee-script'));
try {
fn(null, coffee.compile(str));
} catch (err) {
fn(err);
}
}
}
sass : {
match : /\.css$/,
ext : '.sass',
compile : function ( str, fn ) {
var sass = cache.sass || (cache.sass = require( 'sass' ));
try {
fn( null, sass.render( str ) );
} catch ( err ) {
fn( err );
}
}
},
less : {
match : /\.css$/,
ext : '.less',
compile : function ( str, fn ) {
var less = cache.less || (cache.less = require( 'less' ));
try {
less.render( str, fn );
} catch ( err ) {
fn( err );
}
}
},
coffeescript : {
match : /\.js$/,
ext : '.coffee',
compile : function ( str, fn ) {
var coffee = cache.coffee || (cache.coffee = require( 'coffee-script' ));
try {
fn( null, coffee.compile( str ) );
} catch ( err ) {
fn( err );
}
}
}
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - cookieParser
* Copyright(c) 2010 Sencha Inc.
@ -10,7 +9,7 @@
* Module dependencies.
*/
var utils = require('./../utils');
var utils = require( './../utils' );
/**
* Parse _Cookie_ header and populate `req.cookies`
@ -29,18 +28,20 @@ var utils = require('./../utils');
* @api public
*/
module.exports = function cookieParser(){
return function cookieParser(req, res, next) {
var cookie = req.headers.cookie;
if (req.cookies) return next();
req.cookies = {};
if (cookie) {
try {
req.cookies = utils.parseCookie(cookie);
} catch (err) {
return next(err);
}
}
next();
};
module.exports = function cookieParser() {
return function cookieParser( req, res, next ) {
var cookie = req.headers.cookie;
if ( req.cookies ) {
return next();
}
req.cookies = {};
if ( cookie ) {
try {
req.cookies = utils.parseCookie( cookie );
} catch ( err ) {
return next( err );
}
}
next();
};
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - csrf
* Copyright(c) 2011 Sencha Inc.
@ -9,8 +8,8 @@
* Module dependencies.
*/
var utils = require('../utils')
, crypto = require('crypto');
var utils = require( '../utils' )
, crypto = require( 'crypto' );
/**
* CRSF protection middleware.
@ -37,20 +36,20 @@ var utils = require('../utils')
* <input type="password" name="user[pass]" />\n\
* <input type="submit" value="Login" />\n\
* </form>\n\
* ';
*
* ';
*
* connect(
* connect.cookieParser()
* , connect.session({ secret: 'keyboard cat' })
* , connect.bodyParser()
* , connect.csrf()
*
*
* , function(req, res, next){
* if ('POST' != req.method) return next();
* req.session.user = req.body.user;
* next();
* }
*
*
* , function(req, res){
* res.setHeader('Content-Type', 'text/html');
* var body = form
@ -62,31 +61,35 @@ var utils = require('../utils')
*
* Options:
*
* - `value` a function accepting the request, returning the token
* - `value` a function accepting the request, returning the token
*
* @param {Object} options
* @api public
*/
module.exports = function csrf(options) {
var options = options || {}
, value = options.value || defaultValue;
module.exports = function csrf( options ) {
var options = options || {}
, value = options.value || defaultValue;
return function(req, res, next){
// generate CSRF token
var token = req.session._csrf || (req.session._csrf = utils.uid(24));
return function ( req, res, next ) {
// generate CSRF token
var token = req.session._csrf || (req.session._csrf = utils.uid( 24 ));
// ignore GET (for now)
if ('GET' == req.method) return next();
// ignore GET (for now)
if ( 'GET' == req.method ) {
return next();
}
// determine value
var val = value(req);
// determine value
var val = value( req );
// check
if (val != token) return utils.forbidden(res);
next();
}
// check
if ( val != token ) {
return utils.forbidden( res );
}
next();
}
};
/**
@ -98,8 +101,8 @@ module.exports = function csrf(options) {
* @api private
*/
function defaultValue(req) {
return (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token']);
function defaultValue( req ) {
return (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token']);
}

View file

@ -1,4 +1,3 @@
/*!
* Connect - directory
* Copyright(c) 2011 Sencha Inc.
@ -14,13 +13,13 @@
* Module dependencies.
*/
var fs = require('fs')
, parse = require('url').parse
, utils = require('../utils')
, path = require('path')
, normalize = path.normalize
, extname = path.extname
, join = path.join;
var fs = require( 'fs' )
, parse = require( 'url' ).parse
, utils = require( '../utils' )
, path = require( 'path' )
, normalize = path.normalize
, extname = path.extname
, join = path.join;
/**
* Icon cache.
@ -43,137 +42,161 @@ var cache = {};
* @api public
*/
exports = module.exports = function directory(root, options){
options = options || {};
exports = module.exports = function directory( root, options ) {
options = options || {};
// root required
if (!root) throw new Error('directory() root path required');
var hidden = options.hidden
, icons = options.icons
, filter = options.filter
, root = normalize(root);
// root required
if ( !root ) {
throw new Error( 'directory() root path required' );
}
var hidden = options.hidden
, icons = options.icons
, filter = options.filter
, root = normalize( root );
return function directory(req, res, next) {
var accept = req.headers.accept || 'text/plain'
, url = parse(req.url)
, dir = decodeURIComponent(url.pathname)
, path = normalize(join(root, dir))
, originalUrl = parse(req.originalUrl)
, originalDir = decodeURIComponent(originalUrl.pathname)
, showUp = path != root && path != root + '/';
return function directory( req, res, next ) {
var accept = req.headers.accept || 'text/plain'
, url = parse( req.url )
, dir = decodeURIComponent( url.pathname )
, path = normalize( join( root, dir ) )
, originalUrl = parse( req.originalUrl )
, originalDir = decodeURIComponent( originalUrl.pathname )
, showUp = path != root && path != root + '/';
// null byte(s)
if (~path.indexOf('\0')) return utils.badRequest(res);
// null byte(s)
if ( ~path.indexOf( '\0' ) ) {
return utils.badRequest( res );
}
// malicious path
if (0 != path.indexOf(root)) return utils.forbidden(res);
// malicious path
if ( 0 != path.indexOf( root ) ) {
return utils.forbidden( res );
}
// check if we have a directory
fs.stat(path, function(err, stat){
if (err) return 'ENOENT' == err.code
? next()
: next(err);
// check if we have a directory
fs.stat( path, function ( err, stat ) {
if ( err ) {
return 'ENOENT' == err.code
? next()
: next( err );
}
if (!stat.isDirectory()) return next();
if ( !stat.isDirectory() ) {
return next();
}
// fetch files
fs.readdir(path, function(err, files){
if (err) return next(err);
if (!hidden) files = removeHidden(files);
if (filter) files = files.filter(filter);
files.sort();
// content-negotiation
for (var key in exports) {
if (~accept.indexOf(key) || ~accept.indexOf('*/*')) {
exports[key](req, res, files, next, originalDir, showUp, icons);
return;
}
}
utils.notAcceptable(res);
});
});
};
// fetch files
fs.readdir( path, function ( err, files ) {
if ( err ) {
return next( err );
}
if ( !hidden ) {
files = removeHidden( files );
}
if ( filter ) {
files = files.filter( filter );
}
files.sort();
// content-negotiation
for ( var key in exports ) {
if ( ~accept.indexOf( key ) || ~accept.indexOf( '*/*' ) ) {
exports[key]( req, res, files, next, originalDir, showUp, icons );
return;
}
}
utils.notAcceptable( res );
} );
} );
};
};
/**
* Respond with text/html.
*/
exports.html = function(req, res, files, next, dir, showUp, icons){
fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){
if (err) return next(err);
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){
if (err) return next(err);
if (showUp) files.unshift('..');
str = str
.replace('{style}', style)
.replace('{files}', html(files, dir, icons))
.replace('{directory}', dir)
.replace('{linked-path}', htmlPath(dir));
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', str.length);
res.end(str);
});
});
exports.html = function ( req, res, files, next, dir, showUp, icons ) {
fs.readFile( __dirname + '/../public/directory.html', 'utf8', function ( err, str ) {
if ( err ) {
return next( err );
}
fs.readFile( __dirname + '/../public/style.css', 'utf8', function ( err, style ) {
if ( err ) {
return next( err );
}
if ( showUp ) {
files.unshift( '..' );
}
str = str
.replace( '{style}', style )
.replace( '{files}', html( files, dir, icons ) )
.replace( '{directory}', dir )
.replace( '{linked-path}', htmlPath( dir ) );
res.setHeader( 'Content-Type', 'text/html' );
res.setHeader( 'Content-Length', str.length );
res.end( str );
} );
} );
};
/**
* Respond with application/json.
*/
exports.json = function(req, res, files){
files = JSON.stringify(files);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Length', files.length);
res.end(files);
exports.json = function ( req, res, files ) {
files = JSON.stringify( files );
res.setHeader( 'Content-Type', 'application/json' );
res.setHeader( 'Content-Length', files.length );
res.end( files );
};
/**
* Respond with text/plain.
*/
exports.plain = function(req, res, files){
files = files.join('\n') + '\n';
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Length', files.length);
res.end(files);
exports.plain = function ( req, res, files ) {
files = files.join( '\n' ) + '\n';
res.setHeader( 'Content-Type', 'text/plain' );
res.setHeader( 'Content-Length', files.length );
res.end( files );
};
/**
* Map html `dir`, returning a linked path.
*/
function htmlPath(dir) {
var curr = [];
return dir.split('/').map(function(part){
curr.push(part);
return '<a href="' + curr.join('/') + '">' + part + '</a>';
}).join(' / ');
function htmlPath( dir ) {
var curr = [];
return dir.split( '/' ).map(
function ( part ) {
curr.push( part );
return '<a href="' + curr.join( '/' ) + '">' + part + '</a>';
} ).join( ' / ' );
}
/**
* Map html `files`, returning an html unordered list.
*/
function html(files, dir, useIcons) {
return '<ul id="files">' + files.map(function(file){
var icon = ''
, classes = [];
function html( files, dir, useIcons ) {
return '<ul id="files">' + files.map(
function ( file ) {
var icon = ''
, classes = [];
if (useIcons && '..' != file) {
icon = icons[extname(file)] || icons.default;
icon = '<img src="data:image/png;base64,' + load(icon) + '" />';
classes.push('icon');
}
if ( useIcons && '..' != file ) {
icon = icons[extname( file )] || icons.default;
icon = '<img src="data:image/png;base64,' + load( icon ) + '" />';
classes.push( 'icon' );
}
return '<li><a href="'
+ join(dir, file)
+ '" class="'
+ classes.join(' ') + '"'
+ ' title="' + file + '">'
+ icon + file + '</a></li>';
return '<li><a href="'
+ join( dir, file )
+ '" class="'
+ classes.join( ' ' ) + '"'
+ ' title="' + file + '">'
+ icon + file + '</a></li>';
}).join('\n') + '</ul>';
} ).join( '\n' ) + '</ul>';
}
/**
@ -184,9 +207,11 @@ function html(files, dir, useIcons) {
* @api private
*/
function load(icon) {
if (cache[icon]) return cache[icon];
return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64');
function load( icon ) {
if ( cache[icon] ) {
return cache[icon];
}
return cache[icon] = fs.readFileSync( __dirname + '/../public/icons/' + icon, 'base64' );
}
/**
@ -198,10 +223,10 @@ function load(icon) {
* @api private
*/
function removeHidden(files) {
return files.filter(function(file){
return '.' != file[0];
});
function removeHidden( files ) {
return files.filter( function ( file ) {
return '.' != file[0];
} );
}
/**
@ -209,14 +234,5 @@ function removeHidden(files) {
*/
var icons = {
'.js': 'page_white_code_red.png'
, '.c': 'page_white_c.png'
, '.h': 'page_white_h.png'
, '.cc': 'page_white_cplusplus.png'
, '.php': 'page_white_php.png'
, '.rb': 'page_white_ruby.png'
, '.cpp': 'page_white_cplusplus.png'
, '.swf': 'page_white_flash.png'
, '.pdf': 'page_white_acrobat.png'
, 'default': 'page_white.png'
'.js' : 'page_white_code_red.png', '.c' : 'page_white_c.png', '.h' : 'page_white_h.png', '.cc' : 'page_white_cplusplus.png', '.php' : 'page_white_php.png', '.rb' : 'page_white_ruby.png', '.cpp' : 'page_white_cplusplus.png', '.swf' : 'page_white_flash.png', '.pdf' : 'page_white_acrobat.png', 'default' : 'page_white.png'
};

View file

@ -9,9 +9,9 @@
* Module dependencies.
*/
var utils = require('../utils')
, url = require('url')
, fs = require('fs');
var utils = require( '../utils' )
, url = require( 'url' )
, fs = require( 'fs' );
/**
* Flexible error handler, providing (_optional_) stack traces
@ -32,7 +32,7 @@ var utils = require('../utils')
* JSON:
*
* When _application/json_ is accepted, connect will respond with
* an object in the form of `{ "error": error }`.
* an object in the form of `{ "error": error }`.
*
* HTML:
*
@ -43,54 +43,59 @@ var utils = require('../utils')
* @api public
*/
exports = module.exports = function errorHandler(options){
options = options || {};
exports = module.exports = function errorHandler( options ) {
options = options || {};
// defaults
var showStack = options.showStack || options.stack
, showMessage = options.showMessage || options.message
, dumpExceptions = options.dumpExceptions || options.dump
, formatUrl = options.formatUrl;
// defaults
var showStack = options.showStack || options.stack
, showMessage = options.showMessage || options.message
, dumpExceptions = options.dumpExceptions || options.dump
, formatUrl = options.formatUrl;
return function errorHandler(err, req, res, next){
res.statusCode = 500;
if (dumpExceptions) console.error(err.stack);
if (showStack) {
var accept = req.headers.accept || '';
// html
if (~accept.indexOf('html')) {
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){
fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){
var stack = (err.stack || '')
.split('\n').slice(1)
.map(function(v){ return '<li>' + v + '</li>'; }).join('');
html = html
.replace('{style}', style)
.replace('{stack}', stack)
.replace('{title}', exports.title)
.replace(/\{error\}/g, utils.escape(err.toString()));
res.setHeader('Content-Type', 'text/html');
res.end(html);
});
});
// json
} else if (~accept.indexOf('json')) {
var json = JSON.stringify({ error: err });
res.setHeader('Content-Type', 'application/json');
res.end(json);
// plain text
} else {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end(err.stack);
}
} else {
var body = showMessage
? err.toString()
: 'Internal Server Error';
res.setHeader('Content-Type', 'text/plain');
res.end(body);
}
};
return function errorHandler( err, req, res, next ) {
res.statusCode = 500;
if ( dumpExceptions ) {
console.error( err.stack );
}
if ( showStack ) {
var accept = req.headers.accept || '';
// html
if ( ~accept.indexOf( 'html' ) ) {
fs.readFile( __dirname + '/../public/style.css', 'utf8', function ( e, style ) {
fs.readFile( __dirname + '/../public/error.html', 'utf8', function ( e, html ) {
var stack = (err.stack || '')
.split( '\n' ).slice( 1 )
.map(
function ( v ) {
return '<li>' + v + '</li>';
} ).join( '' );
html = html
.replace( '{style}', style )
.replace( '{stack}', stack )
.replace( '{title}', exports.title )
.replace( /\{error\}/g, utils.escape( err.toString() ) );
res.setHeader( 'Content-Type', 'text/html' );
res.end( html );
} );
} );
// json
} else if ( ~accept.indexOf( 'json' ) ) {
var json = JSON.stringify( { error : err } );
res.setHeader( 'Content-Type', 'application/json' );
res.end( json );
// plain text
} else {
res.writeHead( 500, { 'Content-Type' : 'text/plain' } );
res.end( err.stack );
}
} else {
var body = showMessage
? err.toString()
: 'Internal Server Error';
res.setHeader( 'Content-Type', 'text/plain' );
res.end( body );
}
};
};
/**

View file

@ -1,4 +1,3 @@
/*!
* Connect - favicon
* Copyright(c) 2010 Sencha Inc.
@ -10,8 +9,8 @@
* Module dependencies.
*/
var fs = require('fs')
, utils = require('../utils');
var fs = require( 'fs' )
, utils = require( '../utils' );
/**
* Favicon cache.
@ -30,11 +29,11 @@ var icon;
* Examples:
*
* connect.createServer(
* connect.favicon()
* connect.favicon()
* );
*
* connect.createServer(
* connect.favicon(__dirname + '/public/favicon.ico')
* connect.favicon(__dirname + '/public/favicon.ico')
* );
*
* @param {String} path
@ -43,34 +42,33 @@ var icon;
* @api public
*/
module.exports = function favicon(path, options){
var options = options || {}
, path = path || __dirname + '/../public/favicon.ico'
, maxAge = options.maxAge || 86400000;
module.exports = function favicon( path, options ) {
var options = options || {}
, path = path || __dirname + '/../public/favicon.ico'
, maxAge = options.maxAge || 86400000;
return function favicon(req, res, next){
if ('/favicon.ico' == req.url) {
if (icon) {
res.writeHead(200, icon.headers);
res.end(icon.body);
} else {
fs.readFile(path, function(err, buf){
if (err) return next(err);
icon = {
headers: {
'Content-Type': 'image/x-icon'
, 'Content-Length': buf.length
, 'ETag': '"' + utils.md5(buf) + '"'
, 'Cache-Control': 'public, max-age=' + (maxAge / 1000)
},
body: buf
};
res.writeHead(200, icon.headers);
res.end(icon.body);
});
}
} else {
next();
}
};
return function favicon( req, res, next ) {
if ( '/favicon.ico' == req.url ) {
if ( icon ) {
res.writeHead( 200, icon.headers );
res.end( icon.body );
} else {
fs.readFile( path, function ( err, buf ) {
if ( err ) {
return next( err );
}
icon = {
headers : {
'Content-Type' : 'image/x-icon', 'Content-Length' : buf.length, 'ETag' : '"' + utils.md5( buf ) + '"', 'Cache-Control' : 'public, max-age=' + (maxAge / 1000)
},
body : buf
};
res.writeHead( 200, icon.headers );
res.end( icon.body );
} );
}
} else {
next();
}
};
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - limit
* Copyright(c) 2011 TJ Holowaychuk
@ -22,39 +21,47 @@
* @api public
*/
module.exports = function limit(bytes){
if ('string' == typeof bytes) bytes = parse(bytes);
if ('number' != typeof bytes) throw new Error('limit() bytes required');
return function limit(req, res, next){
var received = 0
, len = req.headers['content-length']
? parseInt(req.headers['content-length'], 10)
: null;
module.exports = function limit( bytes ) {
if ( 'string' == typeof bytes ) {
bytes = parse( bytes );
}
if ( 'number' != typeof bytes ) {
throw new Error( 'limit() bytes required' );
}
return function limit( req, res, next ) {
var received = 0
, len = req.headers['content-length']
? parseInt( req.headers['content-length'], 10 )
: null;
// deny the request
function deny() {
req.destroy();
}
// deny the request
function deny() {
req.destroy();
}
// self-awareness
if (req._limit) return next();
req._limit = true;
// self-awareness
if ( req._limit ) {
return next();
}
req._limit = true;
// limit by content-length
if (len && len > bytes) {
res.statusCode = 413;
res.end('Request Entity Too Large');
return;
}
// limit by content-length
if ( len && len > bytes ) {
res.statusCode = 413;
res.end( 'Request Entity Too Large' );
return;
}
// limit
req.on('data', function(chunk){
received += chunk.length;
if (received > bytes) deny();
});
// limit
req.on( 'data', function ( chunk ) {
received += chunk.length;
if ( received > bytes ) {
deny();
}
} );
next();
};
next();
};
};
/**
@ -65,16 +72,14 @@ module.exports = function limit(bytes){
* @api private
*/
function parse(size) {
var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/)
, n = parseFloat(parts[1])
, type = parts[2];
function parse( size ) {
var parts = size.match( /^(\d+(?:\.\d+)?) *(kb|mb|gb)$/ )
, n = parseFloat( parts[1] )
, type = parts[2];
var map = {
kb: 1024
, mb: 1024 * 1024
, gb: 1024 * 1024 * 1024
};
var map = {
kb : 1024, mb : 1024 * 1024, gb : 1024 * 1024 * 1024
};
return map[type] * n;
return map[type] * n;
}

View file

@ -1,4 +1,3 @@
/*!
* Connect - logger
* Copyright(c) 2010 Sencha Inc.
@ -80,80 +79,88 @@ var defaultBufferDuration = 1000;
* @api public
*/
exports = module.exports = function logger(options) {
if ('object' == typeof options) {
options = options || {};
} else if (options) {
options = { format: options };
} else {
options = {};
}
exports = module.exports = function logger( options ) {
if ( 'object' == typeof options ) {
options = options || {};
} else if ( options ) {
options = { format : options };
} else {
options = {};
}
// output on request instead of response
var immediate = options.immediate;
// output on request instead of response
var immediate = options.immediate;
// format name
var fmt = exports[options.format] || options.format || exports.default;
// format name
var fmt = exports[options.format] || options.format || exports.default;
// compile format
if ('function' != typeof fmt) fmt = compile(fmt);
// compile format
if ( 'function' != typeof fmt ) {
fmt = compile( fmt );
}
// options
var stream = options.stream || process.stdout
, buffer = options.buffer;
// options
var stream = options.stream || process.stdout
, buffer = options.buffer;
// buffering support
if (buffer) {
var realStream = stream
, interval = 'number' == typeof buffer
? buffer
: defaultBufferDuration;
// buffering support
if ( buffer ) {
var realStream = stream
, interval = 'number' == typeof buffer
? buffer
: defaultBufferDuration;
// flush interval
setInterval(function(){
if (buf.length) {
realStream.write(buf.join(''), 'ascii');
buf.length = 0;
}
}, interval);
// flush interval
setInterval( function () {
if ( buf.length ) {
realStream.write( buf.join( '' ), 'ascii' );
buf.length = 0;
}
}, interval );
// swap the stream
stream = {
write: function(str){
buf.push(str);
}
};
}
// swap the stream
stream = {
write : function ( str ) {
buf.push( str );
}
};
}
return function logger(req, res, next) {
req._startTime = new Date;
return function logger( req, res, next ) {
req._startTime = new Date;
// mount safety
if (req._logging) return next();
// mount safety
if ( req._logging ) {
return next();
}
// flag as logging
req._logging = true;
// flag as logging
req._logging = true;
// immediate
if (immediate) {
var line = fmt(exports, req, res);
if (null == line) return;
stream.write(line + '\n', 'ascii');
} else {
// proxy end to output loggging
var end = res.end;
res.end = function(chunk, encoding){
res.end = end;
res.end(chunk, encoding);
var line = fmt(exports, req, res);
if (null == line) return;
stream.write(line + '\n', 'ascii');
};
}
// immediate
if ( immediate ) {
var line = fmt( exports, req, res );
if ( null == line ) {
return;
}
stream.write( line + '\n', 'ascii' );
} else {
// proxy end to output loggging
var end = res.end;
res.end = function ( chunk, encoding ) {
res.end = end;
res.end( chunk, encoding );
var line = fmt( exports, req, res );
if ( null == line ) {
return;
}
stream.write( line + '\n', 'ascii' );
};
}
next();
};
next();
};
};
/**
@ -164,13 +171,14 @@ exports = module.exports = function logger(options) {
* @api private
*/
function compile(fmt) {
fmt = fmt.replace(/"/g, '\\"');
var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
}) + '";'
return new Function('tokens, req, res', js);
};
function compile( fmt ) {
fmt = fmt.replace( /"/g, '\\"' );
var js = ' return "' + fmt.replace( /:([-\w]{2,})(?:\[([^\]]+)\])?/g, function ( _, name, arg ) {
return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
} ) + '";'
return new Function( 'tokens, req, res', js );
}
;
/**
* Define a token function with the given `name`,
@ -182,9 +190,9 @@ function compile(fmt) {
* @api public
*/
exports.token = function(name, fn) {
exports[name] = fn;
return this;
exports.token = function ( name, fn ) {
exports[name] = fn;
return this;
};
/**
@ -196,104 +204,110 @@ exports.token = function(name, fn) {
* @api public
*/
exports.format = function(name, str){
exports[name] = str;
return this;
exports.format = function ( name, str ) {
exports[name] = str;
return this;
};
// default format
exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');
exports.format( 'default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' );
// short format
exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');
exports.format( 'short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' );
// tiny format
exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');
exports.format( 'tiny', ':method :url :status :res[content-length] - :response-time ms' );
// dev (colored)
exports.format('dev', function(tokens, req, res){
var status = res.statusCode
, color = 32;
exports.format( 'dev', function ( tokens, req, res ) {
var status = res.statusCode
, color = 32;
if (status >= 500) color = 31
else if (status >= 400) color = 33
else if (status >= 300) color = 36;
if ( status >= 500 ) {
color = 31
}
else if ( status >= 400 ) {
color = 33
}
else if ( status >= 300 ) {
color = 36;
}
return '\033[90m' + req.method
+ ' ' + req.originalUrl + ' '
+ '\033[' + color + 'm' + res.statusCode
+ ' \033[90m'
+ (new Date - req._startTime)
+ 'ms\033[0m';
});
return '\033[90m' + req.method
+ ' ' + req.originalUrl + ' '
+ '\033[' + color + 'm' + res.statusCode
+ ' \033[90m'
+ (new Date - req._startTime)
+ 'ms\033[0m';
} );
// request url
exports.token('url', function(req){
return req.originalUrl;
});
exports.token( 'url', function ( req ) {
return req.originalUrl;
} );
// request method
exports.token('method', function(req){
return req.method;
});
exports.token( 'method', function ( req ) {
return req.method;
} );
// response time in milliseconds
exports.token('response-time', function(req){
return new Date - req._startTime;
});
exports.token( 'response-time', function ( req ) {
return new Date - req._startTime;
} );
// UTC date
exports.token('date', function(){
return new Date().toUTCString();
});
exports.token( 'date', function () {
return new Date().toUTCString();
} );
// response status code
exports.token('status', function(req, res){
return res.statusCode;
});
exports.token( 'status', function ( req, res ) {
return res.statusCode;
} );
// normalized referrer
exports.token('referrer', function(req){
return req.headers['referer'] || req.headers['referrer'];
});
exports.token( 'referrer', function ( req ) {
return req.headers['referer'] || req.headers['referrer'];
} );
// remote address
exports.token('remote-addr', function(req){
return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress));
});
exports.token( 'remote-addr', function ( req ) {
return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress));
} );
// HTTP version
exports.token('http-version', function(req){
return req.httpVersionMajor + '.' + req.httpVersionMinor;
});
exports.token( 'http-version', function ( req ) {
return req.httpVersionMajor + '.' + req.httpVersionMinor;
} );
// UA string
exports.token('user-agent', function(req){
return req.headers['user-agent'];
});
exports.token( 'user-agent', function ( req ) {
return req.headers['user-agent'];
} );
// request header
exports.token('req', function(req, res, field){
return req.headers[field.toLowerCase()];
});
exports.token( 'req', function ( req, res, field ) {
return req.headers[field.toLowerCase()];
} );
// response header
exports.token('res', function(req, res, field){
return (res._headers || {})[field.toLowerCase()];
});
exports.token( 'res', function ( req, res, field ) {
return (res._headers || {})[field.toLowerCase()];
} );

View file

@ -1,4 +1,3 @@
/*!
* Connect - methodOverride
* Copyright(c) 2010 Sencha Inc.
@ -8,7 +7,7 @@
/**
* Provides faux HTTP method support.
*
*
* Pass an optional `key` to use when checking for
* a method override, othewise defaults to _\_method_.
* The original method is available via `req.originalMethod`.
@ -18,21 +17,21 @@
* @api public
*/
module.exports = function methodOverride(key){
key = key || "_method";
return function methodOverride(req, res, next) {
req.originalMethod = req.originalMethod || req.method;
module.exports = function methodOverride( key ) {
key = key || "_method";
return function methodOverride( req, res, next ) {
req.originalMethod = req.originalMethod || req.method;
// req.body
if (req.body && key in req.body) {
req.method = req.body[key].toUpperCase();
delete req.body[key];
// check X-HTTP-Method-Override
} else if (req.headers['x-http-method-override']) {
req.method = req.headers['x-http-method-override'].toUpperCase();
}
next();
};
// req.body
if ( req.body && key in req.body ) {
req.method = req.body[key].toUpperCase();
delete req.body[key];
// check X-HTTP-Method-Override
} else if ( req.headers['x-http-method-override'] ) {
req.method = req.headers['x-http-method-override'].toUpperCase();
}
next();
};
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - profiler
* Copyright(c) 2011 TJ Holowaychuk
@ -25,28 +24,27 @@
* @api public
*/
module.exports = function profiler(){
return function(req, res, next){
var end = res.end
, start = snapshot();
module.exports = function profiler() {
return function ( req, res, next ) {
var end = res.end
, start = snapshot();
// state snapshot
function snapshot() {
return {
mem: process.memoryUsage()
, time: new Date
};
}
// state snapshot
function snapshot() {
return {
mem : process.memoryUsage(), time : new Date
};
}
// proxy res.end()
res.end = function(data, encoding){
res.end = end;
res.end(data, encoding);
compare(req, start, snapshot())
};
// proxy res.end()
res.end = function ( data, encoding ) {
res.end = end;
res.end( data, encoding );
compare( req, start, snapshot() )
};
next();
}
next();
}
};
/**
@ -58,15 +56,15 @@ module.exports = function profiler(){
* @api private
*/
function compare(req, start, end) {
console.log();
row(req.method, req.url);
row('response time:', (end.time - start.time) + 'ms');
row('memory rss:', formatBytes(end.mem.rss - start.mem.rss));
row('memory vsize:', formatBytes(end.mem.vsize - start.mem.vsize));
row('heap before:', formatBytes(start.mem.heapUsed) + ' / ' + formatBytes(start.mem.heapTotal));
row('heap after:', formatBytes(end.mem.heapUsed) + ' / ' + formatBytes(end.mem.heapTotal));
console.log();
function compare( req, start, end ) {
console.log();
row( req.method, req.url );
row( 'response time:', (end.time - start.time) + 'ms' );
row( 'memory rss:', formatBytes( end.mem.rss - start.mem.rss ) );
row( 'memory vsize:', formatBytes( end.mem.vsize - start.mem.vsize ) );
row( 'heap before:', formatBytes( start.mem.heapUsed ) + ' / ' + formatBytes( start.mem.heapTotal ) );
row( 'heap after:', formatBytes( end.mem.heapUsed ) + ' / ' + formatBytes( end.mem.heapTotal ) );
console.log();
}
/**
@ -77,8 +75,8 @@ function compare(req, start, end) {
* @api private
*/
function row(key, val) {
console.log(' \033[90m%s\033[0m \033[36m%s\033[0m', key, val);
function row( key, val ) {
console.log( ' \033[90m%s\033[0m \033[36m%s\033[0m', key, val );
}
/**
@ -89,12 +87,19 @@ function row(key, val) {
* @api private
*/
function formatBytes(bytes) {
var kb = 1024
, mb = 1024 * kb
, gb = 1024 * mb;
if (bytes < kb) return bytes + 'b';
if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb';
if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb';
return (bytes / gb).toFixed(2) + 'gb';
};
function formatBytes( bytes ) {
var kb = 1024
, mb = 1024 * kb
, gb = 1024 * mb;
if ( bytes < kb ) {
return bytes + 'b';
}
if ( bytes < mb ) {
return (bytes / kb).toFixed( 2 ) + 'kb';
}
if ( bytes < gb ) {
return (bytes / mb).toFixed( 2 ) + 'mb';
}
return (bytes / gb).toFixed( 2 ) + 'gb';
}
;

View file

@ -1,4 +1,3 @@
/*!
* Connect - query
* Copyright(c) 2011 TJ Holowaychuk
@ -10,8 +9,8 @@
* Module dependencies.
*/
var qs = require('qs')
, parse = require('url').parse;
var qs = require( 'qs' )
, parse = require( 'url' ).parse;
/**
* Automatically parse the query-string when available,
@ -30,11 +29,11 @@ var qs = require('qs')
* @api public
*/
module.exports = function query(){
return function query(req, res, next){
req.query = ~req.url.indexOf('?')
? qs.parse(parse(req.url).query)
: {};
next();
};
module.exports = function query() {
return function query( req, res, next ) {
req.query = ~req.url.indexOf( '?' )
? qs.parse( parse( req.url ).query )
: {};
next();
};
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - responseTime
* Copyright(c) 2011 TJ Holowaychuk
@ -13,22 +12,24 @@
* @api public
*/
module.exports = function responseTime(){
return function(req, res, next){
var writeHead = res.writeHead
, start = new Date;
module.exports = function responseTime() {
return function ( req, res, next ) {
var writeHead = res.writeHead
, start = new Date;
if (res._responseTime) return next();
res._responseTime = true;
if ( res._responseTime ) {
return next();
}
res._responseTime = true;
// proxy writeHead to calculate duration
res.writeHead = function(status, headers){
var duration = new Date - start;
res.setHeader('X-Response-Time', duration + 'ms');
res.writeHead = writeHead;
res.writeHead(status, headers);
};
// proxy writeHead to calculate duration
res.writeHead = function ( status, headers ) {
var duration = new Date - start;
res.setHeader( 'X-Response-Time', duration + 'ms' );
res.writeHead = writeHead;
res.writeHead( status, headers );
};
next();
};
next();
};
};

View file

@ -1,4 +1,3 @@
/*!
* Connect - router
* Copyright(c) 2010 Sencha Inc.
@ -10,8 +9,8 @@
* Module dependencies.
*/
var utils = require('../utils')
, parse = require('url').parse;
var utils = require( '../utils' )
, parse = require( 'url' ).parse;
/**
* Expose router.
@ -24,24 +23,24 @@ exports = module.exports = router;
*/
var _methods = exports.methods = [
'get'
, 'post'
, 'put'
, 'delete'
, 'connect'
, 'options'
, 'trace'
, 'copy'
, 'lock'
, 'mkcol'
, 'move'
, 'propfind'
, 'proppatch'
, 'unlock'
, 'report'
, 'mkactivity'
, 'checkout'
, 'merge'
'get'
, 'post'
, 'put'
, 'delete'
, 'connect'
, 'options'
, 'trace'
, 'copy'
, 'lock'
, 'mkcol'
, 'move'
, 'propfind'
, 'proppatch'
, 'unlock'
, 'report'
, 'mkactivity'
, 'checkout'
, 'merge'
];
/**
@ -63,201 +62,205 @@ var _methods = exports.methods = [
* @api public
*/
function router(fn){
var self = this
, methods = {}
, routes = {}
, params = {};
function router( fn ) {
var self = this
, methods = {}
, routes = {}
, params = {};
if (!fn) throw new Error('router provider requires a callback function');
if ( !fn ) {
throw new Error( 'router provider requires a callback function' );
}
// Generate method functions
_methods.forEach(function(method){
methods[method] = generateMethodFunction(method.toUpperCase());
});
// Generate method functions
_methods.forEach( function ( method ) {
methods[method] = generateMethodFunction( method.toUpperCase() );
} );
// Alias del -> delete
methods.del = methods.delete;
// Alias del -> delete
methods.del = methods.delete;
// Apply callback to all methods
methods.all = function(){
var args = arguments;
_methods.forEach(function(name){
methods[name].apply(this, args);
});
return self;
};
// Apply callback to all methods
methods.all = function () {
var args = arguments;
_methods.forEach( function ( name ) {
methods[name].apply( this, args );
} );
return self;
};
// Register param callback
methods.param = function(name, fn){
params[name] = fn;
};
fn.call(this, methods);
// Register param callback
methods.param = function ( name, fn ) {
params[name] = fn;
};
function generateMethodFunction(name) {
var localRoutes = routes[name] = routes[name] || [];
return function(path, fn){
var keys = []
, middleware = [];
fn.call( this, methods );
// slice middleware
if (arguments.length > 2) {
middleware = Array.prototype.slice.call(arguments, 1, arguments.length);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
function generateMethodFunction( name ) {
var localRoutes = routes[name] = routes[name] || [];
return function ( path, fn ) {
var keys = []
, middleware = [];
fn.middleware = middleware;
// slice middleware
if ( arguments.length > 2 ) {
middleware = Array.prototype.slice.call( arguments, 1, arguments.length );
fn = middleware.pop();
middleware = utils.flatten( middleware );
}
if (!path) throw new Error(name + ' route requires a path');
if (!fn) throw new Error(name + ' route ' + path + ' requires a callback');
var regexp = path instanceof RegExp
? path
: normalizePath(path, keys);
localRoutes.push({
fn: fn
, path: regexp
, keys: keys
, orig: path
, method: name
});
return self;
};
}
fn.middleware = middleware;
function router(req, res, next){
var route
, self = this;
if ( !path ) {
throw new Error( name + ' route requires a path' );
}
if ( !fn ) {
throw new Error( name + ' route ' + path + ' requires a callback' );
}
var regexp = path instanceof RegExp
? path
: normalizePath( path, keys );
localRoutes.push( {
fn : fn, path : regexp, keys : keys, orig : path, method : name
} );
return self;
};
}
(function pass(i){
if (route = match(req, routes, i)) {
var i = 0
, keys = route.keys;
function router( req, res, next ) {
var route
, self = this;
req.params = route.params;
(function pass( i ) {
if ( route = match( req, routes, i ) ) {
var i = 0
, keys = route.keys;
// Param preconditions
(function param(err) {
try {
var key = keys[i++]
, val = req.params[key]
, fn = params[key];
req.params = route.params;
if ('route' == err) {
pass(req._route_index + 1);
// Error
} else if (err) {
next(err);
// Param has callback
} else if (fn) {
// Return style
if (1 == fn.length) {
req.params[key] = fn(val);
param();
// Middleware style
} else {
fn(req, res, param, val);
}
// Finished processing params
} else if (!key) {
// route middleware
i = 0;
(function nextMiddleware(err){
var fn = route.middleware[i++];
if ('route' == err) {
pass(req._route_index + 1);
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, nextMiddleware);
} else {
route.call(self, req, res, function(err){
if (err) {
next(err);
} else {
pass(req._route_index + 1);
}
});
}
})();
// More params
} else {
param();
}
} catch (err) {
next(err);
}
})();
} else if ('OPTIONS' == req.method) {
options(req, res, routes);
} else {
next();
}
})();
};
// Param preconditions
(function param( err ) {
try {
var key = keys[i++]
, val = req.params[key]
, fn = params[key];
router.remove = function(path, method){
var fns = router.lookup(path, method);
fns.forEach(function(fn){
routes[fn.method].splice(fn.index, 1);
});
};
if ( 'route' == err ) {
pass( req._route_index + 1 );
// Error
} else if ( err ) {
next( err );
// Param has callback
} else if ( fn ) {
// Return style
if ( 1 == fn.length ) {
req.params[key] = fn( val );
param();
// Middleware style
} else {
fn( req, res, param, val );
}
// Finished processing params
} else if ( !key ) {
// route middleware
i = 0;
(function nextMiddleware( err ) {
var fn = route.middleware[i++];
if ( 'route' == err ) {
pass( req._route_index + 1 );
} else if ( err ) {
next( err );
} else if ( fn ) {
fn( req, res, nextMiddleware );
} else {
route.call( self, req, res, function ( err ) {
if ( err ) {
next( err );
} else {
pass( req._route_index + 1 );
}
} );
}
})();
// More params
} else {
param();
}
} catch ( err ) {
next( err );
}
})();
} else if ( 'OPTIONS' == req.method ) {
options( req, res, routes );
} else {
next();
}
})();
}
router.lookup = function(path, method, ret){
ret = ret || [];
;
// method specific lookup
if (method) {
method = method.toUpperCase();
if (routes[method]) {
routes[method].forEach(function(route, i){
if (path == route.orig) {
var fn = route.fn;
fn.regexp = route.path;
fn.keys = route.keys;
fn.path = route.orig;
fn.method = route.method;
fn.index = i;
ret.push(fn);
}
});
}
// global lookup
} else {
_methods.forEach(function(method){
router.lookup(path, method, ret);
});
}
router.remove = function ( path, method ) {
var fns = router.lookup( path, method );
fns.forEach( function ( fn ) {
routes[fn.method].splice( fn.index, 1 );
} );
};
return ret;
};
router.lookup = function ( path, method, ret ) {
ret = ret || [];
router.match = function(url, method, ret){
var ret = ret || []
, i = 0
, fn
, req;
// method specific lookup
if ( method ) {
method = method.toUpperCase();
if ( routes[method] ) {
routes[method].forEach( function ( route, i ) {
if ( path == route.orig ) {
var fn = route.fn;
fn.regexp = route.path;
fn.keys = route.keys;
fn.path = route.orig;
fn.method = route.method;
fn.index = i;
ret.push( fn );
}
} );
}
// global lookup
} else {
_methods.forEach( function ( method ) {
router.lookup( path, method, ret );
} );
}
// method specific matches
if (method) {
method = method.toUpperCase();
req = { url: url, method: method };
while (fn = match(req, routes, i)) {
i = req._route_index + 1;
ret.push(fn);
}
// global matches
} else {
_methods.forEach(function(method){
router.match(url, method, ret);
});
}
return ret;
};
return ret;
};
router.match = function ( url, method, ret ) {
var ret = ret || []
, i = 0
, fn
, req;
return router;
// method specific matches
if ( method ) {
method = method.toUpperCase();
req = { url : url, method : method };
while ( fn = match( req, routes, i ) ) {
i = req._route_index + 1;
ret.push( fn );
}
// global matches
} else {
_methods.forEach( function ( method ) {
router.match( url, method, ret );
} );
}
return ret;
};
return router;
}
/**
@ -269,14 +272,13 @@ function router(fn){
* @api private
*/
function options(req, res, routes) {
var pathname = parse(req.url).pathname
, body = optionsFor(pathname, routes).join(',');
res.writeHead(200, {
'Content-Length': body.length
, 'Allow': body
});
res.end(body);
function options( req, res, routes ) {
var pathname = parse( req.url ).pathname
, body = optionsFor( pathname, routes ).join( ',' );
res.writeHead( 200, {
'Content-Length' : body.length, 'Allow' : body
} );
res.end( body );
}
/**
@ -288,15 +290,18 @@ function options(req, res, routes) {
* @api private
*/
function optionsFor(path, routes) {
return _methods.filter(function(method){
var arr = routes[method.toUpperCase()];
for (var i = 0, len = arr.length; i < len; ++i) {
if (arr[i].path.test(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
function optionsFor( path, routes ) {
return _methods.filter(
function ( method ) {
var arr = routes[method.toUpperCase()];
for ( var i = 0, len = arr.length; i < len; ++i ) {
if ( arr[i].path.test( path ) ) {
return true;
}
}
} ).map( function ( method ) {
return method.toUpperCase();
} );
}
/**
@ -314,23 +319,23 @@ function optionsFor(path, routes) {
* @api private
*/
function normalizePath(path, keys) {
path = path
.concat('/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
keys.push(key);
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || '([^/]+?)') + ')'
+ (optional || '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.+)');
return new RegExp('^' + path + '$', 'i');
function normalizePath( path, keys ) {
path = path
.concat( '/?' )
.replace( /\/\(/g, '(?:/' )
.replace( /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function ( _, slash, format, key, capture, optional ) {
keys.push( key );
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || '([^/]+?)') + ')'
+ (optional || '');
} )
.replace( /([\/.])/g, '\\$1' )
.replace( /\*/g, '(.+)' );
return new RegExp( '^' + path + '$', 'i' );
}
/**
@ -344,36 +349,38 @@ function normalizePath(path, keys) {
* @api private
*/
function match(req, routes, i) {
var captures
, method = req.method
, i = i || 0;
if ('HEAD' == method) method = 'GET';
if (routes = routes[method]) {
var url = parse(req.url)
, pathname = url.pathname;
for (var len = routes.length; i < len; ++i) {
var route = routes[i]
, fn = route.fn
, path = route.path
, keys = fn.keys = route.keys;
if (captures = path.exec(pathname)) {
fn.method = method;
fn.params = [];
for (var j = 1, len = captures.length; j < len; ++j) {
var key = keys[j-1],
val = typeof captures[j] === 'string'
? decodeURIComponent(captures[j])
: captures[j];
if (key) {
fn.params[key] = val;
} else {
fn.params.push(val);
}
}
req._route_index = i;
return fn;
}
}
}
function match( req, routes, i ) {
var captures
, method = req.method
, i = i || 0;
if ( 'HEAD' == method ) {
method = 'GET';
}
if ( routes = routes[method] ) {
var url = parse( req.url )
, pathname = url.pathname;
for ( var len = routes.length; i < len; ++i ) {
var route = routes[i]
, fn = route.fn
, path = route.path
, keys = fn.keys = route.keys;
if ( captures = path.exec( pathname ) ) {
fn.method = method;
fn.params = [];
for ( var j = 1, len = captures.length; j < len; ++j ) {
var key = keys[j - 1],
val = typeof captures[j] === 'string'
? decodeURIComponent( captures[j] )
: captures[j];
if ( key ) {
fn.params[key] = val;
} else {
fn.params.push( val );
}
}
req._route_index = i;
return fn;
}
}
}
}

View file

@ -1,4 +1,3 @@
/*!
* Connect - session
* Copyright(c) 2010 Sencha Inc.
@ -10,13 +9,13 @@
* Module dependencies.
*/
var Session = require('./session/session')
, MemoryStore = require('./session/memory')
, Cookie = require('./session/cookie')
, Store = require('./session/store')
, utils = require('./../utils')
, parse = require('url').parse
, crypto = require('crypto');
var Session = require( './session/session' )
, MemoryStore = require( './session/memory' )
, Cookie = require( './session/cookie' )
, Store = require( './session/store' )
, utils = require( './../utils' )
, parse = require( 'url' ).parse
, crypto = require( 'crypto' );
// environment
@ -42,16 +41,17 @@ exports.MemoryStore = MemoryStore;
*/
var warning = 'Warning: connection.session() MemoryStore is not\n'
+ 'designed for a production environment, as it will leak\n'
+ 'memory, and obviously only work within a single process.';
+ 'designed for a production environment, as it will leak\n'
+ 'memory, and obviously only work within a single process.';
/**
* Default finger-printing function.
*/
function defaultFingerprint(req) {
return '';
};
function defaultFingerprint( req ) {
return '';
}
;
/**
* Paths to ignore.
@ -93,7 +93,7 @@ exports.ignore = [];
* ## req.session
*
* To store or access session data, simply use the request property `req.session`,
* which is (generally) serialized as JSON by the store, so nested objects
* which is (generally) serialized as JSON by the store, so nested objects
* are typically fine. For example below is a user-specific view counter:
*
* connect(
@ -131,7 +131,7 @@ exports.ignore = [];
* req.session.destroy(function(err){
* // cannot access session here
* });
*
*
* ## Session#reload()
*
* Reloads the session data.
@ -199,147 +199,154 @@ exports.ignore = [];
* @api public
*/
function session(options){
var options = options || {}
, key = options.key || 'connect.sid'
, secret = options.secret
, store = options.store || new MemoryStore
, fingerprint = options.fingerprint || defaultFingerprint
, cookie = options.cookie;
function session( options ) {
var options = options || {}
, key = options.key || 'connect.sid'
, secret = options.secret
, store = options.store || new MemoryStore
, fingerprint = options.fingerprint || defaultFingerprint
, cookie = options.cookie;
// notify user that this store is not
// meant for a production environment
if ('production' == env && store instanceof MemoryStore) {
console.warn(warning);
}
// notify user that this store is not
// meant for a production environment
if ( 'production' == env && store instanceof MemoryStore ) {
console.warn( warning );
}
// ensure secret is present
if (!secret) {
throw new Error('connect.session({ secret: "string" }) required for security');
}
// ensure secret is present
if ( !secret ) {
throw new Error( 'connect.session({ secret: "string" }) required for security' );
}
// session hashing function
store.hash = function(req, base) {
return crypto
.createHmac('sha256', secret)
.update(base + fingerprint(req))
.digest('base64')
.replace(/=*$/, '');
};
// session hashing function
store.hash = function ( req, base ) {
return crypto
.createHmac( 'sha256', secret )
.update( base + fingerprint( req ) )
.digest( 'base64' )
.replace( /=*$/, '' );
};
// generates the new session
store.generate = function(req){
var base = utils.uid(24);
var sessionID = base + '.' + store.hash(req, base);
req.sessionID = sessionID;
req.session = new Session(req);
req.session.cookie = new Cookie(cookie);
};
// generates the new session
store.generate = function ( req ) {
var base = utils.uid( 24 );
var sessionID = base + '.' + store.hash( req, base );
req.sessionID = sessionID;
req.session = new Session( req );
req.session.cookie = new Cookie( cookie );
};
return function session(req, res, next) {
// self-awareness
if (req.session) return next();
return function session( req, res, next ) {
// self-awareness
if ( req.session ) {
return next();
}
// parse url
var url = parse(req.url)
, path = url.pathname;
// parse url
var url = parse( req.url )
, path = url.pathname;
// ignorable paths
if (~exports.ignore.indexOf(path)) return next();
// ignorable paths
if ( ~exports.ignore.indexOf( path ) ) {
return next();
}
// expose store
req.sessionStore = store;
// expose store
req.sessionStore = store;
// proxy writeHead() to Set-Cookie
var writeHead = res.writeHead;
res.writeHead = function(status, headers){
if (req.session) {
var cookie = req.session.cookie;
// only send secure session cookies when there is a secure connection.
// proxySecure is a custom attribute to allow for a reverse proxy
// to handle SSL connections and to communicate to connect over HTTP that
// the incoming connection is secure.
var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure);
if (secured || !cookie.secure) {
res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID));
}
}
// proxy writeHead() to Set-Cookie
var writeHead = res.writeHead;
res.writeHead = function ( status, headers ) {
if ( req.session ) {
var cookie = req.session.cookie;
// only send secure session cookies when there is a secure connection.
// proxySecure is a custom attribute to allow for a reverse proxy
// to handle SSL connections and to communicate to connect over HTTP that
// the incoming connection is secure.
var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure);
if ( secured || !cookie.secure ) {
res.setHeader( 'Set-Cookie', cookie.serialize( key, req.sessionID ) );
}
}
res.writeHead = writeHead;
return res.writeHead(status, headers);
};
res.writeHead = writeHead;
return res.writeHead( status, headers );
};
// proxy end() to commit the session
var end = res.end;
res.end = function(data, encoding){
res.end = end;
if (req.session) {
// HACK: ensure Set-Cookie for implicit writeHead()
if (!res._header) res._implicitHeader();
req.session.resetMaxAge();
req.session.save(function(){
res.end(data, encoding);
});
} else {
res.end(data, encoding);
}
};
// proxy end() to commit the session
var end = res.end;
res.end = function ( data, encoding ) {
res.end = end;
if ( req.session ) {
// HACK: ensure Set-Cookie for implicit writeHead()
if ( !res._header ) {
res._implicitHeader();
}
req.session.resetMaxAge();
req.session.save( function () {
res.end( data, encoding );
} );
} else {
res.end( data, encoding );
}
};
// session hashing
function hash(base) {
return store.hash(req, base);
}
// session hashing
function hash( base ) {
return store.hash( req, base );
}
// generate the session
function generate() {
store.generate(req);
}
// generate the session
function generate() {
store.generate( req );
}
// get the sessionID from the cookie
req.sessionID = req.cookies[key];
// get the sessionID from the cookie
req.sessionID = req.cookies[key];
// make a new session if the browser doesn't send a sessionID
if (!req.sessionID) {
generate();
next();
return;
}
// make a new session if the browser doesn't send a sessionID
if ( !req.sessionID ) {
generate();
next();
return;
}
// check the fingerprint
var parts = req.sessionID.split('.');
if (parts[1] != hash(parts[0])) {
generate();
next();
return;
}
// check the fingerprint
var parts = req.sessionID.split( '.' );
if ( parts[1] != hash( parts[0] ) ) {
generate();
next();
return;
}
// generate the session object
var pause = utils.pause(req);
store.get(req.sessionID, function(err, sess){
// proxy to resume() events
var _next = next;
next = function(err){
_next(err);
pause.resume();
}
// generate the session object
var pause = utils.pause( req );
store.get( req.sessionID, function ( err, sess ) {
// proxy to resume() events
var _next = next;
next = function ( err ) {
_next( err );
pause.resume();
}
// error handling
if (err) {
if ('ENOENT' == err.code) {
generate();
next();
} else {
next(err);
}
// no session
} else if (!sess) {
generate();
next();
// populate req.session
} else {
store.createSession(req, sess);
next();
}
});
};
};
// error handling
if ( err ) {
if ( 'ENOENT' == err.code ) {
generate();
next();
} else {
next( err );
}
// no session
} else if ( !sess ) {
generate();
next();
// populate req.session
} else {
store.createSession( req, sess );
next();
}
} );
};
}
;

View file

@ -1,4 +1,3 @@
/*!
* Connect - session - Cookie
* Copyright(c) 2010 Sencha Inc.
@ -10,7 +9,7 @@
* Module dependencies.
*/
var utils = require('../../utils');
var utils = require( '../../utils' );
/**
* Initialize a new `Cookie` with the given `options`.
@ -19,14 +18,16 @@ var utils = require('../../utils');
* @api private
*/
var Cookie = module.exports = function Cookie(options) {
this.path = '/';
this.httpOnly = true;
this.maxAge = 14400000;
if (options) utils.merge(this, options);
this.originalMaxAge = undefined == this.originalMaxAge
? this.maxAge
: this.originalMaxAge;
var Cookie = module.exports = function Cookie( options ) {
this.path = '/';
this.httpOnly = true;
this.maxAge = 14400000;
if ( options ) {
utils.merge( this, options );
}
this.originalMaxAge = undefined == this.originalMaxAge
? this.maxAge
: this.originalMaxAge;
};
/**
@ -35,92 +36,87 @@ var Cookie = module.exports = function Cookie(options) {
Cookie.prototype = {
/**
* Set expires `date`.
*
* @param {Date} date
* @api public
*/
set expires(date) {
this._expires = date;
this.originalMaxAge = this.maxAge;
},
/**
* Set expires `date`.
*
* @param {Date} date
* @api public
*/
/**
* Get expires `date`.
*
* @return {Date}
* @api public
*/
set expires( date ) {
this._expires = date;
this.originalMaxAge = this.maxAge;
},
get expires() {
return this._expires;
},
/**
* Set expires via max-age in `ms`.
*
* @param {Number} ms
* @api public
*/
set maxAge(ms) {
this.expires = 'number' == typeof ms
? new Date(Date.now() + ms)
: ms;
},
/**
* Get expires `date`.
*
* @return {Date}
* @api public
*/
/**
* Get expires max-age in `ms`.
*
* @return {Number}
* @api public
*/
get expires() {
return this._expires;
},
get maxAge() {
return this.expires instanceof Date
? this.expires.valueOf() - Date.now()
: this.expires;
},
/**
* Set expires via max-age in `ms`.
*
* @param {Number} ms
* @api public
*/
/**
* Return cookie data object.
*
* @return {Object}
* @api private
*/
set maxAge( ms ) {
this.expires = 'number' == typeof ms
? new Date( Date.now() + ms )
: ms;
},
get data() {
return {
originalMaxAge: this.originalMaxAge
, expires: this._expires
, secure: this.secure
, httpOnly: this.httpOnly
, domain: this.domain
, path: this.path
}
},
/**
* Get expires max-age in `ms`.
*
* @return {Number}
* @api public
*/
/**
* Return a serialized cookie string.
*
* @return {String}
* @api public
*/
get maxAge() {
return this.expires instanceof Date
? this.expires.valueOf() - Date.now()
: this.expires;
},
serialize: function(name, val){
return utils.serializeCookie(name, val, this.data);
},
/**
* Return cookie data object.
*
* @return {Object}
* @api private
*/
/**
* Return JSON representation of this cookie.
*
* @return {Object}
* @api private
*/
toJSON: function(){
return this.data;
}
get data() {
return {
originalMaxAge : this.originalMaxAge, expires : this._expires, secure : this.secure, httpOnly : this.httpOnly, domain : this.domain, path : this.path
}
},
/**
* Return a serialized cookie string.
*
* @return {String}
* @api public
*/
serialize : function ( name, val ) {
return utils.serializeCookie( name, val, this.data );
},
/**
* Return JSON representation of this cookie.
*
* @return {Object}
* @api private
*/
toJSON : function () {
return this.data;
}
};

Some files were not shown because too many files have changed in this diff Show more