faye 0.3.4 → 0.5.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 (49) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +16 -33
  3. data/README.txt +9 -274
  4. data/Rakefile +4 -4
  5. data/lib/faye-browser-min.js +1 -0
  6. data/lib/faye.rb +26 -9
  7. data/lib/faye/{rack_adapter.rb → adapters/rack_adapter.rb} +38 -25
  8. data/lib/faye/error.rb +0 -7
  9. data/lib/faye/{logging.rb → mixins/logging.rb} +0 -0
  10. data/lib/faye/mixins/publisher.rb +29 -0
  11. data/lib/faye/{timeouts.rb → mixins/timeouts.rb} +1 -0
  12. data/lib/faye/{transport.rb → network/transport.rb} +32 -43
  13. data/lib/faye/{channel.rb → protocol/channel.rb} +23 -3
  14. data/lib/faye/{client.rb → protocol/client.rb} +124 -90
  15. data/lib/faye/{connection.rb → protocol/connection.rb} +41 -23
  16. data/lib/faye/protocol/extensible.rb +47 -0
  17. data/lib/faye/{grammar.rb → protocol/grammar.rb} +0 -0
  18. data/lib/faye/{server.rb → protocol/server.rb} +98 -54
  19. data/lib/faye/protocol/subscription.rb +23 -0
  20. data/lib/faye/{namespace.rb → util/namespace.rb} +0 -0
  21. data/lib/faye/util/web_socket.rb +119 -0
  22. data/lib/thin_extensions.rb +86 -0
  23. data/test/scenario.rb +68 -14
  24. data/test/test_clients.rb +215 -2
  25. data/test/test_server.rb +10 -10
  26. metadata +102 -71
  27. data/Jakefile +0 -13
  28. data/build/faye-client-min.js +0 -1
  29. data/build/faye.js +0 -1488
  30. data/examples/README.rdoc +0 -41
  31. data/examples/node/app.js +0 -26
  32. data/examples/node/client.js +0 -23
  33. data/examples/node/faye-client-min.js +0 -1
  34. data/examples/node/faye.js +0 -1488
  35. data/examples/rack/app.rb +0 -16
  36. data/examples/rack/client.rb +0 -25
  37. data/examples/rack/config.ru +0 -8
  38. data/examples/shared/public/favicon.ico +0 -0
  39. data/examples/shared/public/index.html +0 -49
  40. data/examples/shared/public/jquery.js +0 -19
  41. data/examples/shared/public/mootools.js +0 -4329
  42. data/examples/shared/public/prototype.js +0 -4874
  43. data/examples/shared/public/robots.txt +0 -0
  44. data/examples/shared/public/soapbox.js +0 -100
  45. data/examples/shared/public/style.css +0 -43
  46. data/jake.yml +0 -40
  47. data/lib/faye-client-min.js +0 -1
  48. data/test/scenario.js +0 -138
  49. data/test/test_clients.js +0 -195
@@ -1,3 +1,16 @@
1
+ === 0.5.0 / 2010-07-17
2
+
3
+ * Handle multiple event listeners bound to a channel
4
+ * Add extension system for adding domain-specific logic to the protocol
5
+ * Improve handling of client reconnections if the server goes down
6
+ * Change default polling interval to 0 (immediate reconnect)
7
+ * Add support for WebSockets (draft75 only) as a network transport
8
+ * Remove support for Ruby servers other than Thin
9
+ * Make client and server compatible with CometD (1.x and 2.0) components
10
+ * Improve clean-up of unused server-side connections
11
+ * Change Node API for adding Faye service to an HTTP server
12
+
13
+
1
14
  === 0.3.4 / 2010-06-20
2
15
 
3
16
  * Stop local clients going into an infinite loop if a subscription block causes a reconnect
@@ -2,43 +2,26 @@ History.txt
2
2
  Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
