faye-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ require 'faye'
2
+
3
+ module FayeRails
4
+ class RackAdapter < ::Faye::RackAdapter
5
+
6
+ attr_reader :server, :endpoint
7
+
8
+ def initialize(app=nil, options=nil)
9
+ super
10
+ FayeRails.servers << self
11
+ end
12
+
13
+ def listen(port, ssl_options = nil)
14
+ if defined? ::Rails
15
+ Faye.ensure_reactor_running!
16
+ super
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ # Rudimentary routing support for channels to controllers.
23
+ #
24
+ # @param opts
25
+ # a Hash of mappings either string keys (channel globs)
26
+ # mapping to controller constants eg:
27
+ #
28
+ # '/widgets/**' => WidgetsController
29
+ #
30
+ # or you can set the behaviour for unknown channels:
31
+ #
32
+ # :default => :block
33
+ #
34
+ # :default can be set to :allow, :drop or :block.
35
+ # if :drop is chosen then messages to unknown channels
36
+ # will be silently dropped, whereas if you choose
37
+ # :block then the message will be returned with the
38
+ # error "Permission denied."
39
+ def map(opts)
40
+ if opts.is_a? Hash
41
+ opts.each do |channel, controller|
42
+ if channel.is_a? String
43
+ if File.fnmatch?('/**', channel)
44
+ routing_extension.map(channel, controller)
45
+ else
46
+ raise ArgumentError, "Invalid channel: #{channel}"
47
+ end
48
+ elsif channel == :default
49
+ if controller == :block
50
+ routing_extension.block_unknown_channels!
51
+ elsif controller == :drop
52
+ routing_extension.drop_unknown_channels!
53
+ elsif controller == :allow
54
+ routing_extension.allow_unknown_channels!
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ # Adds a very simple extension to the server causing
62
+ # it to log all messages in and out to Rails.logger.debug.
63
+ def debug_messages
64
+ add_extension(DebugMessagesExtension.new)
65
+ end
66
+
67
+ private
68
+
69
+ def routing_extension
70
+ if @routing_extension
71
+ @routing_extension
72
+ else
73
+ @routing_extension = RoutingExtension.new
74
+ add_extension(@routing_extension)
75
+ @routing_extension
76
+ end
77
+ end
78
+
79
+ class DebugMessagesExtension
80
+
81
+ def debug(*args)
82
+ if defined? ::Rails
83
+ Rails.logger.debug *args
84
+ else
85
+ puts *args
86
+ end
87
+ end
88
+
89
+ def incoming(m,c)
90
+ debug " ** IN: #{m.inspect}"
91
+ c.call(m)
92
+ end
93
+
94
+ def outgoing(m,c)
95
+ debug " ** OUT: #{m.inspect}"
96
+ c.call(m)
97
+ end
98
+ end
99
+
100
+ class RoutingExtension
101
+
102
+ def initialize
103
+ @default = :allow
104
+ @mappings = {}
105
+ end
106
+
107
+ def incoming(message, callback)
108
+ if message['channel'] == '/meta/subscribe'
109
+ take_action_for message, callback, message['subscription']
110
+ elsif message['channel'] == '/meta/unsubscribe'
111
+ take_action_for message, callback, message['subscription']
112
+ elsif File.fnmatch?('/meta/*', message['channel'])
113
+ callback.call(message)
114
+ elsif File.fnmatch?('/service/**', message['channel'])
115
+ callback.call(message)
116
+ else
117
+ take_action_for message, callback, message['channel']
118
+ end
119
+ end
120
+
121
+ def map(channel, controller)
122
+ if File.fnmatch?('/**', channel)
123
+ (@mappings[channel] ||= []) << controller
124
+ else
125
+ raise ArgumentError, "Invalid channel name: #{channel}"
126
+ end
127
+ end
128
+
129
+ def block_unknown_channels!
130
+ @default = :block
131
+ end
132
+
133
+ def drop_unknown_channels!
134
+ @default = :drop
135
+ end
136
+
137
+ def allow_unknown_channels!
138
+ @default = :allow
139
+ end
140
+
141
+ def take_action_for(message, callback, test='')
142
+ if @mappings.keys.select { |glob| File.fnmatch?(glob,test) }.size > 0
143
+ callback.call(message)
144
+ elsif @default == :block
145
+ message['error'] = "Permission denied"
146
+ callback.call(message)
147
+ elsif @default == :drop
148
+ callback.call(nil)
149
+ elsif @default == :allow
150
+ callback.call(message)
151
+ else
152
+ callback.call(nil)
153
+ end
154
+ end
155
+
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,51 @@
1
+ if defined? ActionDispatch::Routing
2
+
3
+ module ActionDispatch::Routing
4
+ class Mapper
5
+
6
+ def faye_server(mount_path, options={}, &block)
7
+
8
+ defaults = {
9
+ :mount => mount_path||'/faye',
10
+ :timeout => 25,
11
+ :engine => nil,
12
+ :server => 'thin'
13
+ }
14
+
15
+ unknown_options = options.keys - defaults.keys
16
+ if unknown_options.one?
17
+ raise ArgumentError, "Unknown option: #{unknown_options.first}."
18
+ elsif unknown_options.any?
19
+ raise ArgumentError, "Unknown options: #{unknown_options * ", "}."
20
+ end
21
+
22
+ options = defaults.merge(options)
23
+
24
+ Faye::WebSocket.load_adapter(options.delete(:server))
25
+
26
+ adapter = FayeRails::RackAdapter.new(options)
27
+ adapter.instance_eval(&block) if block.respond_to? :call
28
+
29
+ match options[:mount] => adapter
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ if defined? Rails::Application::RoutesReloader
39
+
40
+ class Rails::Application::RoutesReloader
41
+
42
+ def clear_with_faye_servers!
43
+ FayeRails.servers.clear!
44
+ clear_without_faye_servers!
45
+ end
46
+
47
+ alias_method_chain :clear!, :faye_servers
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,28 @@
1
+ class ServerList < Array
2
+ def at(index)
3
+ if index.is_a? String
4
+ select do |server|
5
+ server.endpoint == index
6
+ end
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ def <<(server)
13
+ if at(server.endpoint).size == 0
14
+ super
15
+ else
16
+ raise ArgumentError, "Server at mount point #{server.endpoint} is already present."
17
+ end
18
+ end
19
+ alias push <<
20
+
21
+ def clear!
22
+ self.each do |server|
23
+ server.stop
24
+ end
25
+ clear
26
+ end
27
+ end
28
+
@@ -0,0 +1,3 @@
1
+ module FayeRails
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ var Faye=(typeof Faye==='object')?Faye:{};if(typeof window!=='undefined')window.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.8.2',BAYEUX_VERSION:'1.0',ID_LENGTH:128,JSONP_CALLBACK:'jsonpcallback',CONNECTION_TYPES:['long-polling','cross-origin-long-polling','callback-polling','websocket','eventsource','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},clientIdFromMessages:function(a){var b=[].concat(a)[0];return b&&b.clientId},copyObject:function(a){var b,c,d;if(a instanceof Array){b=[];c=a.length;while(c--)b[c]=Faye.copyObject(a[c]);return b}else if(typeof a==='object'){b=(a===null)?null:{};for(d in a)b[d]=Faye.copyObject(a[d]);return b}else{return a}},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){if(a.indexOf)return a.indexOf(b);for(var c=0,d=a.length;c<d;c++){if(a[c]===b)return c}return-1},map:function(a,b,c){if(a.map)return a.map(b,c);var d=[];if(a instanceof Array){for(var f=0,g=a.length;f<g;f++){d.push(b.call(c||null,a[f],f))}}else{for(var j in a){if(!a.hasOwnProperty(j))continue;d.push(b.call(c||null,j,a[j]))}}return d},filter:function(a,b,c){var d=[];for(var f=0,g=a.length;f<g;f++){if(b.call(c||null,a[f],f))d.push(a[f])}return d},asyncEach:function(a,b,c,d){var f=a.length,g=-1,j=0,i=false;var h=function(){j-=1;g+=1;if(g===f)return c&&c.call(d);b(a[g],m)};var k=function(){if(i)return;i=true;while(j>0)h();i=false};var m=function(){j+=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)},logger:function(a){if(typeof console!=='undefined')console.log(a)},timestamp:function(){var b=new Date(),c=b.getFullYear(),d=b.getMonth()+1,f=b.getDate(),g=b.getHours(),j=b.getMinutes(),i=b.getSeconds();var h=function(a){return a<10?'0'+a:String(a)};return h(c)+'-'+h(d)+'-'+h(f)+' '+h(g)+':'+h(j)+':'+h(i)}});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._d={}},exists:function(a){return this._d.hasOwnProperty(a)},generate:function(){var a=Faye.random();while(this._d.hasOwnProperty(a))a=Faye.random();return this._d[a]=a},release:function(a){delete this._d[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._v==='succeeded')return a.apply(b,this._j);this._k=this._k||[];this._k.push([a,b])},timeout:function(a,b){var c=this;var d=Faye.ENV.setTimeout(function(){c.setDeferredStatus('failed',b)},a*1000);this._w=d},errback:function(a,b){if(!a)return;if(this._v==='failed')return a.apply(b,this._j);this._l=this._l||[];this._l.push([a,b])},setDeferredStatus:function(){if(this._w)Faye.ENV.clearTimeout(this._w);var a=Array.prototype.slice.call(arguments),b=a.shift(),c;this._v=b;this._j=a;if(b==='succeeded')c=this._k;else if(b==='failed')c=this._l;if(!c)return;var d;while(d=c.shift())d[0].apply(d[1],this._j)}};Faye.Publisher={countListeners:function(a){if(!this._3||!this._3[a])return 0;return this._3[a].length},bind:function(a,b,c){this._3=this._3||{};var d=this._3[a]=this._3[a]||[];d.push([b,c])},unbind: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)}},trigger:function(){var a=Array.prototype.slice.call(arguments),b=a.shift();if(!this._3||!this._3[b])return;var c=this._3[b].slice(),d;for(var f=0,g=c.length;f<g;f++){d=c[f];d[0].apply(d[1],a)}}};Faye.Timeouts={addTimeout:function(a,b,c,d){this._5=this._5||{};if(this._5.hasOwnProperty(a))return;var f=this;this._5[a]=Faye.ENV.setTimeout(function(){delete f._5[a];c.call(d)},1000*b)},removeTimeout:function(a){this._5=this._5||{};var b=this._5[a];if(!b)return;clearTimeout(b);delete this._5[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 j in Faye){if(f)continue;if(typeof Faye[j]!=='function')continue;if(this instanceof Faye[j])f=j}if(f)d+='.'+f;d+='] ';Faye.logger(Faye.timestamp()+d+g)}};(function(){for(var c in Faye.Logging.LOG_LEVELS)(function(a,b){Faye.Logging[a]=function(){this.log(arguments,a)}})(c,Faye.Logging.LOG_LEVELS[c])})();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._6=this._6||[];this._6.push(a);if(a.added)a.added(this)},removeExtension:function(a){if(!this._6)return;var b=this._6.length;while(b--){if(this._6[b]!==a)continue;this._6.splice(b,1);if(a.removed)a.removed(this)}},pipeThroughExtensions:function(c,d,f,g){this.debug('Passing through ? extensions: ?',c,d);if(!this._6)return f.call(g,d);var j=this._6.slice();var i=function(a){if(!a)return f.call(g,a);var b=j.shift();if(!b)return f.call(g,a);if(b[c])b[c](a,i);else i(a)};i(d)}};Faye.extend(Faye.Extensible,Faye.Logging);Faye.Channel=Faye.Class({initialize:function(a){this.id=this.name=a},push:function(a){this.trigger('message',a)},isUnused:function(){return this.countListeners('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 a=[];for(var b in this._2)a.push(b);return a},remove:function(a){delete this._2[a]},hasSubscription:function(a){return this._2.hasOwnProperty(a)},subscribe:function(a,b,c){if(!b)return;var d;for(var f=0,g=a.length;f<g;f++){d=a[f];var j=this._2[d]=this._2[d]||new Faye.Channel(d);j.bind('message',b,c)}},unsubscribe:function(a,b,c){var d=this._2[a];if(!d)return false;d.unbind('message',b,c);if(d.isUnused()){this.remove(a);return true}else{return false}},distributeMessage:function(a){var b=Faye.Channel.expand(a.channel);for(var c=0,d=b.length;c<d;c++){var f=this._2[b[c]];if(f)f.trigger('message',a.data)}}})});Faye.Publication=Faye.Class(Faye.Deferrable);Faye.Subscription=Faye.Class({initialize:function(a,b,c,d){this._7=a;this._2=b;this._m=c;this._n=d;this._x=false},cancel:function(){if(this._x)return;this._7.unsubscribe(this._2,this._m,this._n);this._x=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_RETRY:5.0,DEFAULT_ENDPOINT:'/bayeux',INTERVAL:0.0,initialize:function(a,b){this.info('New client created for ?',a);this.endpoint=a||this.DEFAULT_ENDPOINT;this._E=Faye.CookieJar&&new Faye.CookieJar();this._y={};this._o=b||{};this._p=[];this.retry=this._o.retry||this.DEFAULT_RETRY;this._z(Faye.MANDATORY_CONNECTION_TYPES);this._1=this.UNCONNECTED;this._2=new Faye.Channel.Set();this._e=0;this._q={};this._8={reconnect:this.RETRY,interval:1000*(this._o.interval||this.INTERVAL),timeout:1000*(this._o.timeout||this.CONNECTION_TIMEOUT)};if(Faye.Event)Faye.Event.on(Faye.ENV,'beforeunload',function(){if(Faye.indexOf(this._p,'autodisconnect')<0)this.disconnect()},this)},disable:function(a){this._p.push(a)},setHeader:function(a,b){this._y[a]=b},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(d,f){if(this._8.reconnect===this.NONE)return;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var g=this;this.info('Initiating handshake with ?',this.endpoint);this._9({channel:Faye.Channel.HANDSHAKE,version:Faye.BAYEUX_VERSION,supportedConnectionTypes:[this._a.connectionType]},function(b){if(b.successful){this._1=this.CONNECTED;this._0=b.clientId;var c=Faye.filter(b.supportedConnectionTypes,function(a){return Faye.indexOf(this._p,a)<0},this);this._z(c);this.info('Handshake successful: ?',this._0);this.subscribe(this._2.getKeys(),true);if(d)d.call(f)}else{this.info('Handshake unsuccessful');Faye.ENV.setTimeout(function(){g.handshake(d,f)},this._8.interval);this._1=this.UNCONNECTED}},this)},connect:function(a,b){if(this._8.reconnect===this.NONE)return;if(this._1===this.DISCONNECTED)return;if(this._1===this.UNCONNECTED)return this.handshake(function(){this.connect(a,b)},this);this.callback(a,b);if(this._1!==this.CONNECTED)return;this.info('Calling deferred actions for ?',this._0);this.setDeferredStatus('succeeded');this.setDeferredStatus('deferred');if(this._r)return;this._r=true;this.info('Initiating connection for ?',this._0);this._9({channel:Faye.Channel.CONNECT,clientId:this._0,connectionType:this._a.connectionType},this._A,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},function(a){if(a.successful)this._a.close()},this);this.info('Clearing channel listeners for ?',this._0);this._2=new Faye.Channel.Set()},subscribe:function(c,d,f){if(c instanceof Array){for(var g=0,j=c.length;g<j;g++){this.subscribe(c[g],d,f)}return}var i=new Faye.Subscription(this,c,d,f),h=(d===true),k=this._2.hasSubscription(c);if(k&&!h){this._2.subscribe([c],d,f);i.setDeferredStatus('succeeded');return i}this.connect(function(){this.info('Client ? attempting to subscribe to ?',this._0,c);if(!h)this._2.subscribe([c],d,f);this._9({channel:Faye.Channel.SUBSCRIBE,clientId:this._0,subscription:c},function(a){if(!a.successful){i.setDeferredStatus('failed',Faye.Error.parse(a.error));return this._2.unsubscribe(c,d,f)}var b=[].concat(a.subscription);this.info('Subscription acknowledged for ? to ?',this._0,b);i.setDeferredStatus('succeeded')},this)},this);return i},unsubscribe:function(c,d,f){if(c instanceof Array){for(var g=0,j=c.length;g<j;g++){this.unsubscribe(c[g],d,f)}return}var i=this._2.unsubscribe(c,d,f);if(!i)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(b,c){var d=new Faye.Publication();this.connect(function(){this.info('Client ? queueing published message to ?: ?',this._0,b,c);this._9({channel:b,data:c,clientId:this._0},function(a){if(a.successful)d.setDeferredStatus('succeeded');else d.setDeferredStatus('failed',Faye.Error.parse(a.error))},this)},this);return d},receiveMessage:function(c){this.pipeThroughExtensions('incoming',c,function(a){if(!a)return;if(a.advice)this._F(a.advice);this._G(a);if(a.successful===undefined)return;var b=this._q[a.id];if(!b)return;delete this._q[a.id];b[0].call(b[1],a)},this)},_z:function(b){Faye.Transport.get(this,b,function(a){this._a=a;this._a.cookies=this._E;this._a.headers=this._y;a.bind('down',function(){if(this._c!==undefined&&!this._c)return;this._c=false;this.trigger('transport:down')},this);a.bind('up',function(){if(this._c!==undefined&&this._c)return;this._c=true;this.trigger('transport:up')},this)},this)},_9:function(b,c,d){b.id=this._H();if(c)this._q[b.id]=[c,d];this.pipeThroughExtensions('outgoing',b,function(a){if(!a)return;this._a.send(a,this._8.timeout/1000)},this)},_H:function(){this._e+=1;if(this._e>=Math.pow(2,32))this._e=0;return this._e.toString(36)},_F:function(a){Faye.extend(this._8,a);if(this._8.reconnect===this.HANDSHAKE&&this._1!==this.DISCONNECTED){this._1=this.UNCONNECTED;this._0=null;this._A()}},_G:function(a){if(!a.channel||a.data===undefined)return;this.info('Client ? calling listeners for ? with ?',this._0,a.channel,a.data);this._2.distributeMessage(a)},_I:function(){if(!this._r)return;this._r=null;this.info('Closed connection for ?',this._0)},_A:function(){this._I();var a=this;Faye.ENV.setTimeout(function(){a.connect()},this._8.interval)}});Faye.extend(Faye.Client.prototype,Faye.Deferrable);Faye.extend(Faye.Client.prototype,Faye.Publisher);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._7=a;this._b=b;this._f=[]},close:function(){},send:function(a,b){this.debug('Client ? sending message to ?: ?',this._7._0,this._b,a);if(!this.batching)return this.request([a],b);this._f.push(a);this._J=b;if(a.channel===Faye.Channel.HANDSHAKE)return this.flush();if(a.channel===Faye.Channel.CONNECT)this._s=a;this.addTimeout('publish',this.MAX_DELAY,this.flush,this)},flush:function(){this.removeTimeout('publish');if(this._f.length>1&&this._s)this._s.advice={timeout:0};this.request(this._f,this._J);this._s=null;this._f=[]},receive:function(a){this.debug('Client ? received from ?: ?',this._7._0,this._b,a);for(var b=0,c=a.length;b<c;b++){this._7.receiveMessage(a[b])}},retry:function(a,b){var c=false,d=this._7.retry*1000,f=this;return function(){if(c)return;c=true;Faye.ENV.setTimeout(function(){f.request(a,b)},d)}}}),{get:function(g,j,i,h){var k=g.endpoint;if(j===undefined)j=this.supportedConnectionTypes();Faye.asyncEach(this._t,function(b,c){var d=b[0],f=b[1];if(Faye.indexOf(j,d)<0)return c();f.isUsable(k,function(a){if(a)i.call(h,new f(g,k));else c()})},function(){throw new Error('Could not find a usable connection type for '+k);})},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.extend(Faye.Transport.prototype,Faye.Publisher);Faye.extend(Faye.Transport.prototype,Faye.Timeouts);Faye.Event={_g:[],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._g.push({_h:a,_u:b,_m:c,_n:d,_B:f})},detach:function(a,b,c,d){var f=this._g.length,g;while(f--){g=this._g[f];if((a&&a!==g._h)||(b&&b!==g._u)||(c&&c!==g._m)||(d&&d!==g._n))continue;if(g._h.removeEventListener)g._h.removeEventListener(g._u,g._B,false);else g._h.detachEvent('on'+g._u,g._B);this._g.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 a=[];for(var b in this.params){if(!this.params.hasOwnProperty(b))continue;a.push(encodeURIComponent(b)+'='+encodeURIComponent(this.params[b]))}return a.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 j=function(b,c){d=d.replace(c,function(a){if(a)g[b]=a;return''})};j('protocol',/^https?\:\/+/);j('hostname',/^[^\/\:]+/);j('port',/^:[0-9]+/);Faye.extend(g,{protocol:Faye.ENV.location.protocol+'//',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(),k=i.join('?'),m=k?k.split('&'):[],n=m.length,l={};while(n--){i=m[n].split('=');l[decodeURIComponent(i[0]||'')]=decodeURIComponent(i[1]||'')}if(typeof f==='object')Faye.extend(l,f);g.pathname=h;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,n=/[\\\"\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','"':'\\"','\\':'\\\\'},o;function r(c){n.lastIndex=0;return n.test(c)?'"'+c.replace(n,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,j=l,i,h=b[a];if(h&&typeof h==='object'&&typeof h.toJSON==='function'){h=h.toJSON(a)}if(typeof o==='function'){h=o.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'}l+=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?'[]':l?'[\n'+l+i.join(',\n'+l)+'\n'+j+']':'['+i.join(',')+']';l=j;return f}if(o&&typeof o==='object'){g=o.length;for(c=0;c<g;c+=1){d=o[c];if(typeof d==='string'){f=q(d,h);if(f){i.push(r(d)+(l?': ':':')+f)}}}}else{for(d in h){if(Object.hasOwnProperty.call(h,d)){f=q(d,h);if(f){i.push(r(d)+(l?': ':':')+f)}}}}f=i.length===0?'{}':l?'{\n'+l+i.join(',\n'+l)+'\n'+j+'}':'{'+i.join(',')+'}';l=j;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}o=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,j){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 j.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,''))){i=eval('('+g+')');return typeof j==='function'?h({'':i},''):i}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){if(b.length===0)return;this._i=this._i||{};for(var d=0,f=b.length;d<f;d++){this._i[b[d].id]=b[d]}this.withSocket(function(a){a.send(Faye.toJSON(b))})},withSocket:function(a,b){this.callback(a,b);this.connect()},close:function(){if(this._C)return;this._C=true;if(this._4)this._4.close()},connect:function(){if(this._C)return;this._1=this._1||this.UNCONNECTED;if(this._1!==this.UNCONNECTED)return;this._1=this.CONNECTING;var f=Faye.Transport.WebSocket.getClass();this._4=new f(Faye.Transport.WebSocket.getSocketUrl(this._b));var g=this;this._4.onopen=function(){g._1=g.CONNECTED;g.setDeferredStatus('succeeded',g._4);g.trigger('up')};this._4.onmessage=function(a){var b=[].concat(JSON.parse(a.data));for(var c=0,d=b.length;c<d;c++){delete g._i[b[c].id]}g.receive(b)};this._4.onclose=function(){var a=(g._1===g.CONNECTED);g.setDeferredStatus('deferred');g._1=g.UNCONNECTED;delete g._4;if(a)return g.resend();var b=g._7.retry*1000;Faye.ENV.setTimeout(function(){g.connect()},b);g.trigger('down')}},resend:function(){var c=Faye.map(this._i,function(a,b){return b});this.request(c)}}),{WEBSOCKET_TIMEOUT:1000,getSocketUrl:function(a){if(Faye.URI)a=Faye.URI.parse(a).toURL();return a.replace(/^http(s?):/ig,'ws$1:')},getClass:function(){return(Faye.WebSocket&&Faye.WebSocket.Client)||Faye.ENV.WebSocket||Faye.ENV.MozWebSocket},isUsable:function(a,b,c){var d=this.getClass();if(!d)return b.call(c,false);var f=false,g=false,j=this.getSocketUrl(a),i=new d(j);i.onopen=function(){f=true;i.close();b.call(c,true);g=true;i=null};var h=function(){if(!g&&!f)b.call(c,false);g=true};i.onclose=i.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.EventSource=Faye.extend(Faye.Class(Faye.Transport,{initialize:function(b,c){Faye.Transport.prototype.initialize.call(this,b,c);this._K=new Faye.Transport.XHR(b,c);var d=new EventSource(c+'/'+b.getClientId()),f=this;d.onopen=function(){f.trigger('up')};d.onerror=function(){f.trigger('down')};d.onmessage=function(a){f.receive(JSON.parse(a.data))};this._4=d},request:function(a,b){this._K.request(a,b)},close:function(){this._4.close()}}),{isUsable:function(b,c,d){Faye.Transport.XHR.isUsable(b,function(a){c.call(d,a&&Faye.ENV.EventSource)})}});Faye.Transport.register('eventsource',Faye.Transport.EventSource);Faye.Transport.XHR=Faye.extend(Faye.Class(Faye.Transport,{request:function(d,f){var g=this.retry(d,f),j=Faye.URI.parse(this._b).pathname,i=this,h=Faye.ENV.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();h.open('POST',j,true);h.setRequestHeader('Content-Type','application/json');h.setRequestHeader('X-Requested-With','XMLHttpRequest');var k=this.headers;for(var m in k){if(!k.hasOwnProperty(m))continue;h.setRequestHeader(m,k[m])}var n=function(){h.abort()};Faye.Event.on(Faye.ENV,'beforeunload',n);var l=function(){Faye.Event.detach(Faye.ENV,'beforeunload',n);h.onreadystatechange=function(){};h=null};h.onreadystatechange=function(){if(h.readyState!==4)return;var a=null,b=h.status,c=((b>=200&&b<300)||b===304||b===1223);if(!c){l();g();return i.trigger('down')}try{a=JSON.parse(h.responseText)}catch(e){}l();if(a){i.receive(a);i.trigger('up')}else{g();i.trigger('down')}};h.send(Faye.toJSON(d))}}),{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(b,c){var d=Faye.ENV.XDomainRequest?XDomainRequest:XMLHttpRequest,f=new d(),g=this.retry(b,c),j=this;f.open('POST',this._b,true);var i=function(){if(!f)return false;f.onload=f.onerror=f.ontimeout=f.onprogress=null;f=null;Faye.ENV.clearTimeout(k);return true};f.onload=function(){var a=null;try{a=JSON.parse(f.responseText)}catch(e){}i();if(a){j.receive(a);j.trigger('up')}else{g();j.trigger('down')}};var h=function(){i();g();j.trigger('down')};var k=Faye.ENV.setTimeout(h,1.5*1000*c);f.onerror=h;f.ontimeout=h;f.onprogress=function(){};f.send('message='+encodeURIComponent(Faye.toJSON(b)))}}),{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'),j=Faye.Transport.JSONP.getCallbackName(),i=Faye.URI.parse(this._b,d),h=this.retry(b,c),k=this;Faye.ENV[j]=function(a){n();k.receive(a);k.trigger('up')};var m=Faye.ENV.setTimeout(function(){n();h();k.trigger('down')},1.5*1000*c);var n=function(){if(!Faye.ENV[j])return false;Faye.ENV[j]=undefined;try{delete Faye.ENV[j]}catch(e){}Faye.ENV.clearTimeout(m);g.parentNode.removeChild(g);return true};i.params.jsonp=j;g.type='text/javascript';g.src=i.toURL();f.appendChild(g)}}),{_D:0,getCallbackName:function(){this._D+=1;return'__jsonp'+this._D+'__'},isUsable:function(a,b,c){b.call(c,true)}});Faye.Transport.register('callback-polling',Faye.Transport.JSONP);
2
+ //@ sourceMappingURL=faye-browser-min.js.map
@@ -0,0 +1,2130 @@
1
+ var Faye = (typeof Faye === 'object') ? Faye : {};
2
+ if (typeof window !== 'undefined') window.Faye = Faye;
3
+
4
+ Faye.extend = function(dest, source, overwrite) {
5
+ if (!source) return dest;
6
+ for (var key in source) {
7
+ if (!source.hasOwnProperty(key)) continue;
8
+ if (dest.hasOwnProperty(key) && overwrite === false) continue;
9
+ if (dest[key] !== source[key])
10
+ dest[key] = source[key];
11
+ }
12
+ return dest;
13
+ };
14
+
15
+ Faye.extend(Faye, {
16
+ VERSION: '0.8.2',
17
+
18
+ BAYEUX_VERSION: '1.0',
19
+ ID_LENGTH: 128,
20
+ JSONP_CALLBACK: 'jsonpcallback',
21
+ CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'],
22
+
23
+ MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'],
24
+
25
+ ENV: (function() { return this })(),
26
+
27
+ random: function(bitlength) {
28
+ bitlength = bitlength || this.ID_LENGTH;
29
+ if (bitlength > 32) {
30
+ var parts = Math.ceil(bitlength / 32),
31
+ string = '';
32
+ while (parts--) string += this.random(32);
33
+ return string;
34
+ }
35
+ var limit = Math.pow(2, bitlength) - 1,
36
+ maxSize = limit.toString(36).length,
37
+ string = Math.floor(Math.random() * limit).toString(36);
38
+
39
+ while (string.length < maxSize) string = '0' + string;
40
+ return string;
41
+ },
42
+
43
+ clientIdFromMessages: function(messages) {
44
+ var first = [].concat(messages)[0];
45
+ return first && first.clientId;
46
+ },
47
+
48
+ copyObject: function(object) {
49
+ var clone, i, key;
50
+ if (object instanceof Array) {
51
+ clone = [];
52
+ i = object.length;
53
+ while (i--) clone[i] = Faye.copyObject(object[i]);
54
+ return clone;
55
+ } else if (typeof object === 'object') {
56
+ clone = (object === null) ? null : {};
57
+ for (key in object) clone[key] = Faye.copyObject(object[key]);
58
+ return clone;
59
+ } else {
60
+ return object;
61
+ }
62
+ },
63
+
64
+ commonElement: function(lista, listb) {
65
+ for (var i = 0, n = lista.length; i < n; i++) {
66
+ if (this.indexOf(listb, lista[i]) !== -1)
67
+ return lista[i];
68
+ }
69
+ return null;
70
+ },
71
+
72
+ indexOf: function(list, needle) {
73
+ if (list.indexOf) return list.indexOf(needle);
74
+
75
+ for (var i = 0, n = list.length; i < n; i++) {
76
+ if (list[i] === needle) return i;
77
+ }
78
+ return -1;
79
+ },
80
+
81
+ map: function(object, callback, context) {
82
+ if (object.map) return object.map(callback, context);
83
+ var result = [];
84
+
85
+ if (object instanceof Array) {
86
+ for (var i = 0, n = object.length; i < n; i++) {
87
+ result.push(callback.call(context || null, object[i], i));
88
+ }
89
+ } else {
90
+ for (var key in object) {
91
+ if (!object.hasOwnProperty(key)) continue;
92
+ result.push(callback.call(context || null, key, object[key]));
93
+ }
94
+ }
95
+ return result;
96
+ },
97
+
98
+ filter: function(array, callback, context) {
99
+ var result = [];
100
+ for (var i = 0, n = array.length; i < n; i++) {
101
+ if (callback.call(context || null, array[i], i))
102
+ result.push(array[i]);
103
+ }
104
+ return result;
105
+ },
106
+
107
+ asyncEach: function(list, iterator, callback, context) {
108
+ var n = list.length,
109
+ i = -1,
110
+ calls = 0,
111
+ looping = false;
112
+
113
+ var iterate = function() {
114
+ calls -= 1;
115
+ i += 1;
116
+ if (i === n) return callback && callback.call(context);
117
+ iterator(list[i], resume);
118
+ };
119
+
120
+ var loop = function() {
121
+ if (looping) return;
122
+ looping = true;
123
+ while (calls > 0) iterate();
124
+ looping = false;
125
+ };
126
+
127
+ var resume = function() {
128
+ calls += 1;
129
+ loop();
130
+ };
131
+ resume();
132
+ },
133
+
134
+ // http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/
135
+ toJSON: function(object) {
136
+ if (this.stringify)
137
+ return this.stringify(object, function(key, value) {
138
+ return (this[key] instanceof Array)
139
+ ? this[key]
140
+ : value;
141
+ });
142
+
143
+ return JSON.stringify(object);
144
+ },
145
+
146
+ logger: function(message) {
147
+ if (typeof console !== 'undefined') console.log(message);
148
+ },
149
+
150
+ timestamp: function() {
151
+ var date = new Date(),
152
+ year = date.getFullYear(),
153
+ month = date.getMonth() + 1,
154
+ day = date.getDate(),
155
+ hour = date.getHours(),
156
+ minute = date.getMinutes(),
157
+ second = date.getSeconds();
158
+
159
+ var pad = function(n) {
160
+ return n < 10 ? '0' + n : String(n);
161
+ };
162
+
163
+ return pad(year) + '-' + pad(month) + '-' + pad(day) + ' ' +
164
+ pad(hour) + ':' + pad(minute) + ':' + pad(second);
165
+ }
166
+ });
167
+
168
+
169
+ Faye.Class = function(parent, methods) {
170
+ if (typeof parent !== 'function') {
171
+ methods = parent;
172
+ parent = Object;
173
+ }
174
+
175
+ var klass = function() {
176
+ if (!this.initialize) return this;
177
+ return this.initialize.apply(this, arguments) || this;
178
+ };
179
+
180
+ var bridge = function() {};
181
+ bridge.prototype = parent.prototype;
182
+
183
+ klass.prototype = new bridge();
184
+ Faye.extend(klass.prototype, methods);
185
+
186
+ return klass;
187
+ };
188
+
189
+
190
+ Faye.Namespace = Faye.Class({
191
+ initialize: function() {
192
+ this._used = {};
193
+ },
194
+
195
+ exists: function(id) {
196
+ return this._used.hasOwnProperty(id);
197
+ },
198
+
199
+ generate: function() {
200
+ var name = Faye.random();
201
+ while (this._used.hasOwnProperty(name))
202
+ name = Faye.random();
203
+ return this._used[name] = name;
204
+ },
205
+
206
+ release: function(id) {
207
+ delete this._used[id];
208
+ }
209
+ });
210
+
211
+
212
+ Faye.Error = Faye.Class({
213
+ initialize: function(code, params, message) {
214
+ this.code = code;
215
+ this.params = Array.prototype.slice.call(params);
216
+ this.message = message;
217
+ },
218
+
219
+ toString: function() {
220
+ return this.code + ':' +
221
+ this.params.join(',') + ':' +
222
+ this.message;
223
+ }
224
+ });
225
+
226
+ Faye.Error.parse = function(message) {
227
+ message = message || '';
228
+ if (!Faye.Grammar.ERROR.test(message)) return new this(null, [], message);
229
+
230
+ var parts = message.split(':'),
231
+ code = parseInt(parts[0]),
232
+ params = parts[1].split(','),
233
+ message = parts[2];
234
+
235
+ return new this(code, params, message);
236
+ };
237
+
238
+
239
+ Faye.Error.versionMismatch = function() {
240
+ return new this(300, arguments, "Version mismatch").toString();
241
+ };
242
+
243
+ Faye.Error.conntypeMismatch = function() {
244
+ return new this(301, arguments, "Connection types not supported").toString();
245
+ };
246
+
247
+ Faye.Error.extMismatch = function() {
248
+ return new this(302, arguments, "Extension mismatch").toString();
249
+ };
250
+
251
+ Faye.Error.badRequest = function() {
252
+ return new this(400, arguments, "Bad request").toString();
253
+ };
254
+
255
+ Faye.Error.clientUnknown = function() {
256
+ return new this(401, arguments, "Unknown client").toString();
257
+ };
258
+
259
+ Faye.Error.parameterMissing = function() {
260
+ return new this(402, arguments, "Missing required parameter").toString();
261
+ };
262
+
263
+ Faye.Error.channelForbidden = function() {
264
+ return new this(403, arguments, "Forbidden channel").toString();
265
+ };
266
+
267
+ Faye.Error.channelUnknown = function() {
268
+ return new this(404, arguments, "Unknown channel").toString();
269
+ };
270
+
271
+ Faye.Error.channelInvalid = function() {
272
+ return new this(405, arguments, "Invalid channel").toString();
273
+ };
274
+
275
+ Faye.Error.extUnknown = function() {
276
+ return new this(406, arguments, "Unknown extension").toString();
277
+ };
278
+
279
+ Faye.Error.publishFailed = function() {
280
+ return new this(407, arguments, "Failed to publish").toString();
281
+ };
282
+
283
+ Faye.Error.serverError = function() {
284
+ return new this(500, arguments, "Internal server error").toString();
285
+ };
286
+
287
+
288
+
289
+ Faye.Deferrable = {
290
+ callback: function(callback, context) {
291
+ if (!callback) return;
292
+
293
+ if (this._deferredStatus === 'succeeded')
294
+ return callback.apply(context, this._deferredArgs);
295
+
296
+ this._callbacks = this._callbacks || [];
297
+ this._callbacks.push([callback, context]);
298
+ },
299
+
300
+ timeout: function(seconds, message) {
301
+ var _this = this;
302
+ var timer = Faye.ENV.setTimeout(function() {
303
+ _this.setDeferredStatus('failed', message);
304
+ }, seconds * 1000);
305
+ this._timer = timer;
306
+ },
307
+
308
+ errback: function(callback, context) {
309
+ if (!callback) return;
310
+
311
+ if (this._deferredStatus === 'failed')
312
+ return callback.apply(context, this._deferredArgs);
313
+
314
+ this._errbacks = this._errbacks || [];
315
+ this._errbacks.push([callback, context]);
316
+ },
317
+
318
+ setDeferredStatus: function() {
319
+ if (this._timer)
320
+ Faye.ENV.clearTimeout(this._timer);
321
+
322
+ var args = Array.prototype.slice.call(arguments),
323
+ status = args.shift(),
324
+ callbacks;
325
+
326
+ this._deferredStatus = status;
327
+ this._deferredArgs = args;
328
+
329
+ if (status === 'succeeded')
330
+ callbacks = this._callbacks;
331
+ else if (status === 'failed')
332
+ callbacks = this._errbacks;
333
+
334
+ if (!callbacks) return;
335
+
336
+ var callback;
337
+ while (callback = callbacks.shift())
338
+ callback[0].apply(callback[1], this._deferredArgs);
339
+ }
340
+ };
341
+
342
+
343
+ Faye.Publisher = {
344
+ countListeners: function(eventType) {
345
+ if (!this._subscribers || !this._subscribers[eventType]) return 0;
346
+ return this._subscribers[eventType].length;
347
+ },
348
+
349
+ bind: function(eventType, listener, context) {
350
+ this._subscribers = this._subscribers || {};
351
+ var list = this._subscribers[eventType] = this._subscribers[eventType] || [];
352
+ list.push([listener, context]);
353
+ },
354
+
355
+ unbind: function(eventType, listener, context) {
356
+ if (!this._subscribers || !this._subscribers[eventType]) return;
357
+
358
+ if (!listener) {
359
+ delete this._subscribers[eventType];
360
+ return;
361
+ }
362
+ var list = this._subscribers[eventType],
363
+ i = list.length;
364
+
365
+ while (i--) {
366
+ if (listener !== list[i][0]) continue;
367
+ if (context && list[i][1] !== context) continue;
368
+ list.splice(i,1);
369
+ }
370
+ },
371
+
372
+ trigger: function() {
373
+ var args = Array.prototype.slice.call(arguments),
374
+ eventType = args.shift();
375
+
376
+ if (!this._subscribers || !this._subscribers[eventType]) return;
377
+
378
+ var listeners = this._subscribers[eventType].slice(),
379
+ listener;
380
+
381
+ for (var i = 0, n = listeners.length; i < n; i++) {
382
+ listener = listeners[i];
383
+ listener[0].apply(listener[1], args);
384
+ }
385
+ }
386
+ };
387
+
388
+
389
+ Faye.Timeouts = {
390
+ addTimeout: function(name, delay, callback, context) {
391
+ this._timeouts = this._timeouts || {};
392
+ if (this._timeouts.hasOwnProperty(name)) return;
393
+ var self = this;
394
+ this._timeouts[name] = Faye.ENV.setTimeout(function() {
395
+ delete self._timeouts[name];
396
+ callback.call(context);
397
+ }, 1000 * delay);
398
+ },
399
+
400
+ removeTimeout: function(name) {
401
+ this._timeouts = this._timeouts || {};
402
+ var timeout = this._timeouts[name];
403
+ if (!timeout) return;
404
+ clearTimeout(timeout);
405
+ delete this._timeouts[name];
406
+ }
407
+ };
408
+
409
+
410
+ Faye.Logging = {
411
+ LOG_LEVELS: {
412
+ error: 3,
413
+ warn: 2,
414
+ info: 1,
415
+ debug: 0
416
+ },
417
+
418
+ logLevel: 'error',
419
+
420
+ log: function(messageArgs, level) {
421
+ if (!Faye.logger) return;
422
+
423
+ var levels = Faye.Logging.LOG_LEVELS;
424
+ if (levels[Faye.Logging.logLevel] > levels[level]) return;
425
+
426
+ var messageArgs = Array.prototype.slice.apply(messageArgs),
427
+ banner = ' [' + level.toUpperCase() + '] [Faye',
428
+ klass = this.className,
429
+
430
+ message = messageArgs.shift().replace(/\?/g, function() {
431
+ try {
432
+ return Faye.toJSON(messageArgs.shift());
433
+ } catch (e) {
434
+ return '[Object]';
435
+ }
436
+ });
437
+
438
+ for (var key in Faye) {
439
+ if (klass) continue;
440
+ if (typeof Faye[key] !== 'function') continue;
441
+ if (this instanceof Faye[key]) klass = key;
442
+ }
443
+ if (klass) banner += '.' + klass;
444
+ banner += '] ';
445
+
446
+ Faye.logger(Faye.timestamp() + banner + message);
447
+ }
448
+ };
449
+
450
+ (function() {
451
+ for (var key in Faye.Logging.LOG_LEVELS)
452
+ (function(level, value) {
453
+ Faye.Logging[level] = function() {
454
+ this.log(arguments, level);
455
+ };
456
+ })(key, Faye.Logging.LOG_LEVELS[key]);
457
+ })();
458
+
459
+
460
+ Faye.Grammar = {
461
+
462
+ LOWALPHA: /^[a-z]$/,
463
+
464
+ UPALPHA: /^[A-Z]$/,
465
+
466
+ ALPHA: /^([a-z]|[A-Z])$/,
467
+
468
+ DIGIT: /^[0-9]$/,
469
+
470
+ ALPHANUM: /^(([a-z]|[A-Z])|[0-9])$/,
471
+
472
+ MARK: /^(\-|\_|\!|\~|\(|\)|\$|\@)$/,
473
+
474
+ STRING: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
475
+
476
+ TOKEN: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
477
+
478
+ INTEGER: /^([0-9])+$/,
479
+
480
+ CHANNEL_SEGMENT: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
481
+
482
+ CHANNEL_SEGMENTS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
483
+
484
+ CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
485
+
486
+ WILD_CARD: /^\*{1,2}$/,
487
+
488
+ CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
489
+
490
+ VERSION_ELEMENT: /^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,
491
+
492
+ VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,
493
+
494
+ CLIENT_ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
495
+
496
+ ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
497
+
498
+ ERROR_MESSAGE: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
499
+
500
+ ERROR_ARGS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,
501
+
502
+ ERROR_CODE: /^[0-9][0-9][0-9]$/,
503
+
504
+ 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])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/
505
+
506
+ };
507
+
508
+
509
+ Faye.Extensible = {
510
+ addExtension: function(extension) {
511
+ this._extensions = this._extensions || [];
512
+ this._extensions.push(extension);
513
+ if (extension.added) extension.added(this);
514
+ },
515
+
516
+ removeExtension: function(extension) {
517
+ if (!this._extensions) return;
518
+ var i = this._extensions.length;
519
+ while (i--) {
520
+ if (this._extensions[i] !== extension) continue;
521
+ this._extensions.splice(i,1);
522
+ if (extension.removed) extension.removed(this);
523
+ }
524
+ },
525
+
526
+ pipeThroughExtensions: function(stage, message, callback, context) {
527
+ this.debug('Passing through ? extensions: ?', stage, message);
528
+
529
+ if (!this._extensions) return callback.call(context, message);
530
+ var extensions = this._extensions.slice();
531
+
532
+ var pipe = function(message) {
533
+ if (!message) return callback.call(context, message);
534
+
535
+ var extension = extensions.shift();
536
+ if (!extension) return callback.call(context, message);
537
+
538
+ if (extension[stage]) extension[stage](message, pipe);
539
+ else pipe(message);
540
+ };
541
+ pipe(message);
542
+ }
543
+ };
544
+
545
+ Faye.extend(Faye.Extensible, Faye.Logging);
546
+
547
+ Faye.Channel = Faye.Class({
548
+ initialize: function(name) {
549
+ this.id = this.name = name;
550
+ },
551
+
552
+ push: function(message) {
553
+ this.trigger('message', message);
554
+ },
555
+
556
+ isUnused: function() {
557
+ return this.countListeners('message') === 0;
558
+ }
559
+ });
560
+
561
+ Faye.extend(Faye.Channel.prototype, Faye.Publisher);
562
+
563
+ Faye.extend(Faye.Channel, {
564
+ HANDSHAKE: '/meta/handshake',
565
+ CONNECT: '/meta/connect',
566
+ SUBSCRIBE: '/meta/subscribe',
567
+ UNSUBSCRIBE: '/meta/unsubscribe',
568
+ DISCONNECT: '/meta/disconnect',
569
+
570
+ META: 'meta',
571
+ SERVICE: 'service',
572
+
573
+ expand: function(name) {
574
+ var segments = this.parse(name),
575
+ channels = ['/**', name];
576
+
577
+ var copy = segments.slice();
578
+ copy[copy.length - 1] = '*';
579
+ channels.push(this.unparse(copy));
580
+
581
+ for (var i = 1, n = segments.length; i < n; i++) {
582
+ copy = segments.slice(0, i);
583
+ copy.push('**');
584
+ channels.push(this.unparse(copy));
585
+ }
586
+
587
+ return channels;
588
+ },
589
+
590
+ isValid: function(name) {
591
+ return Faye.Grammar.CHANNEL_NAME.test(name) ||
592
+ Faye.Grammar.CHANNEL_PATTERN.test(name);
593
+ },
594
+
595
+ parse: function(name) {
596
+ if (!this.isValid(name)) return null;
597
+ return name.split('/').slice(1);
598
+ },
599
+
600
+ unparse: function(segments) {
601
+ return '/' + segments.join('/');
602
+ },
603
+
604
+ isMeta: function(name) {
605
+ var segments = this.parse(name);
606
+ return segments ? (segments[0] === this.META) : null;
607
+ },
608
+
609
+ isService: function(name) {
610
+ var segments = this.parse(name);
611
+ return segments ? (segments[0] === this.SERVICE) : null;
612
+ },
613
+
614
+ isSubscribable: function(name) {
615
+ if (!this.isValid(name)) return null;
616
+ return !this.isMeta(name) && !this.isService(name);
617
+ },
618
+
619
+ Set: Faye.Class({
620
+ initialize: function() {
621
+ this._channels = {};
622
+ },
623
+
624
+ getKeys: function() {
625
+ var keys = [];
626
+ for (var key in this._channels) keys.push(key);
627
+ return keys;
628
+ },
629
+
630
+ remove: function(name) {
631
+ delete this._channels[name];
632
+ },
633
+
634
+ hasSubscription: function(name) {
635
+ return this._channels.hasOwnProperty(name);
636
+ },
637
+
638
+ subscribe: function(names, callback, context) {
639
+ if (!callback) return;
640
+ var name;
641
+ for (var i = 0, n = names.length; i < n; i++) {
642
+ name = names[i];
643
+ var channel = this._channels[name] = this._channels[name] || new Faye.Channel(name);
644
+ channel.bind('message', callback, context);
645
+ }
646
+ },
647
+
648
+ unsubscribe: function(name, callback, context) {
649
+ var channel = this._channels[name];
650
+ if (!channel) return false;
651
+ channel.unbind('message', callback, context);
652
+
653
+ if (channel.isUnused()) {
654
+ this.remove(name);
655
+ return true;
656
+ } else {
657
+ return false;
658
+ }
659
+ },
660
+
661
+ distributeMessage: function(message) {
662
+ var channels = Faye.Channel.expand(message.channel);
663
+
664
+ for (var i = 0, n = channels.length; i < n; i++) {
665
+ var channel = this._channels[channels[i]];
666
+ if (channel) channel.trigger('message', message.data);
667
+ }
668
+ }
669
+ })
670
+ });
671
+
672
+
673
+ Faye.Publication = Faye.Class(Faye.Deferrable);
674
+
675
+
676
+ Faye.Subscription = Faye.Class({
677
+ initialize: function(client, channels, callback, context) {
678
+ this._client = client;
679
+ this._channels = channels;
680
+ this._callback = callback;
681
+ this._context = context;
682
+ this._cancelled = false;
683
+ },
684
+
685
+ cancel: function() {
686
+ if (this._cancelled) return;
687
+ this._client.unsubscribe(this._channels, this._callback, this._context);
688
+ this._cancelled = true;
689
+ },
690
+
691
+ unsubscribe: function() {
692
+ this.cancel();
693
+ }
694
+ });
695
+
696
+ Faye.extend(Faye.Subscription.prototype, Faye.Deferrable);
697
+
698
+
699
+ Faye.Client = Faye.Class({
700
+ UNCONNECTED: 1,
701
+ CONNECTING: 2,
702
+ CONNECTED: 3,
703
+ DISCONNECTED: 4,
704
+
705
+ HANDSHAKE: 'handshake',
706
+ RETRY: 'retry',
707
+ NONE: 'none',
708
+
709
+ CONNECTION_TIMEOUT: 60.0,
710
+ DEFAULT_RETRY: 5.0,
711
+
712
+ DEFAULT_ENDPOINT: '/bayeux',
713
+ INTERVAL: 0.0,
714
+
715
+ initialize: function(endpoint, options) {
716
+ this.info('New client created for ?', endpoint);
717
+
718
+ this.endpoint = endpoint || this.DEFAULT_ENDPOINT;
719
+ this._cookies = Faye.CookieJar && new Faye.CookieJar();
720
+ this._headers = {};
721
+ this._options = options || {};
722
+ this._disabled = [];
723
+ this.retry = this._options.retry || this.DEFAULT_RETRY;
724
+
725
+ this._selectTransport(Faye.MANDATORY_CONNECTION_TYPES);
726
+
727
+ this._state = this.UNCONNECTED;
728
+ this._channels = new Faye.Channel.Set();
729
+ this._messageId = 0;
730
+
731
+ this._responseCallbacks = {};
732
+
733
+ this._advice = {
734
+ reconnect: this.RETRY,
735
+ interval: 1000 * (this._options.interval || this.INTERVAL),
736
+ timeout: 1000 * (this._options.timeout || this.CONNECTION_TIMEOUT)
737
+ };
738
+
739
+ if (Faye.Event)
740
+ Faye.Event.on(Faye.ENV, 'beforeunload', function() {
741
+ if (Faye.indexOf(this._disabled, 'autodisconnect') < 0)
742
+ this.disconnect();
743
+ }, this);
744
+ },
745
+
746
+ disable: function(feature) {
747
+ this._disabled.push(feature);
748
+ },
749
+
750
+ setHeader: function(name, value) {
751
+ this._headers[name] = value;
752
+ },
753
+
754
+ getClientId: function() {
755
+ return this._clientId;
756
+ },
757
+
758
+ getState: function() {
759
+ switch (this._state) {
760
+ case this.UNCONNECTED: return 'UNCONNECTED';
761
+ case this.CONNECTING: return 'CONNECTING';
762
+ case this.CONNECTED: return 'CONNECTED';
763
+ case this.DISCONNECTED: return 'DISCONNECTED';
764
+ }
765
+ },
766
+
767
+ // Request
768
+ // MUST include: * channel
769
+ // * version
770
+ // * supportedConnectionTypes
771
+ // MAY include: * minimumVersion
772
+ // * ext
773
+ // * id
774
+ //
775
+ // Success Response Failed Response
776
+ // MUST include: * channel MUST include: * channel
777
+ // * version * successful
778
+ // * supportedConnectionTypes * error
779
+ // * clientId MAY include: * supportedConnectionTypes
780
+ // * successful * advice
781
+ // MAY include: * minimumVersion * version
782
+ // * advice * minimumVersion
783
+ // * ext * ext
784
+ // * id * id
785
+ // * authSuccessful
786
+ handshake: function(callback, context) {
787
+ if (this._advice.reconnect === this.NONE) return;
788
+ if (this._state !== this.UNCONNECTED) return;
789
+
790
+ this._state = this.CONNECTING;
791
+ var self = this;
792
+
793
+ this.info('Initiating handshake with ?', this.endpoint);
794
+
795
+ this._send({
796
+ channel: Faye.Channel.HANDSHAKE,
797
+ version: Faye.BAYEUX_VERSION,
798
+ supportedConnectionTypes: [this._transport.connectionType]
799
+
800
+ }, function(response) {
801
+
802
+ if (response.successful) {
803
+ this._state = this.CONNECTED;
804
+ this._clientId = response.clientId;
805
+
806
+ var connectionTypes = Faye.filter(response.supportedConnectionTypes, function(connType) {
807
+ return Faye.indexOf(this._disabled, connType) < 0;
808
+ }, this);
809
+ this._selectTransport(connectionTypes);
810
+
811
+ this.info('Handshake successful: ?', this._clientId);
812
+
813
+ this.subscribe(this._channels.getKeys(), true);
814
+ if (callback) callback.call(context);
815
+
816
+ } else {
817
+ this.info('Handshake unsuccessful');
818
+ Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._advice.interval);
819
+ this._state = this.UNCONNECTED;
820
+ }
821
+ }, this);
822
+ },
823
+
824
+ // Request Response
825
+ // MUST include: * channel MUST include: * channel
826
+ // * clientId * successful
827
+ // * connectionType * clientId
828
+ // MAY include: * ext MAY include: * error
829
+ // * id * advice
830
+ // * ext
831
+ // * id
832
+ // * timestamp
833
+ connect: function(callback, context) {
834
+ if (this._advice.reconnect === this.NONE) return;
835
+ if (this._state === this.DISCONNECTED) return;
836
+
837
+ if (this._state === this.UNCONNECTED)
838
+ return this.handshake(function() { this.connect(callback, context) }, this);
839
+
840
+ this.callback(callback, context);
841
+ if (this._state !== this.CONNECTED) return;
842
+
843
+ this.info('Calling deferred actions for ?', this._clientId);
844
+ this.setDeferredStatus('succeeded');
845
+ this.setDeferredStatus('deferred');
846
+
847
+ if (this._connectRequest) return;
848
+ this._connectRequest = true;
849
+
850
+ this.info('Initiating connection for ?', this._clientId);
851
+
852
+ this._send({
853
+ channel: Faye.Channel.CONNECT,
854
+ clientId: this._clientId,
855
+ connectionType: this._transport.connectionType
856
+
857
+ }, this._cycleConnection, this);
858
+ },
859
+
860
+ // Request Response
861
+ // MUST include: * channel MUST include: * channel
862
+ // * clientId * successful
863
+ // MAY include: * ext * clientId
864
+ // * id MAY include: * error
865
+ // * ext
866
+ // * id
867
+ disconnect: function() {
868
+ if (this._state !== this.CONNECTED) return;
869
+ this._state = this.DISCONNECTED;
870
+
871
+ this.info('Disconnecting ?', this._clientId);
872
+
873
+ this._send({
874
+ channel: Faye.Channel.DISCONNECT,
875
+ clientId: this._clientId
876
+
877
+ }, function(response) {
878
+ if (response.successful) this._transport.close();
879
+ }, this);
880
+
881
+ this.info('Clearing channel listeners for ?', this._clientId);
882
+ this._channels = new Faye.Channel.Set();
883
+ },
884
+
885
+ // Request Response
886
+ // MUST include: * channel MUST include: * channel
887
+ // * clientId * successful
888
+ // * subscription * clientId
889
+ // MAY include: * ext * subscription
890
+ // * id MAY include: * error
891
+ // * advice
892
+ // * ext
893
+ // * id
894
+ // * timestamp
895
+ subscribe: function(channel, callback, context) {
896
+ if (channel instanceof Array) {
897
+ for (var i = 0, n = channel.length; i < n; i++) {
898
+ this.subscribe(channel[i], callback, context);
899
+ }
900
+ return;
901
+ }
902
+
903
+ var subscription = new Faye.Subscription(this, channel, callback, context),
904
+ force = (callback === true),
905
+ hasSubscribe = this._channels.hasSubscription(channel);
906
+
907
+ if (hasSubscribe && !force) {
908
+ this._channels.subscribe([channel], callback, context);
909
+ subscription.setDeferredStatus('succeeded');
910
+ return subscription;
911
+ }
912
+
913
+ this.connect(function() {
914
+ this.info('Client ? attempting to subscribe to ?', this._clientId, channel);
915
+ if (!force) this._channels.subscribe([channel], callback, context);
916
+
917
+ this._send({
918
+ channel: Faye.Channel.SUBSCRIBE,
919
+ clientId: this._clientId,
920
+ subscription: channel
921
+
922
+ }, function(response) {
923
+ if (!response.successful) {
924
+ subscription.setDeferredStatus('failed', Faye.Error.parse(response.error));
925
+ return this._channels.unsubscribe(channel, callback, context);
926
+ }
927
+
928
+ var channels = [].concat(response.subscription);
929
+ this.info('Subscription acknowledged for ? to ?', this._clientId, channels);
930
+ subscription.setDeferredStatus('succeeded');
931
+ }, this);
932
+ }, this);
933
+
934
+ return subscription;
935
+ },
936
+
937
+ // Request Response
938
+ // MUST include: * channel MUST include: * channel
939
+ // * clientId * successful
940
+ // * subscription * clientId
941
+ // MAY include: * ext * subscription
942
+ // * id MAY include: * error
943
+ // * advice
944
+ // * ext
945
+ // * id
946
+ // * timestamp
947
+ unsubscribe: function(channel, callback, context) {
948
+ if (channel instanceof Array) {
949
+ for (var i = 0, n = channel.length; i < n; i++) {
950
+ this.unsubscribe(channel[i], callback, context);
951
+ }
952
+ return;
953
+ }
954
+
955
+ var dead = this._channels.unsubscribe(channel, callback, context);
956
+ if (!dead) return;
957
+
958
+ this.connect(function() {
959
+ this.info('Client ? attempting to unsubscribe from ?', this._clientId, channel);
960
+
961
+ this._send({
962
+ channel: Faye.Channel.UNSUBSCRIBE,
963
+ clientId: this._clientId,
964
+ subscription: channel
965
+
966
+ }, function(response) {
967
+ if (!response.successful) return;
968
+
969
+ var channels = [].concat(response.subscription);
970
+ this.info('Unsubscription acknowledged for ? from ?', this._clientId, channels);
971
+ }, this);
972
+ }, this);
973
+ },
974
+
975
+ // Request Response
976
+ // MUST include: * channel MUST include: * channel
977
+ // * data * successful
978
+ // MAY include: * clientId MAY include: * id
979
+ // * id * error
980
+ // * ext * ext
981
+ publish: function(channel, data) {
982
+ var publication = new Faye.Publication();
983
+
984
+ this.connect(function() {
985
+ this.info('Client ? queueing published message to ?: ?', this._clientId, channel, data);
986
+
987
+ this._send({
988
+ channel: channel,
989
+ data: data,
990
+ clientId: this._clientId
991
+ }, function(response) {
992
+ if (response.successful)
993
+ publication.setDeferredStatus('succeeded');
994
+ else
995
+ publication.setDeferredStatus('failed', Faye.Error.parse(response.error));
996
+ }, this);
997
+ }, this);
998
+
999
+ return publication;
1000
+ },
1001
+
1002
+ receiveMessage: function(message) {
1003
+ this.pipeThroughExtensions('incoming', message, function(message) {
1004
+ if (!message) return;
1005
+
1006
+ if (message.advice) this._handleAdvice(message.advice);
1007
+ this._deliverMessage(message);
1008
+
1009
+ if (message.successful === undefined) return;
1010
+
1011
+ var callback = this._responseCallbacks[message.id];
1012
+ if (!callback) return;
1013
+
1014
+ delete this._responseCallbacks[message.id];
1015
+ callback[0].call(callback[1], message);
1016
+ }, this);
1017
+ },
1018
+
1019
+ _selectTransport: function(transportTypes) {
1020
+ Faye.Transport.get(this, transportTypes, function(transport) {
1021
+ this._transport = transport;
1022
+ this._transport.cookies = this._cookies;
1023
+ this._transport.headers = this._headers;
1024
+
1025
+ transport.bind('down', function() {
1026
+ if (this._transportUp !== undefined && !this._transportUp) return;
1027
+ this._transportUp = false;
1028
+ this.trigger('transport:down');
1029
+ }, this);
1030
+
1031
+ transport.bind('up', function() {
1032
+ if (this._transportUp !== undefined && this._transportUp) return;
1033
+ this._transportUp = true;
1034
+ this.trigger('transport:up');
1035
+ }, this);
1036
+ }, this);
1037
+ },
1038
+
1039
+ _send: function(message, callback, context) {
1040
+ message.id = this._generateMessageId();
1041
+ if (callback) this._responseCallbacks[message.id] = [callback, context];
1042
+
1043
+ this.pipeThroughExtensions('outgoing', message, function(message) {
1044
+ if (!message) return;
1045
+ this._transport.send(message, this._advice.timeout / 1000);
1046
+ }, this);
1047
+ },
1048
+
1049
+ _generateMessageId: function() {
1050
+ this._messageId += 1;
1051
+ if (this._messageId >= Math.pow(2,32)) this._messageId = 0;
1052
+ return this._messageId.toString(36);
1053
+ },
1054
+
1055
+ _handleAdvice: function(advice) {
1056
+ Faye.extend(this._advice, advice);
1057
+
1058
+ if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
1059
+ this._state = this.UNCONNECTED;
1060
+ this._clientId = null;
1061
+ this._cycleConnection();
1062
+ }
1063
+ },
1064
+
1065
+ _deliverMessage: function(message) {
1066
+ if (!message.channel || message.data === undefined) return;
1067
+ this.info('Client ? calling listeners for ? with ?', this._clientId, message.channel, message.data);
1068
+ this._channels.distributeMessage(message);
1069
+ },
1070
+
1071
+ _teardownConnection: function() {
1072
+ if (!this._connectRequest) return;
1073
+ this._connectRequest = null;
1074
+ this.info('Closed connection for ?', this._clientId);
1075
+ },
1076
+
1077
+ _cycleConnection: function() {
1078
+ this._teardownConnection();
1079
+ var self = this;
1080
+ Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
1081
+ }
1082
+ });
1083
+
1084
+ Faye.extend(Faye.Client.prototype, Faye.Deferrable);
1085
+ Faye.extend(Faye.Client.prototype, Faye.Publisher);
1086
+ Faye.extend(Faye.Client.prototype, Faye.Logging);
1087
+ Faye.extend(Faye.Client.prototype, Faye.Extensible);
1088
+
1089
+
1090
+ Faye.Transport = Faye.extend(Faye.Class({
1091
+ MAX_DELAY: 0.0,
1092
+ batching: true,
1093
+
1094
+ initialize: function(client, endpoint) {
1095
+ this.debug('Created new ? transport for ?', this.connectionType, endpoint);
1096
+ this._client = client;
1097
+ this._endpoint = endpoint;
1098
+ this._outbox = [];
1099
+ },
1100
+
1101
+ close: function() {},
1102
+
1103
+ send: function(message, timeout) {
1104
+ this.debug('Client ? sending message to ?: ?',
1105
+ this._client._clientId, this._endpoint, message);
1106
+
1107
+ if (!this.batching) return this.request([message], timeout);
1108
+
1109
+ this._outbox.push(message);
1110
+ this._timeout = timeout;
1111
+
1112
+ if (message.channel === Faye.Channel.HANDSHAKE)
1113
+ return this.flush();
1114
+
1115
+ if (message.channel === Faye.Channel.CONNECT)
1116
+ this._connectMessage = message;
1117
+
1118
+ this.addTimeout('publish', this.MAX_DELAY, this.flush, this);
1119
+ },
1120
+
1121
+ flush: function() {
1122
+ this.removeTimeout('publish');
1123
+
1124
+ if (this._outbox.length > 1 && this._connectMessage)
1125
+ this._connectMessage.advice = {timeout: 0};
1126
+
1127
+ this.request(this._outbox, this._timeout);
1128
+
1129
+ this._connectMessage = null;
1130
+ this._outbox = [];
1131
+ },
1132
+
1133
+ receive: function(responses) {
1134
+ this.debug('Client ? received from ?: ?',
1135
+ this._client._clientId, this._endpoint, responses);
1136
+
1137
+ for (var i = 0, n = responses.length; i < n; i++) {
1138
+ this._client.receiveMessage(responses[i]);
1139
+ }
1140
+ },
1141
+
1142
+ retry: function(message, timeout) {
1143
+ var called = false,
1144
+ retry = this._client.retry * 1000,
1145
+ self = this;
1146
+
1147
+ return function() {
1148
+ if (called) return;
1149
+ called = true;
1150
+ Faye.ENV.setTimeout(function() { self.request(message, timeout) }, retry);
1151
+ };
1152
+ }
1153
+
1154
+ }), {
1155
+ get: function(client, connectionTypes, callback, context) {
1156
+ var endpoint = client.endpoint;
1157
+ if (connectionTypes === undefined) connectionTypes = this.supportedConnectionTypes();
1158
+
1159
+ Faye.asyncEach(this._transports, function(pair, resume) {
1160
+ var connType = pair[0], klass = pair[1];
1161
+ if (Faye.indexOf(connectionTypes, connType) < 0) return resume();
1162
+
1163
+ klass.isUsable(endpoint, function(isUsable) {
1164
+ if (isUsable) callback.call(context, new klass(client, endpoint));
1165
+ else resume();
1166
+ });
1167
+ }, function() {
1168
+ throw new Error('Could not find a usable connection type for ' + endpoint);
1169
+ });
1170
+ },
1171
+
1172
+ register: function(type, klass) {
1173
+ this._transports.push([type, klass]);
1174
+ klass.prototype.connectionType = type;
1175
+ },
1176
+
1177
+ _transports: [],
1178
+
1179
+ supportedConnectionTypes: function() {
1180
+ return Faye.map(this._transports, function(pair) { return pair[0] });
1181
+ }
1182
+ });
1183
+
1184
+ Faye.extend(Faye.Transport.prototype, Faye.Logging);
1185
+ Faye.extend(Faye.Transport.prototype, Faye.Publisher);
1186
+ Faye.extend(Faye.Transport.prototype, Faye.Timeouts);
1187
+
1188
+
1189
+ Faye.Event = {
1190
+ _registry: [],
1191
+
1192
+ on: function(element, eventName, callback, context) {
1193
+ var wrapped = function() { callback.call(context) };
1194
+
1195
+ if (element.addEventListener)
1196
+ element.addEventListener(eventName, wrapped, false);
1197
+ else
1198
+ element.attachEvent('on' + eventName, wrapped);
1199
+
1200
+ this._registry.push({
1201
+ _element: element,
1202
+ _type: eventName,
1203
+ _callback: callback,
1204
+ _context: context,
1205
+ _handler: wrapped
1206
+ });
1207
+ },
1208
+
1209
+ detach: function(element, eventName, callback, context) {
1210
+ var i = this._registry.length, register;
1211
+ while (i--) {
1212
+ register = this._registry[i];
1213
+
1214
+ if ((element && element !== register._element) ||
1215
+ (eventName && eventName !== register._type) ||
1216
+ (callback && callback !== register._callback) ||
1217
+ (context && context !== register._context))
1218
+ continue;
1219
+
1220
+ if (register._element.removeEventListener)
1221
+ register._element.removeEventListener(register._type, register._handler, false);
1222
+ else
1223
+ register._element.detachEvent('on' + register._type, register._handler);
1224
+
1225
+ this._registry.splice(i,1);
1226
+ register = null;
1227
+ }
1228
+ }
1229
+ };
1230
+
1231
+ Faye.Event.on(Faye.ENV, 'unload', Faye.Event.detach, Faye.Event);
1232
+
1233
+
1234
+ Faye.URI = Faye.extend(Faye.Class({
1235
+ queryString: function() {
1236
+ var pairs = [];
1237
+ for (var key in this.params) {
1238
+ if (!this.params.hasOwnProperty(key)) continue;
1239
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(this.params[key]));
1240
+ }
1241
+ return pairs.join('&');
1242
+ },
1243
+
1244
+ isLocal: function() {
1245
+ var host = Faye.URI.parse(Faye.ENV.location.href);
1246
+
1247
+ var external = (host.hostname !== this.hostname) ||
1248
+ (host.port !== this.port) ||
1249
+ (host.protocol !== this.protocol);
1250
+
1251
+ return !external;
1252
+ },
1253
+
1254
+ toURL: function() {
1255
+ var query = this.queryString();
1256
+ return this.protocol + this.hostname + ':' + this.port +
1257
+ this.pathname + (query ? '?' + query : '');
1258
+ }
1259
+ }), {
1260
+ parse: function(url, params) {
1261
+ if (typeof url !== 'string') return url;
1262
+
1263
+ var location = new this();
1264
+
1265
+ var consume = function(name, pattern) {
1266
+ url = url.replace(pattern, function(match) {
1267
+ if (match) location[name] = match;
1268
+ return '';
1269
+ });
1270
+ };
1271
+ consume('protocol', /^https?\:\/+/);
1272
+ consume('hostname', /^[^\/\:]+/);
1273
+ consume('port', /^:[0-9]+/);
1274
+
1275
+ Faye.extend(location, {
1276
+ protocol: Faye.ENV.location.protocol + '//',
1277
+ hostname: Faye.ENV.location.hostname,
1278
+ port: Faye.ENV.location.port
1279
+ }, false);
1280
+
1281
+ if (!location.port) location.port = (location.protocol === 'https://') ? '443' : '80';
1282
+ location.port = location.port.replace(/\D/g, '');
1283
+
1284
+ var parts = url.split('?'),
1285
+ path = parts.shift(),
1286
+ query = parts.join('?'),
1287
+
1288
+ pairs = query ? query.split('&') : [],
1289
+ n = pairs.length,
1290
+ data = {};
1291
+
1292
+ while (n--) {
1293
+ parts = pairs[n].split('=');
1294
+ data[decodeURIComponent(parts[0] || '')] = decodeURIComponent(parts[1] || '');
1295
+ }
1296
+ if (typeof params === 'object') Faye.extend(data, params);
1297
+
1298
+ location.pathname = path;
1299
+ location.params = data;
1300
+
1301
+ return location;
1302
+ }
1303
+ });
1304
+
1305
+
1306
+ /*
1307
+ http://www.JSON.org/json2.js
1308
+ 2009-04-16
1309
+
1310
+ Public Domain.
1311
+
1312
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
1313
+
1314
+ See http://www.JSON.org/js.html
1315
+
1316
+ This file creates a global JSON object containing two methods: stringify
1317
+ and parse.
1318
+
1319
+ JSON.stringify(value, replacer, space)
1320
+ value any JavaScript value, usually an object or array.
1321
+
1322
+ replacer an optional parameter that determines how object
1323
+ values are stringified for objects. It can be a
1324
+ function or an array of strings.
1325
+
1326
+ space an optional parameter that specifies the indentation
1327
+ of nested structures. If it is omitted, the text will
1328
+ be packed without extra whitespace. If it is a number,
1329
+ it will specify the number of spaces to indent at each
1330
+ level. If it is a string (such as '\t' or '&nbsp;'),
1331
+ it contains the characters used to indent at each level.
1332
+
1333
+ This method produces a JSON text from a JavaScript value.
1334
+
1335
+ When an object value is found, if the object contains a toJSON
1336
+ method, its toJSON method will be called and the result will be
1337
+ stringified. A toJSON method does not serialize: it returns the
1338
+ value represented by the name/value pair that should be serialized,
1339
+ or undefined if nothing should be serialized. The toJSON method
1340
+ will be passed the key associated with the value, and this will be
1341
+ bound to the object holding the key.
1342
+
1343
+ For example, this would serialize Dates as ISO strings.
1344
+
1345
+ Date.prototype.toJSON = function (key) {
1346
+ function f(n) {
1347
+ // Format integers to have at least two digits.
1348
+ return n < 10 ? '0' + n : n;
1349
+ }
1350
+
1351
+ return this.getUTCFullYear() + '-' +
1352
+ f(this.getUTCMonth() + 1) + '-' +
1353
+ f(this.getUTCDate()) + 'T' +
1354
+ f(this.getUTCHours()) + ':' +
1355
+ f(this.getUTCMinutes()) + ':' +
1356
+ f(this.getUTCSeconds()) + 'Z';
1357
+ };
1358
+
1359
+ You can provide an optional replacer method. It will be passed the
1360
+ key and value of each member, with this bound to the containing
1361
+ object. The value that is returned from your method will be
1362
+ serialized. If your method returns undefined, then the member will
1363
+ be excluded from the serialization.
1364
+
1365
+ If the replacer parameter is an array of strings, then it will be
1366
+ used to select the members to be serialized. It filters the results
1367
+ such that only members with keys listed in the replacer array are
1368
+ stringified.
1369
+
1370
+ Values that do not have JSON representations, such as undefined or
1371
+ functions, will not be serialized. Such values in objects will be
1372
+ dropped; in arrays they will be replaced with null. You can use
1373
+ a replacer function to replace those with JSON values.
1374
+ JSON.stringify(undefined) returns undefined.
1375
+
1376
+ The optional space parameter produces a stringification of the
1377
+ value that is filled with line breaks and indentation to make it
1378
+ easier to read.
1379
+
1380
+ If the space parameter is a non-empty string, then that string will
1381
+ be used for indentation. If the space parameter is a number, then
1382
+ the indentation will be that many spaces.
1383
+
1384
+ Example:
1385
+
1386
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
1387
+ // text is '["e",{"pluribus":"unum"}]'
1388
+
1389
+
1390
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
1391
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
1392
+
1393
+ text = JSON.stringify([new Date()], function (key, value) {
1394
+ return this[key] instanceof Date ?
1395
+ 'Date(' + this[key] + ')' : value;
1396
+ });
1397
+ // text is '["Date(---current time---)"]'
1398
+
1399
+
1400
+ JSON.parse(text, reviver)
1401
+ This method parses a JSON text to produce an object or array.
1402
+ It can throw a SyntaxError exception.
1403
+
1404
+ The optional reviver parameter is a function that can filter and
1405
+ transform the results. It receives each of the keys and values,
1406
+ and its return value is used instead of the original value.
1407
+ If it returns what it received, then the structure is not modified.
1408
+ If it returns undefined then the member is deleted.
1409
+
1410
+ Example:
1411
+
1412
+ // Parse the text. Values that look like ISO date strings will
1413
+ // be converted to Date objects.
1414
+
1415
+ myData = JSON.parse(text, function (key, value) {
1416
+ var a;
1417
+ if (typeof value === 'string') {
1418
+ a =
1419
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
1420
+ if (a) {
1421
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
1422
+ +a[5], +a[6]));
1423
+ }
1424
+ }
1425
+ return value;
1426
+ });
1427
+
1428
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
1429
+ var d;
1430
+ if (typeof value === 'string' &&
1431
+ value.slice(0, 5) === 'Date(' &&
1432
+ value.slice(-1) === ')') {
1433
+ d = new Date(value.slice(5, -1));
1434
+ if (d) {
1435
+ return d;
1436
+ }
1437
+ }
1438
+ return value;
1439
+ });
1440
+
1441
+
1442
+ This is a reference implementation. You are free to copy, modify, or
1443
+ redistribute.
1444
+
1445
+ This code should be minified before deployment.
1446
+ See http://javascript.crockford.com/jsmin.html
1447
+
1448
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
1449
+ NOT CONTROL.
1450
+ */
1451
+
1452
+ /*jslint evil: true */
1453
+
1454
+ /*global JSON */
1455
+
1456
+ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
1457
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
1458
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
1459
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
1460
+ test, toJSON, toString, valueOf
1461
+ */
1462
+
1463
+ // Create a JSON object only if one does not already exist. We create the
1464
+ // methods in a closure to avoid creating global variables.
1465
+
1466
+ if (!this.JSON) {
1467
+ JSON = {};
1468
+ }
1469
+ (function () {
1470
+
1471
+ function f(n) {
1472
+ // Format integers to have at least two digits.
1473
+ return n < 10 ? '0' + n : n;
1474
+ }
1475
+
1476
+ if (typeof Date.prototype.toJSON !== 'function') {
1477
+
1478
+ Date.prototype.toJSON = function (key) {
1479
+
1480
+ return this.getUTCFullYear() + '-' +
1481
+ f(this.getUTCMonth() + 1) + '-' +
1482
+ f(this.getUTCDate()) + 'T' +
1483
+ f(this.getUTCHours()) + ':' +
1484
+ f(this.getUTCMinutes()) + ':' +
1485
+ f(this.getUTCSeconds()) + 'Z';
1486
+ };
1487
+
1488
+ String.prototype.toJSON =
1489
+ Number.prototype.toJSON =
1490
+ Boolean.prototype.toJSON = function (key) {
1491
+ return this.valueOf();
1492
+ };
1493
+ }
1494
+
1495
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
1496
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
1497
+ gap,
1498
+ indent,
1499
+ meta = { // table of character substitutions
1500
+ '\b': '\\b',
1501
+ '\t': '\\t',
1502
+ '\n': '\\n',
1503
+ '\f': '\\f',
1504
+ '\r': '\\r',
1505
+ '"' : '\\"',
1506
+ '\\': '\\\\'
1507
+ },
1508
+ rep;
1509
+
1510
+
1511
+ function quote(string) {
1512
+
1513
+ // If the string contains no control characters, no quote characters, and no
1514
+ // backslash characters, then we can safely slap some quotes around it.
1515
+ // Otherwise we must also replace the offending characters with safe escape
1516
+ // sequences.
1517
+
1518
+ escapable.lastIndex = 0;
1519
+ return escapable.test(string) ?
1520
+ '"' + string.replace(escapable, function (a) {
1521
+ var c = meta[a];
1522
+ return typeof c === 'string' ? c :
1523
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1524
+ }) + '"' :
1525
+ '"' + string + '"';
1526
+ }
1527
+
1528
+
1529
+ function str(key, holder) {
1530
+
1531
+ // Produce a string from holder[key].
1532
+
1533
+ var i, // The loop counter.
1534
+ k, // The member key.
1535
+ v, // The member value.
1536
+ length,
1537
+ mind = gap,
1538
+ partial,
1539
+ value = holder[key];
1540
+
1541
+ // If the value has a toJSON method, call it to obtain a replacement value.
1542
+
1543
+ if (value && typeof value === 'object' &&
1544
+ typeof value.toJSON === 'function') {
1545
+ value = value.toJSON(key);
1546
+ }
1547
+
1548
+ // If we were called with a replacer function, then call the replacer to
1549
+ // obtain a replacement value.
1550
+
1551
+ if (typeof rep === 'function') {
1552
+ value = rep.call(holder, key, value);
1553
+ }
1554
+
1555
+ // What happens next depends on the value's type.
1556
+
1557
+ switch (typeof value) {
1558
+ case 'string':
1559
+ return quote(value);
1560
+
1561
+ case 'number':
1562
+
1563
+ // JSON numbers must be finite. Encode non-finite numbers as null.
1564
+
1565
+ return isFinite(value) ? String(value) : 'null';
1566
+
1567
+ case 'boolean':
1568
+ case 'null':
1569
+
1570
+ // If the value is a boolean or null, convert it to a string. Note:
1571
+ // typeof null does not produce 'null'. The case is included here in
1572
+ // the remote chance that this gets fixed someday.
1573
+
1574
+ return String(value);
1575
+
1576
+ // If the type is 'object', we might be dealing with an object or an array or
1577
+ // null.
1578
+
1579
+ case 'object':
1580
+
1581
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
1582
+ // so watch out for that case.
1583
+
1584
+ if (!value) {
1585
+ return 'null';
1586
+ }
1587
+
1588
+ // Make an array to hold the partial results of stringifying this object value.
1589
+
1590
+ gap += indent;
1591
+ partial = [];
1592
+
1593
+ // Is the value an array?
1594
+
1595
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
1596
+
1597
+ // The value is an array. Stringify every element. Use null as a placeholder
1598
+ // for non-JSON values.
1599
+
1600
+ length = value.length;
1601
+ for (i = 0; i < length; i += 1) {
1602
+ partial[i] = str(i, value) || 'null';
1603
+ }
1604
+
1605
+ // Join all of the elements together, separated with commas, and wrap them in
1606
+ // brackets.
1607
+
1608
+ v = partial.length === 0 ? '[]' :
1609
+ gap ? '[\n' + gap +
1610
+ partial.join(',\n' + gap) + '\n' +
1611
+ mind + ']' :
1612
+ '[' + partial.join(',') + ']';
1613
+ gap = mind;
1614
+ return v;
1615
+ }
1616
+
1617
+ // If the replacer is an array, use it to select the members to be stringified.
1618
+
1619
+ if (rep && typeof rep === 'object') {
1620
+ length = rep.length;
1621
+ for (i = 0; i < length; i += 1) {
1622
+ k = rep[i];
1623
+ if (typeof k === 'string') {
1624
+ v = str(k, value);
1625
+ if (v) {
1626
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1627
+ }
1628
+ }
1629
+ }
1630
+ } else {
1631
+
1632
+ // Otherwise, iterate through all of the keys in the object.
1633
+
1634
+ for (k in value) {
1635
+ if (Object.hasOwnProperty.call(value, k)) {
1636
+ v = str(k, value);
1637
+ if (v) {
1638
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1639
+ }
1640
+ }
1641
+ }
1642
+ }
1643
+
1644
+ // Join all of the member texts together, separated with commas,
1645
+ // and wrap them in braces.
1646
+
1647
+ v = partial.length === 0 ? '{}' :
1648
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
1649
+ mind + '}' : '{' + partial.join(',') + '}';
1650
+ gap = mind;
1651
+ return v;
1652
+ }
1653
+ }
1654
+
1655
+ // If the JSON object does not yet have a stringify method, give it one.
1656
+
1657
+ // NOTE we've hacked this to expose this method to Faye. We need to use this
1658
+ // to avoid problems with buggy Firefox version and bad #toJSON implementations
1659
+
1660
+ Faye.stringify = function (value, replacer, space) {
1661
+
1662
+ // The stringify method takes a value and an optional replacer, and an optional
1663
+ // space parameter, and returns a JSON text. The replacer can be a function
1664
+ // that can replace values, or an array of strings that will select the keys.
1665
+ // A default replacer method can be provided. Use of the space parameter can
1666
+ // produce text that is more easily readable.
1667
+
1668
+ var i;
1669
+ gap = '';
1670
+ indent = '';
1671
+
1672
+ // If the space parameter is a number, make an indent string containing that
1673
+ // many spaces.
1674
+
1675
+ if (typeof space === 'number') {
1676
+ for (i = 0; i < space; i += 1) {
1677
+ indent += ' ';
1678
+ }
1679
+
1680
+ // If the space parameter is a string, it will be used as the indent string.
1681
+
1682
+ } else if (typeof space === 'string') {
1683
+ indent = space;
1684
+ }
1685
+
1686
+ // If there is a replacer, it must be a function or an array.
1687
+ // Otherwise, throw an error.
1688
+
1689
+ rep = replacer;
1690
+ if (replacer && typeof replacer !== 'function' &&
1691
+ (typeof replacer !== 'object' ||
1692
+ typeof replacer.length !== 'number')) {
1693
+ throw new Error('JSON.stringify');
1694
+ }
1695
+
1696
+ // Make a fake root object containing our value under the key of ''.
1697
+ // Return the result of stringifying the value.
1698
+
1699
+ return str('', {'': value});
1700
+ };
1701
+
1702
+ if (typeof JSON.stringify !== 'function') {
1703
+ JSON.stringify = Faye.stringify;
1704
+ }
1705
+
1706
+
1707
+ // If the JSON object does not yet have a parse method, give it one.
1708
+
1709
+ if (typeof JSON.parse !== 'function') {
1710
+ JSON.parse = function (text, reviver) {
1711
+
1712
+ // The parse method takes a text and an optional reviver function, and returns
1713
+ // a JavaScript value if the text is a valid JSON text.
1714
+
1715
+ var j;
1716
+
1717
+ function walk(holder, key) {
1718
+
1719
+ // The walk method is used to recursively walk the resulting structure so
1720
+ // that modifications can be made.
1721
+
1722
+ var k, v, value = holder[key];
1723
+ if (value && typeof value === 'object') {
1724
+ for (k in value) {
1725
+ if (Object.hasOwnProperty.call(value, k)) {
1726
+ v = walk(value, k);
1727
+ if (v !== undefined) {
1728
+ value[k] = v;
1729
+ } else {
1730
+ delete value[k];
1731
+ }
1732
+ }
1733
+ }
1734
+ }
1735
+ return reviver.call(holder, key, value);
1736
+ }
1737
+
1738
+
1739
+ // Parsing happens in four stages. In the first stage, we replace certain
1740
+ // Unicode characters with escape sequences. JavaScript handles many characters
1741
+ // incorrectly, either silently deleting them, or treating them as line endings.
1742
+
1743
+ cx.lastIndex = 0;
1744
+ if (cx.test(text)) {
1745
+ text = text.replace(cx, function (a) {
1746
+ return '\\u' +
1747
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1748
+ });
1749
+ }
1750
+
1751
+ // In the second stage, we run the text against regular expressions that look
1752
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
1753
+ // because they can cause invocation, and '=' because it can cause mutation.
1754
+ // But just to be safe, we want to reject all unexpected forms.
1755
+
1756
+ // We split the second stage into 4 regexp operations in order to work around
1757
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
1758
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
1759
+ // replace all simple value tokens with ']' characters. Third, we delete all
1760
+ // open brackets that follow a colon or comma or that begin the text. Finally,
1761
+ // we look to see that the remaining characters are only whitespace or ']' or
1762
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
1763
+
1764
+ if (/^[\],:{}\s]*$/.
1765
+ test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
1766
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
1767
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
1768
+
1769
+ // In the third stage we use the eval function to compile the text into a
1770
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
1771
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
1772
+ // in parens to eliminate the ambiguity.
1773
+
1774
+ j = eval('(' + text + ')');
1775
+
1776
+ // In the optional fourth stage, we recursively walk the new structure, passing
1777
+ // each name/value pair to a reviver function for possible transformation.
1778
+
1779
+ return typeof reviver === 'function' ?
1780
+ walk({'': j}, '') : j;
1781
+ }
1782
+
1783
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
1784
+
1785
+ throw new SyntaxError('JSON.parse');
1786
+ };
1787
+ }
1788
+ }());
1789
+
1790
+
1791
+ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
1792
+ UNCONNECTED: 1,
1793
+ CONNECTING: 2,
1794
+ CONNECTED: 3,
1795
+
1796
+ batching: false,
1797
+
1798
+ request: function(messages, timeout) {
1799
+ if (messages.length === 0) return;
1800
+ this._messages = this._messages || {};
1801
+
1802
+ for (var i = 0, n = messages.length; i < n; i++) {
1803
+ this._messages[messages[i].id] = messages[i];
1804
+ }
1805
+ this.withSocket(function(socket) { socket.send(Faye.toJSON(messages)) });
1806
+ },
1807
+
1808
+ withSocket: function(callback, context) {
1809
+ this.callback(callback, context);
1810
+ this.connect();
1811
+ },
1812
+
1813
+ close: function() {
1814
+ if (this._closed) return;
1815
+ this._closed = true;
1816
+ if (this._socket) this._socket.close();
1817
+ },
1818
+
1819
+ connect: function() {
1820
+ if (this._closed) return;
1821
+
1822
+ this._state = this._state || this.UNCONNECTED;
1823
+ if (this._state !== this.UNCONNECTED) return;
1824
+
1825
+ this._state = this.CONNECTING;
1826
+
1827
+ var ws = Faye.Transport.WebSocket.getClass();
1828
+ this._socket = new ws(Faye.Transport.WebSocket.getSocketUrl(this._endpoint));
1829
+ var self = this;
1830
+
1831
+ this._socket.onopen = function() {
1832
+ self._state = self.CONNECTED;
1833
+ self.setDeferredStatus('succeeded', self._socket);
1834
+ self.trigger('up');
1835
+ };
1836
+
1837
+ this._socket.onmessage = function(event) {
1838
+ var messages = [].concat(JSON.parse(event.data));
1839
+ for (var i = 0, n = messages.length; i < n; i++) {
1840
+ delete self._messages[messages[i].id];
1841
+ }
1842
+ self.receive(messages);
1843
+ };
1844
+
1845
+ this._socket.onclose = function() {
1846
+ var wasConnected = (self._state === self.CONNECTED);
1847
+ self.setDeferredStatus('deferred');
1848
+ self._state = self.UNCONNECTED;
1849
+ delete self._socket;
1850
+
1851
+ if (wasConnected) return self.resend();
1852
+
1853
+ var retry = self._client.retry * 1000;
1854
+ Faye.ENV.setTimeout(function() { self.connect() }, retry);
1855
+ self.trigger('down');
1856
+ };
1857
+ },
1858
+
1859
+ resend: function() {
1860
+ var messages = Faye.map(this._messages, function(id, msg) { return msg });
1861
+ this.request(messages);
1862
+ }
1863
+ }), {
1864
+ WEBSOCKET_TIMEOUT: 1000,
1865
+
1866
+ getSocketUrl: function(endpoint) {
1867
+ if (Faye.URI) endpoint = Faye.URI.parse(endpoint).toURL();
1868
+ return endpoint.replace(/^http(s?):/ig, 'ws$1:');
1869
+ },
1870
+
1871
+ getClass: function() {
1872
+ return (Faye.WebSocket && Faye.WebSocket.Client) ||
1873
+ Faye.ENV.WebSocket ||
1874
+ Faye.ENV.MozWebSocket;
1875
+ },
1876
+
1877
+ isUsable: function(endpoint, callback, context) {
1878
+ var ws = this.getClass();
1879
+ if (!ws) return callback.call(context, false);
1880
+
1881
+ var connected = false,
1882
+ called = false,
1883
+ socketUrl = this.getSocketUrl(endpoint),
1884
+ socket = new ws(socketUrl);
1885
+
1886
+ socket.onopen = function() {
1887
+ connected = true;
1888
+ socket.close();
1889
+ callback.call(context, true);
1890
+ called = true;
1891
+ socket = null;
1892
+ };
1893
+
1894
+ var notconnected = function() {
1895
+ if (!called && !connected) callback.call(context, false);
1896
+ called = true;
1897
+ };
1898
+
1899
+ socket.onclose = socket.onerror = notconnected;
1900
+ Faye.ENV.setTimeout(notconnected, this.WEBSOCKET_TIMEOUT);
1901
+ }
1902
+ });
1903
+
1904
+ Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable);
1905
+ Faye.Transport.register('websocket', Faye.Transport.WebSocket);
1906
+
1907
+ Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
1908
+ initialize: function(client, endpoint) {
1909
+ Faye.Transport.prototype.initialize.call(this, client, endpoint);
1910
+ this._xhr = new Faye.Transport.XHR(client, endpoint);
1911
+
1912
+ var socket = new EventSource(endpoint + '/' + client.getClientId()),
1913
+ self = this;
1914
+
1915
+ socket.onopen = function() {
1916
+ self.trigger('up');
1917
+ };
1918
+
1919
+ socket.onerror = function() {
1920
+ self.trigger('down');
1921
+ };
1922
+
1923
+ socket.onmessage = function(event) {
1924
+ self.receive(JSON.parse(event.data));
1925
+ };
1926
+
1927
+ this._socket = socket;
1928
+ },
1929
+
1930
+ request: function(message, timeout) {
1931
+ this._xhr.request(message, timeout);
1932
+ },
1933
+
1934
+ close: function() {
1935
+ this._socket.close();
1936
+ }
1937
+ }), {
1938
+ isUsable: function(endpoint, callback, context) {
1939
+ Faye.Transport.XHR.isUsable(endpoint, function(usable) {
1940
+ callback.call(context, usable && Faye.ENV.EventSource);
1941
+ });
1942
+ }
1943
+ });
1944
+
1945
+ Faye.Transport.register('eventsource', Faye.Transport.EventSource);
1946
+
1947
+
1948
+ Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, {
1949
+ request: function(message, timeout) {
1950
+ var retry = this.retry(message, timeout),
1951
+ path = Faye.URI.parse(this._endpoint).pathname,
1952
+ self = this,
1953
+ xhr = Faye.ENV.ActiveXObject
1954
+ ? new ActiveXObject("Microsoft.XMLHTTP")
1955
+ : new XMLHttpRequest();
1956
+
1957
+ xhr.open('POST', path, true);
1958
+ xhr.setRequestHeader('Content-Type', 'application/json');
1959
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1960
+
1961
+ var headers = this.headers;
1962
+ for (var key in headers) {
1963
+ if (!headers.hasOwnProperty(key)) continue;
1964
+ xhr.setRequestHeader(key, headers[key]);
1965
+ }
1966
+
1967
+ var abort = function() { xhr.abort() };
1968
+ Faye.Event.on(Faye.ENV, 'beforeunload', abort);
1969
+
1970
+ var cleanUp = function() {
1971
+ Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
1972
+ xhr.onreadystatechange = function() {};
1973
+ xhr = null;
1974
+ };
1975
+
1976
+ xhr.onreadystatechange = function() {
1977
+ if (xhr.readyState !== 4) return;
1978
+
1979
+ var parsedMessage = null,
1980
+ status = xhr.status,
1981
+ successful = ((status >= 200 && status < 300) ||
1982
+ status === 304 ||
1983
+ status === 1223);
1984
+
1985
+ if (!successful) {
1986
+ cleanUp();
1987
+ retry();
1988
+ return self.trigger('down');
1989
+ }
1990
+
1991
+ try {
1992
+ parsedMessage = JSON.parse(xhr.responseText);
1993
+ } catch (e) {}
1994
+
1995
+ cleanUp();
1996
+
1997
+ if (parsedMessage) {
1998
+ self.receive(parsedMessage);
1999
+ self.trigger('up');
2000
+ } else {
2001
+ retry();
2002
+ self.trigger('down');
2003
+ }
2004
+ };
2005
+
2006
+ xhr.send(Faye.toJSON(message));
2007
+ }
2008
+ }), {
2009
+ isUsable: function(endpoint, callback, context) {
2010
+ callback.call(context, Faye.URI.parse(endpoint).isLocal());
2011
+ }
2012
+ });
2013
+
2014
+ Faye.Transport.register('long-polling', Faye.Transport.XHR);
2015
+
2016
+ Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
2017
+ request: function(message, timeout) {
2018
+ var xhrClass = Faye.ENV.XDomainRequest ? XDomainRequest : XMLHttpRequest,
2019
+ xhr = new xhrClass(),
2020
+ retry = this.retry(message, timeout),
2021
+ self = this;
2022
+
2023
+ xhr.open('POST', this._endpoint, true);
2024
+
2025
+ var cleanUp = function() {
2026
+ if (!xhr) return false;
2027
+ xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null;
2028
+ xhr = null;
2029
+ Faye.ENV.clearTimeout(timer);
2030
+ return true;
2031
+ };
2032
+
2033
+ xhr.onload = function() {
2034
+ var parsedMessage = null;
2035
+ try {
2036
+ parsedMessage = JSON.parse(xhr.responseText);
2037
+ } catch (e) {}
2038
+
2039
+ cleanUp();
2040
+
2041
+ if (parsedMessage) {
2042
+ self.receive(parsedMessage);
2043
+ self.trigger('up');
2044
+ } else {
2045
+ retry();
2046
+ self.trigger('down');
2047
+ }
2048
+ };
2049
+
2050
+ var onerror = function() {
2051
+ cleanUp();
2052
+ retry();
2053
+ self.trigger('down');
2054
+ };
2055
+ var timer = Faye.ENV.setTimeout(onerror, 1.5 * 1000 * timeout);
2056
+ xhr.onerror = onerror;
2057
+ xhr.ontimeout = onerror;
2058
+
2059
+ xhr.onprogress = function() {};
2060
+ xhr.send('message=' + encodeURIComponent(Faye.toJSON(message)));
2061
+ }
2062
+ }), {
2063
+ isUsable: function(endpoint, callback, context) {
2064
+ if (Faye.URI.parse(endpoint).isLocal())
2065
+ return callback.call(context, false);
2066
+
2067
+ if (Faye.ENV.XDomainRequest)
2068
+ return callback.call(context, true);
2069
+
2070
+ if (Faye.ENV.XMLHttpRequest) {
2071
+ var xhr = new Faye.ENV.XMLHttpRequest();
2072
+ return callback.call(context, xhr.withCredentials !== undefined);
2073
+ }
2074
+ return callback.call(context, false);
2075
+ }
2076
+ });
2077
+
2078
+ Faye.Transport.register('cross-origin-long-polling', Faye.Transport.CORS);
2079
+
2080
+
2081
+ Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
2082
+ request: function(message, timeout) {
2083
+ var params = {message: Faye.toJSON(message)},
2084
+ head = document.getElementsByTagName('head')[0],
2085
+ script = document.createElement('script'),
2086
+ callbackName = Faye.Transport.JSONP.getCallbackName(),
2087
+ location = Faye.URI.parse(this._endpoint, params),
2088
+ retry = this.retry(message, timeout),
2089
+ self = this;
2090
+
2091
+ Faye.ENV[callbackName] = function(data) {
2092
+ cleanUp();
2093
+ self.receive(data);
2094
+ self.trigger('up');
2095
+ };
2096
+
2097
+ var timer = Faye.ENV.setTimeout(function() {
2098
+ cleanUp();
2099
+ retry();
2100
+ self.trigger('down');
2101
+ }, 1.5 * 1000 * timeout);
2102
+
2103
+ var cleanUp = function() {
2104
+ if (!Faye.ENV[callbackName]) return false;
2105
+ Faye.ENV[callbackName] = undefined;
2106
+ try { delete Faye.ENV[callbackName] } catch (e) {}
2107
+ Faye.ENV.clearTimeout(timer);
2108
+ script.parentNode.removeChild(script);
2109
+ return true;
2110
+ };
2111
+
2112
+ location.params.jsonp = callbackName;
2113
+ script.type = 'text/javascript';
2114
+ script.src = location.toURL();
2115
+ head.appendChild(script);
2116
+ }
2117
+ }), {
2118
+ _cbCount: 0,
2119
+
2120
+ getCallbackName: function() {
2121
+ this._cbCount += 1;
2122
+ return '__jsonp' + this._cbCount + '__';
2123
+ },
2124
+
2125
+ isUsable: function(endpoint, callback, context) {
2126
+ callback.call(context, true);
2127
+ }
2128
+ });
2129
+
2130
+ Faye.Transport.register('callback-polling', Faye.Transport.JSONP);