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