faye 0.5.5 → 0.6.0

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.

Files changed (62) hide show
  1. data/History.txt +14 -0
  2. data/README.rdoc +98 -0
  3. data/Rakefile +17 -15
  4. data/lib/faye-browser-min.js +1 -1
  5. data/lib/faye.rb +14 -5
  6. data/lib/faye/adapters/rack_adapter.rb +12 -5
  7. data/lib/faye/engines/base.rb +62 -0
  8. data/lib/faye/engines/connection.rb +63 -0
  9. data/lib/faye/engines/memory.rb +89 -0
  10. data/lib/faye/engines/redis.rb +141 -0
  11. data/lib/faye/error.rb +16 -4
  12. data/lib/faye/mixins/publisher.rb +6 -0
  13. data/lib/faye/protocol/channel.rb +34 -86
  14. data/lib/faye/protocol/client.rb +36 -52
  15. data/lib/faye/protocol/extensible.rb +3 -0
  16. data/lib/faye/protocol/server.rb +119 -169
  17. data/lib/faye/transport/http.rb +45 -0
  18. data/lib/faye/transport/local.rb +15 -0
  19. data/lib/faye/{network → transport}/transport.rb +36 -49
  20. data/spec/browser.html +35 -0
  21. data/spec/install.sh +48 -0
  22. data/spec/javascript/channel_spec.js +15 -0
  23. data/spec/javascript/client_spec.js +610 -0
  24. data/spec/javascript/engine_spec.js +319 -0
  25. data/spec/javascript/faye_spec.js +15 -0
  26. data/spec/javascript/grammar_spec.js +66 -0
  27. data/spec/javascript/node_adapter_spec.js +276 -0
  28. data/spec/javascript/server/connect_spec.js +168 -0
  29. data/spec/javascript/server/disconnect_spec.js +121 -0
  30. data/spec/javascript/server/extensions_spec.js +60 -0
  31. data/spec/javascript/server/handshake_spec.js +153 -0
  32. data/spec/javascript/server/subscribe_spec.js +245 -0
  33. data/spec/javascript/server/unsubscribe_spec.js +245 -0
  34. data/spec/javascript/server_spec.js +146 -0
  35. data/spec/javascript/transport_spec.js +130 -0
  36. data/spec/node.js +34 -0
  37. data/spec/ruby/channel_spec.rb +17 -0
  38. data/spec/ruby/client_spec.rb +615 -0
  39. data/spec/ruby/engine_spec.rb +312 -0
  40. data/spec/ruby/faye_spec.rb +14 -0
  41. data/spec/ruby/grammar_spec.rb +68 -0
  42. data/spec/ruby/rack_adapter_spec.rb +209 -0
  43. data/spec/ruby/server/connect_spec.rb +170 -0
  44. data/spec/ruby/server/disconnect_spec.rb +120 -0
  45. data/spec/ruby/server/extensions_spec.rb +69 -0
  46. data/spec/ruby/server/handshake_spec.rb +151 -0
  47. data/spec/ruby/server/subscribe_spec.rb +247 -0
  48. data/spec/ruby/server/unsubscribe_spec.rb +247 -0
  49. data/spec/ruby/server_spec.rb +138 -0
  50. data/spec/ruby/transport_spec.rb +128 -0
  51. data/spec/spec_helper.rb +5 -0
  52. data/spec/testswarm.pl +200 -0
  53. data/spec/thin_proxy.rb +36 -0
  54. metadata +119 -84
  55. data/Manifest.txt +0 -27
  56. data/README.txt +0 -98
  57. data/lib/faye/protocol/connection.rb +0 -111
  58. data/test/scenario.rb +0 -172
  59. data/test/test_channel.rb +0 -54
  60. data/test/test_clients.rb +0 -381
  61. data/test/test_grammar.rb +0 -86
  62. data/test/test_server.rb +0 -488
data/History.txt CHANGED
@@ -1,3 +1,17 @@
1
+ === 0.6.0 / 2011-05-21
2
+ TestSwarm build: http://swarm.jcoglan.com/job/27/
3
+
4
+ * Extract core logic into the Engine class to support swappable backends
5
+ * Introduce a Redis-backed engine to support clustered web front-ends
6
+ * Use CORS for cross-domain long-polling
7
+ * Make server more resilient against bad requests, including empty message lists
8
+ * Perform subscription validation on the server and use errbacks to signal errors
9
+ * Prohibit publishing to wildcard channels
10
+ * Unsubscribing from a channel is now O(1) instead of O(N)
11
+ * Much more thorough and consistent unit test coverage of both versions
12
+ * Automatic integration tests using Terminus and TestSwarm
13
+
14
+
1
15
  === 0.5.5 / 2011-01-16
2
16
 
3
17
  * Open a real socket to check for WebSocket usability, not just object detection
