faye 0.1.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +28 -0
- data/Jakefile +11 -0
- data/Manifest.txt +15 -17
- data/README.txt +149 -47
- data/build/faye-client-min.js +1 -0
- data/build/faye.js +868 -0
- data/examples/README.rdoc +41 -0
- data/examples/node/app.js +26 -0
- data/examples/node/faye-client-min.js +1 -0
- data/examples/node/faye.js +868 -0
- data/examples/rack/app.rb +6 -0
- data/examples/{soapbox → rack}/config.ru +1 -1
- data/examples/shared/public/favicon.ico +0 -0
- data/examples/{soapbox/views/index.erb → shared/public/index.html} +0 -0
- data/examples/{soapbox → shared}/public/jquery.js +0 -0
- data/examples/shared/public/robots.txt +0 -0
- data/examples/{soapbox → shared}/public/soapbox.js +0 -0
- data/examples/{soapbox → shared}/public/style.css +0 -0
- data/jake.yml +24 -9
- data/lib/faye-client-min.js +1 -0
- data/lib/faye.rb +2 -2
- data/lib/faye/rack_adapter.rb +44 -12
- data/lib/faye/server.rb +38 -31
- metadata +44 -23
- data/client/channel.js +0 -135
- data/client/client.js +0 -265
- data/client/faye.js +0 -95
- data/client/transport.js +0 -91
- data/client/util/class.js +0 -20
- data/client/util/event.js +0 -44
- data/client/util/json.js +0 -479
- data/client/util/uri.js +0 -69
- data/client/util/xhr.js +0 -99
- data/examples/soapbox/README +0 -22
- data/examples/soapbox/app.rb +0 -8
- data/lib/faye-min.js +0 -1
data/History.txt
CHANGED
@@ -1 +1,29 @@
|
|
1
|
+
=== 0.2.2 / 2010-02-10
|
2
|
+
|
3
|
+
* Kick out requests with malformed JSON as 400s
|
4
|
+
|
5
|
+
|
6
|
+
=== 0.2.1 / 2010-02-04
|
7
|
+
|
8
|
+
* Fix server-side flushing of callback-polling connections
|
9
|
+
* Backend can be used cross-domain if running on Node or Thin
|
10
|
+
|
11
|
+
|
12
|
+
=== 0.2.0 / 2010-02-02
|
13
|
+
|
14
|
+
* Port server to JavaScript with an adapter for Node.js
|
15
|
+
* Support Thin's async responses in the Ruby version for complete non-blocking
|
16
|
+
* Fix some minor client-side bugs in transport choice
|
17
|
+
|
18
|
+
|
19
|
+
=== 0.1.1 / 2009-07-26
|
20
|
+
|
21
|
+
* Fix a broken client build
|
22
|
+
|
23
|
+
|
24
|
+
=== 0.1.0 / 2009-06-15
|
25
|
+
|
26
|
+
* Ruby Bayeux server and Rack adapter
|
27
|
+
* Internally evented using EventMachine, web frontend blocks
|
28
|
+
* JavaScript client with long-polling and callback-polling
|
1
29
|
|
data/Jakefile
CHANGED
@@ -1,2 +1,13 @@
|
|
1
1
|
require File.join('lib', 'faye')
|
2
|
+
DIR = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
jake_hook :build_complete do |build|
|
5
|
+
FileUtils.cp build.package('faye-client').build_path(:min), DIR + '/lib/faye-client-min.js'
|
6
|
+
FileUtils.cp build.package('faye-client').build_path(:min), DIR + '/examples/node/faye-client-min.js'
|
7
|
+
FileUtils.cp build.package(:faye).build_path(:src), DIR + '/examples/node/faye.js'
|
8
|
+
|
9
|
+
[['faye-client', :src], [:faye, :min], [:core, :src], [:core, :min]].each do |(pkg,typ)|
|
10
|
+
FileUtils.rm build.package(pkg).build_path(typ)
|
11
|
+
end
|
12
|
+
end
|
2
13
|
|
data/Manifest.txt
CHANGED
@@ -4,16 +4,21 @@ README.txt
|
|
4
4
|
Rakefile
|
5
5
|
jake.yml
|
6
6
|
Jakefile
|
7
|
-
|
8
|
-
client
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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/robots.txt
|
14
|
+
examples/shared/public/soapbox.js
|
15
|
+
examples/shared/public/style.css
|
16
|
+
examples/node/app.js
|
17
|
+
examples/node/faye.js
|
18
|
+
examples/node/faye-client-min.js
|
19
|
+
examples/rack/app.rb
|
20
|
+
examples/rack/config.ru
|
21
|
+
lib/faye-client-min.js
|
17
22
|
lib/faye.rb
|
18
23
|
lib/faye/channel.rb
|
19
24
|
lib/faye/connection.rb
|
@@ -21,13 +26,6 @@ lib/faye/error.rb
|
|
21
26
|
lib/faye/grammar.rb
|
22
27
|
lib/faye/rack_adapter.rb
|
23
28
|
lib/faye/server.rb
|
24
|
-
examples/soapbox/README
|
25
|
-
examples/soapbox/app.rb
|
26
|
-
examples/soapbox/config.ru
|
27
|
-
examples/soapbox/public/jquery.js
|
28
|
-
examples/soapbox/public/soapbox.js
|
29
|
-
examples/soapbox/public/style.css
|
30
|
-
examples/soapbox/views/index.erb
|
31
29
|
test/test_channel.rb
|
32
30
|
test/test_grammar.rb
|
33
31
|
test/test_server.rb
|
data/README.txt
CHANGED
@@ -2,54 +2,64 @@
|
|
2
2
|
|
3
3
|
* http://github.com/jcoglan/faye
|
4
4
|
|
5
|
-
Faye
|
6
|
-
|
7
|
-
|
8
|
-
'server push' or 'Ajax push', a technique for pushing messages from the
|
9
|
-
server to the client to minimize latency.
|
5
|
+
Faye provides dirt-simple Comet messaging based on the Bayeux protocol.
|
6
|
+
It ships with a self-contained client for use in the browser and two
|
7
|
+
message-routing backends; one for Node.js and one for Rack.
|
10
8
|
|
11
|
-
NOTE this is not currently intended for large-scale use. It is being
|
12
|
-
developed mostly as a tool for scripting multiple browsers and automating
|
13
|
-
JavaScript testing. However, use of a well-known protocol should allow
|
14
|
-
it to be repurposed fairly easily.
|
15
9
|
|
10
|
+
== Introduction
|
16
11
|
|
17
|
-
|
12
|
+
Comet is an umbrella term for a collection of techniques that let web
|
13
|
+
servers push data to clients, allowing for low-latency data transfer.
|
14
|
+
Bayeux is a protocol designed to support publish/subscribe-style messaging
|
15
|
+
between web clients and servers. Clients can subscribe to named channels
|
16
|
+
and publish messages, each message being distributed to all clients
|
17
|
+
subscribed to its target channel. See http://svn.cometd.com/trunk/bayeux/bayeux.html
|
18
18
|
|
19
|
-
Faye
|
20
|
-
|
19
|
+
Faye's messaging backend was originally developed in Ruby for a toy
|
20
|
+
project of mine, but was not written to scale across multiple processes
|
21
|
+
as required for scaling a Ruby server. It has since been ported to Node.js
|
22
|
+
which should handle the required connection load more gracefully.
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
The two backends are architecturally identical, using evented messaging
|
25
|
+
throughout and maintaining subscription data in memory. Node.js allows
|
26
|
+
for far more concurrent connections per process than a typical Ruby server,
|
27
|
+
and will get you more mileage from a single process.
|
28
|
+
|
29
|
+
|
30
|
+
== Installation
|
31
|
+
|
32
|
+
The JavaScript client and Node.js server are in the +build+ directory. Just
|
33
|
+
copy them onto your machine and <tt>require('path/to/faye')</tt>.
|
34
|
+
|
35
|
+
The Rack server is distributed as a Ruby gem:
|
36
|
+
|
37
|
+
sudo gem install faye
|
31
38
|
|
32
|
-
This starts a Comet endpoint at <tt>http://localhost:9292/comet</tt> with
|
33
|
-
the client script at <tt>http://localhost:9292/comet.js</tt>. The +timeout+
|
34
|
-
option sets how long (in seconds) a long-polling request will wait before
|
35
|
-
timing out; this must be less than the timeout set on your frontend web server
|
36
|
-
so that the Comet server can send a response before Apache (for example)
|
37
|
-
closes the connection.
|
38
39
|
|
39
|
-
|
40
|
+
== Using the client
|
41
|
+
|
42
|
+
Both backends allow you to specify a 'mount point' that the Comet server
|
43
|
+
accepts requests on. Say you set this to <tt>/comet</tt>, the client script
|
44
|
+
will be available from <tt>/comet.js</tt> and should connect to <tt>/comet</tt>.
|
45
|
+
|
46
|
+
You should set up the client as follows:
|
40
47
|
|
41
48
|
<script type="text/javascript" src="/comet.js"></script>
|
42
49
|
|
43
50
|
<script type="text/javascript">
|
44
|
-
|
45
|
-
|
51
|
+
CometClient = new Faye.Client('/comet');
|
52
|
+
CometClient.connect();
|
46
53
|
</script>
|
47
54
|
|
48
|
-
|
49
|
-
|
55
|
+
Take care only to have one instance of the client per page; since each one
|
56
|
+
opens a long-running request you will hit the two-requests-per-host limit
|
57
|
+
and block all other Ajax calls if you use more than one client.
|
58
|
+
|
59
|
+
This client object can be used to publish and subscribe to named channels:
|
50
60
|
|
51
61
|
CometClient.subscribe('/path/to/channel', function(message) {
|
52
|
-
|
62
|
+
// process received message object
|
53
63
|
});
|
54
64
|
|
55
65
|
CometClient.publish('/some/other/channel', {foo: 'bar'});
|
@@ -60,36 +70,127 @@ Channel names must be formatted as absolute path names as shown. Channels
|
|
60
70
|
beginning with <tt>/meta/</tt> are reserved for use by the messaging protocol
|
61
71
|
and may not be subscribed to.
|
62
72
|
|
73
|
+
The client can also be used cross-domain if connecting to a backend that
|
74
|
+
supports callback polling (see below under 'Transports'). Just pass in the
|
75
|
+
full path to the endpoint including the domain. Faye figures out whether the
|
76
|
+
server is on the same domain and uses an appropriate transport.
|
77
|
+
|
78
|
+
CometClient = new Faye.Client('http://example.com/comet');
|
79
|
+
|
80
|
+
|
81
|
+
=== Transports
|
82
|
+
|
83
|
+
The Bayeux spec defines several transport mechanisms for clients to establish
|
84
|
+
low-latency connections with the server, the two required types being
|
85
|
+
<tt>long-polling</tt> and <tt>callback-polling</tt>.
|
86
|
+
|
87
|
+
<tt>long-polling</tt> is where the client makes an <tt>XMLHttpRequest</tt>
|
88
|
+
to the server, and the server waits until it has new messages for that client
|
89
|
+
before it returns a response. Faye's client and server backends all support
|
90
|
+
this transport. Since it uses XHR, the Comet endpoint must be on the same
|
91
|
+
domain as the client page.
|
92
|
+
|
93
|
+
<tt>callback-polling</tt> involves the client using JSON-P to make the request.
|
94
|
+
The server wraps its JSON responses in a JavaScript function call that the
|
95
|
+
client then executes. This transport does not require the client and server
|
96
|
+
to be on the same domain. Faye's client supports this transport, as does the
|
97
|
+
Node.js backend. The Rack backend supports it if running under Thin.
|
98
|
+
|
99
|
+
|
100
|
+
== Using the backend
|
101
|
+
|
102
|
+
Both the Node.js and Rack backends have identical architectures and are
|
103
|
+
designed to be easily plugged into other web services. For Rack the adapter
|
104
|
+
is explicitly designed as middleware, while for Node.js the adapter is
|
105
|
+
a simple object you can manually offload requests to.
|
106
|
+
|
107
|
+
The backends provide a service for routing messages between clients; no
|
108
|
+
server-side programming is needed to control them, just start them up
|
109
|
+
and they'll sit there merrily chewing through requests. They are both
|
110
|
+
currently single-process since they hold all channel subscriptions in
|
111
|
+
memory. This means, for example, that the Rack backend will not work
|
112
|
+
under Passenger since that spawns multiple Ruby processes to serve your
|
113
|
+
site.
|
63
114
|
|
64
|
-
|
115
|
+
Faye uses async messaging internally so nothing blocks while waiting for
|
116
|
+
new messages. If running under a Ruby web server (except Thin) the server
|
117
|
+
will block while waiting for a response from Faye. Thin supports async
|
118
|
+
responses and is a better choice for long-running concurrent connections.
|
65
119
|
|
66
|
-
|
67
|
-
installing the gem you will need to build the JavaScript client from source, this
|
68
|
-
is done as follows:
|
120
|
+
Both backends support the following initialization options:
|
69
121
|
|
70
|
-
|
71
|
-
|
122
|
+
* +mount+ - the path at which the Comet service is accessible. e.g. if
|
123
|
+
set to <tt>/faye</tt>, the Comet endpoint will be at <tt>http://yoursite.com/faye</tt>
|
124
|
+
and the client script at <tt>http://yoursite.com/faye.js</tt>.
|
125
|
+
* +timeout+ - the maximum time (seconds) to hold a long-running request
|
126
|
+
open before returning a response. This must be smaller than the timeout
|
127
|
+
on your frontend webserver to make sure Faye sends a response before
|
128
|
+
the server kills the connection.
|
129
|
+
|
130
|
+
Usage examples and a demo app are in the +examples+ directory.
|
131
|
+
|
132
|
+
|
133
|
+
=== Node.js backend
|
134
|
+
|
135
|
+
Here's a very simple Node web server that offloads requests to the Comet
|
136
|
+
service to Faye and deals with all other requests itself. The Faye object
|
137
|
+
returns +true+ or +false+ to indicate whether it handled the request.
|
138
|
+
You'll need <tt>faye.js</tt> and <tt>faye-client-min.js</tt> in the same
|
139
|
+
directory.
|
140
|
+
|
141
|
+
var http = require('http')
|
142
|
+
faye = require('./faye');
|
143
|
+
|
144
|
+
var comet = new faye.NodeAdapter({mount: '/comet', timeout: 45});
|
145
|
+
|
146
|
+
http.createServer(function(request, response) {
|
147
|
+
if (comet.call(request, response)) return;
|
148
|
+
|
149
|
+
response.sendHeader(200, {'Content-Type': 'text/plain'});
|
150
|
+
response.sendBody('Hello, non-Comet request!');
|
151
|
+
response.finish();
|
152
|
+
|
153
|
+
}).listen(9292);
|
154
|
+
|
155
|
+
|
156
|
+
=== Rack backend
|
157
|
+
|
158
|
+
Faye can be installed as middleware in front of any Rack application. The
|
159
|
+
Rack backend uses EventMachine for asynchronous message distribution and
|
160
|
+
timeouts. It can run under any web server, though Thin is best placed for
|
161
|
+
handling long-running concurrent connections and supports async server
|
162
|
+
responses. Under other servers the request thread will block while waiting
|
163
|
+
for a response from Faye.
|
164
|
+
|
165
|
+
Here's a <tt>config.ru</tt> for running it with Sinatra:
|
166
|
+
|
167
|
+
require 'rubygems'
|
168
|
+
require 'faye'
|
169
|
+
require 'sinatra'
|
170
|
+
require 'path/to/sinatra/app'
|
171
|
+
|
172
|
+
use Faye::RackAdapter, :mount => '/comet',
|
173
|
+
:timeout => 25
|
174
|
+
|
175
|
+
run Sinatra::Application
|
176
|
+
|
177
|
+
This functions much the same as the Node.js example; Faye catches Comet
|
178
|
+
requests and deals with them, letting all other requests fall down the
|
179
|
+
stack of Rack middlewares.
|
72
180
|
|
73
181
|
|
74
182
|
== To-do
|
75
183
|
|
76
184
|
* Provide support for user-defined <tt>/service/*</tt> channels
|
77
|
-
* Support Thin's async response
|
78
185
|
* Allow server to scale to multiple nodes
|
79
186
|
* Provide a server-side client
|
80
|
-
* Console for scripting browsers from the command line
|
81
|
-
|
82
|
-
|
83
|
-
== Installation
|
84
|
-
|
85
|
-
sudo gem install hoe faye
|
86
187
|
|
87
188
|
|
88
189
|
== License
|
89
190
|
|
90
191
|
(The MIT License)
|
91
192
|
|
92
|
-
Copyright (c) 2009 James Coglan
|
193
|
+
Copyright (c) 2009-2010 James Coglan
|
93
194
|
|
94
195
|
Permission is hereby granted, free of charge, to any person obtaining
|
95
196
|
a copy of this software and associated documentation files (the
|
@@ -109,3 +210,4 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
109
210
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
110
211
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
111
212
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
213
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
if(!this.Faye)Faye={};Faye.extend=function(a,b,c){if(!b)return a;for(var d in b){if(b.hasOwnProperty(d)&&a[d]!==b[d]){if(!a.hasOwnProperty(d)||c!==false)a[d]=b[d]}}return a};Faye.extend(Faye,{BAYEUX_VERSION:'1.0',VERSION:'0.2.2',JSONP_CALLBACK:'jsonpcallback',ID_LENGTH:128,CONNECTION_TYPES:["long-polling","callback-polling"],ENV:this,Grammar:{LOWALPHA:/^[a-z]$/,UPALPHA:/^[A-Z]$/,ALPHA:/^([a-z]|[A-Z])$/,DIGIT:/^[0-9]$/,ALPHANUM:/^(([a-z]|[A-Z])|[0-9])$/,MARK:/^(\-|\_|\!|\~|\(|\)|\$|\@)$/,STRING:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,TOKEN:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,INTEGER:/^([0-9])+$/,CHANNEL_SEGMENT:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,CHANNEL_SEGMENTS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,WILD_CARD:/^\*{1,2}$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,VERSION_ELEMENT:/^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,CLIENT_ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ID:/^((([a-z]|[A-Z])|[0-9]))+$/,ERROR_MESSAGE:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,ERROR_ARGS:/^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,ERROR_CODE:/^[0-9][0-9][0-9]$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/},commonElement:function(a,b){for(var c=0,d=a.length;c<d;c++){if(this.indexOf(b,a[c])!==-1)return a[c]}return null},indexOf:function(a,b){for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},each:function(a,b,c){if(a instanceof Array){for(var d=0,f=a.length;d<f;d++){if(a[d]!==undefined)b.call(c||null,a[d],d)}}else{for(var g in a){if(a.hasOwnProperty(g))b.call(c||null,g,a[g])}}},size:function(a){var b=0;this.each(a,function(){b+=1});return b},random:function(){var a=Math.pow(2,this.ID_LENGTH);return(Math.random()*a).toString(16).replace(/0*$/,'')},enumEqual:function(c,d){if(d instanceof Array){if(!(c instanceof Array))return false;var f=c.length;if(f!==d.length)return false;while(f--){if(c[f]!==d[f])return false}return true}else{if(!(c instanceof Object))return false;if(this.size(d)!==this.size(c))return false;var g=true;this.each(c,function(a,b){g=g&&(d[a]===b)});return g}}});Faye.Class=function(a,b){if(typeof a!=='function'){b=a;a=Object}var c=function(){if(!this.initialize)return this;return this.initialize.apply(this,arguments)||this};var d=function(){};d.prototype=a.prototype;c.prototype=new d();Faye.extend(c.prototype,b);return c};Faye.Observable={on:function(a,b,c){this._1=this._1||{};var d=this._1[a]=this._1[a]||[];d.push([b,c])},stopObserving:function(a,b,c){if(!this._1||!this._1[a])return;if(!b){delete this._1[a];return}var d=this._1[a],f=d.length;while(f--){if(b&&d[f][0]!==b)continue;if(c&&d[f][1]!==c)continue;d.splice(f,1)}},fire:function(){var b=Array.prototype.slice.call(arguments),c=b.shift();if(!this._1||!this._1[c])return;Faye.each(this._1[c],function(a){a[0].apply(a[1],b.slice())})}};Faye.Channel=Faye.Class({initialize:function(a){this.__id=this._A=a},push:function(a){this.fire('message',a)}});Faye.extend(Faye.Channel.prototype,Faye.Observable);Faye.extend(Faye.Channel,{HANDSHAKE:'/meta/handshake',CONNECT:'/meta/connect',SUBSCRIBE:'/meta/subscribe',UNSUBSCRIBE:'/meta/unsubscribe',DISCONNECT:'/meta/disconnect',META:'meta',SERVICE:'service',isValid:function(a){return Faye.Grammar.CHANNEL_NAME.test(a)||Faye.Grammar.CHANNEL_PATTERN.test(a)},parse:function(a){if(!this.isValid(a))return null;return a.split('/').slice(1)},isMeta:function(a){var b=this.parse(a);return b?(b[0]===this.META):null},isService:function(a){var b=this.parse(a);return b?(b[0]===this.SERVICE):null},isSubscribable:function(a){if(!this.isValid(a))return null;return!this.isMeta(a)&&!this.isService(a)},Tree:Faye.Class({initialize:function(a){this._3=a;this._5={}},eachChild:function(c,d){Faye.each(this._5,function(a,b){c.call(d,a,b)})},each:function(c,d,f){this.eachChild(function(a,b){a=c.concat(a);b.each(a,d,f)});if(this._3!==undefined)d.call(f,c,this._3)},map:function(c,d){var f=[];this.each([],function(a,b){f.push(c.call(d,a,b))});return f},get:function(a){var b=this.traverse(a);return b?b._3:null},set:function(a,b){var c=this.traverse(a,true);if(c)c._3=b},traverse:function(a,b){if(typeof a==='string')a=Faye.Channel.parse(a);if(a===null)return null;if(a.length===0)return this;var c=this._5[a[0]];if(!c&&!b)return null;if(!c)c=this._5[a[0]]=new Faye.Channel.Tree();return c.traverse(a.slice(1),b)},findOrCreate:function(a){var b=this.get(a);if(b)return b;b=new Faye.Channel(a);this.set(a,b);return b},glob:function(f){if(typeof f==='string')f=Faye.Channel.parse(f);if(f===null)return[];if(f.length===0)return(this._3===undefined)?[]:[this._3];var g=[];if(Faye.enumEqual(f,['*'])){Faye.each(this._5,function(a,b){if(b._3!==undefined)g.push(b._3)});return g}if(Faye.enumEqual(f,['**'])){g=this.map(function(a,b){return b});g.pop();return g}Faye.each(this._5,function(b,c){if(b!==f[0]&&b!=='*')return;var d=c.glob(f.slice(1));Faye.each(d,function(a){g.push(a)})});if(this._5['**'])g.push(this._5['**']._3);return g}})});Faye.Event={_a:[],on:function(a,b,c,d){var f=function(){c.call(d)};if(a.addEventListener)a.addEventListener(b,f,false);else a.attachEvent('on'+b,f);this._a.push({_b:a,_h:b,_s:c,_c:d,_m:f})},detach:function(a,b,c,d){var f=this._a.length,g;while(f--){g=this._a[f];if((a&&a!==g._b)||(b&&b!==g._h)||(c&&c!==g._s)||(d&&d!==g._c))continue;if(g._b.removeEventListener)g._b.removeEventListener(g._h,g._m,false);else g._b.detachEvent('on'+g._h,g._m);this._a.splice(f,1);g=null}}};Faye.Event.on(Faye.ENV,'unload',Faye.Event.detach,Faye.Event);Faye.URI=Faye.extend(Faye.Class({queryString:function(){var c=[],d;Faye.each(this.params,function(a,b){c.push(encodeURIComponent(a)+'='+encodeURIComponent(b))});return c.join('&')},isLocal:function(){var a=Faye.URI.parse(Faye.ENV.location.href);var b=(a.hostname!==this.hostname)||(a.port!==this.port)||(a.protocol!==this.protocol);return!b},toURL:function(){return this.protocol+this.hostname+':'+this.port+this.pathname+'?'+this.queryString()}}),{parse:function(d,f){if(typeof d!=='string')return d;var g=new this();var k=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};k('protocol',/^https?\:\/+/);k('hostname',/^[^\/\:]+/);k('port',/^:[0-9]+/);Faye.extend(g,{protocol:'http://',hostname:Faye.ENV.location.hostname,port:Faye.ENV.location.port},false);if(!g.port)g.port=(g.protocol==='https://')?'443':'80';g.port=g.port.replace(/\D/g,'');var i=d.split('?'),h=i.shift(),l=i.join('?'),n=l?l.split('&'):[],o=n.length,j={};while(o--){i=n[o].split('=');j[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}Faye.extend(j,f);g.pathname=h;g.params=j;return g}});Faye.XHR={request:function(a,b,c,d,f){var g=new this.Request(a,b,c,d,f);g.send();return g},getXhrObject:function(){return Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},Request:Faye.Class({initialize:function(a,b,c,d,f){this._d=a.toUpperCase();this._6=Faye.URI.parse(b,c);this._t=(typeof d==='function')?{success:d}:d;this._c=f||null;this._2=null},send:function(){if(this._i)return;var a=this._6.pathname,b=this._6.queryString();if(this._d==='GET')a+='?'+b;var c=this._d==='POST'?b:'';this._i=true;this._2=Faye.XHR.getXhrObject();this._2.open(this._d,a,true);if(this._d==='POST')this._2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');var d=this,f=function(){if(d._2.readyState!==4)return;if(g){clearInterval(g);g=null}Faye.Event.detach(Faye.ENV,'beforeunload',d.abort,d);d._i=false;d._u();d=null};var g=setInterval(f,10);Faye.Event.on(Faye.ENV,'beforeunload',this.abort,this);this._2.send(c)},abort:function(){this._2.abort()},_u:function(){var a=this._t;if(!a)return;return this.success()?a.success&&a.success.call(this._c,this):a.failure&&a.failure.call(this._c,this)},waiting:function(){return!!this._i},complete:function(){return this._2&&!this.waiting()},success:function(){if(!this.complete())return false;var a=this._2.status;return(a>=200&&a<300)||a===304||a===1223},failure:function(){if(!this.complete())return false;return!this.success()},text:function(){if(!this.complete())return null;return this._2.responseText},status:function(){if(!this.complete())return null;return this._2.status}})};Faye.Transport=Faye.extend(Faye.Class({initialize:function(a,b){this._n=a;this._6=b},send:function(c,d,f){c={message:JSON.stringify(c)};return this.request(c,function(b){if(!d)return;Faye.each([].concat(b),function(a){d.call(f,a);if(a.advice)this._n._v(a.advice);if(a.data&&a.channel)this._n._w(a)},this)},this)}}),{get:function(a,b){var c=a._6;if(b===undefined)b=this.supportedConnectionTypes();var d=Faye.URI.parse(c).isLocal()?['long-polling','callback-polling']:['callback-polling'];var f=Faye.commonElement(d,b);if(!f)throw'Could not find a usable connection type for '+c;var g=this._j[f];return new g(a,c)},register:function(a,b){this._j[a]=b;b.prototype.connectionType=a},_j:{},supportedConnectionTypes:function(){var c=[],d;Faye.each(this._j,function(a,b){c.push(a)});return c}});Faye.XHRTransport=Faye.Class(Faye.Transport,{request:function(b,c,d){Faye.XHR.request('post',this._6,b,function(a){if(c)c.call(d,JSON.parse(a.text()))})}});Faye.Transport.register('long-polling',Faye.XHRTransport);Faye.JSONPTransport=Faye.extend(Faye.Class(Faye.Transport,{request:function(b,c,d){var f=document.getElementsByTagName('head')[0],g=document.createElement('script'),k=Faye.JSONPTransport.getCallbackName(),i=Faye.URI.parse(this._6,b);Faye.ENV[k]=function(a){Faye.ENV[k]=undefined;try{delete Faye.ENV[k]}catch(e){}f.removeChild(g);if(c)c.call(d,a)};i.params.jsonp=k;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_o:0,getCallbackName:function(){this._o+=1;return'__jsonp'+this._o+'__'}});Faye.Transport.register('callback-polling',Faye.JSONPTransport);Faye.Client=Faye.Class({_e:{},_x:{},_8:{},_y:{},_p:'handshake',_z:'retry',_q:'none',DEFAULT_ENDPOINT:'/bayeux',MAX_DELAY:0.1,INTERVAL:1000.0,initialize:function(a){this._6=a||this.DEFAULT_ENDPOINT;this._4=Faye.Transport.get(this);this._0=this._e;this._k=[];this._f=new Faye.Channel.Tree();this._7={reconnect:this._z,interval:this.INTERVAL};Faye.Event.on(Faye.ENV,'beforeunload',this.disconnect,this)},handshake:function(b,c){if(this._7.reconnect===this._q)return;if(this._0!==this._e)return;this._0=this._x;var d=this,f=this.generateId();this._4.send({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:Faye.Transport.supportedConnectionTypes(),id:f},function(a){if(a.id!==f)return;if(!a.successful){setTimeout(function(){d.handshake(b,c)},this._7.interval);return this._0=this._e}this._0=this._8;this._9=a.clientId;this._4=Faye.Transport.get(this,a.supportedConnectionTypes);if(b)b.call(c)},this)},connect:function(b,c){if(this._7.reconnect===this._q)return;if(this._7.reconnect===this._p||this._0===this._e)return this.handshake(function(){this.connect(b,c)},this);if(this._0!==this._8)return;if(this._g)return;this._g=this.generateId();var d=this;this._4.send({channel:Faye.Channel.CONNECT,clientId:this._9,connectionType:this._4.connectionType,id:this._g},function(a){if(a.id!==this._g)return;delete this._g;if(a.successful)this.connect();else setTimeout(function(){d.connect()},this._7.interval)},this);if(b)b.call(c)},disconnect:function(){if(this._0!==this._8)return;this._0=this._y;this._4.send({channel:Faye.Channel.DISCONNECT,clientId:this._9});this._f=new Faye.Channel.Tree()},subscribe:function(c,d,f){if(this._0!==this._8)return;c=[].concat(c);this._l(c);var g=this.generateId();this._4.send({channel:Faye.Channel.SUBSCRIBE,clientId:this._9,subscription:c,id:g},function(b){if(b.id!==g)return;if(!b.successful)return;c=[].concat(b.subscription);Faye.each(c,function(a){this._f.set(a,[d,f])},this)},this)},unsubscribe:function(c,d,f){if(this._0!==this._8)return;c=[].concat(c);this._l(c);var g=this.generateId();this._4.send({channel:Faye.Channel.UNSUBSCRIBE,clientId:this._9,subscription:c,id:g},function(b){if(b.id!==g)return;if(!b.successful)return;c=[].concat(b.subscription);Faye.each(c,function(a){this._f.set(a,null)},this)},this)},publish:function(a,b){if(this._0!==this._8)return;this._l([a]);this.enqueue({channel:a,data:b,clientId:this._9});if(this._r)return;var c=this;this._r=setTimeout(function(){delete c._r;c.flush()},this.MAX_DELAY*1000)},generateId:function(a){a=a||32;return Math.floor(Math.pow(2,a)*Math.random()).toString(16)},enqueue:function(a){this._k.push(a)},flush:function(){this._4.send(this._k);this._k=[]},_l:function(b){Faye.each(b,function(a){if(!Faye.Channel.isValid(a))throw'"'+a+'" is not a valid channel name';if(!Faye.Channel.isSubscribable(a))throw'Clients may not subscribe to channel "'+a+'"';})},_v:function(a){Faye.extend(this._7,a);if(this._7.reconnect===this._p)this._9=null},_w:function(b){var c=this._f.glob(b.channel);Faye.each(c,function(a){if(!a)return;a[0].call(a[1],b.data)})}});if(!this.JSON){JSON={}}(function(){function l(a){return a<10?'0'+a:a}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(a){return this.getUTCFullYear()+'-'+l(this.getUTCMonth()+1)+'-'+l(this.getUTCDate())+'T'+l(this.getUTCHours())+':'+l(this.getUTCMinutes())+':'+l(this.getUTCSeconds())+'Z'};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()}}var n=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j,p,s={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},m;function r(c){o.lastIndex=0;return o.test(c)?'"'+c.replace(o,function(a){var b=s[a];return typeof b==='string'?b:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+c+'"'}function q(a,b){var c,d,f,g,k=j,i,h=b[a];if(h&&typeof h==='object'&&typeof h.toJSON==='function'){h=h.toJSON(a)}if(typeof m==='function'){h=m.call(b,a,h)}switch(typeof h){case'string':return r(h);case'number':return isFinite(h)?String(h):'null';case'boolean':case'null':return String(h);case'object':if(!h){return'null'}j+=p;i=[];if(Object.prototype.toString.apply(h)==='[object Array]'){g=h.length;for(c=0;c<g;c+=1){i[c]=q(c,h)||'null'}f=i.length===0?'[]':j?'[\n'+j+i.join(',\n'+j)+'\n'+k+']':'['+i.join(',')+']';j=k;return f}if(m&&typeof m==='object'){g=m.length;for(c=0;c<g;c+=1){d=m[c];if(typeof d==='string'){f=q(d,h);if(f){i.push(r(d)+(j?': ':':')+f)}}}}else{for(d in h){if(Object.hasOwnProperty.call(h,d)){f=q(d,h);if(f){i.push(r(d)+(j?': ':':')+f)}}}}f=i.length===0?'{}':j?'{\n'+j+i.join(',\n'+j)+'\n'+k+'}':'{'+i.join(',')+'}';j=k;return f}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(a,b,c){var d;j='';p='';if(typeof c==='number'){for(d=0;d<c;d+=1){p+=' '}}else if(typeof c==='string'){p=c}m=b;if(b&&typeof b!=='function'&&(typeof b!=='object'||typeof b.length!=='number')){throw new Error('JSON.stringify');}return q('',{'':a})}}if(typeof JSON.parse!=='function'){JSON.parse=function(g,k){var i;function h(a,b){var c,d,f=a[b];if(f&&typeof f==='object'){for(c in f){if(Object.hasOwnProperty.call(f,c)){d=h(f,c);if(d!==undefined){f[c]=d}else{delete f[c]}}}}return k.call(a,b,f)}n.lastIndex=0;if(n.test(g)){g=g.replace(n,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(g.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){i=eval('('+g+')');return typeof k==='function'?h({'':i},''):i}throw new SyntaxError('JSON.parse');}}}());
|
data/build/faye.js
ADDED
@@ -0,0 +1,868 @@
|
|
1
|
+
if (!this.Faye) Faye = {};
|
2
|
+
|
3
|
+
Faye.extend = function(dest, source, overwrite) {
|
4
|
+
if (!source) return dest;
|
5
|
+
for (var key in source) {
|
6
|
+
if (source.hasOwnProperty(key) && dest[key] !== source[key]) {
|
7
|
+
if (!dest.hasOwnProperty(key) || overwrite !== false)
|
8
|
+
dest[key] = source[key];
|
9
|
+
}
|
10
|
+
}
|
11
|
+
return dest;
|
12
|
+
};
|
13
|
+
|
14
|
+
Faye.extend(Faye, {
|
15
|
+
BAYEUX_VERSION: '1.0',
|
16
|
+
VERSION: '0.2.2',
|
17
|
+
JSONP_CALLBACK: 'jsonpcallback',
|
18
|
+
ID_LENGTH: 128,
|
19
|
+
CONNECTION_TYPES: ["long-polling", "callback-polling"],
|
20
|
+
|
21
|
+
ENV: this,
|
22
|
+
|
23
|
+
Grammar: {
|
24
|
+
|
25
|
+
LOWALPHA: /^[a-z]$/,
|
26
|
+
|
27
|
+
UPALPHA: /^[A-Z]$/,
|
28
|
+
|
29
|
+
ALPHA: /^([a-z]|[A-Z])$/,
|
30
|
+
|
31
|
+
DIGIT: /^[0-9]$/,
|
32
|
+
|
33
|
+
ALPHANUM: /^(([a-z]|[A-Z])|[0-9])$/,
|
34
|
+
|
35
|
+
MARK: /^(\-|\_|\!|\~|\(|\)|\$|\@)$/,
|
36
|
+
|
37
|
+
STRING: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
38
|
+
|
39
|
+
TOKEN: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
40
|
+
|
41
|
+
INTEGER: /^([0-9])+$/,
|
42
|
+
|
43
|
+
CHANNEL_SEGMENT: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
44
|
+
|
45
|
+
CHANNEL_SEGMENTS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
46
|
+
|
47
|
+
CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
48
|
+
|
49
|
+
WILD_CARD: /^\*{1,2}$/,
|
50
|
+
|
51
|
+
CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
|
52
|
+
|
53
|
+
VERSION_ELEMENT: /^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,
|
54
|
+
|
55
|
+
VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,
|
56
|
+
|
57
|
+
CLIENT_ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
58
|
+
|
59
|
+
ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
60
|
+
|
61
|
+
ERROR_MESSAGE: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
62
|
+
|
63
|
+
ERROR_ARGS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,
|
64
|
+
|
65
|
+
ERROR_CODE: /^[0-9][0-9][0-9]$/,
|
66
|
+
|
67
|
+
ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/
|
68
|
+
|
69
|
+
},
|
70
|
+
|
71
|
+
commonElement: function(lista, listb) {
|
72
|
+
for (var i = 0, n = lista.length; i < n; i++) {
|
73
|
+
if (this.indexOf(listb, lista[i]) !== -1)
|
74
|
+
return lista[i];
|
75
|
+
}
|
76
|
+
return null;
|
77
|
+
},
|
78
|
+
|
79
|
+
indexOf: function(list, needle) {
|
80
|
+
for (var i = 0, n = list.length; i < n; i++) {
|
81
|
+
if (list[i] === needle) return i;
|
82
|
+
}
|
83
|
+
return -1;
|
84
|
+
},
|
85
|
+
|
86
|
+
each: function(object, callback, scope) {
|
87
|
+
if (object instanceof Array) {
|
88
|
+
for (var i = 0, n = object.length; i < n; i++) {
|
89
|
+
if (object[i] !== undefined)
|
90
|
+
callback.call(scope || null, object[i], i);
|
91
|
+
}
|
92
|
+
} else {
|
93
|
+
for (var key in object) {
|
94
|
+
if (object.hasOwnProperty(key))
|
95
|
+
callback.call(scope || null, key, object[key]);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
},
|
99
|
+
|
100
|
+
size: function(object) {
|
101
|
+
var size = 0;
|
102
|
+
this.each(object, function() { size += 1 });
|
103
|
+
return size;
|
104
|
+
},
|
105
|
+
|
106
|
+
random: function() {
|
107
|
+
var field = Math.pow(2, this.ID_LENGTH);
|
108
|
+
return (Math.random() * field).toString(16).replace(/0*$/, '');
|
109
|
+
},
|
110
|
+
|
111
|
+
enumEqual: function(actual, expected) {
|
112
|
+
if (expected instanceof Array) {
|
113
|
+
if (!(actual instanceof Array)) return false;
|
114
|
+
var i = actual.length;
|
115
|
+
if (i !== expected.length) return false;
|
116
|
+
while (i--) {
|
117
|
+
if (actual[i] !== expected[i]) return false;
|
118
|
+
}
|
119
|
+
return true;
|
120
|
+
} else {
|
121
|
+
if (!(actual instanceof Object)) return false;
|
122
|
+
if (this.size(expected) !== this.size(actual)) return false;
|
123
|
+
var result = true;
|
124
|
+
this.each(actual, function(key, value) {
|
125
|
+
result = result && (expected[key] === value);
|
126
|
+
});
|
127
|
+
return result;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
});
|
131
|
+
|
132
|
+
|
133
|
+
Faye.Class = function(parent, methods) {
|
134
|
+
if (typeof parent !== 'function') {
|
135
|
+
methods = parent;
|
136
|
+
parent = Object;
|
137
|
+
}
|
138
|
+
|
139
|
+
var klass = function() {
|
140
|
+
if (!this.initialize) return this;
|
141
|
+
return this.initialize.apply(this, arguments) || this;
|
142
|
+
};
|
143
|
+
|
144
|
+
var bridge = function() {};
|
145
|
+
bridge.prototype = parent.prototype;
|
146
|
+
|
147
|
+
klass.prototype = new bridge();
|
148
|
+
Faye.extend(klass.prototype, methods);
|
149
|
+
|
150
|
+
return klass;
|
151
|
+
};
|
152
|
+
|
153
|
+
|
154
|
+
Faye.Observable = {
|
155
|
+
on: function(eventType, block, scope) {
|
156
|
+
this._observers = this._observers || {};
|
157
|
+
var list = this._observers[eventType] = this._observers[eventType] || [];
|
158
|
+
list.push([block, scope]);
|
159
|
+
},
|
160
|
+
|
161
|
+
stopObserving: function(eventType, block, scope) {
|
162
|
+
if (!this._observers || !this._observers[eventType]) return;
|
163
|
+
|
164
|
+
if (!block) {
|
165
|
+
delete this._observers[eventType];
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
var list = this._observers[eventType],
|
169
|
+
i = list.length;
|
170
|
+
|
171
|
+
while (i--) {
|
172
|
+
if (block && list[i][0] !== block) continue;
|
173
|
+
if (scope && list[i][1] !== scope) continue;
|
174
|
+
list.splice(i,1);
|
175
|
+
}
|
176
|
+
},
|
177
|
+
|
178
|
+
fire: function() {
|
179
|
+
var args = Array.prototype.slice.call(arguments),
|
180
|
+
eventType = args.shift();
|
181
|
+
|
182
|
+
if (!this._observers || !this._observers[eventType]) return;
|
183
|
+
|
184
|
+
Faye.each(this._observers[eventType], function(listener) {
|
185
|
+
listener[0].apply(listener[1], args.slice());
|
186
|
+
});
|
187
|
+
}
|
188
|
+
};
|
189
|
+
|
190
|
+
|
191
|
+
Faye.Channel = Faye.Class({
|
192
|
+
initialize: function(name) {
|
193
|
+
this.__id = this._name = name;
|
194
|
+
},
|
195
|
+
|
196
|
+
push: function(message) {
|
197
|
+
this.fire('message', message);
|
198
|
+
}
|
199
|
+
});
|
200
|
+
|
201
|
+
Faye.extend(Faye.Channel.prototype, Faye.Observable);
|
202
|
+
|
203
|
+
Faye.extend(Faye.Channel, {
|
204
|
+
HANDSHAKE: '/meta/handshake',
|
205
|
+
CONNECT: '/meta/connect',
|
206
|
+
SUBSCRIBE: '/meta/subscribe',
|
207
|
+
UNSUBSCRIBE: '/meta/unsubscribe',
|
208
|
+
DISCONNECT: '/meta/disconnect',
|
209
|
+
|
210
|
+
META: 'meta',
|
211
|
+
SERVICE: 'service',
|
212
|
+
|
213
|
+
isValid: function(name) {
|
214
|
+
return Faye.Grammar.CHANNEL_NAME.test(name) ||
|
215
|
+
Faye.Grammar.CHANNEL_PATTERN.test(name);
|
216
|
+
},
|
217
|
+
|
218
|
+
parse: function(name) {
|
219
|
+
if (!this.isValid(name)) return null;
|
220
|
+
return name.split('/').slice(1);
|
221
|
+
},
|
222
|
+
|
223
|
+
isMeta: function(name) {
|
224
|
+
var segments = this.parse(name);
|
225
|
+
return segments ? (segments[0] === this.META) : null;
|
226
|
+
},
|
227
|
+
|
228
|
+
isService: function(name) {
|
229
|
+
var segments = this.parse(name);
|
230
|
+
return segments ? (segments[0] === this.SERVICE) : null;
|
231
|
+
},
|
232
|
+
|
233
|
+
isSubscribable: function(name) {
|
234
|
+
if (!this.isValid(name)) return null;
|
235
|
+
return !this.isMeta(name) && !this.isService(name);
|
236
|
+
},
|
237
|
+
|
238
|
+
Tree: Faye.Class({
|
239
|
+
initialize: function(value) {
|
240
|
+
this._value = value;
|
241
|
+
this._children = {};
|
242
|
+
},
|
243
|
+
|
244
|
+
eachChild: function(block, context) {
|
245
|
+
Faye.each(this._children, function(key, subtree) {
|
246
|
+
block.call(context, key, subtree);
|
247
|
+
});
|
248
|
+
},
|
249
|
+
|
250
|
+
each: function(prefix, block, context) {
|
251
|
+
this.eachChild(function(path, subtree) {
|
252
|
+
path = prefix.concat(path);
|
253
|
+
subtree.each(path, block, context);
|
254
|
+
});
|
255
|
+
if (this._value !== undefined) block.call(context, prefix, this._value);
|
256
|
+
},
|
257
|
+
|
258
|
+
map: function(block, context) {
|
259
|
+
var result = [];
|
260
|
+
this.each([], function(path, value) {
|
261
|
+
result.push(block.call(context, path, value));
|
262
|
+
});
|
263
|
+
return result;
|
264
|
+
},
|
265
|
+
|
266
|
+
get: function(name) {
|
267
|
+
var tree = this.traverse(name);
|
268
|
+
return tree ? tree._value : null;
|
269
|
+
},
|
270
|
+
|
271
|
+
set: function(name, value) {
|
272
|
+
var subtree = this.traverse(name, true);
|
273
|
+
if (subtree) subtree._value = value;
|
274
|
+
},
|
275
|
+
|
276
|
+
traverse: function(path, createIfAbsent) {
|
277
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
278
|
+
|
279
|
+
if (path === null) return null;
|
280
|
+
if (path.length === 0) return this;
|
281
|
+
|
282
|
+
var subtree = this._children[path[0]];
|
283
|
+
if (!subtree && !createIfAbsent) return null;
|
284
|
+
if (!subtree) subtree = this._children[path[0]] = new Faye.Channel.Tree();
|
285
|
+
|
286
|
+
return subtree.traverse(path.slice(1), createIfAbsent);
|
287
|
+
},
|
288
|
+
|
289
|
+
findOrCreate: function(channel) {
|
290
|
+
var existing = this.get(channel);
|
291
|
+
if (existing) return existing;
|
292
|
+
existing = new Faye.Channel(channel);
|
293
|
+
this.set(channel, existing);
|
294
|
+
return existing;
|
295
|
+
},
|
296
|
+
|
297
|
+
glob: function(path) {
|
298
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
299
|
+
|
300
|
+
if (path === null) return [];
|
301
|
+
if (path.length === 0) return (this._value === undefined) ? [] : [this._value];
|
302
|
+
|
303
|
+
var list = [];
|
304
|
+
|
305
|
+
if (Faye.enumEqual(path, ['*'])) {
|
306
|
+
Faye.each(this._children, function(key, subtree) {
|
307
|
+
if (subtree._value !== undefined) list.push(subtree._value);
|
308
|
+
});
|
309
|
+
return list;
|
310
|
+
}
|
311
|
+
|
312
|
+
if (Faye.enumEqual(path, ['**'])) {
|
313
|
+
list = this.map(function(key, value) { return value });
|
314
|
+
list.pop();
|
315
|
+
return list;
|
316
|
+
}
|
317
|
+
|
318
|
+
Faye.each(this._children, function(key, subtree) {
|
319
|
+
if (key !== path[0] && key !== '*') return;
|
320
|
+
var sublist = subtree.glob(path.slice(1));
|
321
|
+
Faye.each(sublist, function(channel) { list.push(channel) });
|
322
|
+
});
|
323
|
+
|
324
|
+
if (this._children['**']) list.push(this._children['**']._value);
|
325
|
+
return list;
|
326
|
+
}
|
327
|
+
|
328
|
+
/**
|
329
|
+
Tests
|
330
|
+
|
331
|
+
glob = new Faye.Channel.Tree();
|
332
|
+
list = '/foo/bar /foo/boo /foo /foobar /foo/bar/boo /foobar/boo /foo/* /foo/**'.split(' ');
|
333
|
+
|
334
|
+
Faye.each(list, function(c, i) {
|
335
|
+
glob.set(c, i + 1);
|
336
|
+
});
|
337
|
+
|
338
|
+
console.log(glob.glob('/foo/*').sort()); // 1,2,7,8
|
339
|
+
console.log(glob.glob('/foo/bar').sort()); // 1,7,8
|
340
|
+
console.log(glob.glob('/foo/**').sort()); // 1,2,5,7,8
|
341
|
+
console.log(glob.glob('/foo/bar/boo').sort()); // 5,8
|
342
|
+
**/
|
343
|
+
})
|
344
|
+
});
|
345
|
+
|
346
|
+
|
347
|
+
Faye.Set = Faye.Class({
|
348
|
+
initialize: function() {
|
349
|
+
this._index = {};
|
350
|
+
},
|
351
|
+
|
352
|
+
add: function(item) {
|
353
|
+
var key = (item.__id !== undefined) ? item.__id : item;
|
354
|
+
if (this._index.hasOwnProperty(key)) return false;
|
355
|
+
this._index[key] = item;
|
356
|
+
return true;
|
357
|
+
},
|
358
|
+
|
359
|
+
forEach: function(block, scope) {
|
360
|
+
for (var key in this._index) {
|
361
|
+
if (this._index.hasOwnProperty(key))
|
362
|
+
block.call(scope, this._index[key]);
|
363
|
+
}
|
364
|
+
},
|
365
|
+
|
366
|
+
isEmpty: function() {
|
367
|
+
for (var key in this._index) {
|
368
|
+
if (this._index.hasOwnProperty(key)) return false;
|
369
|
+
}
|
370
|
+
return true;
|
371
|
+
},
|
372
|
+
|
373
|
+
member: function(item) {
|
374
|
+
for (var key in this._index) {
|
375
|
+
if (this._index[key] === item) return true;
|
376
|
+
}
|
377
|
+
return false;
|
378
|
+
},
|
379
|
+
|
380
|
+
remove: function(item) {
|
381
|
+
var key = (item.__id !== undefined) ? item.__id : item;
|
382
|
+
delete this._index[key];
|
383
|
+
},
|
384
|
+
|
385
|
+
toArray: function() {
|
386
|
+
var array = [];
|
387
|
+
this.forEach(function(item) { array.push(item) });
|
388
|
+
return array;
|
389
|
+
}
|
390
|
+
});
|
391
|
+
|
392
|
+
|
393
|
+
Faye.Server = Faye.Class({
|
394
|
+
initialize: function(options) {
|
395
|
+
this._options = options || {};
|
396
|
+
this._channels = new Faye.Channel.Tree();
|
397
|
+
this._clients = {};
|
398
|
+
},
|
399
|
+
|
400
|
+
clientIds: function() {
|
401
|
+
var ids = [];
|
402
|
+
Faye.each(this._clients, function(key, value) { ids.push(key) });
|
403
|
+
return ids;
|
404
|
+
},
|
405
|
+
|
406
|
+
process: function(messages, local, callback) {
|
407
|
+
messages = (messages instanceof Array) ? messages : [messages];
|
408
|
+
var processed = 0, responses = [];
|
409
|
+
|
410
|
+
Faye.each(messages, function(message) {
|
411
|
+
this._handle(message, local, function(reply) {
|
412
|
+
responses = responses.concat(reply);
|
413
|
+
processed += 1;
|
414
|
+
if (processed === messages.length) callback(responses);
|
415
|
+
});
|
416
|
+
}, this);
|
417
|
+
},
|
418
|
+
|
419
|
+
flushConnection: function(messages) {
|
420
|
+
messages = (messages instanceof Array) ? messages : [messages];
|
421
|
+
Faye.each(messages, function(message) {
|
422
|
+
var client = this._clients[message.clientId];
|
423
|
+
if (client) client.flush();
|
424
|
+
}, this);
|
425
|
+
},
|
426
|
+
|
427
|
+
_generateId: function() {
|
428
|
+
var id = Faye.random();
|
429
|
+
while (this._clients.hasOwnProperty(id)) id = Faye.random();
|
430
|
+
return this._connection(id).id;
|
431
|
+
},
|
432
|
+
|
433
|
+
_connection: function(id) {
|
434
|
+
if (this._clients.hasOwnProperty(id)) return this._clients[id];
|
435
|
+
var client = new Faye.Connection(id, this._options);
|
436
|
+
client.on('stale', this._destroyClient, this);
|
437
|
+
return this._clients[id] = client;
|
438
|
+
},
|
439
|
+
|
440
|
+
_destroyClient: function(client) {
|
441
|
+
client.disconnect();
|
442
|
+
client.stopObserving('stale', this._destroyClient, this);
|
443
|
+
delete this._clients[client.id];
|
444
|
+
},
|
445
|
+
|
446
|
+
_handle: function(message, local, callback) {
|
447
|
+
var clientId = message.clientId,
|
448
|
+
channel = message.channel,
|
449
|
+
response;
|
450
|
+
|
451
|
+
if (Faye.Channel.isMeta(channel)) {
|
452
|
+
response = this[Faye.Channel.parse(channel)[1]](message, local);
|
453
|
+
|
454
|
+
clientId = clientId || response.clientId;
|
455
|
+
response.advice = response.advice || {};
|
456
|
+
Faye.extend(response.advice, {
|
457
|
+
reconnect: this._clients.hasOwnProperty(clientId) ? 'retry' : 'handshake',
|
458
|
+
interval: Faye.Connection.INTERVAL * 1000
|
459
|
+
}, false);
|
460
|
+
|
461
|
+
response.id = message.id;
|
462
|
+
|
463
|
+
if (response.channel !== Faye.Channel.CONNECT ||
|
464
|
+
response.successful !== true)
|
465
|
+
return callback(response);
|
466
|
+
|
467
|
+
return this._connection(response.clientId).connect(function(events) {
|
468
|
+
Faye.each(events, function(e) { delete e.__id });
|
469
|
+
callback([response].concat(events));
|
470
|
+
});
|
471
|
+
}
|
472
|
+
|
473
|
+
if (!message.clientId || Faye.Channel.isService(channel))
|
474
|
+
return callback([]);
|
475
|
+
|
476
|
+
message.__id = Faye.random();
|
477
|
+
Faye.each(this._channels.glob(channel), function(c) { c.push(message) });
|
478
|
+
|
479
|
+
callback( { channel: channel,
|
480
|
+
successful: true,
|
481
|
+
id: message.id } );
|
482
|
+
},
|
483
|
+
|
484
|
+
handshake: function(message, local) {
|
485
|
+
var response = { channel: Faye.Channel.HANDSHAKE,
|
486
|
+
version: Faye.BAYEUX_VERSION,
|
487
|
+
supportedConnectionTypes: Faye.CONNECTION_TYPES,
|
488
|
+
id: message.id };
|
489
|
+
|
490
|
+
if (!message.version)
|
491
|
+
response.error = Faye.Error.parameterMissing('version');
|
492
|
+
|
493
|
+
var clientConns = message.supportedConnectionTypes,
|
494
|
+
commonConns;
|
495
|
+
|
496
|
+
if (clientConns) {
|
497
|
+
commonConns = clientConns.filter(function(conn) {
|
498
|
+
return Faye.CONNECTION_TYPES.indexOf(conn) !== -1;
|
499
|
+
});
|
500
|
+
if (commonConns.length === 0)
|
501
|
+
response.error = Faye.Error.conntypeMismatch(clientConns);
|
502
|
+
} else {
|
503
|
+
response.error = Faye.Error.parameterMissing('supportedConnectionTypes');
|
504
|
+
}
|
505
|
+
|
506
|
+
response.successful = !response.error;
|
507
|
+
if (!response.successful) return response;
|
508
|
+
|
509
|
+
response.clientId = this._generateId();
|
510
|
+
return response;
|
511
|
+
},
|
512
|
+
|
513
|
+
connect: function(message, local) {
|
514
|
+
var response = { channel: Faye.Channel.CONNECT,
|
515
|
+
id: message.id };
|
516
|
+
|
517
|
+
var clientId = message.clientId,
|
518
|
+
client = clientId ? this._clients[clientId] : null,
|
519
|
+
connectionType = message.connectionType;
|
520
|
+
|
521
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
522
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
523
|
+
if (!connectionType) response.error = Faye.Error.parameterMissing('connectionType');
|
524
|
+
|
525
|
+
response.successful = !response.error;
|
526
|
+
if (!response.successful) return response;
|
527
|
+
|
528
|
+
response.clientId = client.id;
|
529
|
+
return response;
|
530
|
+
},
|
531
|
+
|
532
|
+
disconnect: function(message, local) {
|
533
|
+
var response = { channel: Faye.Channel.DISCONNECT,
|
534
|
+
id: message.id };
|
535
|
+
|
536
|
+
var clientId = message.clientId,
|
537
|
+
client = clientId ? this._clients[clientId] : null;
|
538
|
+
|
539
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
540
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
541
|
+
|
542
|
+
response.successful = !response.error;
|
543
|
+
if (!response.successful) return response;
|
544
|
+
|
545
|
+
this._destroyClient(client);
|
546
|
+
|
547
|
+
response.clientId = clientId;
|
548
|
+
return response;
|
549
|
+
},
|
550
|
+
|
551
|
+
subscribe: function(message, local) {
|
552
|
+
var response = { channel: Faye.Channel.SUBSCRIBE,
|
553
|
+
clientId: message.clientId,
|
554
|
+
id: message.id };
|
555
|
+
|
556
|
+
var clientId = message.clientId,
|
557
|
+
client = clientId ? this._clients[clientId] : null,
|
558
|
+
subscription = message.subscription;
|
559
|
+
|
560
|
+
subscription = (subscription instanceof Array) ? subscription : [subscription];
|
561
|
+
|
562
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
563
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
564
|
+
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
565
|
+
|
566
|
+
response.subscription = subscription;
|
567
|
+
|
568
|
+
Faye.each(subscription, function(channel) {
|
569
|
+
if (response.error) return;
|
570
|
+
if (!Faye.Channel.isSubscribable(channel)) response.error = Faye.Error.channelForbidden(channel);
|
571
|
+
if (!Faye.Channel.isValid(channel)) response.error = Faye.Error.channelInvalid(channel);
|
572
|
+
|
573
|
+
if (response.error) return;
|
574
|
+
channel = this._channels.findOrCreate(channel);
|
575
|
+
client.subscribe(channel);
|
576
|
+
}, this);
|
577
|
+
|
578
|
+
response.successful = !response.error;
|
579
|
+
return response;
|
580
|
+
},
|
581
|
+
|
582
|
+
unsubscribe: function(message, local) {
|
583
|
+
var response = { channel: Faye.Channel.UNSUBSCRIBE,
|
584
|
+
clientId: message.clientId,
|
585
|
+
id: message.id };
|
586
|
+
|
587
|
+
var clientId = message.clientId,
|
588
|
+
client = clientId ? this._clients[clientId] : null,
|
589
|
+
subscription = message.subscription;
|
590
|
+
|
591
|
+
subscription = (subscription instanceof Array) ? subscription : [subscription];
|
592
|
+
|
593
|
+
if (!client) response.error = Faye.Error.clientUnknown(clientId);
|
594
|
+
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
595
|
+
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
596
|
+
|
597
|
+
Faye.each(subscription, function(channel) {
|
598
|
+
if (response.error) return;
|
599
|
+
|
600
|
+
if (!Faye.Channel.isValid(channel))
|
601
|
+
return response.error = Faye.Error.channelInvalid(channel);
|
602
|
+
|
603
|
+
channel = this._channels.get(channel);
|
604
|
+
if (channel) client.unsubscribe(channel);
|
605
|
+
}, this);
|
606
|
+
|
607
|
+
response.successful = !response.error;
|
608
|
+
return response;
|
609
|
+
}
|
610
|
+
});
|
611
|
+
|
612
|
+
|
613
|
+
Faye.Connection = Faye.Class({
|
614
|
+
initialize: function(id, options) {
|
615
|
+
this.id = id;
|
616
|
+
this._options = options;
|
617
|
+
this._observers = {};
|
618
|
+
this._channels = new Faye.Set();
|
619
|
+
this._inbox = new Faye.Set();
|
620
|
+
},
|
621
|
+
|
622
|
+
timeout: function() {
|
623
|
+
return this._options.timeout || Faye.Connection.TIMEOUT;
|
624
|
+
},
|
625
|
+
|
626
|
+
_onMessage: function(event) {
|
627
|
+
this._inbox.add(event);
|
628
|
+
this._beginDeliveryTimeout();
|
629
|
+
},
|
630
|
+
|
631
|
+
subscribe: function(channel) {
|
632
|
+
if (!this._channels.add(channel)) return;
|
633
|
+
channel.on('message', this._onMessage, this);
|
634
|
+
},
|
635
|
+
|
636
|
+
unsubscribe: function(channel) {
|
637
|
+
if (channel === 'all') return this._channels.forEach(this.unsubscribe, this);
|
638
|
+
if (!this._channels.member(channel)) return;
|
639
|
+
this._channels.remove(channel);
|
640
|
+
channel.stopObserving('message', this._onMessage, this);
|
641
|
+
},
|
642
|
+
|
643
|
+
connect: function(callback) {
|
644
|
+
this.on('flush', callback);
|
645
|
+
if (this._connected) return;
|
646
|
+
|
647
|
+
this._markForDeletion = false;
|
648
|
+
this._connected = true;
|
649
|
+
|
650
|
+
if (!this._inbox.isEmpty()) this._beginDeliveryTimeout();
|
651
|
+
this._beginConnectionTimeout();
|
652
|
+
},
|
653
|
+
|
654
|
+
flush: function() {
|
655
|
+
if (!this._connected) return;
|
656
|
+
this._releaseConnection();
|
657
|
+
|
658
|
+
var events = this._inbox.toArray();
|
659
|
+
this._inbox = new Faye.Set();
|
660
|
+
|
661
|
+
this.fire('flush', events);
|
662
|
+
this.stopObserving('flush');
|
663
|
+
},
|
664
|
+
|
665
|
+
disconnect: function() {
|
666
|
+
this.unsubscribe('all');
|
667
|
+
this.flush();
|
668
|
+
},
|
669
|
+
|
670
|
+
_beginDeliveryTimeout: function() {
|
671
|
+
if (this._deliveryTimeout || !this._connected || this._inbox.isEmpty())
|
672
|
+
return;
|
673
|
+
|
674
|
+
var self = this;
|
675
|
+
this._deliveryTimeout = setTimeout(function () { self.flush() },
|
676
|
+
Faye.Connection.MAX_DELAY * 1000);
|
677
|
+
},
|
678
|
+
|
679
|
+
_beginConnectionTimeout: function() {
|
680
|
+
if (this._connectionTimeout || !this._connected)
|
681
|
+
return;
|
682
|
+
|
683
|
+
var self = this;
|
684
|
+
this._connectionTimeout = setTimeout(function() { self.flush() },
|
685
|
+
this.timeout() * 1000);
|
686
|
+
},
|
687
|
+
|
688
|
+
_releaseConnection: function() {
|
689
|
+
if (this._connectionTimeout) {
|
690
|
+
clearTimeout(this._connectionTimeout);
|
691
|
+
delete this._connectionTimeout;
|
692
|
+
}
|
693
|
+
|
694
|
+
if (this._deliveryTimeout) {
|
695
|
+
clearTimeout(this._deliveryTimeout);
|
696
|
+
delete this._deliveryTimeout;
|
697
|
+
}
|
698
|
+
|
699
|
+
this._connected = false;
|
700
|
+
this._scheduleForDeletion();
|
701
|
+
},
|
702
|
+
|
703
|
+
_scheduleForDeletion: function() {
|
704
|
+
if (this._markForDeletion) return;
|
705
|
+
this._markForDeletion = true;
|
706
|
+
var self = this;
|
707
|
+
|
708
|
+
setTimeout(function() {
|
709
|
+
if (!self._markForDeletion) return;
|
710
|
+
self.fire('stale', self);
|
711
|
+
}, 10000 * Faye.Connection.INTERVAL);
|
712
|
+
}
|
713
|
+
});
|
714
|
+
|
715
|
+
Faye.extend(Faye.Connection.prototype, Faye.Observable);
|
716
|
+
|
717
|
+
Faye.extend(Faye.Connection, {
|
718
|
+
MAX_DELAY: 0.1,
|
719
|
+
INTERVAL: 1.0,
|
720
|
+
TIMEOUT: 60.0
|
721
|
+
});
|
722
|
+
|
723
|
+
|
724
|
+
Faye.Error = Faye.Class({
|
725
|
+
initialize: function(code, args, message) {
|
726
|
+
this.code = code;
|
727
|
+
this.args = Array.prototype.slice.call(args);
|
728
|
+
this.message = message;
|
729
|
+
},
|
730
|
+
|
731
|
+
toString: function() {
|
732
|
+
return this.code + ':' +
|
733
|
+
this.args.join(',') + ':' +
|
734
|
+
this.message;
|
735
|
+
}
|
736
|
+
});
|
737
|
+
|
738
|
+
|
739
|
+
Faye.Error.versionMismatch = function() {
|
740
|
+
return new this(300, arguments, "Version mismatch").toString();
|
741
|
+
};
|
742
|
+
|
743
|
+
Faye.Error.conntypeMismatch = function() {
|
744
|
+
return new this(301, arguments, "Connection types not supported").toString();
|
745
|
+
};
|
746
|
+
|
747
|
+
Faye.Error.extMismatch = function() {
|
748
|
+
return new this(302, arguments, "Extension mismatch").toString();
|
749
|
+
};
|
750
|
+
|
751
|
+
Faye.Error.badRequest = function() {
|
752
|
+
return new this(400, arguments, "Bad request").toString();
|
753
|
+
};
|
754
|
+
|
755
|
+
Faye.Error.clientUnknown = function() {
|
756
|
+
return new this(401, arguments, "Unknown client").toString();
|
757
|
+
};
|
758
|
+
|
759
|
+
Faye.Error.parameterMissing = function() {
|
760
|
+
return new this(402, arguments, "Missing required parameter").toString();
|
761
|
+
};
|
762
|
+
|
763
|
+
Faye.Error.channelForbidden = function() {
|
764
|
+
return new this(403, arguments, "Forbidden channel").toString();
|
765
|
+
};
|
766
|
+
|
767
|
+
Faye.Error.channelUnknown = function() {
|
768
|
+
return new this(404, arguments, "Unknown channel").toString();
|
769
|
+
};
|
770
|
+
|
771
|
+
Faye.Error.channelInvalid = function() {
|
772
|
+
return new this(405, arguments, "Invalid channel").toString();
|
773
|
+
};
|
774
|
+
|
775
|
+
Faye.Error.extUnknown = function() {
|
776
|
+
return new this(406, arguments, "Unknown extension").toString();
|
777
|
+
};
|
778
|
+
|
779
|
+
Faye.Error.publishFailed = function() {
|
780
|
+
return new this(407, arguments, "Failed to publish").toString();
|
781
|
+
};
|
782
|
+
|
783
|
+
Faye.Error.serverError = function() {
|
784
|
+
return new this(500, arguments, "Internal server error").toString();
|
785
|
+
};
|
786
|
+
|
787
|
+
|
788
|
+
|
789
|
+
var path = require('path'),
|
790
|
+
posix = require('posix'),
|
791
|
+
sys = require('sys'),
|
792
|
+
url = require('url'),
|
793
|
+
querystring = require('querystring');
|
794
|
+
|
795
|
+
Faye.NodeAdapter = Faye.Class({
|
796
|
+
initialize: function(options) {
|
797
|
+
this._options = options || {};
|
798
|
+
this._endpoint = this._options.mount || Faye.NodeAdapter.DEFAULT_ENDPOINT;
|
799
|
+
this._script = this._endpoint + '.js';
|
800
|
+
this._server = new Faye.Server(this._options);
|
801
|
+
},
|
802
|
+
|
803
|
+
call: function(request, response) {
|
804
|
+
var requestUrl = url.parse(request.url, true);
|
805
|
+
switch (requestUrl.pathname) {
|
806
|
+
|
807
|
+
case this._endpoint:
|
808
|
+
var isGet = (request.method === 'GET'),
|
809
|
+
self = this;
|
810
|
+
|
811
|
+
if (isGet)
|
812
|
+
this._callWithParams(request, response, requestUrl.query);
|
813
|
+
|
814
|
+
else
|
815
|
+
request.addListener('body', function(chunk) {
|
816
|
+
self._callWithParams(request, response, querystring.parse(chunk));
|
817
|
+
});
|
818
|
+
|
819
|
+
return true;
|
820
|
+
break;
|
821
|
+
|
822
|
+
case this._script:
|
823
|
+
posix.cat(Faye.NodeAdapter.SCRIPT_PATH).addCallback(function(content) {
|
824
|
+
response.sendHeader(200, Faye.NodeAdapter.TYPE_SCRIPT);
|
825
|
+
response.sendBody(content);
|
826
|
+
response.finish();
|
827
|
+
});
|
828
|
+
return true;
|
829
|
+
break;
|
830
|
+
|
831
|
+
default: return false;
|
832
|
+
}
|
833
|
+
},
|
834
|
+
|
835
|
+
_callWithParams: function(request, response, params) {
|
836
|
+
try {
|
837
|
+
var message = JSON.parse(params.message),
|
838
|
+
jsonp = params.jsonp || Faye.JSONP_CALLBACK,
|
839
|
+
isGet = (request.method === 'GET'),
|
840
|
+
type = isGet ? Faye.NodeAdapter.TYPE_SCRIPT : Faye.NodeAdapter.TYPE_JSON;
|
841
|
+
|
842
|
+
if (isGet) this._server.flushConnection(message);
|
843
|
+
|
844
|
+
this._server.process(message, false, function(replies) {
|
845
|
+
var body = JSON.stringify(replies);
|
846
|
+
if (isGet) body = jsonp + '(' + body + ');';
|
847
|
+
response.sendHeader(200, type);
|
848
|
+
response.sendBody(body);
|
849
|
+
response.finish();
|
850
|
+
});
|
851
|
+
} catch (e) {
|
852
|
+
response.sendHeader(400, {'Content-Type': 'text/plain'});
|
853
|
+
response.sendBody('Bad request');
|
854
|
+
response.finish();
|
855
|
+
}
|
856
|
+
}
|
857
|
+
});
|
858
|
+
|
859
|
+
Faye.extend(Faye.NodeAdapter, {
|
860
|
+
DEFAULT_ENDPOINT: '/bayeux',
|
861
|
+
SCRIPT_PATH: path.dirname(__filename) + '/faye-client-min.js',
|
862
|
+
|
863
|
+
TYPE_JSON: {'Content-Type': 'text/json'},
|
864
|
+
TYPE_SCRIPT: {'Content-Type': 'text/javascript'},
|
865
|
+
TYPE_TEXT: {'Content-Type': 'text/plain'}
|
866
|
+
});
|
867
|
+
|
868
|
+
exports.NodeAdapter = Faye.NodeAdapter;
|