faye 0.1.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +28 -0
- data/Jakefile +11 -0
- data/Manifest.txt +15 -17
- data/README.txt +149 -47
- data/build/faye-client-min.js +1 -0
- data/build/faye.js +868 -0
- data/examples/README.rdoc +41 -0
- data/examples/node/app.js +26 -0
- data/examples/node/faye-client-min.js +1 -0
- data/examples/node/faye.js +868 -0
- data/examples/rack/app.rb +6 -0
- data/examples/{soapbox → rack}/config.ru +1 -1
- data/examples/shared/public/favicon.ico +0 -0
- data/examples/{soapbox/views/index.erb → shared/public/index.html} +0 -0
- data/examples/{soapbox → shared}/public/jquery.js +0 -0
- data/examples/shared/public/robots.txt +0 -0
- data/examples/{soapbox → shared}/public/soapbox.js +0 -0
- data/examples/{soapbox → shared}/public/style.css +0 -0
- data/jake.yml +24 -9
- data/lib/faye-client-min.js +1 -0
- data/lib/faye.rb +2 -2
- data/lib/faye/rack_adapter.rb +44 -12
- data/lib/faye/server.rb +38 -31
- metadata +44 -23
- data/client/channel.js +0 -135
- data/client/client.js +0 -265
- data/client/faye.js +0 -95
- data/client/transport.js +0 -91
- data/client/util/class.js +0 -20
- data/client/util/event.js +0 -44
- data/client/util/json.js +0 -479
- data/client/util/uri.js +0 -69
- data/client/util/xhr.js +0 -99
- data/examples/soapbox/README +0 -22
- data/examples/soapbox/app.rb +0 -8
- data/lib/faye-min.js +0 -1
@@ -0,0 +1,41 @@
|
|
1
|
+
= Soapbox -- a Twitter-style chat app
|
2
|
+
|
3
|
+
Soapbox is a chat app written using Faye and jQuery, with functionality
|
4
|
+
modelled on that of Twitter. You can pick a username, type in some users
|
5
|
+
to follow, and enter messages. You receive messages from anyone you're
|
6
|
+
following, and any messages that mention you using the <tt>@username</tt>
|
7
|
+
syntax.
|
8
|
+
|
9
|
+
|
10
|
+
== Running the demo
|
11
|
+
|
12
|
+
There are two backends that handle message routing; one for Node.js
|
13
|
+
and one for Rack. To run the Node.js version:
|
14
|
+
|
15
|
+
node examples/node/app.js
|
16
|
+
|
17
|
+
To run the Rack version you need a few gems first:
|
18
|
+
|
19
|
+
sudo gem install eventmachine rack json sinatra
|
20
|
+
rackup -s mongrel examples/rack/config.ru
|
21
|
+
|
22
|
+
You can also use Thin to run the app as long as <tt>Rack::Lint</tt>
|
23
|
+
is not being used (it complains about the status codes Thin uses
|
24
|
+
for async responses).
|
25
|
+
|
26
|
+
rackup -s thin -E production examples/rack/config.ru
|
27
|
+
|
28
|
+
Note that Faye does not depend on Sinatra; the demo just uses it to
|
29
|
+
provide a concrete app behind the Faye middleware.
|
30
|
+
|
31
|
+
|
32
|
+
== Source code
|
33
|
+
|
34
|
+
There is no server-side logic for this app; the Faye server simply
|
35
|
+
handles message routing between clients and all subscriptions are set
|
36
|
+
up on the client side. Since this is a simple demo of client-side
|
37
|
+
messaging, your username and followees are not persisted across page
|
38
|
+
reloads. The source code for the client is at:
|
39
|
+
|
40
|
+
shared/public/soapbox.js
|
41
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
var posix = require('posix'),
|
2
|
+
path = require('path'),
|
3
|
+
sys = require('sys'),
|
4
|
+
http = require('http')
|
5
|
+
faye = require('./faye');
|
6
|
+
|
7
|
+
var PUBLIC_DIR = path.dirname(__filename) + '/../shared/public',
|
8
|
+
comet = new faye.NodeAdapter({mount: '/comet', timeout: 45}),
|
9
|
+
|
10
|
+
port = process.ARGV[2] || '8000';
|
11
|
+
|
12
|
+
sys.puts('Listening on ' + port);
|
13
|
+
|
14
|
+
http.createServer(function(request, response) {
|
15
|
+
sys.puts(request.method + ' ' + request.url);
|
16
|
+
if (comet.call(request, response)) return;
|
17
|
+
|
18
|
+
var path = (request.url === '/') ? '/index.html' : request.url;
|
19
|
+
|
20
|
+
posix.cat(PUBLIC_DIR + path).addCallback(function(content) {
|
21
|
+
response.sendHeader(200, {'Content-Type': 'text/html'});
|
22
|
+
response.sendBody(content);
|
23
|
+
response.finish();
|
24
|
+
});
|
25
|
+
}).listen(Number(port));
|
26
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(b.hasOwnProperty(d)&&a[d]!==b[d]){if(!a.hasOwnProperty(d)||c!==false)a[d]=b[d]}}return a};Faye.extend(Faye,{BAYEUX_VERSION:'1.0',VERSION:'0.2.2',JSONP_CALLBACK:'jsonpcallback',ID_LENGTH:128,CONNECTION_TYPES:["long-polling","callback-polling"],ENV:this,Grammar:{LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},size:function(a){var b=0;this.each(a,function(){b+=1});return b},random:function(){var a=Math.pow(2,this.ID_LENGTH);return(Math.random()*a).toString(16).replace(/0*$/,'')},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Observable={on:function(a,b,c){this._1=this._1||{};var d=this._1[a]=this._1[a]||[];d.push([b,c])},stopObserving:function(a,b,c){if(!this._1||!this._1[a])return;if(!b){delete this._1[a];return}var d=this._1[a],f=d.length;while(f--){if(b&&d[f][0]!==b)continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},fire:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._1||!this._1[c])return;Faye.each(this._1[c],function(a){a[0].apply(a[1],b.slice())})}};Faye.Channel=Faye.Class({initialize:function(a){this.__id=this._A=a},push:function(a){this.fire('message',a)}});Faye.extend(Faye.Channel.prototype,Faye.Observable);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Tree:Faye.Class({initialize:function(a){this._3=a;this._5={}},eachChild:function(c,d){Faye.each(this._5,function(a,b){c.call(d,a,b)})},each:function(c,d,f){this.eachChild(function(a,b){a=c.concat(a);b.each(a,d,f)});if(this._3!==undefined)d.call(f,c,this._3)},map:function(c,d){var f=[];this.each([],function(a,b){f.push(c.call(d,a,b))});return f},get:function(a){var b=this.traverse(a);return b?b._3:null},set:function(a,b){var c=this.traverse(a,true);if(c)c._3=b},traverse:function(a,b){if(typeof a==='string')a=Faye.Channel.parse(a);if(a===null)return null;if(a.length===0)return this;var c=this._5[a[0]];if(!c&&!b)return null;if(!c)c=this._5[a[0]]=new Faye.Channel.Tree();return c.traverse(a.slice(1),b)},findOrCreate:function(a){var b=this.get(a);if(b)return b;b=new Faye.Channel(a);this.set(a,b);return b},glob:function(f){if(typeof f==='string')f=Faye.Channel.parse(f);if(f===null)return[];if(f.length===0)return(this._3===undefined)?[]:[this._3];var g=[];if(Faye.enumEqual(f,['*'])){Faye.each(this._5,function(a,b){if(b._3!==undefined)g.push(b._3)});return g}if(Faye.enumEqual(f,['**'])){g=this.map(function(a,b){return b});g.pop();return g}Faye.each(this._5,function(b,c){if(b!==f[0]&&b!=='*')return;var d=c.glob(f.slice(1));Faye.each(d,function(a){g.push(a)})});if(this._5['**'])g.push(this._5['**']._3);return g}})});Faye.Event={_a:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._a.push({_b:a,_h:b,_s:c,_c:d,_m:f})},detach:function(a,b,c,d){var f=this._a.length,g;while(f--){g=this._a[f];if((a&&a!==g._b)||(b&&b!==g._h)||(c&&c!==g._s)||(d&&d!==g._c))continue;if(g._b.removeEventListener)g._b.removeEventListener(g._h,g._m,false);else g._b.detachEvent('on'+g._h,g._m);this._a.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){return this.protocol+this.hostname+':'+this.port+this.pathname+'?'+this.queryString()}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var k=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};k('protocol',/^https?\:\/+/);k('hostname',/^[^\/\:]+/);k('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),h=i.shift(),l=i.join('?'),n=l?l.split('&'):[],o=n.length,j={};while(o--){i=n[o].split('=');j[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}Faye.extend(j,f);g.pathname=h;g.params=j;return g}});Faye.XHR={request:function(a,b,c,d,f){var g=new this.Request(a,b,c,d,f);g.send();return g},getXhrObject:function(){return Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},Request:Faye.Class({initialize:function(a,b,c,d,f){this._d=a.toUpperCase();this._6=Faye.URI.parse(b,c);this._t=(typeof d==='function')?{success:d}:d;this._c=f||null;this._2=null},send:function(){if(this._i)return;var a=this._6.pathname,b=this._6.queryString();if(this._d==='GET')a+='?'+b;var c=this._d==='POST'?b:'';this._i=true;this._2=Faye.XHR.getXhrObject();this._2.open(this._d,a,true);if(this._d==='POST')this._2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');var d=this,f=function(){if(d._2.readyState!==4)return;if(g){clearInterval(g);g=null}Faye.Event.detach(Faye.ENV,'beforeunload',d.abort,d);d._i=false;d._u();d=null};var g=setInterval(f,10);Faye.Event.on(Faye.ENV,'beforeunload',this.abort,this);this._2.send(c)},abort:function(){this._2.abort()},_u:function(){var a=this._t;if(!a)return;return this.success()?a.success&&a.success.call(this._c,this):a.failure&&a.failure.call(this._c,this)},waiting:function(){return!!this._i},complete:function(){return this._2&&!this.waiting()},success:function(){if(!this.complete())return false;var a=this._2.status;return(a>=200&&a<300)||a===304||a===1223},failure:function(){if(!this.complete())return false;return!this.success()},text:function(){if(!this.complete())return null;return this._2.responseText},status:function(){if(!this.complete())return null;return this._2.status}})};Faye.Transport=Faye.extend(Faye.Class({initialize:function(a,b){this._n=a;this._6=b},send:function(c,d,f){c={message:JSON.stringify(c)};return this.request(c,function(b){if(!d)return;Faye.each([].concat(b),function(a){d.call(f,a);if(a.advice)this._n._v(a.advice);if(a.data&&a.channel)this._n._w(a)},this)},this)}}),{get:function(a,b){var c=a._6;if(b===undefined)b=this.supportedConnectionTypes();var d=Faye.URI.parse(c).isLocal()?['long-polling','callback-polling']:['callback-polling'];var f=Faye.commonElement(d,b);if(!f)throw'Could not find a usable connection type for '+c;var g=this._j[f];return new g(a,c)},register:function(a,b){this._j[a]=b;b.prototype.connectionType=a},_j:{},supportedConnectionTypes:function(){var c=[],d;Faye.each(this._j,function(a,b){c.push(a)});return c}});Faye.XHRTransport=Faye.Class(Faye.Transport,{request:function(b,c,d){Faye.XHR.request('post',this._6,b,function(a){if(c)c.call(d,JSON.parse(a.text()))})}});Faye.Transport.register('long-polling',Faye.XHRTransport);Faye.JSONPTransport=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c,d){var f=document.getElementsByTagName('head')[0],g=document.createElement('script'),k=Faye.JSONPTransport.getCallbackName(),i=Faye.URI.parse(this._6,b);Faye.ENV[k]=function(a){Faye.ENV[k]=undefined;try{delete Faye.ENV[k]}catch(e){}f.removeChild(g);if(c)c.call(d,a)};i.params.jsonp=k;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_o:0,getCallbackName:function(){this._o+=1;return'__jsonp'+this._o+'__'}});Faye.Transport.register('callback-polling',Faye.JSONPTransport);Faye.Client=Faye.Class({_e:{},_x:{},_8:{},_y:{},_p:'handshake',_z:'retry',_q:'none',DEFAULT_ENDPOINT:'/bayeux',MAX_DELAY:0.1,INTERVAL:1000.0,initialize:function(a){this._6=a||this.DEFAULT_ENDPOINT;this._4=Faye.Transport.get(this);this._0=this._e;this._k=[];this._f=new Faye.Channel.Tree();this._7={reconnect:this._z,interval:this.INTERVAL};Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},handshake:function(b,c){if(this._7.reconnect===this._q)return;if(this._0!==this._e)return;this._0=this._x;var d=this,f=this.generateId();this._4.send({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:Faye.Transport.supportedConnectionTypes(),id:f},function(a){if(a.id!==f)return;if(!a.successful){setTimeout(function(){d.handshake(b,c)},this._7.interval);return this._0=this._e}this._0=this._8;this._9=a.clientId;this._4=Faye.Transport.get(this,a.supportedConnectionTypes);if(b)b.call(c)},this)},connect:function(b,c){if(this._7.reconnect===this._q)return;if(this._7.reconnect===this._p||this._0===this._e)return this.handshake(function(){this.connect(b,c)},this);if(this._0!==this._8)return;if(this._g)return;this._g=this.generateId();var d=this;this._4.send({channel:Faye.Channel.CONNECT,clientId:this._9,connectionType:this._4.connectionType,id:this._g},function(a){if(a.id!==this._g)return;delete this._g;if(a.successful)this.connect();else setTimeout(function(){d.connect()},this._7.interval)},this);if(b)b.call(c)},disconnect:function(){if(this._0!==this._8)return;this._0=this._y;this._4.send({channel:Faye.Channel.DISCONNECT,clientId:this._9});this._f=new Faye.Channel.Tree()},subscribe:function(c,d,f){if(this._0!==this._8)return;c=[].concat(c);this._l(c);var g=this.generateId();this._4.send({channel:Faye.Channel.SUBSCRIBE,clientId:this._9,subscription:c,id:g},function(b){if(b.id!==g)return;if(!b.successful)return;c=[].concat(b.subscription);Faye.each(c,function(a){this._f.set(a,[d,f])},this)},this)},unsubscribe:function(c,d,f){if(this._0!==this._8)return;c=[].concat(c);this._l(c);var g=this.generateId();this._4.send({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._9,subscription:c,id:g},function(b){if(b.id!==g)return;if(!b.successful)return;c=[].concat(b.subscription);Faye.each(c,function(a){this._f.set(a,null)},this)},this)},publish:function(a,b){if(this._0!==this._8)return;this._l([a]);this.enqueue({channel:a,data:b,clientId:this._9});if(this._r)return;var c=this;this._r=setTimeout(function(){delete c._r;c.flush()},this.MAX_DELAY*1000)},generateId:function(a){a=a||32;return Math.floor(Math.pow(2,a)*Math.random()).toString(16)},enqueue:function(a){this._k.push(a)},flush:function(){this._4.send(this._k);this._k=[]},_l:function(b){Faye.each(b,function(a){if(!Faye.Channel.isValid(a))throw'"'+a+'" is not a valid channel name';if(!Faye.Channel.isSubscribable(a))throw'Clients may not subscribe to channel "'+a+'"';})},_v:function(a){Faye.extend(this._7,a);if(this._7.reconnect===this._p)this._9=null},_w:function(b){var c=this._f.glob(b.channel);Faye.each(c,function(a){if(!a)return;a[0].call(a[1],b.data)})}});if(!this.JSON){JSON={}}(function(){function l(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+l(this.getUTCMonth()+1)+'-'+l(this.getUTCDate())+'T'+l(this.getUTCHours())+':'+l(this.getUTCMinutes())+':'+l(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var n=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},m;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,k=j,i,h=b[a];if(h&&typeof h==='object'&&typeof h.toJSON==='function'){h=h.toJSON(a)}if(typeof m==='function'){h=m.call(b,a,h)}switch(typeof h){case'string':return r(h);case'number':return isFinite(h)?String(h):'null';case'boolean':case'null':return String(h);case'object':if(!h){return'null'}j+=p;i=[];if(Object.prototype.toString.apply(h)==='[object Array]'){g=h.length;for(c=0;c<g;c+=1){i[c]=q(c,h)||'null'}f=i.length===0?'[]':j?'[\n'+j+i.join(',\n'+j)+'\n'+k+']':'['+i.join(',')+']';j=k;return f}if(m&&typeof m==='object'){g=m.length;for(c=0;c<g;c+=1){d=m[c];if(typeof d==='string'){f=q(d,h);if(f){i.push(r(d)+(j?': ':':')+f)}}}}else{for(d in h){if(Object.hasOwnProperty.call(h,d)){f=q(d,h);if(f){i.push(r(d)+(j?': ':':')+f)}}}}f=i.length===0?'{}':j?'{\n'+j+i.join(',\n'+j)+'\n'+k+'}':'{'+i.join(',')+'}';j=k;return f}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(a,b,c){var d;j='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}m=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})}}if(typeof JSON.parse!=='function'){JSON.parse=function(g,k){var i;function h(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=h(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return k.call(a,b,f)}n.lastIndex=0;if(n.test(g)){g=g.replace(n,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof k==='function'?h({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());
|
@@ -0,0 +1,868 @@
|
|
1
|
+
if (!this.Faye) Faye = {};
|
2
|
+
|
3
|
+
Faye.extend = function(dest, source, overwrite) {
|
4
|
+
if (!source) return dest;
|
5
|
+
for (var key in source) {
|
6
|
+
if (source.hasOwnProperty(key) && dest[key] !== source[key]) {
|
7
|
+
if (!dest.hasOwnProperty(key) || overwrite !== false)
|
8
|
+
dest[key] = source[key];
|
9
|
+
}
|
10
|
+
}
|
11
|
+
return dest;
|
12
|
+
};
|
13
|
+
|
14
|
+
Faye.extend(Faye, {
|
15
|
+
BAYEUX_VERSION: '1.0',
|
16
|
+
VERSION: '0.2.2',
|
17
|
+
JSONP_CALLBACK: 'jsonpcallback',
|
18
|
+
ID_LENGTH: 128,
|
19
|
+
CONNECTION_TYPES: ["long-polling", "callback-polling"],
|
20
|
+
|
21
|
+
ENV: this,
|
22
|
+
|
23
|
+
Grammar: {
|
24
|
+
|
25
|
+
LOWALPHA: /^[a-z]$/,
|
26
|
+
|
27
|
+
UPALPHA: /^[A-Z]$/,
|
28
|
+
|
29
|
+
ALPHA: /^([a-z]|[A-Z])$/,
|
30
|
+
|
31
|
+
DIGIT: /^[0-9]$/,
|
32
|
+
|
33
|
+
ALPHANUM: /^(([a-z]|[A-Z])|[0-9])$/,
|
34
|
+
|
35
|
+
MARK: /^(\-|\_|\!|\~|\(|\)|\$|\@)$/,
|
36
|
+
|
37
|
+
STRING: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
38
|
+
|
39
|
+
TOKEN: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
40
|
+
|
41
|
+
INTEGER: /^([0-9])+$/,
|
42
|
+
|
43
|
+
CHANNEL_SEGMENT: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
44
|
+
|
45
|
+
CHANNEL_SEGMENTS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
46
|
+
|
47
|
+
CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
48
|
+
|
49
|
+
WILD_CARD: /^\*{1,2}$/,
|
50
|
+
|
51
|
+
CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
|
52
|
+
|
53
|
+
VERSION_ELEMENT: /^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,
|
54
|
+
|
55
|
+
VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,
|
56
|
+
|
57
|
+
CLIENT_ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
58
|
+
|
59
|
+
ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
60
|
+
|
61
|
+
ERROR_MESSAGE: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
62
|
+
|
63
|
+
ERROR_ARGS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,
|
64
|
+
|
65
|
+
ERROR_CODE: /^[0-9][0-9][0-9]$/,
|
66
|
+
|
67
|
+
ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/
|
68
|
+
|
69
|
+
},
|
70
|
+
|
71
|
+
commonElement: function(lista, listb) {
|
72
|
+
for (var i = 0, n = lista.length; i < n; i++) {
|
73
|
+
if (this.indexOf(listb, lista[i]) !== -1)
|
74
|
+
return lista[i];
|
75
|
+
}
|
76
|
+
return null;
|
77
|
+
},
|
78
|
+
|
79
|
+
indexOf: function(list, needle) {
|
80
|
+
for (var i = 0, n = list.length; i < n; i++) {
|
81
|
+
if (list[i] === needle) return i;
|
82
|
+
}
|
83
|
+
return -1;
|
84
|
+
},
|
85
|
+
|
86
|
+
each: function(object, callback, scope) {
|
87
|
+
if (object instanceof Array) {
|
88
|
+
for (var i = 0, n = object.length; i < n; i++) {
|
89
|
+
if (object[i] !== undefined)
|
90
|
+
callback.call(scope || null, object[i], i);
|
91
|
+
}
|
92
|
+
} else {
|
93
|
+
for (var key in object) {
|
94
|
+
if (object.hasOwnProperty(key))
|
95
|
+
callback.call(scope || null, key, object[key]);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
},
|
99
|
+
|
100
|
+
size: function(object) {
|
101
|
+
var size = 0;
|
102
|
+
this.each(object, function() { size += 1 });
|
103
|
+
return size;
|
104
|
+
},
|
105
|
+
|
106
|
+
random: function() {
|
107
|
+
var field = Math.pow(2, this.ID_LENGTH);
|
108
|
+
return (Math.random() * field).toString(16).replace(/0*$/, '');
|
109
|
+
},
|
110
|
+
|
111
|
+
enumEqual: function(actual, expected) {
|
112
|
+
if (expected instanceof Array) {
|
113
|
+
if (!(actual instanceof Array)) return false;
|
114
|
+
var i = actual.length;
|
115
|
+
if (i !== expected.length) return false;
|
116
|
+
while (i--) {
|
117
|
+
if (actual[i] !== expected[i]) return false;
|
118
|
+
}
|
119
|
+
return true;
|
120
|
+
} else {
|
121
|
+
if (!(actual instanceof Object)) return false;
|
122
|
+
if (this.size(expected) !== this.size(actual)) return false;
|
123
|
+
var result = true;
|
124
|
+
this.each(actual, function(key, value) {
|
125
|
+
result = result && (expected[key] === value);
|
126
|
+
});
|
127
|
+
return result;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
});
|
131
|
+
|
132
|
+
|
133
|
+
Faye.Class = function(parent, methods) {
|
134
|
+
if (typeof parent !== 'function') {
|
135
|
+
methods = parent;
|
136
|
+
parent = Object;
|
137
|
+
}
|
138
|
+
|
139
|
+
var klass = function() {
|
140
|
+
if (!this.initialize) return this;
|
141
|
+
return this.initialize.apply(this, arguments) || this;
|
142
|
+
};
|
143
|
+
|
144
|
+
var bridge = function() {};
|
145
|
+
bridge.prototype = parent.prototype;
|
146
|
+
|
147
|
+
klass.prototype = new bridge();
|
148
|
+
Faye.extend(klass.prototype, methods);
|
149
|
+
|
150
|
+
return klass;
|
151
|
+
};
|
152
|
+
|
153
|
+
|
154
|
+
Faye.Observable = {
|
155
|
+
on: function(eventType, block, scope) {
|
156
|
+
this._observers = this._observers || {};
|
157
|
+
var list = this._observers[eventType] = this._observers[eventType] || [];
|
158
|
+
list.push([block, scope]);
|
159
|
+
},
|
160
|
+
|
161
|
+
stopObserving: function(eventType, block, scope) {
|
162
|
+
if (!this._observers || !this._observers[eventType]) return;
|
163
|
+
|
164
|
+
if (!block) {
|
165
|
+
delete this._observers[eventType];
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
var list = this._observers[eventType],
|
169
|
+
i = list.length;
|
170
|
+
|
171
|
+
while (i--) {
|
172
|
+
if (block && list[i][0] !== block) continue;
|
173
|
+
if (scope && list[i][1] !== scope) continue;
|
174
|
+
list.splice(i,1);
|
175
|
+
}
|
176
|
+
},
|
177
|
+
|
178
|
+
fire: function() {
|
179
|
+
var args = Array.prototype.slice.call(arguments),
|
180
|
+
eventType = args.shift();
|
181
|
+
|
182
|
+
if (!this._observers || !this._observers[eventType]) return;
|
183
|
+
|
184
|
+
Faye.each(this._observers[eventType], function(listener) {
|
185
|
+
listener[0].apply(listener[1], args.slice());
|
186
|
+
});
|
187
|
+
}
|
188
|
+
};
|
189
|
+
|
190
|
+
|
191
|
+
Faye.Channel = Faye.Class({
|
192
|
+
initialize: function(name) {
|
193
|
+
this.__id = this._name = name;
|
194
|
+
},
|
195
|
+
|
196
|
+
push: function(message) {
|
197
|
+
this.fire('message', message);
|
198
|
+
}
|
199
|
+
});
|
200
|
+
|
201
|
+
Faye.extend(Faye.Channel.prototype, Faye.Observable);
|
202
|
+
|
203
|
+
Faye.extend(Faye.Channel, {
|
204
|
+
HANDSHAKE: '/meta/handshake',
|
205
|
+
CONNECT: '/meta/connect',
|
206
|
+
SUBSCRIBE: '/meta/subscribe',
|
207
|
+
UNSUBSCRIBE: '/meta/unsubscribe',
|
208
|
+
DISCONNECT: '/meta/disconnect',
|
209
|
+
|
210
|
+
META: 'meta',
|
211
|
+
SERVICE: 'service',
|
212
|
+
|
213
|
+
isValid: function(name) {
|
214
|
+
return Faye.Grammar.CHANNEL_NAME.test(name) ||
|
215
|
+
Faye.Grammar.CHANNEL_PATTERN.test(name);
|
216
|
+
},
|
217
|
+
|
218
|
+
parse: function(name) {
|
219
|
+
if (!this.isValid(name)) return null;
|
220
|
+
return name.split('/').slice(1);
|
221
|
+
},
|
222
|
+
|
223
|
+
isMeta: function(name) {
|
224
|
+
var segments = this.parse(name);
|
225
|
+
return segments ? (segments[0] === this.META) : null;
|
226
|
+
},
|
227
|
+
|
228
|
+
isService: function(name) {
|
229
|
+
var segments = this.parse(name);
|
230
|
+
return segments ? (segments[0] === this.SERVICE) : null;
|
231
|
+
},
|
232
|
+
|
233
|
+
isSubscribable: function(name) {
|
234
|
+
if (!this.isValid(name)) return null;
|
235
|
+
return !this.isMeta(name) && !this.isService(name);
|
236
|
+
},
|
237
|
+
|
238
|
+
Tree: Faye.Class({
|
239
|
+
initialize: function(value) {
|
240
|
+
this._value = value;
|
241
|
+
this._children = {};
|
242
|
+
},
|
243
|
+
|
244
|
+
eachChild: function(block, context) {
|
245
|
+
Faye.each(this._children, function(key, subtree) {
|
246
|
+
block.call(context, key, subtree);
|
247
|
+
});
|
248
|
+
},
|
249
|
+
|
250
|
+
each: function(prefix, block, context) {
|
251
|
+
this.eachChild(function(path, subtree) {
|
252
|
+
path = prefix.concat(path);
|
253
|
+
subtree.each(path, block, context);
|
254
|
+
});
|
255
|
+
if (this._value !== undefined) block.call(context, prefix, this._value);
|
256
|
+
},
|
257
|
+
|
258
|
+
map: function(block, context) {
|
259
|
+
var result = [];
|
260
|
+
this.each([], function(path, value) {
|
261
|
+
result.push(block.call(context, path, value));
|
262
|
+
});
|
263
|
+
return result;
|
264
|
+
},
|
265
|
+
|
266
|
+
get: function(name) {
|
267
|
+
var tree = this.traverse(name);
|
268
|
+
return tree ? tree._value : null;
|
269
|
+
},
|
270
|
+
|
271
|
+
set: function(name, value) {
|
272
|
+
var subtree = this.traverse(name, true);
|
273
|
+
if (subtree) subtree._value = value;
|
274
|
+
},
|
275
|
+
|
276
|
+
traverse: function(path, createIfAbsent) {
|
277
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
278
|
+
|
279
|
+
if (path === null) return null;
|
280
|
+
if (path.length === 0) return this;
|
281
|
+
|
282
|
+
var subtree = this._children[path[0]];
|
283
|
+
if (!subtree && !createIfAbsent) return null;
|
284
|
+
if (!subtree) subtree = this._children[path[0]] = new Faye.Channel.Tree();
|
285
|
+
|
286
|
+
return subtree.traverse(path.slice(1), createIfAbsent);
|
287
|
+
},
|
288
|
+
|
289
|
+
findOrCreate: function(channel) {
|
290
|
+
var existing = this.get(channel);
|
291
|
+
if (existing) return existing;
|
292
|
+
existing = new Faye.Channel(channel);
|
293
|
+
this.set(channel, existing);
|
294
|
+
return existing;
|
295
|
+
},
|
296
|
+
|
297
|
+
glob: function(path) {
|
298
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
299
|
+
|
300
|
+
if (path === null) return [];
|
301
|
+
if (path.length === 0) return (this._value === undefined) ? [] : [this._value];
|
302
|
+
|
303
|
+
var list = [];
|
304
|
+
|
305
|
+
if (Faye.enumEqual(path, ['*'])) {
|
306
|
+
Faye.each(this._children, function(key, subtree) {
|
307
|
+
if (subtree._value !== undefined) list.push(subtree._value);
|
308
|
+
});
|
309
|
+
return list;
|
310
|
+
}
|
311
|
+
|
312
|
+
if (Faye.enumEqual(path, ['**'])) {
|
313
|
+
list = this.map(function(key, value) { return value });
|
314
|
+
list.pop();
|
315
|
+
return list;
|
316
|
+
}
|
317
|
+
|
318
|
+
Faye.each(this._children, function(key, subtree) {
|
319
|
+
if (key !== path[0] && key !== '*') return;
|
320
|
+
var sublist = subtree.glob(path.slice(1));
|
321
|
+
Faye.each(sublist, function(channel) { list.push(channel) });
|
322
|
+
});
|
323
|
+
|
324
|
+
if (this._children['**']) list.push(this._children['**']._value);
|
325
|
+
return list;
|
326
|
+
}
|
327
|
+
|
328
|
+
/**
|
329
|
+
Tests
|
330
|
+
|
331
|
+
glob = new Faye.Channel.Tree();
|
332
|
+
list = '/foo/bar /foo/boo /foo /foobar /foo/bar/boo /foobar/boo /foo/* /foo/**'.split(' ');
|
333
|
+
|
334
|
+
Faye.each(list, function(c, i) {
|
335
|
+
glob.set(c, i + 1);
|
336
|
+
});
|
337
|
+
|
338
|
+
console.log(glob.glob('/foo/*').sort()); // 1,2,7,8
|
339
|
+
console.log(glob.glob('/foo/bar').sort()); // 1,7,8
|
340
|
+
console.log(glob.glob('/foo/**').sort()); // 1,2,5,7,8
|
341
|
+
console.log(glob.glob('/foo/bar/boo').sort()); // 5,8
|
342
|
+
**/
|
343
|
+
})
|
344
|
+
});
|
345
|
+
|
346
|
+
|
347
|
+
Faye.Set = Faye.Class({
|
348
|
+
initialize: function() {
|
349
|
+
this._index = {};
|
350
|
+
},
|
351
|
+
|
352
|
+
add: function(item) {
|
353
|
+
var key = (item.__id !== undefined) ? item.__id : item;
|
354
|
+
if (this._index.hasOwnProperty(key)) return false;
|
355
|
+
this._index[key] = item;
|
356
|
+
return true;
|
357
|
+
},
|
358
|
+
|
359
|
+
forEach: function(block, scope) {
|
360
|
+
for (var key in this._index) {
|
361
|
+
if (this._index.hasOwnProperty(key))
|
362
|
+
block.call(scope, this._index[key]);
|
363
|
+
}
|
364
|
+
},
|
365
|
+
|
366
|
+
isEmpty: function() {
|
367
|
+
for (var key in this._index) {
|
368
|
+
if (this._index.hasOwnProperty(key)) return false;
|
369
|
+
}
|
370
|
+
return true;
|
371
|
+
},
|
372
|
+
|
373
|
+
member: function(item) {
|
374
|
+
for (var key in this._index) {
|
375
|
+
if (this._index[key] === item) return true;
|
376
|
+
}
|
377
|
+
return false;
|
378
|
+
},
|
379
|
+
|
380
|
+
remove: function(item) {
|
381
|
+
var key = (item.__id !== undefined) ? item.__id : item;
|
382
|
+
delete this._index[key];
|
383
|
+
},
|
384
|
+
|
385
|
+
toArray: function() {
|
386
|
+
var array = [];
|
387
|
+
this.forEach(function(item) { array.push(item) });
|
388
|
+
return array;
|
389
|
+
}
|
390
|
+
});
|
391
|
+
|
392
|
+
|
393
|
+
Faye.Server = Faye.Class({
|
394
|
+
initialize: function(options) {
|
395
|
+
this._options = options || {};
|
396
|
+
this._channels = new Faye.Channel.Tree();
|
397
|
+
this._clients = {};
|
398
|
+
},
|
399
|
+
|
400
|
+
clientIds: function() {
|
401
|
+
var ids = [];
|
402
|
+
Faye.each(this._clients, function(key, value) { ids.push(key) });
|
403
|
+
return ids;
|
404
|
+
},
|
405
|
+
|
406
|
+
process: function(messages, local, callback) {
|
407
|
+
messages = (messages instanceof Array) ? messages : [messages];
|
408
|
+
var processed = 0, responses = [];
|
409
|
+
|
410
|
+
Faye.each(messages, function(message) {
|
411
|
+
this._handle(message, local, function(reply) {
|
412
|
+
responses = responses.concat(reply);
|
413
|
+
processed += 1;
|
414
|
+
if (processed === messages.length) callback(responses);
|
415
|
+
});
|
416
|
+
}, this);
|
417
|
+
},
|
418
|
+
|
419
|
+
flushConnection: function(messages) {
|
420
|
+
messages = (messages instanceof Array) ? messages : [messages];
|
421
|
+
Faye.each(messages, function(message) {
|
422
|
+
var client = this._clients[message.clientId];
|
423
|
+
if (client) client.flush();
|
424
|
+
}, this);
|
425
|
+
},
|
426
|
+
|
427
|
+
_generateId: function() {
|
428
|
+
var id = Faye.random();
|
429
|
+
while (this._clients.hasOwnProperty(id)) id = Faye.random();
|
430
|
+
return this._connection(id).id;
|
431
|
+
},
|
432
|
+
|
433
|
+
_connection: function(id) {
|
434
|
+
if (this._clients.hasOwnProperty(id)) return this._clients[id];
|
435
|
+
var client = new Faye.Connection(id, this._options);
|
436
|
+
client.on('stale', this._destroyClient, this);
|
437
|
+
return this._clients[id] = client;
|
438
|
+
},
|
439
|
+
|
440
|
+
_destroyClient: function(client) {
|
441
|
+
client.disconnect();
|
442
|
+
client.stopObserving('stale', this._destroyClient, this);
|
443
|
+
delete this._clients[client.id];
|
444
|
+
},
|
445
|
+
|
446
|
+
_handle: function(message, local, callback) {
|
447
|
+
var clientId = message.clientId,
|
448
|
+
channel = message.channel,
|
449
|
+
response;
|
450
|
+
|
451
|
+
if (Faye.Channel.isMeta(channel)) {
|
452
|
+
response = this[Faye.Channel.parse(channel)[1]](message, local);
|
453
|
+
|
454
|
+
clientId = clientId || response.clientId;
|
455
|
+
response.advice = response.advice || {};
|
456
|
+
Faye.extend(response.advice, {
|
457
|
+
reconnect: this._clients.hasOwnProperty(clientId) ? 'retry' : 'handshake',
|
458
|
+
interval: Faye.Connection.INTERVAL * 1000
|
459
|
+
}, false);
|
460
|
+
|
461
|
+
response.id = message.id;
|
462
|
+
|
463
|
+
if (response.channel !== Faye.Channel.CONNECT ||
|
464
|
+
response.successful !== true)
|
465
|
+
return callback(response);
|
466
|
+
|
467
|
+
return this._connection(response.clientId).connect(function(events) {
|
468
|
+
Faye.each(events, function(e) { delete e.__id });
|
469
|
+
callback([response].concat(events));
|
470
|
+
});
|
471
|
+
}
|
472
|
+
|
473
|
+
if (!message.clientId || Faye.Channel.isService(channel))
|
474
|
+
return callback([]);
|
475
|
+
|
476
|
+
message.__id = Faye.random();
|
477
|
+
Faye.each(this._channels.glob(channel), function(c) { c.push(message) });
|
478
|
+
|
479
|
+
callback( { channel: channel,
|
480
|
+
successful: true,
|
481
|
+
id: message.id } );
|
482
|
+
},
|
483
|
+
|
484
|
+
handshake: function(message, local) {
|
485
|
+
var response = { channel: Faye.Channel.HANDSHAKE,
|
486
|
+
version: Faye.BAYEUX_VERSION,
|
487
|
+
supportedConnectionTypes: Faye.CONNECTION_TYPES,
|
488
|
+
id: message.id };
|
489
|
+
|
490
|
+
if (!message.version)
|
491
|
+
response.error = Faye.Error.parameterMissing('version');
|
492
|
+
|
493
|
+
var clientConns = message.supportedConnectionTypes,
|
494
|
+
commonConns;
|
495
|
+
|
496
|
+
if (clientConns) {
|
497
|
+
commonConns = clientConns.filter(function(conn) {
|
498
|
+
return Faye.CONNECTION_TYPES.indexOf(conn) !== -1;
|
499
|
+
});
|
500
|
+
if (commonConns.length === 0)
|
501
|
+
response.error = Faye.Error.conntypeMismatch(clientConns);
|
502
|
+
} else {
|
503
|
+
response.error = Faye.Error.parameterMissing('supportedConnectionTypes');
|
504
|
+
}
|
505
|
+
|
506
|
+
response.successful = !response.error;
|
507
|
+
if (!response.successful) return response;
|
508
|
+
|
509
|
+
response.clientId = this._generateId();
|
510
|
+
return response;
|
511
|
+
},
|
512
|
+
|
513
|
+
connect: function(message, local) {
|
514
|
+
var response = { channel: Faye.Channel.CONNECT,
|
515
|
+
id: message.id };
|
516
|
+
|
517
|
+
var clientId = message.clientId,
|
518
|
+
client = clientId ? this._clients[clientId] : null,
|
519
|
+
connectionType = message.connectionType;
|
520
|
+
|
521
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
522
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
523
|
+
if (!connectionType) response.error = Faye.Error.parameterMissing('connectionType');
|
524
|
+
|
525
|
+
response.successful = !response.error;
|
526
|
+
if (!response.successful) return response;
|
527
|
+
|
528
|
+
response.clientId = client.id;
|
529
|
+
return response;
|
530
|
+
},
|
531
|
+
|
532
|
+
disconnect: function(message, local) {
|
533
|
+
var response = { channel: Faye.Channel.DISCONNECT,
|
534
|
+
id: message.id };
|
535
|
+
|
536
|
+
var clientId = message.clientId,
|
537
|
+
client = clientId ? this._clients[clientId] : null;
|
538
|
+
|
539
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
540
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
541
|
+
|
542
|
+
response.successful = !response.error;
|
543
|
+
if (!response.successful) return response;
|
544
|
+
|
545
|
+
this._destroyClient(client);
|
546
|
+
|
547
|
+
response.clientId = clientId;
|
548
|
+
return response;
|
549
|
+
},
|
550
|
+
|
551
|
+
subscribe: function(message, local) {
|
552
|
+
var response = { channel: Faye.Channel.SUBSCRIBE,
|
553
|
+
clientId: message.clientId,
|
554
|
+
id: message.id };
|
555
|
+
|
556
|
+
var clientId = message.clientId,
|
557
|
+
client = clientId ? this._clients[clientId] : null,
|
558
|
+
subscription = message.subscription;
|
559
|
+
|
560
|
+
subscription = (subscription instanceof Array) ? subscription : [subscription];
|
561
|
+
|
562
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
563
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
564
|
+
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
565
|
+
|
566
|
+
response.subscription = subscription;
|
567
|
+
|
568
|
+
Faye.each(subscription, function(channel) {
|
569
|
+
if (response.error) return;
|
570
|
+
if (!Faye.Channel.isSubscribable(channel)) response.error = Faye.Error.channelForbidden(channel);
|
571
|
+
if (!Faye.Channel.isValid(channel)) response.error = Faye.Error.channelInvalid(channel);
|
572
|
+
|
573
|
+
if (response.error) return;
|
574
|
+
channel = this._channels.findOrCreate(channel);
|
575
|
+
client.subscribe(channel);
|
576
|
+
}, this);
|
577
|
+
|
578
|
+
response.successful = !response.error;
|
579
|
+
return response;
|
580
|
+
},
|
581
|
+
|
582
|
+
unsubscribe: function(message, local) {
|
583
|
+
var response = { channel: Faye.Channel.UNSUBSCRIBE,
|
584
|
+
clientId: message.clientId,
|
585
|
+
id: message.id };
|
586
|
+
|
587
|
+
var clientId = message.clientId,
|
588
|
+
client = clientId ? this._clients[clientId] : null,
|
589
|
+
subscription = message.subscription;
|
590
|
+
|
591
|
+
subscription = (subscription instanceof Array) ? subscription : [subscription];
|
592
|
+
|
593
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
594
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
595
|
+
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
596
|
+
|
597
|
+
Faye.each(subscription, function(channel) {
|
598
|
+
if (response.error) return;
|
599
|
+
|
600
|
+
if (!Faye.Channel.isValid(channel))
|
601
|
+
return response.error = Faye.Error.channelInvalid(channel);
|
602
|
+
|
603
|
+
channel = this._channels.get(channel);
|
604
|
+
if (channel) client.unsubscribe(channel);
|
605
|
+
}, this);
|
606
|
+
|
607
|
+
response.successful = !response.error;
|
608
|
+
return response;
|
609
|
+
}
|
610
|
+
});
|
611
|
+
|
612
|
+
|
613
|
+
Faye.Connection = Faye.Class({
|
614
|
+
initialize: function(id, options) {
|
615
|
+
this.id = id;
|
616
|
+
this._options = options;
|
617
|
+
this._observers = {};
|
618
|
+
this._channels = new Faye.Set();
|
619
|
+
this._inbox = new Faye.Set();
|
620
|
+
},
|
621
|
+
|
622
|
+
timeout: function() {
|
623
|
+
return this._options.timeout || Faye.Connection.TIMEOUT;
|
624
|
+
},
|
625
|
+
|
626
|
+
_onMessage: function(event) {
|
627
|
+
this._inbox.add(event);
|
628
|
+
this._beginDeliveryTimeout();
|
629
|
+
},
|
630
|
+
|
631
|
+
subscribe: function(channel) {
|
632
|
+
if (!this._channels.add(channel)) return;
|
633
|
+
channel.on('message', this._onMessage, this);
|
634
|
+
},
|
635
|
+
|
636
|
+
unsubscribe: function(channel) {
|
637
|
+
if (channel === 'all') return this._channels.forEach(this.unsubscribe, this);
|
638
|
+
if (!this._channels.member(channel)) return;
|
639
|
+
this._channels.remove(channel);
|
640
|
+
channel.stopObserving('message', this._onMessage, this);
|
641
|
+
},
|
642
|
+
|
643
|
+
connect: function(callback) {
|
644
|
+
this.on('flush', callback);
|
645
|
+
if (this._connected) return;
|
646
|
+
|
647
|
+
this._markForDeletion = false;
|
648
|
+
this._connected = true;
|
649
|
+
|
650
|
+
if (!this._inbox.isEmpty()) this._beginDeliveryTimeout();
|
651
|
+
this._beginConnectionTimeout();
|
652
|
+
},
|
653
|
+
|
654
|
+
flush: function() {
|
655
|
+
if (!this._connected) return;
|
656
|
+
this._releaseConnection();
|
657
|
+
|
658
|
+
var events = this._inbox.toArray();
|
659
|
+
this._inbox = new Faye.Set();
|
660
|
+
|
661
|
+
this.fire('flush', events);
|
662
|
+
this.stopObserving('flush');
|
663
|
+
},
|
664
|
+
|
665
|
+
disconnect: function() {
|
666
|
+
this.unsubscribe('all');
|
667
|
+
this.flush();
|
668
|
+
},
|
669
|
+
|
670
|
+
_beginDeliveryTimeout: function() {
|
671
|
+
if (this._deliveryTimeout || !this._connected || this._inbox.isEmpty())
|
672
|
+
return;
|
673
|
+
|
674
|
+
var self = this;
|
675
|
+
this._deliveryTimeout = setTimeout(function () { self.flush() },
|
676
|
+
Faye.Connection.MAX_DELAY * 1000);
|
677
|
+
},
|
678
|
+
|
679
|
+
_beginConnectionTimeout: function() {
|
680
|
+
if (this._connectionTimeout || !this._connected)
|
681
|
+
return;
|
682
|
+
|
683
|
+
var self = this;
|
684
|
+
this._connectionTimeout = setTimeout(function() { self.flush() },
|
685
|
+
this.timeout() * 1000);
|
686
|
+
},
|
687
|
+
|
688
|
+
_releaseConnection: function() {
|
689
|
+
if (this._connectionTimeout) {
|
690
|
+
clearTimeout(this._connectionTimeout);
|
691
|
+
delete this._connectionTimeout;
|
692
|
+
}
|
693
|
+
|
694
|
+
if (this._deliveryTimeout) {
|
695
|
+
clearTimeout(this._deliveryTimeout);
|
696
|
+
delete this._deliveryTimeout;
|
697
|
+
}
|
698
|
+
|
699
|
+
this._connected = false;
|
700
|
+
this._scheduleForDeletion();
|
701
|
+
},
|
702
|
+
|
703
|
+
_scheduleForDeletion: function() {
|
704
|
+
if (this._markForDeletion) return;
|
705
|
+
this._markForDeletion = true;
|
706
|
+
var self = this;
|
707
|
+
|
708
|
+
setTimeout(function() {
|
709
|
+
if (!self._markForDeletion) return;
|
710
|
+
self.fire('stale', self);
|
711
|
+
}, 10000 * Faye.Connection.INTERVAL);
|
712
|
+
}
|
713
|
+
});
|
714
|
+
|
715
|
+
Faye.extend(Faye.Connection.prototype, Faye.Observable);
|
716
|
+
|
717
|
+
Faye.extend(Faye.Connection, {
|
718
|
+
MAX_DELAY: 0.1,
|
719
|
+
INTERVAL: 1.0,
|
720
|
+
TIMEOUT: 60.0
|
721
|
+
});
|
722
|
+
|
723
|
+
|
724
|
+
Faye.Error = Faye.Class({
|
725
|
+
initialize: function(code, args, message) {
|
726
|
+
this.code = code;
|
727
|
+
this.args = Array.prototype.slice.call(args);
|
728
|
+
this.message = message;
|
729
|
+
},
|
730
|
+
|
731
|
+
toString: function() {
|
732
|
+
return this.code + ':' +
|
733
|
+
this.args.join(',') + ':' +
|
734
|
+
this.message;
|
735
|
+
}
|
736
|
+
});
|
737
|
+
|
738
|
+
|
739
|
+
Faye.Error.versionMismatch = function() {
|
740
|
+
return new this(300, arguments, "Version mismatch").toString();
|
741
|
+
};
|
742
|
+
|
743
|
+
Faye.Error.conntypeMismatch = function() {
|
744
|
+
return new this(301, arguments, "Connection types not supported").toString();
|
745
|
+
};
|
746
|
+
|
747
|
+
Faye.Error.extMismatch = function() {
|
748
|
+
return new this(302, arguments, "Extension mismatch").toString();
|
749
|
+
};
|
750
|
+
|
751
|
+
Faye.Error.badRequest = function() {
|
752
|
+
return new this(400, arguments, "Bad request").toString();
|
753
|
+
};
|
754
|
+
|
755
|
+
Faye.Error.clientUnknown = function() {
|
756
|
+
return new this(401, arguments, "Unknown client").toString();
|
757
|
+
};
|
758
|
+
|
759
|
+
Faye.Error.parameterMissing = function() {
|
760
|
+
return new this(402, arguments, "Missing required parameter").toString();
|
761
|
+
};
|
762
|
+
|
763
|
+
Faye.Error.channelForbidden = function() {
|
764
|
+
return new this(403, arguments, "Forbidden channel").toString();
|
765
|
+
};
|
766
|
+
|
767
|
+
Faye.Error.channelUnknown = function() {
|
768
|
+
return new this(404, arguments, "Unknown channel").toString();
|
769
|
+
};
|
770
|
+
|
771
|
+
Faye.Error.channelInvalid = function() {
|
772
|
+
return new this(405, arguments, "Invalid channel").toString();
|
773
|
+
};
|
774
|
+
|
775
|
+
Faye.Error.extUnknown = function() {
|
776
|
+
return new this(406, arguments, "Unknown extension").toString();
|
777
|
+
};
|
778
|
+
|
779
|
+
Faye.Error.publishFailed = function() {
|
780
|
+
return new this(407, arguments, "Failed to publish").toString();
|
781
|
+
};
|
782
|
+
|
783
|
+
Faye.Error.serverError = function() {
|
784
|
+
return new this(500, arguments, "Internal server error").toString();
|
785
|
+
};
|
786
|
+
|
787
|
+
|
788
|
+
|
789
|
+
var path = require('path'),
|
790
|
+
posix = require('posix'),
|
791
|
+
sys = require('sys'),
|
792
|
+
url = require('url'),
|
793
|
+
querystring = require('querystring');
|
794
|
+
|
795
|
+
Faye.NodeAdapter = Faye.Class({
|
796
|
+
initialize: function(options) {
|
797
|
+
this._options = options || {};
|
798
|
+
this._endpoint = this._options.mount || Faye.NodeAdapter.DEFAULT_ENDPOINT;
|
799
|
+
this._script = this._endpoint + '.js';
|
800
|
+
this._server = new Faye.Server(this._options);
|
801
|
+
},
|
802
|
+
|
803
|
+
call: function(request, response) {
|
804
|
+
var requestUrl = url.parse(request.url, true);
|
805
|
+
switch (requestUrl.pathname) {
|
806
|
+
|
807
|
+
case this._endpoint:
|
808
|
+
var isGet = (request.method === 'GET'),
|
809
|
+
self = this;
|
810
|
+
|
811
|
+
if (isGet)
|
812
|
+
this._callWithParams(request, response, requestUrl.query);
|
813
|
+
|
814
|
+
else
|
815
|
+
request.addListener('body', function(chunk) {
|
816
|
+
self._callWithParams(request, response, querystring.parse(chunk));
|
817
|
+
});
|
818
|
+
|
819
|
+
return true;
|
820
|
+
break;
|
821
|
+
|
822
|
+
case this._script:
|
823
|
+
posix.cat(Faye.NodeAdapter.SCRIPT_PATH).addCallback(function(content) {
|
824
|
+
response.sendHeader(200, Faye.NodeAdapter.TYPE_SCRIPT);
|
825
|
+
response.sendBody(content);
|
826
|
+
response.finish();
|
827
|
+
});
|
828
|
+
return true;
|
829
|
+
break;
|
830
|
+
|
831
|
+
default: return false;
|
832
|
+
}
|
833
|
+
},
|
834
|
+
|
835
|
+
_callWithParams: function(request, response, params) {
|
836
|
+
try {
|
837
|
+
var message = JSON.parse(params.message),
|
838
|
+
jsonp = params.jsonp || Faye.JSONP_CALLBACK,
|
839
|
+
isGet = (request.method === 'GET'),
|
840
|
+
type = isGet ? Faye.NodeAdapter.TYPE_SCRIPT : Faye.NodeAdapter.TYPE_JSON;
|
841
|
+
|
842
|
+
if (isGet) this._server.flushConnection(message);
|
843
|
+
|
844
|
+
this._server.process(message, false, function(replies) {
|
845
|
+
var body = JSON.stringify(replies);
|
846
|
+
if (isGet) body = jsonp + '(' + body + ');';
|
847
|
+
response.sendHeader(200, type);
|
848
|
+
response.sendBody(body);
|
849
|
+
response.finish();
|
850
|
+
});
|
851
|
+
} catch (e) {
|
852
|
+
response.sendHeader(400, {'Content-Type': 'text/plain'});
|
853
|
+
response.sendBody('Bad request');
|
854
|
+
response.finish();
|
855
|
+
}
|
856
|
+
}
|
857
|
+
});
|
858
|
+
|
859
|
+
Faye.extend(Faye.NodeAdapter, {
|
860
|
+
DEFAULT_ENDPOINT: '/bayeux',
|
861
|
+
SCRIPT_PATH: path.dirname(__filename) + '/faye-client-min.js',
|
862
|
+
|
863
|
+
TYPE_JSON: {'Content-Type': 'text/json'},
|
864
|
+
TYPE_SCRIPT: {'Content-Type': 'text/javascript'},
|
865
|
+
TYPE_TEXT: {'Content-Type': 'text/plain'}
|
866
|
+
});
|
867
|
+
|
868
|
+
exports.NodeAdapter = Faye.NodeAdapter;
|