- jake.yml
6
- Jakefile
7
- build/faye.js
8
- build/faye-client-min.js
9
- examples/README.rdoc
10
- examples/shared/public/index.html
11
- examples/shared/public/favicon.ico
12
- examples/shared/public/jquery.js
13
- examples/shared/public/mootools.js
14
- examples/shared/public/prototype.js
15
- examples/shared/public/robots.txt
16
- examples/shared/public/soapbox.js
17
- examples/shared/public/style.css
18
- examples/node/app.js
19
- examples/node/client.js
20
- examples/node/faye.js
21
- examples/node/faye-client-min.js
22
- examples/rack/app.rb
23
- examples/rack/client.rb
24
- examples/rack/config.ru
25
- lib/faye-client-min.js
5
+ lib/faye-browser-min.js
26
6
  lib/faye.rb
27
- lib/faye/channel.rb
28
- lib/faye/client.rb
29
- lib/faye/connection.rb
7
+ lib/thin_extensions.rb
8
+ lib/faye/adapters/rack_adapter.rb
9
+ lib/faye/mixins/logging.rb
10
+ lib/faye/mixins/publisher.rb
11
+ lib/faye/mixins/timeouts.rb
12
+ lib/faye/network/transport.rb
13
+ lib/faye/protocol/channel.rb
14
+ lib/faye/protocol/client.rb
15
+ lib/faye/protocol/connection.rb
16
+ lib/faye/protocol/extensible.rb
17
+ lib/faye/protocol/grammar.rb
18
+ lib/faye/protocol/server.rb
19
+ lib/faye/protocol/subscription.rb
20
+ lib/faye/util/namespace.rb
21
+ lib/faye/util/web_socket.rb
30
22
  lib/faye/error.rb
31
- lib/faye/grammar.rb
32
- lib/faye/logging.rb
33
- lib/faye/namespace.rb
34
- lib/faye/rack_adapter.rb
35
- lib/faye/server.rb
36
- lib/faye/timeouts.rb
37
- lib/faye/transport.rb
38
- test/scenario.js
39
23
  test/scenario.rb
40
24
  test/test_channel.rb
41
- test/test_clients.js
42
25
  test/test_clients.rb
43
26
  test/test_grammar.rb
44
27
  test/test_server.rb
data/README.txt CHANGED
@@ -1,5 +1,6 @@
1
1
  = Faye
2
2
 
3
+ * http://faye.jcoglan.com
3
4
  * http://github.com/jcoglan/faye
4
5
 
5
6
  Faye is a set of tools for dirt-simple publish-subscribe messaging
@@ -7,292 +8,26 @@ between web clients. It ships with easy-to-use message routing servers
7
8
  for Node.js and Rack applications, and clients that can be used on
8
9
  the server and in the browser.
9
10
 