data/README.rdoc ADDED
@@ -0,0 +1,98 @@
1
+ = Faye
2
+
3
+ * http://faye.jcoglan.com
4
+ * http://groups.google.com/group/faye-users
5
+ * http://github.com/jcoglan/faye
6
+
7
+ Faye is a set of tools for dirt-simple publish-subscribe messaging between web
8
+ clients. It ships with easy-to-use message routing servers for Node.js and Rack
9
+ applications, and clients that can be used on the server and in the browser.
10
+
11
+ See http://faye.jcoglan.com for documentation.
12
+
13
+
14
+ == Questions, issues, ideas
15
+
16
+ Please raise questions on the mailing list: http://groups.google.com/group/faye-users,
17
+ and feel free to announce and share ideas on Faye-related projects here too. I'd
18
+ appreciate it if we only use the GitHub issue tracker for bona fide bugs; You'll
19
+ probably get better and quicker answers to questions from the mailing list.
20
+
21
+
22
+ == Development
23
+
24
+ To hack on Faye, you'll need Ruby in order to build both the Gem and the NPM
25
+ package. There are also a few submodules we use for testing. The following
26
+ should get you up and running:
27
+
28
+ # Download the code from Git
29
+ git clone git://github.com/jcoglan/faye.git
30
+ cd faye
31
+ git submodule update --init --recursive
32
+
33
+ # Install dependencies
34
+ gem install bundler
35
+ bundle install
36
+ npm install redis
37
+
38
+ # Build test tools and Faye itself
39
+ cd vendor/js.class
40
+ jake
41
+ cd ../../
42
+ jake
43
+
44
+ # Run tests
45
+ sudo redis-server &
46
+ rspec -c spec/
47
+ node spec/node.js
48
+
49
+ # Build Ruby gem
50
+ gem build faye.gemspec
51
+ gem install faye-0.6.0.gem
52
+
53
+ # Build NPM package
54
+ tar zcvf faye-0.6.0.tgz build/
55
+ npm install ./faye-0.6.0.tgz
56
+
57
+
58
+ == To-do
59
+
60
+ Notification API:
61
+
62
+ * Notify listeners of network drop-out (may be transport dependent)
63
+ * Provide reflection API for internal stats on channels, subscriptions, message queues
64
+ * (Maybe) build a monitoring GUI into the server
65
+
66
+ Sugar:
67
+
68
+ * Add sugar for authentication extensions for protected subscribe + publish
69
+
70
+ Missing protocol features:
71
+
72
+ * Provide support for user-defined <tt>/service/*</tt> channels
73
+ * Let local server-side clients listen to <tt>/meta/*</tt> channels
74
+
75
+
76
+ == License
77
+
78
+ (The MIT License)
79
+
80
+ Copyright (c) 2009-2011 James Coglan and contributors
81
+
82
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
83
+ this software and associated documentation files (the 'Software'), to deal in
84
+ the Software without restriction, including without limitation the rights to use,
85
+ copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
86
+ Software, and to permit persons to whom the Software is furnished to do so,
87
+ subject to the following conditions:
88
+
89
+ The above copyright notice and this permission notice shall be included in all
90
+ copies or substantial portions of the Software.
91
+
92
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
94
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
95
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
96
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
97
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
98
+
data/Rakefile CHANGED
@@ -1,19 +1,21 @@
1
- # -*- ruby -*-
2
-
3
1
  require 'rubygems'
4
- require 'hoe'
5
- require './lib/faye.rb'
2
+ require './lib/faye'
6
3
 
7
- Hoe.spec('faye') do
8
- self.developer('James Coglan', 'jcoglan@googlemail.com')
9
- self.description = 'Simple pub/sub messaging for the web'
10
- self.extra_deps = [
11
- ['eventmachine', '>= 0.12'],
12
- ['em-http-request', '>= 0.2'],
13
- ['rack', '>= 1.0'],
14
- ['thin', '>= 1.2'],
15
- ['json', '>= 1.0']
16
- ]
4
+ task :example, :port do |t, args|
5
+ system "rackup -s thin -E production -p #{args[:port]} examples/rack/config.ru"
17
6
  end
18
7
 
19
- # vim: syntax=Ruby
8
+ task :handshake, :port, :n, :c do |t, args|
9
+ require 'cgi'
10
+ require 'json'
11
+
12
+ message = {:channel => '/meta/handshake',
13
+ :version => '1.0',
14
+ :supportedConnectionTypes => ['long-polling']}
15
+
16
+ message = CGI.escape(JSON.dump message)
17
+ url = "http://127.0.0.1:#{args[:port]}/bayeux?jsonp=callback&message=#{message}"
18
+ puts "Request URL:\n#{url}\n\n"
19
+
20
+ system "ab -n #{args[:n]} -c #{args[:c]} '#{url}'"
21
+ end
@@ -1 +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))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.5.5',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','callback-polling','websocket'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a);return Math.floor(Math.random()*d).toString(36)},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])}}},map:function(a,b,c){var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},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}},asyncEach:function(a,b){var c=a.length,d=-1,f=0,g=false;var i=function(){f-=1;d+=1;if(d===c)return;b(a[d],h)};var j=function(){if(g)return;g=true;while(f>0)i();g=false};var h=function(){f+=1;j()};h()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),i=b.getMinutes(),j=b.getSeconds();var h=function(a){return a<10?'0'+a:String(a)};return h(c)+'-'+h(d)+'-'+h(f)+' '+h(g)+':'+h(i)+':'+h(j)}});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.Namespace=Faye.Class({initialize:function(){this._w={}},generate:function(){var a=Faye.random();while(this._w.hasOwnProperty(a))a=Faye.random();return this._w[a]=a}});Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._D==='succeeded')return a.apply(b,this._x);this._a=this._a||[];this._a.push([a,b])},setDeferredStatus:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();this._D=c;this._x=b;if(c!=='succeeded')return;if(!this._a)return;Faye.each(this._a,function(a){a[0].apply(a[1],this._x)},this);this._a=[]}};Faye.Publisher={countSubscribers:function(a){if(!this._2||!this._2[a])return 0;return this._2[a].length},addSubscriber:function(a,b,c){this._2=this._2||{};var d=this._2[a]=this._2[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._2||!this._2[a])return;var d=this._2[a],f=d.length;while(f--){if(b&&d[f][0]!==b)continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._2||!this._2[c])return;Faye.each(this._2[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._7=this._7||{};if(this._7.hasOwnProperty(a))return;var f=this;this._7[a]=setTimeout(function(){delete f._7[a];c.call(d)},1000*b)},removeTimeout:function(a){this._7=this._7||{};var b=this._7[a];if(!b)return;clearTimeout(b);delete this._7[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=null,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var i in Faye){if(f)continue;if(typeof Faye[i]!=='function')continue;if(this instanceof Faye[i])f=i}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.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])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._8=this._8||[];this._8.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._8)return;var b=this._8.length;while(b--){if(this._8[b]!==a)continue;this._8.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){if(!this._8)return f.call(g,d);var i=this._8.slice();var j=function(a){if(!a)return f.call(g,a);var b=i.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,j);else j(a)};j(d)}};Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);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,b){this._n=a;this._3=b;this._6={}},eachChild:function(c,d){Faye.each(this._6,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)},getKeys:function(){return this.map(function(a,b){return'/'+a.join('/')})},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},remove:function(a){if(a){var b=this.traverse(a);if(b)b.remove()}else{if(!this._n)return;this._n.removeChild(this);this._n=this._3=undefined}},removeChild:function(c){this.eachChild(function(a,b){if(b===c)delete this._6[a]},this);if(Faye.size(this._6)===0&&this._3===undefined)this.remove()},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._6[a[0]];if(!c&&!b)return null;if(!c)c=this._6[a[0]]=new Faye.Channel.Tree(this);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._6,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});if(this._3!==undefined)g.pop();return g}Faye.each(this._6,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._6['**'])g.push(this._6['**']._3);return g},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this.findOrCreate(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this.get(a);if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(b){var c=this.glob(b.channel);Faye.each(c,function(a){a.publishEvent('message',b.data)})}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._c=a;this._b=b;this._o=c;this._d=d;this._y=false},cancel:function(){if(this._y)return;this._c.unsubscribe(this._b,this._o,this._d);this._y=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',MAX_DELAY:0.001,INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this._4=b||this.DEFAULT_ENDPOINT;this._z=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._f=a},this);this._1=this.UNCONNECTED;this._i=[];this._b=new Faye.Channel.Tree();this._E=new Faye.Namespace();this._p={};this._9={reconnect:this.RETRY,interval:1000*(this._z.interval||this.INTERVAL),timeout:1000*(this._z.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},handshake:function(c,d){if(this._9.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this._4);this._e({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._f.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._f=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._b.getKeys());if(c)c.call(d)}else{this.info('Handshake unsuccessful');setTimeout(function(){f.handshake(c,d)},this._9.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._9.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._q)return;this._q=true;this.info('Initiating connection for ?',this._0);this._e({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._f.connectionType},this._A,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._e({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._b=new Faye.Channel.Tree()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);this._r(c);var g=new Faye.Subscription(this,c,d,f);this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._e({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);this._b.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);this._r(c);var g=this._b.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._e({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){this._r(a);this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._e({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._F(a.advice);var b=this._p[a.id];if(b){delete this._p[a.id];b[0].call(b[1],a)}this._G(a)},this)},_F:function(a){Faye.extend(this._9,a);if(this._9.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._A()}},_G:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._b.distributeMessage(a)},_H:function(){if(!this._q)return;this._q=null;this.info('Closed connection for ?',this._0)},_A:function(){this._H();var a=this;setTimeout(function(){a.connect()},this._9.interval)},_e:function(b,c,d){b.id=this._E.generate();if(c)this._p[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;if(a.channel===Faye.Channel.HANDSHAKE)return this._f.send(a,this._9.timeout/1000);this._i.push(a);if(a.channel===Faye.Channel.CONNECT)this._s=a;this.addTimeout('publish',this.MAX_DELAY,this._I,this)},this)},_I:function(){this.removeTimeout('publish');if(this._i.length>1&&this._s)this._s.advice={timeout:0};this._s=null;this._f.send(this._i,this._9.timeout/1000);this._i=[]},_r: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+'"';}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Timeouts);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._c=a;this._4=b},send:function(a,b){a=[].concat(a);this.debug('Client ? sending message to ?: ?',this._c._0,this._4,a);return this.request(a,b)},receive:function(a){this.debug('Client ? received from ?: ?',this._c._0,this._4,a);Faye.each(a,this._c.receiveMessage,this._c)},retry:function(a,b){var c=this;return function(){setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,i,j,h){var l=g._4;if(i===undefined)i=this.supportedConnectionTypes();Faye.asyncEach(this._t,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(i,d)<0)return c();f.isUsable(l,function(a){if(a)j.call(h,new f(g,l));else c()})},function(){throw'Could not find a usable connection type for '+l;})},register:function(a,b){this._t.push([a,b]);b.prototype.connectionType=a},_t:[],supportedConnectionTypes:function(){return Faye.map(this._t,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.Event={_j:[],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._j.push({_k:a,_u:b,_o:c,_d:d,_B:f})},detach:function(a,b,c,d){var f=this._j.length,g;while(f--){g=this._j[f];if((a&&a!==g._k)||(b&&b!==g._u)||(c&&c!==g._o)||(d&&d!==g._d))continue;if(g._k.removeEventListener)g._k.removeEventListener(g._u,g._B,false);else g._k.detachEvent('on'+g._u,g._B);this._j.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(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var i=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};i('protocol',/^https?\:\/+/);i('hostname',/^[^\/\:]+/);i('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 j=d.split('?'),h=j.shift(),l=j.join('?'),n=l?l.split('&'):[],o=n.length,k={};while(o--){j=n[o].split('=');k[decodeURIComponent(j[0]||'')]=decodeURIComponent(j[1]||'')}if(typeof f==='object')Faye.extend(k,f);g.pathname=h;g.params=k;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._l=a.toUpperCase();this._4=Faye.URI.parse(b,c);this._J=(typeof c==='string')?c:null;this._a=(typeof d==='function')?{success:d}:d;this._d=f||null;this._5=null},send:function(){if(this._v)return;var a=this._4.pathname,b=this._4.queryString();if(this._l==='GET')a+='?'+b;var c=(this._l==='POST')?(this._J||b):'';this._v=true;this._5=Faye.XHR.getXhrObject();this._5.open(this._l,a,true);if(this._l==='POST')this._5.setRequestHeader('Content-Type','application/json');var d=this,f=function(){if(d._5.readyState!==4)return;if(g){clearInterval(g);g=null}Faye.Event.detach(Faye.ENV,'beforeunload',d.abort,d);d._v=false;d._K();d=null};var g=setInterval(f,10);Faye.Event.on(Faye.ENV,'beforeunload',this.abort,this);this._5.send(c)},abort:function(){this._5.abort()},_K:function(){var a=this._a;if(!a)return;return this.success()?a.success&&a.success.call(this._d,this):a.failure&&a.failure.call(this._d,this)},waiting:function(){return!!this._v},complete:function(){return this._5&&!this.waiting()},success:function(){if(!this.complete())return false;var a=this._5.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._5.responseText},status:function(){if(!this.complete())return null;return this._5.status}})};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,k,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,i=k,j,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'}k+=p;j=[];if(Object.prototype.toString.apply(h)==='[object Array]'){g=h.length;for(c=0;c<g;c+=1){j[c]=q(c,h)||'null'}f=j.length===0?'[]':k?'[\n'+k+j.join(',\n'+k)+'\n'+i+']':'['+j.join(',')+']';k=i;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){j.push(r(d)+(k?': ':':')+f)}}}}else{for(d in h){if(Object.hasOwnProperty.call(h,d)){f=q(d,h);if(f){j.push(r(d)+(k?': ':':')+f)}}}}f=j.length===0?'{}':k?'{\n'+k+j.join(',\n'+k)+'\n'+i+'}':'{'+j.join(',')+'}';k=i;return f}}Faye.stringify=function(a,b,c){var d;k='';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.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,i){var j;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 i.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,''))){j=eval('('+g+')');return typeof i==='function'?h({'':j},''):j}throw new SyntaxError('JSON.parse');}}}());Faye.WebSocketTransport=Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,request:function(b,c){this._g=this._g||c;this._m=this._m||{};Faye.each(b,function(a){this._m[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;this._h=new WebSocket(Faye.WebSocketTransport.getSocketUrl(this._4));var d=this;this._h.onopen=function(){delete d._g;d._1=d.CONNECTED;d.setDeferredStatus('succeeded',d._h)};this._h.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete d._m[a.id]});d.receive(c)};this._h.onclose=function(){var a=(d._1===d.CONNECTED);d.setDeferredStatus('deferred');d._1=d.UNCONNECTED;delete d._h;if(a)return d.resend();setTimeout(function(){d.connect()},1000*d._g);d._g=d._g*2}},resend:function(){var c=Faye.map(this._m,function(a,b){return b});this.request(c)}});Faye.WebSocketTransport.getSocketUrl=function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')};Faye.extend(Faye.WebSocketTransport.prototype,Faye.Deferrable);Faye.WebSocketTransport.isUsable=function(a,b,c){if(!Faye.ENV.WebSocket)return b.call(c,false);var d=false,f=this.getSocketUrl(a),g=new WebSocket(f);g.onopen=function(){d=true;g.close();b.call(c,true);g=null};var i=function(){if(!d)b.call(c,false)};g.onclose=g.onerror=i;setTimeout(i,this.WEBSOCKET_TIMEOUT)};Faye.WebSocketTransport.WEBSOCKET_TIMEOUT=1000;Faye.Transport.register('websocket',Faye.WebSocketTransport);Faye.XHRTransport=Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c);Faye.XHR.request('post',this._4,Faye.toJSON(b),{success:function(a){try{this.receive(JSON.parse(a.text()))}catch(e){d()}},failure:d},this)}});Faye.XHRTransport.isUsable=function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())};Faye.Transport.register('long-polling',Faye.XHRTransport);Faye.JSONPTransport=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),i=Faye.JSONPTransport.getCallbackName(),j=Faye.URI.parse(this._4,d),h=this;var l=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[i]=function(a){Faye.ENV[i]=undefined;try{delete Faye.ENV[i]}catch(e){}if(!l())return;h.receive(a)};setTimeout(function(){if(!Faye.ENV[i])return;l();h.request(b,2*c)},1000*c);j.params.jsonp=i;g.type='text/javascript';g.src=j.toURL();f.appendChild(g)}}),{_C:0,getCallbackName:function(){this._C+=1;return'__jsonp'+this._C+'__'}});Faye.JSONPTransport.isUsable=function(a,b,c){b.call(c,true)};Faye.Transport.register('callback-polling',Faye.JSONPTransport);
1
+ if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(!b.hasOwnProperty(d))continue;if(a.hasOwnProperty(d)&&c===false)continue;if(a[d]!==b[d])a[d]=b[d]}return a};Faye.extend(Faye,{VERSION:'0.6.0',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','in-process'],MANDATORY_CONNECTION_TYPES:['long-polling','callback-polling','in-process'],ENV:(function(){return this})(),random:function(a){a=a||this.ID_LENGTH;if(a>32){var b=Math.ceil(a/32),c='';while(b--)c+=this.random(32);return c}var d=Math.pow(2,a)-1,f=d.toString(36).length,c=Math.floor(Math.random()*d).toString(36);while(c.length<f)c='0'+c;return c},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])}}},map:function(a,b,c){var d=[];this.each(a,function(){d.push(b.apply(c||null,arguments))});return d},filter:function(a,b,c){var d=[];this.each(a,function(){if(b.apply(c,arguments))d.push(arguments[0])});return d},size:function(a){var b=0;this.each(a,function(){b+=1});return b},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}},asyncEach:function(a,b,c,d){var f=a.length,g=-1,h=0,j=false;var i=function(){h-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(j)return;j=true;while(h>0)i();j=false};var m=function(){h+=1;k()};m()},toJSON:function(a){if(this.stringify)return this.stringify(a,function(key,value){return(this[key]instanceof Array)?this[key]:value});return JSON.stringify(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes(),j=b.getSeconds();var i=function(a){return a<10?'0'+a:String(a)};return i(c)+'-'+i(d)+'-'+i(f)+' '+i(g)+':'+i(h)+':'+i(j)}});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.Namespace=Faye.Class({initialize:function(){this._r={}},generate:function(){var a=Faye.random();while(this._r.hasOwnProperty(a))a=Faye.random();return this._r[a]=a}});Faye.Error=Faye.Class({initialize:function(a,b,c){this.code=a;this.params=Array.prototype.slice.call(b);this.message=c},toString:function(){return this.code+':'+this.params.join(',')+':'+this.message}});Faye.Error.parse=function(a){a=a||'';if(!Faye.Grammar.ERROR.test(a))return new this(null,[],a);var b=a.split(':'),c=parseInt(b[0]),d=b[1].split(','),a=b[2];return new this(c,d,a)};Faye.Error.versionMismatch=function(){return new this(300,arguments,"Version mismatch").toString()};Faye.Error.conntypeMismatch=function(){return new this(301,arguments,"Connection types not supported").toString()};Faye.Error.extMismatch=function(){return new this(302,arguments,"Extension mismatch").toString()};Faye.Error.badRequest=function(){return new this(400,arguments,"Bad request").toString()};Faye.Error.clientUnknown=function(){return new this(401,arguments,"Unknown client").toString()};Faye.Error.parameterMissing=function(){return new this(402,arguments,"Missing required parameter").toString()};Faye.Error.channelForbidden=function(){return new this(403,arguments,"Forbidden channel").toString()};Faye.Error.channelUnknown=function(){return new this(404,arguments,"Unknown channel").toString()};Faye.Error.channelInvalid=function(){return new this(405,arguments,"Invalid channel").toString()};Faye.Error.extUnknown=function(){return new this(406,arguments,"Unknown extension").toString()};Faye.Error.publishFailed=function(){return new this(407,arguments,"Failed to publish").toString()};Faye.Error.serverError=function(){return new this(500,arguments,"Internal server error").toString()};Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._s==='succeeded')return a.apply(b,this._h);this._i=this._i||[];this._i.push([a,b])},errback:function(a,b){if(!a)return;if(this._s==='failed')return a.apply(b,this._h);this._j=this._j||[];this._j.push([a,b])},setDeferredStatus:function(){var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._s=b;this._h=a;if(b==='succeeded')c=this._i;else if(b==='failed')c=this._j;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._h)}};Faye.Publisher={countSubscribers:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},addSubscriber:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},removeSubscriber:function(a,b,c){if(!this._3||!this._3[a])return;if(!b){delete this._3[a];return}var d=this._3[a],f=d.length;while(f--){if(b!==d[f][0])continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},removeSubscribers:function(){this._3={}},publishEvent:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._3||!this._3[c])return;Faye.each(this._3[c],function(a){a[0].apply(a[1],b)})}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._4=this._4||{};if(this._4.hasOwnProperty(a))return;var f=this;this._4[a]=Faye.ENV.setTimeout(function(){delete f._4[a];c.call(d)},1000*b)},removeTimeout:function(a){this._4=this._4||{};var b=this._4[a];if(!b)return;clearTimeout(b);delete this._4[a]}};Faye.Logging={LOG_LEVELS:{error:3,warn:2,info:1,debug:0},logLevel:'error',log:function(a,b){if(!Faye.logger)return;var c=Faye.Logging.LOG_LEVELS;if(c[Faye.Logging.logLevel]>c[b])return;var a=Array.prototype.slice.apply(a),d=' ['+b.toUpperCase()+'] [Faye',f=this.className,g=a.shift().replace(/\?/g,function(){try{return Faye.toJSON(a.shift())}catch(e){return'[Object]'}});for(var h in Faye){if(f)continue;if(typeof Faye[h]!=='function')continue;if(this instanceof Faye[h])f=h}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};Faye.each(Faye.Logging.LOG_LEVELS,function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}});Faye.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])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/};Faye.Extensible={addExtension:function(a){this._5=this._5||[];this._5.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._5)return;var b=this._5.length;while(b--){if(this._5[b]!==a)continue;this._5.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._5)return f.call(g,d);var h=this._5.slice();var j=function(a){if(!a)return f.call(g,a);var b=h.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,j);else j(a)};j(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)},isUnused:function(){return this.countSubscribers('message')===0}});Faye.extend(Faye.Channel.prototype,Faye.Publisher);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',expand:function(a){var b=this.parse(a),c=['/**',a];var d=b.slice();d[d.length-1]='*';c.push(this.unparse(d));for(var f=1,g=b.length;f<g;f++){d=b.slice(0,f);d.push('**');c.push(this.unparse(d))}return c},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)},unparse:function(a){return'/'+a.join('/')},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)},Set:Faye.Class({initialize:function(){this._2={}},getKeys:function(){var c=[];Faye.each(this._2,function(a,b){c.push(a)});return c},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(c,d,f){if(!d)return;Faye.each(c,function(a){var b=this._2[a]=this._2[a]||new Faye.Channel(a);b.addSubscriber('message',d,f)},this)},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.removeSubscriber('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(c){var d=Faye.Channel.expand(c.channel);Faye.each(d,function(a){var b=this._2[a];if(b)b.publishEvent('message',c.data)},this)}})});Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._8=a;this._2=b;this._k=c;this._l=d;this._t=false},cancel:function(){if(this._t)return;this._8.unsubscribe(this._2,this._k,this._l);this._t=true},unsubscribe:function(){this.cancel()}});Faye.extend(Faye.Subscription.prototype,Faye.Deferrable);Faye.Client=Faye.Class({UNCONNECTED:1,CONNECTING:2,CONNECTED:3,DISCONNECTED:4,HANDSHAKE:'handshake',RETRY:'retry',NONE:'none',CONNECTION_TIMEOUT:60.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(b,c){this.info('New client created for ?',b);this.endpoint=b||this.DEFAULT_ENDPOINT;this._u=c||{};Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES,function(a){this._c=a},this);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._y=new Faye.Namespace();this._m={};this._6={reconnect:this.RETRY,interval:1000*(this._u.interval||this.INTERVAL),timeout:1000*(this._u.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},getClientId:function(){return this._0},getState:function(){switch(this._1){case this.UNCONNECTED:return'UNCONNECTED';case this.CONNECTING:return'CONNECTING';case this.CONNECTED:return'CONNECTED';case this.DISCONNECTED:return'DISCONNECTED'}},handshake:function(c,d){if(this._6.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._c.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;Faye.Transport.get(this,b.supportedConnectionTypes,function(a){this._c=a},this);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(c)c.call(d)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){f.handshake(c,d)},this._6.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._6.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._n)return;this._n=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._c.connectionType},this._v,this)},disconnect:function(){if(this._1!==this.CONNECTED)return;this._1=this.DISCONNECTED;this.info('Disconnecting ?',this._0);this._9({channel:Faye.Channel.DISCONNECT,clientId:this._0});this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.subscribe(channel,d,f)},this);var g=new Faye.Subscription(this,c,d,f);var h=(d===true);if(!h&&this._2.hasSubscription(c)){this._2.subscribe([c],d,f);g.setDeferredStatus('succeeded');return g}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return g.setDeferredStatus('failed',Faye.Error.parse(a.error));var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);this._2.subscribe(b,d,f);g.setDeferredStatus('succeeded')},this)},this);return g},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);var g=this._2.unsubscribe(c,d,f);if(!g)return;this.connect(function(){this.info('Client ? attempting to unsubscribe from ?',this._0,c);this._9({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful)return;var b=[].concat(a.subscription);this.info('Unsubscription acknowledged for ? from ?',this._0,b)},this)},this)},publish:function(a,b){if(!Faye.Grammar.CHANNEL_NAME.test(a))throw new Error("Cannot publish: '"+a+"' is not a valid channel name");this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,a,b);this._9({channel:a,data:b,clientId:this._0})},this)},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._z(a.advice);var b=this._m[a.id];if(b){delete this._m[a.id];b[0].call(b[1],a)}this._A(a)},this)},_9:function(b,c,d){b.id=this._y.generate();if(c)this._m[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._c.send(a,this._6.timeout/1000)},this)},_z:function(a){Faye.extend(this._6,a);if(this._6.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._v()}},_A:function(a){if(!a.channel||!a.data)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_B:function(){if(!this._n)return;this._n=null;this.info('Closed connection for ?',this._0)},_v:function(){this._B();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._6.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Logging);Faye.extend(Faye.Client.prototype,Faye.Extensible);Faye.Transport=Faye.extend(Faye.Class({MAX_DELAY:0.0,batching:true,initialize:function(a,b){this.debug('Created new ? transport for ?',this.connectionType,b);this._8=a;this._a=b;this._d=[]},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._8._0,this._a,a);if(!this.batching)return this.request([a],b);this._d.push(a);this._7=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._o=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._d.length>1&&this._o)this._o.advice={timeout:0};this.request(this._d,this._7);this._o=null;this._d=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._8._0,this._a,a);Faye.each(a,this._8.receiveMessage,this._8)},retry:function(a,b){var c=this;return function(){Faye.ENV.setTimeout(function(){c.request(a,2*b)},1000*b)}}}),{get:function(g,h,j,i){var k=g.endpoint;if(h===undefined)h=this.supportedConnectionTypes();Faye.asyncEach(this._p,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(h,d)<0)return c();f.isUsable(k,function(a){if(a)j.call(i,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},register:function(a,b){this._p.push([a,b]);b.prototype.connectionType=a},_p:[],supportedConnectionTypes:function(){return Faye.map(this._p,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_e:[],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._e.push({_f:a,_q:b,_k:c,_l:d,_w:f})},detach:function(a,b,c,d){var f=this._e.length,g;while(f--){g=this._e[f];if((a&&a!==g._f)||(b&&b!==g._q)||(c&&c!==g._k)||(d&&d!==g._l))continue;if(g._f.removeEventListener)g._f.removeEventListener(g._q,g._w,false);else g._f.detachEvent('on'+g._q,g._w);this._e.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(){var a=this.queryString();return this.protocol+this.hostname+':'+this.port+this.pathname+(a?'?'+a:'')}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var h=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};h('protocol',/^https?\:\/+/);h('hostname',/^[^\/\:]+/);h('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 j=d.split('?'),i=j.shift(),k=j.join('?'),m=k?k.split('&'):[],o=m.length,l={};while(o--){j=m[o].split('=');l[decodeURIComponent(j[0]||'')]=decodeURIComponent(j[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=i;g.params=l;return g}});if(!this.JSON){JSON={}}(function(){function k(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+k(this.getUTCMonth()+1)+'-'+k(this.getUTCDate())+'T'+k(this.getUTCHours())+':'+k(this.getUTCMinutes())+':'+k(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var m=/[\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,l,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},n;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,h=l,j,i=b[a];if(i&&typeof i==='object'&&typeof i.toJSON==='function'){i=i.toJSON(a)}if(typeof n==='function'){i=n.call(b,a,i)}switch(typeof i){case'string':return r(i);case'number':return isFinite(i)?String(i):'null';case'boolean':case'null':return String(i);case'object':if(!i){return'null'}l+=p;j=[];if(Object.prototype.toString.apply(i)==='[object Array]'){g=i.length;for(c=0;c<g;c+=1){j[c]=q(c,i)||'null'}f=j.length===0?'[]':l?'[\n'+l+j.join(',\n'+l)+'\n'+h+']':'['+j.join(',')+']';l=h;return f}if(n&&typeof n==='object'){g=n.length;for(c=0;c<g;c+=1){d=n[c];if(typeof d==='string'){f=q(d,i);if(f){j.push(r(d)+(l?': ':':')+f)}}}}else{for(d in i){if(Object.hasOwnProperty.call(i,d)){f=q(d,i);if(f){j.push(r(d)+(l?': ':':')+f)}}}}f=j.length===0?'{}':l?'{\n'+l+j.join(',\n'+l)+'\n'+h+'}':'{'+j.join(',')+'}';l=h;return f}}Faye.stringify=function(a,b,c){var d;l='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}n=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})};if(typeof JSON.stringify!=='function'){JSON.stringify=Faye.stringify}if(typeof JSON.parse!=='function'){JSON.parse=function(g,h){var j;function i(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=i(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return h.call(a,b,f)}m.lastIndex=0;if(m.test(g)){g=g.replace(m,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,''))){j=eval('('+g+')');return typeof h==='function'?i({'':j},''):j}throw new SyntaxError('JSON.parse');}}}());Faye.Transport.WebSocket=Faye.extend(Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:false,request:function(b,c){this._7=this._7||c;this._g=this._g||{};Faye.each(b,function(a){this._g[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},connect:function(){this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;this._b=new WebSocket(Faye.Transport.WebSocket.getSocketUrl(this._a));var d=this;this._b.onopen=function(){delete d._7;d._1=d.CONNECTED;d.setDeferredStatus('succeeded',d._b)};this._b.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete d._g[a.id]});d.receive(c)};this._b.onclose=function(){var a=(d._1===d.CONNECTED);d.setDeferredStatus('deferred');d._1=d.UNCONNECTED;delete d._b;if(a)return d.resend();Faye.ENV.setTimeout(function(){d.connect()},1000*d._7);d._7=d._7*2}},resend:function(){var c=Faye.map(this._g,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){return Faye.URI.parse(a).toURL().replace(/^http(s?):/ig,'ws$1:')},isUsable:function(a,b,c){if(!Faye.ENV.WebSocket)return b.call(c,false);var d=false,f=this.getSocketUrl(a),g=new WebSocket(f);g.onopen=function(){d=true;g.close();b.call(c,true);g=null};var h=function(){if(!d)b.call(c,false)};g.onclose=g.onerror=h;Faye.ENV.setTimeout(h,this.WEBSOCKET_TIMEOUT)}});Faye.extend(Faye.Transport.WebSocket.prototype,Faye.Deferrable);Faye.Transport.register('websocket',Faye.Transport.WebSocket);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d=this.retry(b,c),f=Faye.URI.parse(this._a).pathname,g=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',f,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');h.onreadystatechange=function(){if(h.readyState!==4)return;var a=h.status;try{if((a>=200&&a<300)||a===304||a===1223)g.receive(JSON.parse(h.responseText));else d()}catch(e){d()}finally{Faye.Event.detach(Faye.ENV,'beforeunload',j);h.onreadystatechange=function(){};h=null}};var j=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',j);h.send(Faye.toJSON(b))}}),{isUsable:function(a,b,c){b.call(c,Faye.URI.parse(a).isLocal())}});Faye.Transport.register('long-polling',Faye.Transport.XHR);Faye.Transport.CORS=Faye.extend(Faye.Class(Faye.Transport,{request:function(a,b){var c=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,d=new c(),f=this.retry(a,b),g=this;d.open('POST',this._a,true);d.onload=function(){try{g.receive(JSON.parse(d.responseText))}catch(e){f()}finally{d.onload=d.onerror=null;d=null}};d.onerror=f;d.onprogress=function(){};d.send('message='+encodeURIComponent(Faye.toJSON(a)))}}),{isUsable:function(a,b,c){if(Faye.URI.parse(a).isLocal())return b.call(c,false);if(Faye.ENV.XDomainRequest)return b.call(c,true);if(Faye.ENV.XMLHttpRequest){var d=new Faye.ENV.XMLHttpRequest();return b.call(c,d.withCredentials!==undefined)}return b.call(c,false)}});Faye.Transport.register('cross-origin-long-polling',Faye.Transport.CORS);Faye.Transport.JSONP=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c){var d={message:Faye.toJSON(b)},f=document.getElementsByTagName('head')[0],g=document.createElement('script'),h=Faye.Transport.JSONP.getCallbackName(),j=Faye.URI.parse(this._a,d),i=this;var k=function(){if(!g.parentNode)return false;g.parentNode.removeChild(g);return true};Faye.ENV[h]=function(a){Faye.ENV[h]=undefined;try{delete Faye.ENV[h]}catch(e){}if(!k())return;i.receive(a)};Faye.ENV.setTimeout(function(){if(!Faye.ENV[h])return;k();i.request(b,2*c)},1000*c);j.params.jsonp=h;g.type='text/javascript';g.src=j.toURL();f.appendChild(g)}}),{_x:0,getCallbackName:function(){this._x+=1;return'__jsonp'+this._x+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
data/lib/faye.rb CHANGED
@@ -5,14 +5,14 @@ require 'eventmachine'
5
5
  require 'json'
6
6
 
7
7
  module Faye
8
- VERSION = '0.5.5'
8
+ VERSION = '0.6.0'
9
9
 
10
10
  ROOT = File.expand_path(File.dirname(__FILE__))
11
11
 
12
12
  BAYEUX_VERSION = '1.0'
13
13
  ID_LENGTH = 128
14
14
  JSONP_CALLBACK = 'jsonpcallback'
15
- CONNECTION_TYPES = %w[long-polling callback-polling websocket]
15
+ CONNECTION_TYPES = %w[long-polling cross-origin-long-polling callback-polling websocket in-process]
16
16
 
17
17
  MANDATORY_CONNECTION_TYPES = %w[long-polling callback-polling in-process]
18
18
 
@@ -20,14 +20,19 @@ module Faye
20
20
  mixins/timeouts
21
21
  mixins/logging
22
22
  util/namespace
23
+ engines/base
24
+ engines/connection
25
+ engines/memory
26
+ engines/redis
23
27
  protocol/grammar
24
28
  protocol/extensible
25
29
  protocol/channel
26
30
  protocol/subscription
27
31
  protocol/client
28
32
  protocol/server
29
- protocol/connection
30
- network/transport
33
+ transport/transport
34
+ transport/local
35
+ transport/http
31
36
  error
32
37
 
33
38
  ].each do |lib|
@@ -38,7 +43,11 @@ module Faye
38
43
  autoload :WebSocket, File.join(ROOT, 'faye', 'util', 'web_socket')
39
44
 
40
45
  def self.random(bitlength = ID_LENGTH)
41
- rand(2 ** bitlength).to_s(36)
46
+ limit = 2 ** bitlength - 1
47
+ max_size = limit.to_s(36).size
48
+ string = rand(limit).to_s(36)
49
+ string = '0' + string while string.size < max_size
50
+ string
42
51
  end
43
52
 
44
53
  def self.to_json(value)
@@ -7,6 +7,8 @@ require Faye::ROOT + '/thin_extensions'
7
7
  module Faye
8
8
  class RackAdapter
9
9
 
10
+ include Logging
11
+
10
12
  # Only supported under Thin
11
13
  ASYNC_RESPONSE = [-1, {}, []].freeze
12
14
 
@@ -51,7 +53,6 @@ module Faye
51
53
  request = Rack::Request.new(env)
52
54
 
53
55
  unless request.path_info =~ @endpoint_re
54
- env['faye.client'] = get_client
55
56
  return @app ? @app.call(env) :
56
57
  [404, TYPE_TEXT, ["Sure you're not looking for #{@endpoint} ?"]]
57
58
  end
@@ -68,24 +69,28 @@ module Faye
68
69
  json_msg = message_from_request(request)
69
70
  message = JSON.parse(json_msg)
70
71
  jsonp = request.params['jsonp'] || JSONP_CALLBACK
71
- type = request.get? ? TYPE_SCRIPT : TYPE_JSON
72
+ head = request.get? ? TYPE_SCRIPT.dup : TYPE_JSON.dup
73
+ origin = request.env['HTTP_ORIGIN']
72
74
  callback = env['async.callback']
73
75
  body = DeferredBody.new
74
76
 
77
+ debug 'Received ?: ?', env['REQUEST_METHOD'], json_msg
75
78
  @server.flush_connection(message) if request.get?
76
79
 
77
- callback.call [200, type, body]
80
+ head['Access-Control-Allow-Origin'] = origin if origin
81
+ callback.call [200, head, body]
78
82
 
79
83
  @server.process(message, false) do |replies|
80
84
  response = JSON.unparse(replies)
81
85
  response = "#{ jsonp }(#{ response });" if request.get?
86
+ debug 'Returning ?', response
82
87
  body.succeed(response)
83
88
  end
84
89
 
85
90
  ASYNC_RESPONSE
86
91
 
87
92
  rescue
88
- [400, TYPE_TEXT, 'Bad request']
93
+ [400, TYPE_TEXT, ['Bad request']]
89
94
  end
90
95
  end
91
96
 
@@ -108,7 +113,9 @@ module Faye
108
113
  socket.onmessage = lambda do |message|
109
114
  begin
110
115
  message = JSON.parse(message.data)
111
- @server.process(message, socket) do |replies|
116
+ debug 'Received via WebSocket: ?', message
117
+ @server.process(message, false) do |replies|
118
+ debug 'Sending via WebSocket: ?', replies
112
119
  socket.send(JSON.unparse(replies))
113
120
  end
114
121
  rescue
@@ -0,0 +1,62 @@
1
+ module Faye
2
+ module Engine
3
+
4
+ MAX_DELAY = 0.0
5
+ INTERVAL = 0.0
6
+ TIMEOUT = 60.0
7
+
8
+ def self.register(type, klass)
9
+ @backends ||= {}
10
+ @backends[type] = klass
11
+ end
12
+
13
+ def self.get(options)
14
+ options ||= {}
15
+ klass = @backends[options[:type]] || Memory
16
+ klass.new(options)
17
+ end
18
+
19
+ class Base
20
+ include Logging
21
+ include Timeouts
22
+
23
+ attr_reader :interval, :timeout
24
+
25
+ def initialize(options)
26
+ @options = options
27
+ @connections = {}
28
+ @interval = @options[:interval] || INTERVAL
29
+ @timeout = @options[:timeout] || TIMEOUT
30
+
31
+ debug 'Created new engine: ?', @options
32
+ end
33
+
34
+ def connect(client_id, options = {}, &callback)
35
+ debug 'Accepting connection from ?', client_id
36
+ ping(client_id)
37
+ conn = connection(client_id, true)
38
+ conn.connect(options, &callback)
39
+ empty_queue(client_id)
40
+ end
41
+
42
+ def connection(client_id, create)
43
+ conn = @connections[client_id]
44
+ return conn if conn or not create
45
+ @connections[client_id] = Connection.new(self, client_id)
46
+ end
47
+
48
+ def close_connection(client_id)
49
+ debug 'Closing connection for ?', client_id
50
+ @connections.delete(client_id)
51
+ end
52
+
53
+ def flush(client_id)
54
+ debug 'Flushing message queue for ?', client_id
55
+ conn = @connections[client_id]
56
+ conn.flush! if conn
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+
@@ -0,0 +1,63 @@
1
+ module Faye
2
+ module Engine
3
+
4
+ class Connection
5
+ include EventMachine::Deferrable
6
+ include Timeouts
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(engine, id, options = {})
11
+ @engine = engine
12
+ @id = id
13
+ @options = options
14
+ @inbox = Set.new
15
+ end
16
+
17
+ def deliver(message)
18
+ return unless @inbox.add?(message)
19
+ begin_delivery_timeout
20
+ end
21
+
22
+ def connect(options, &block)
23
+ options = options || {}
24
+ timeout = options['timeout'] ? options['timeout'] / 1000.0 : @engine.timeout
25
+
26
+ set_deferred_status(:deferred)
27
+
28
+ callback(&block)
29
+ return if @connected
30
+
31
+ @connected = true
32
+
33
+ begin_delivery_timeout
34
+ begin_connection_timeout(timeout)
35
+ end
36
+
37
+ def flush!
38
+ release_connection!
39
+ set_deferred_status(:succeeded, @inbox.entries)
40
+ end
41
+
42
+ private
43
+
44
+ def release_connection!
45
+ @engine.close_connection(@id)
46
+ remove_timeout(:connection)
47
+ remove_timeout(:delivery)
48
+ @connected = false
49
+ end
50
+
51
+ def begin_delivery_timeout
52
+ return if @inbox.empty?
53
+ add_timeout(:delivery, MAX_DELAY) { flush! }
54
+ end
55
+
56
+ def begin_connection_timeout(timeout)
57
+ add_timeout(:connection, timeout) { flush! }
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+
@@ -0,0 +1,89 @@
1
+ module Faye
2
+ module Engine
3
+
4
+ class Memory < Base
5
+ def initialize(options)
6
+ @clients = {}
7
+ @channels = {}
8
+ @messages = {}
9
+ @namespace = Namespace.new
10
+ super
11
+ end
12
+
13
+ def create_client(&callback)
14
+ client_id = @namespace.generate
15
+ debug 'Created new client ?', client_id
16
+ @clients[client_id] = Set.new
17
+ ping(client_id)
18
+ callback.call(client_id)
19
+ end
20
+
21
+ def destroy_client(client_id, &callback)
22
+ return unless @clients.has_key?(client_id)
23
+ @clients[client_id].each do |channel|
24
+ unsubscribe(client_id, channel)
25
+ end
26
+ remove_timeout(client_id)
27
+ @clients.delete(client_id)
28
+ @messages.delete(client_id)
29
+ debug 'Destroyed client ?', client_id
30
+ callback.call if callback
31
+ end
32
+
33
+ def client_exists(client_id, &callback)
34
+ callback.call(@clients.has_key?(client_id))
35
+ end
36
+
37
+ def ping(client_id)
38
+ timeout = @options[:timeout]
39
+ return unless Numeric === timeout
40
+ debug 'Ping ?, ?', client_id, timeout
41
+ remove_timeout(client_id)
42
+ add_timeout(client_id, 2 * timeout) { destroy_client(client_id) }
43
+ end
44
+
45
+ def subscribe(client_id, channel, &callback)
46
+ @clients[client_id] ||= Set.new
47
+ @channels[channel] ||= Set.new
48
+ @clients[client_id].add(channel)
49
+ @channels[channel].add(client_id)
50
+ debug 'Subscribed client ? to channel ?', client_id, channel
51
+ callback.call(true) if callback
52
+ end
53
+
54
+ def unsubscribe(client_id, channel, &callback)
55
+ @clients[client_id].delete(channel) if @clients.has_key?(client_id)
56
+ @channels[channel].delete(client_id) if @channels.has_key?(channel)
57
+ debug 'Unsubscribed client ? from channel ?', client_id, channel
58
+ callback.call(true) if callback
59
+ end
60
+
61
+ def publish(message)
62
+ debug 'Publishing message ?', message
63
+ channels = Channel.expand(message['channel'])
64
+ channels.each do |channel|
65
+ next unless clients = @channels[channel]
66
+ clients.each do |client_id|
67
+ debug 'Queueing for client ?: ?', client_id, message
68
+ @messages[client_id] ||= Set.new
69
+ @messages[client_id].add(message)
70
+ empty_queue(client_id)
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def empty_queue(client_id)
78
+ return unless conn = connection(client_id, false) and
79
+ messages = @messages.delete(client_id)
80
+
81
+ messages.each(&conn.method(:deliver))
82
+ end
83
+ end
84
+
85
+ register 'memory', Memory
86
+
87
+ end
88
+ end
89
+