10
-
11
- == Introduction
12
-
13
- Faye is an implementation of the Bayeux prototcol (http://svn.cometd.com/trunk/bayeux/bayeux.html),
14
- a publish-subscribe messaging protocol designed primarily to allow
15
- client-side JavaScript programs to send messages to each other with
16
- low latency over HTTP. It also allows for server-side clients that let
17
- your backend applications push data to the client side.
18
-
19
- Bayeux works by letting clients publish and subscribe to named data
20
- channels. For example, one client may publish a message:
21
-
22
- clientA.publish('/foo', {hello: 'world'});
23
-
24
- And another client can subscribe to that channel to receive messages
25
- that are published to it:
26
-
27
- // alerts "world"
28
- clientB.subscribe('/foo', function(message) {
29
- alert(message.hello);
30
- });
31
-
32
- Faye's messaging backend was originally developed in Ruby for a toy
33
- project of mine, but was not written to scale across multiple processes
34
- as required for deployment under Passenger. It has since been ported to
35
- Node.js which is better designed for handling highly concurrent traffic.
36
- If you use the Ruby version, I recommend deploying it behind Thin, an
37
- event-driven Ruby web server.
38
-
39
- The two backends are architecturally identical, using evented messaging
40
- throughout and maintaining subscription data in memory. Neither is geared
41
- up yet for running multi-process servers, but their event-driven setup
42
- should let them handle more concurrent connections than a typical
43
- threaded server.
44
-
45
-
46
- == Installation
47
-
48
- The JavaScript client and Node.js server are in the +build+ directory. Just
49
- copy them onto your machine and <tt>require('path/to/faye')</tt>.
50
-
51
- The Rack server is distributed as a Ruby gem:
52
-
53
- sudo gem install faye
54
-
55
-
56
- == Using the client
57
-
58
- Both backends allow you to specify a 'mount point' that the message server
59
- accepts requests on. Say you set this to <tt>/faye</tt>, the client script
60
- will be available from <tt>/faye.js</tt> and should connect to <tt>/faye</tt>.
61
-
62
- You should set up the client as follows:
63
-
64
- <script type="text/javascript" src="/faye.js"></script>
65
-
66
- <script type="text/javascript">
67
- fayeClient = new Faye.Client('/faye', {timeout: 120});
68
- </script>
69
-
70
- The client (and the Node.js and Rack server-side clients) accepts the following
71
- options in its constructor:
72
-
73
- * +timeout+ - the maximum time (seconds) to wait for a connection response
74
- from the server before attempting to reconnect. This should be greater than
75
- the timeout set up on the server side, the idea being that if the server
76
- doesn't send a connection response within this time then there has probably
77
- been a network failure and we should reconnect. When the client reconnects,
78
- any existing channel subscriptions are re-established automatically.
79
-
80
- Take care only to have one instance of the client per page; since each one
81
- opens a long-running request you will hit the two-requests-per-host limit
82
- and block all other Ajax calls if you use more than one client.
83
-
84
- This client object can be used to publish and subscribe to named channels:
85
-
86
- fayeClient.subscribe('/path/to/channel', function(message) {
87
- // process received message object
88
- });
89
-
90
- fayeClient.publish('/some/other/channel', {foo: 'bar'});
91
-
92
- You can publish arbitrary JavaScript objects to a channel, and the object will
93
- be transmitted as the +message+ parameter to any subscribers to that channel.
94
- Channel names must be formatted as absolute path names as shown. Channels
95
- beginning with <tt>/meta/</tt> are reserved for use by the messaging protocol
96
- and may not be subscribed to.
97
-
98
- The client can also be used cross-domain if connecting to a backend that
99
- supports callback polling (see below under 'Transports'). Just pass in the
100
- full path to the endpoint including the domain. Faye figures out whether the
101
- server is on the same domain and uses an appropriate transport.
102
-
103
- fayeClient = new Faye.Client('http://example.com/faye');
104
-
105
-
106
- === Transports
107
-
108
- The Bayeux spec defines several transport mechanisms for clients to establish
109
- low-latency connections with the server, the two required types being
110
- <tt>long-polling</tt> and <tt>callback-polling</tt>.
111
-
112
- <tt>long-polling</tt> is where the client makes an <tt>XMLHttpRequest</tt>
113
- to the server, and the server waits until it has new messages for that client
114
- before it returns a response. Faye's client and server backends all support
115
- this transport. Since it uses XHR, the server endpoint must be on the same
116
- domain as the client page.
117
-
118
- <tt>callback-polling</tt> involves the client using JSON-P to make the request.
119
- The server wraps its JSON responses in a JavaScript function call that the
120
- client then executes. This transport does not require the client and server
121
- to be on the same domain. Faye's client supports this transport, as does the
122
- Node.js backend. The Rack backend supports it if running under Thin.
123
-
124
- Faye also supports an <tt>in-process</tt> transport, which is used when a
125
- server-side client has direct in-memory access to the server without going
126
- over HTTP.
127
-
128
-
129
- == Using the backend
130
-
131
- Both the Node.js and Rack backends have identical architectures and are
132
- designed to be easily plugged into other web services. For Rack the adapter
133
- is explicitly designed as middleware, while for Node.js the adapter is
134
- a simple object you can manually offload requests to.
135
-
136
- The backends provide a service for routing messages between clients; no
137
- server-side programming is needed to control them, just start them up
138
- and they'll sit there merrily chewing through requests. They are both
139
- currently single-process since they hold all channel subscriptions in
140
- memory. This means, for example, that the Rack backend will not work
141
- under Passenger since that spawns multiple Ruby processes to serve your
142
- site.
143
-
144
- Faye uses async messaging internally so nothing blocks while waiting for
145
- new messages. If running under a Ruby web server (except Thin) the server
146
- will block while waiting for a response from Faye. Thin supports async
147
- responses and is a better choice for long-running concurrent connections.
148
-
149
- Both backends support the following initialization options:
150
-
151
- * +mount+ - the path at which the Faye service is accessible. e.g. if
152
- set to <tt>/faye</tt>, the Faye endpoint will be at <tt>http://yoursite.com/faye</tt>
153
- and the client script at <tt>http://yoursite.com/faye.js</tt>.
154
- * +timeout+ - the maximum time (seconds) to hold a long-running request
155
- open before returning a response. This must be smaller than the timeout
156
- on your frontend webserver to make sure Faye sends a response before
157
- the server kills the connection.
158
-
159
- Regarding the +mount+ parameter: Faye will respond to any path under the
160
- mount point, so <tt>/faye</tt>, <tt>/faye.js</tt> and <tt>/faye/some/subpath</tt>
161
- will all be handled by Faye rather than your application. This is so that
162
- Faye can interoperate with clients that use different URLs for different
163
- message types.
164
-
165
- Usage examples and a demo app are in the +examples+ directory.
166
-
167
-
168
- === Node.js backend
169
-
170
- Here's a very simple Node web server that offloads requests to the messaging
171
- service to Faye and deals with all other requests itself. The Faye object
172
- returns +true+ or +false+ to indicate whether it handled the request.
173
- You'll need <tt>faye.js</tt> and <tt>faye-client-min.js</tt> in the same
174
- directory.
175
-
176
- var http = require('http')
177
- faye = require('./faye');
178
-
179
- var server = new faye.NodeAdapter({mount: '/faye', timeout: 45});
180
-
181
- http.createServer(function(request, response) {
182
- if (server.call(request, response)) return;
183
-
184
- response.sendHeader(200, {'Content-Type': 'text/plain'});
185
- response.write('Hello, non-Faye request!');
186
- response.close();
187
-
188
- }).listen(9292);
189
-
190
-
191
- === Node.js client
192
-
193
- Faye's JavaScript client can be used server-side under Node. There are
194
- two ways you can set a client up: either connect to as remote server
195
- over HTTP or connect directly to the <tt>NodeAdapter</tt>. Either way,
196
- the API for the client is exactly as it is in the browser.
197
-
198
- // Remote client
199
- client = new faye.Client('http://example.com/faye', {timeout: 120});
200
-
201
- // Local client
202
- server = new faye.NodeAdapter(options);
203
- client = server.getClient();
204
-
205
- Note <tt>getClient()</tt> returns the same client object every time you
206
- call it, so any subscriptions you make with it are retained.
207
-
208
-
209
- === Rack backend
210
-
211
- Faye can be installed as middleware in front of any Rack application. The
212
- Rack backend uses EventMachine for asynchronous message distribution and
213
- timeouts. It can run under any web server, though Thin is best placed for
214
- handling long-running concurrent connections and supports async server
215
- responses. Under other servers the request thread will block while waiting
216
- for a response from Faye.
217
-
218
- Here's a <tt>config.ru</tt> for running it with Sinatra:
219
-
220
- require 'rubygems'
221
- require 'faye'
222
- require 'sinatra'
223
- require 'path/to/sinatra/app'
224
-
225
- use Faye::RackAdapter, :mount => '/faye',
226
- :timeout => 25
227
-
228
- run Sinatra::Application
229
-
230
- This functions much the same as the Node.js example; Faye catches messaging
231
- requests and deals with them, letting all other requests fall down the
232
- stack of Rack middlewares.
233
-
234
-
235
- === Rack client
236
-
237
- Again mirroring the Node tools, Faye has a server-side Ruby client that
238
- can be used under EventMachine. Setup is identical:
239
-
240
- # Remote client
241
- client = Faye::Client.new('http://example.com/faye', :timeout => 120)
242
-
243
- # Local client
244
- server = Faye::RackAdapter.new(options)
245
- client = server.get_client
246
-
247
- Both types of client must be run inside EventMachine, and can publish and
248
- subscribe just like the JavaScript client:
249
-
250
- EM.run {
251
- client.subscribe('/from/*') do |message|
252
- # do something with message hash
253
- end
254
-
255
- client.publish('/from/jcoglan', 'hello' => 'world')
256
- }
257
-
258
- If you're using the <tt>RackAdapter</tt> as middleware and running your app
259
- using Thin, applications further down the chain can get a client for it from
260
- the Rack environment, for example in a Sinatra application:
261
-
262
- get '/post' do
263
- env['faye.client'].publish('/mentioning/*', {
264
- 'user' => 'sinatra',
265
- 'message' => params[:message]
266
- })
267
- params[:message]
268
- end
11
+ See http://faye.jcoglan.com for documentation.
269
12
 
270
13
 
271
14
  == Development
272
15
 
273
- If you want to hack on Faye, you'll need Node.js, Ruby and the following
274
- gems installed:
275
-
276
- sudo gem install eventmachine em-http-request rack thin json jake hoe
16
+ To hack on Faye, you'll need Ruby and Jake, which we use to build
17
+ the JavaScript packages. Once you have Ruby installed:
277
18
 
278
- DO NOT edit any JavaScript files outside the +javascript+ and +test+
279
- directories. They are generated using Jake, which you should run after
280
- editing the JavaScript source.
19
+ sudo gem install jake
281
20
 
282
- jake -f
21
+ Just run `jake` from the root of the project to build the JavaScripts.
283
22
 
284
- Ordinarily I would keep generated files out of the repo but I'm keeping
285
- some in here as it's currently the only place to download them.
23
+ The Ruby version depends on the following gems, which you'll need
24
+ to install:
286
25
 
287
- If you want to submit patches, they're far more likely to make it in
288
- if you update both the Ruby and JavaScript versions with equivalent
289
- changes where appropriate.
26
+ sudo gem install hoe eventmachine em-http-request rack thin json
290
27
 
291
28
 
292
29
  == To-do
293
30
 
294
- * Investigate WebSockets as a possible message transport
295
- * Support Bayeux extensions for authentication
296
31
  * Let local server-side clients listen to <tt>/meta/*</tt> channels
297
32
  * Provide support for user-defined <tt>/service/*</tt> channels
298
33
  * Allow server to scale to multiple nodes
data/Rakefile CHANGED
@@ -4,10 +4,10 @@ require 'rubygems'
4
4
  require 'hoe'
5
5
  require './lib/faye.rb'
6
6
 
7
- Hoe.spec('faye') do |p|
8
- # p.rubyforge_name = 'fayex' # if different than lowercase project name
9
- p.developer('James Coglan', 'jcoglan@googlemail.com')
10
- p.extra_deps = [
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
11
  ['eventmachine', '>= 0.12'],
12
12
  ['em-http-request', '>= 0.2'],
13
13
  ['rack', '>= 1.0'],
@@ -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))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.0',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}},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._u={}},generate:function(){var a=Faye.random();while(this._u.hasOwnProperty(a))a=Faye.random();return this._u[a]=a}});Faye.Deferrable={callback:function(a,b){if(!a)return;if(this._B==='succeeded')return a.apply(b,this._v);this._9=this._9||[];this._9.push([a,b])},setDeferredStatus:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();this._B=c;this._v=b;if(c!=='succeeded')return;if(!this._9)return;Faye.each(this._9,function(a){a[0].apply(a[1],this._v)},this);this._9=[]}};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._6=this._6||{};if(this._6.hasOwnProperty(a))return;var f=this;this._6[a]=setTimeout(function(){delete f._6[a];c.call(d)},1000*b)},removeTimeout:function(a){this._6=this._6||{};var b=this._6[a];if(!b)return;clearTimeout(b);delete this._6[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 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._7=this._7||[];this._7.push(a);if(a.added)a.added()},removeExtension:function(a){if(!this._7)return;var b=this._7.length;while(b--){if(this._7[b]!==a)continue;this._7.splice(b,1);if(a.removed)a.removed()}},pipeThroughExtensions:function(c,d,f,g){if(!this._7)return f.call(g,d);var h=this._7.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.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.publishEvent('message',a)}});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){this._4=a;this._a={}},eachChild:function(c,d){Faye.each(this._a,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._4!==undefined)d.call(f,c,this._4)},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._4:null},set:function(a,b){var c=this.traverse(a,true);if(c)c._4=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._a[a[0]];if(!c&&!b)return null;if(!c)c=this._a[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._4===undefined)?[]:[this._4];var g=[];if(Faye.enumEqual(f,['*'])){Faye.each(this._a,function(a,b){if(b._4!==undefined)g.push(b._4)});return g}if(Faye.enumEqual(f,['**'])){g=this.map(function(a,b){return b});if(this._4!==undefined)g.pop();return g}Faye.each(this._a,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._a['**'])g.push(this._a['**']._4);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);return d.countSubscribers('message')===0},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._m=c;this._d=d;this._w=false},cancel:function(){if(this._w)return;this._c.unsubscribe(this._b,this._m,this._d);this._w=true},unsubscribe:function(){this.cancel()}});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.1,INTERVAL:0.0,initialize:function(a,b){this.info('New client created for ?',a);this._3=a||this.DEFAULT_ENDPOINT;this._x=b||{};this._f=Faye.Transport.get(this,Faye.MANDATORY_CONNECTION_TYPES);this._1=this.UNCONNECTED;this._h=[];this._b=new Faye.Channel.Tree();this._C=new Faye.Namespace();this._n={};this._8={reconnect:this.RETRY,interval:1000*(this._x.interval||this.INTERVAL),timeout:1000*(this._x.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},handshake:function(b,c){if(this._8.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var d=this;this.info('Initiating handshake with ?',this._3);this._e({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._f.connectionType]},function(a){if(a.successful){this._1=this.CONNECTED;this._0=a.clientId;this._f=Faye.Transport.get(this,a.supportedConnectionTypes);this.info('Handshake successful: ?',this._0);this.subscribe(this._b.getKeys());if(b)b.call(c)}else{this.info('Handshake unsuccessful');setTimeout(function(){d.handshake(b,c)},this._8.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._8.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._o)return;this._o=true;this.info('Initiating connection for ?',this._0);this._e({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._f.connectionType},this._y,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._p(c);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)},this)},this);return new Faye.Subscription(this,c,d,f)},unsubscribe:function(c,d,f){if(c instanceof Array)return Faye.each(c,function(channel){this.unsubscribe(channel,d,f)},this);this._p(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._p(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._D(a.advice);var b=this._n[a.id];if(b){delete this._n[a.id];b[0].call(b[1],a)}this._E(a)},this)},_D:function(a){Faye.extend(this._8,a);if(this._8.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._y()}},_E: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)},_F:function(){if(!this._o)return;this._o=null;this.info('Closed connection for ?',this._0)},_y:function(){this._F();var a=this;setTimeout(function(){a.connect()},this._8.interval)},_e:function(b,c,d){b.id=this._C.generate();if(c)this._n[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._8.timeout/1000);this._h.push(a);if(a.channel===Faye.Channel.CONNECT)this._q=a;this.addTimeout('publish',this.MAX_DELAY,this._G,this)},this)},_G:function(){this.removeTimeout('publish');if(this._h.length>1&&this._q)this._q.advice={timeout:0};this._q=null;this._f.send(this._h,this._8.timeout/1000);this._h=[]},_p: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._3=b},send:function(a,b){a=[].concat(a);this.debug('Client ? sending message to ?: ?',this._c._0,this._3,a);return this.request(a,b)},receive:function(a){this.debug('Client ? received from ?: ?',this._c._0,this._3,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(d,f){var g=d._3;if(f===undefined)f=this.supportedConnectionTypes();var h=null;Faye.each(this._r,function(a){var b=a[0],c=a[1];if(Faye.indexOf(f,b)<0)return;if(h)return;if(c.isUsable(g))h=c});if(!h)throw'Could not find a usable connection type for '+g;return new h(d,g)},register:function(a,b){this._r.push([a,b]);b.prototype.connectionType=a},_r:[],supportedConnectionTypes:function(){return Faye.map(this._r,function(a){return a[0]})}});Faye.extend(Faye.Transport.prototype,Faye.Logging);Faye.Event={_i:[],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._i.push({_j:a,_s:b,_m:c,_d:d,_z:f})},detach:function(a,b,c,d){var f=this._i.length,g;while(f--){g=this._i[f];if((a&&a!==g._j)||(b&&b!==g._s)||(c&&c!==g._m)||(d&&d!==g._d))continue;if(g._j.removeEventListener)g._j.removeEventListener(g._s,g._z,false);else g._j.detachEvent('on'+g._s,g._z);this._i.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(),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=i;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._k=a.toUpperCase();this._3=Faye.URI.parse(b,c);this._H=(typeof c==='string')?c:null;this._9=(typeof d==='function')?{success:d}:d;this._d=f||null;this._5=null},send:function(){if(this._t)return;var a=this._3.pathname,b=this._3.queryString();if(this._k==='GET')a+='?'+b;var c=(this._k==='POST')?(this._H||b):'';this._t=true;this._5=Faye.XHR.getXhrObject();this._5.open(this._k,a,true);if(this._k==='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._t=false;d._I();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()},_I:function(){var a=this._9;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._t},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,h=k,j,i=b[a];if(i&&typeof i==='object'&&typeof i.toJSON==='function'){i=i.toJSON(a)}if(typeof m==='function'){i=m.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'}k+=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?'[]':k?'[\n'+k+j.join(',\n'+k)+'\n'+h+']':'['+j.join(',')+']';k=h;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,i);if(f){j.push(r(d)+(k?': ':':')+f)}}}}else{for(d in i){if(Object.hasOwnProperty.call(i,d)){f=q(d,i);if(f){j.push(r(d)+(k?': ':':')+f)}}}}f=j.length===0?'{}':k?'{\n'+k+j.join(',\n'+k)+'\n'+h+'}':'{'+j.join(',')+'}';k=h;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,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)}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 h==='function'?i({'':j},''):j}throw new SyntaxError('JSON.parse');}}}());Faye.WebSocketTransport=Faye.Class(Faye.Transport,{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,request:function(b,c){this._l=this._l||{};Faye.each(b,function(a){this._l[a.id]=a},this);this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(d,f){this.callback(d,f);this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var g=Faye.URI.parse(this._3).toURL().replace(/^https?/ig,'ws');this._g=new WebSocket(g);var h=this;this._g.onopen=function(){h._1=h.CONNECTED;h.setDeferredStatus('succeeded',h._g)};this._g.onmessage=function(b){var c=[].concat(JSON.parse(b.data));Faye.each(c,function(a){delete h._l[a.id]});h.receive(c)};this._g.onclose=function(){h.setDeferredStatus('deferred');h._1=h.UNCONNECTED;h._g=null;h.resend()}},resend:function(){var c=Faye.map(this._l,function(a,b){return b});this.request(c)}});Faye.extend(Faye.WebSocketTransport.prototype,Faye.Deferrable);Faye.WebSocketTransport.isUsable=function(a){return!!Faye.ENV.WebSocket};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._3,Faye.toJSON(b),{success:function(a){try{this.receive(JSON.parse(a.text()))}catch(e){d()}},failure:d},this)}});Faye.XHRTransport.isUsable=function(a){return 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'),h=Faye.JSONPTransport.getCallbackName(),j=Faye.URI.parse(this._3,d),i=this;var l=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(!l())return;i.receive(a)};setTimeout(function(){if(!Faye.ENV[h])return;l();i.request(b,2*c)},1000*c);j.params.jsonp=h;g.type='text/javascript';g.src=j.toURL();f.appendChild(g)}}),{_A:0,getCallbackName:function(){this._A+=1;return'__jsonp'+this._A+'__'}});Faye.JSONPTransport.isUsable=function(a){return true};Faye.Transport.register('callback-polling',Faye.JSONPTransport);
@@ -1,32 +1,44 @@
1
1
  require 'forwardable'
2
- require 'observer'
3
2
  require 'set'
4
3
  require 'rubygems'
5
4
  require 'eventmachine'
6
5
  require 'json'
7
6
 
8
7
  module Faye
9
- VERSION = '0.3.4'
8
+ VERSION = '0.5.0'
10
9
 
11
10
  ROOT = File.expand_path(File.dirname(__FILE__))
12
11
 
13
12
  BAYEUX_VERSION = '1.0'
14
13
  ID_LENGTH = 128
15
14
  JSONP_CALLBACK = 'jsonpcallback'
16
- CONNECTION_TYPES = %w[long-polling callback-polling]
15
+ CONNECTION_TYPES = %w[long-polling callback-polling websocket]
17
16
 
18
- %w[ logging timeouts grammar namespace server
19
- channel connection error client transport
17
+ MANDATORY_CONNECTION_TYPES = %w[long-polling callback-polling in-process]
18
+
19
+ %w[ mixins/publisher
20
+ mixins/timeouts
21
+ mixins/logging
22
+ util/namespace
23
+ protocol/grammar
24
+ protocol/extensible
25
+ protocol/channel
26
+ protocol/subscription
27
+ protocol/client
28
+ protocol/server
29
+ protocol/connection
30
+ network/transport
31
+ error
32
+
20
33
  ].each do |lib|
21
34
  require File.join(ROOT, 'faye', lib)
22
35
  end
23
36
 
24
- autoload :RackAdapter, File.join(ROOT, 'faye', 'rack_adapter')
37
+ autoload :RackAdapter, File.join(ROOT, 'faye', 'adapters', 'rack_adapter')
38
+ autoload :WebSocket, File.join(ROOT, 'faye', 'util', 'web_socket')
25
39
 
26
40
  def self.random(bitlength = ID_LENGTH)
27
- field = 2 ** bitlength
28
- strlen = bitlength / 4
29
- ("%0#{strlen}s" % rand(field).to_s(16)).gsub(' ', '0')
41
+ rand(2 ** bitlength).to_s(36)
30
42
  end
31
43
 
32
44
  def self.to_json(value)
@@ -36,5 +48,10 @@ module Faye
36
48
  else value.to_s
37
49
  end
38
50
  end
51
+
52
+ def self.ensure_reactor_running!
53
+ Thread.new { EM.run } unless EM.reactor_running?
54
+ while not EM.reactor_running?; end
55
+ end
39
56
  end
40
57