pusher_rails 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,12 @@
1
+ v0.2.2
2
+ ======
3
+ - Upgraded to pusher.js 1.12.0
4
+ - Upgraded to backpusher.js 0.0.2
5
+
6
+ v0.2.1
7
+ ======
8
+ - Upgraded to pusher-gem 0.9.2
9
+
1
10
  v0.2.0
2
11
  ======
3
12
  - Upgraded to pusher-gem 0.9.1
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
  =====================
3
3
 
4
4
  Adds:
5
- - [pusher-gem v0.9.1](https://github.com/pusher/pusher-gem/tree/v0.9.1)
6
- - [pusher.js v1.11.0](https://github.com/pusher/pusher-js/tree/v1.11.0)
7
- - [backpusher.js](https://github.com/pusher/backpusher/commit/e61c9d7a761fcb48f312416408d1bf4ed418735b#diff-1)
5
+ - [pusher-gem v0.9.2](https://github.com/pusher/pusher-gem/tree/v0.9.2)
6
+ - [pusher.js v1.12.0](https://github.com/pusher/pusher-js/tree/v1.12.0)
7
+ - [backpusher.js](https://github.com/pusher/backpusher)
8
8
 
9
9
  This pulls in the *pusher-gem* as well as adding *pusher.js* and *backpusher.js* to the assets pipeline of your Rails 3.1+ app.
10
10
 
@@ -21,16 +21,16 @@ Licenses
21
21
  ========
22
22
 
23
23
  /*!
24
- * Pusher JavaScript Library v1.11.0
24
+ * Pusher JavaScript Library v1.12.0
25
25
  * http://pusherapp.com/
26
26
  *
27
27
  * Copyright 2011, Pusher
28
28
  * Released under the MIT licence.
29
29
  */
30
30
 
31
- // Backpusher.js 0.0.1
32
- // (c) 2011 Pusher.
33
- // Backpusher may be freely distributed under the MIT license.
34
- // For all details and documentation:
35
- // http://github.com/pusher/backpusher
31
+ // Backpusher.js 0.0.2
32
+ // (c) 2011-2012 Pusher.
33
+ // Backpusher may be freely distributed under the MIT license.
34
+ // For all details and documentation:
35
+ // http://github.com/pusher/backpusher
36
36
 
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'pusher_rails'
6
- s.version = '0.2.0'
6
+ s.version = '0.2.2'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["David Grandinetti"]
9
9
  s.email = ["dave@wegoto12.com"]
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.description = 'Adds pusher.js/backpusher.js to the asset pipeline and pusher-gem to to your app.'
12
12
  s.homepage = 'https://github.com/dbgrandi/pusher_rails'
13
13
 
14
- s.add_dependency "pusher", "~> 0.9.1"
14
+ s.add_dependency "pusher", "~> 0.9.2"
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.require_paths = ["lib"]
@@ -1,5 +1,5 @@
1
- // Backpusher.js 0.0.1
2
- // (c) 2011 Pusher.
1
+ // Backpusher.js 0.0.2
2
+ // (c) 2011-2012 Pusher.
3
3
  // Backpusher may be freely distributed under the MIT license.
4
4
  // For all details and documentation:
5
5
  // http://github.com/pusher/backpusher
@@ -92,65 +92,17 @@
92
92
  }
93
93
  };
94
94
 
95
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
96
- var methodMap = {
97
- 'create': 'POST',
98
- 'update': 'PUT',
99
- 'delete': 'DELETE',
100
- 'read' : 'GET'
101
- };
102
-
95
+ // Add socket ID to every Backbone.sync request
96
+ var origBackboneSync = Backbone.sync;
103
97
  Backbone.sync = function(method, model, options) {
104
- var type = methodMap[method];
105
-
106
- // Default JSON-request options.
107
- var params = _.extend({
108
- type: type,
109
- dataType: 'json'
110
- }, options);
111
-
112
- if (!(model && model.url)) {
113
- throw new Error("A 'url' property or function must be specified");
114
- }
115
-
116
- if (!params.url) {
117
- params.url = _.isFunction(model.url) ? model.url() : model.url;
118
- params.url += '?socket_id=' + Backbone.pusher_socket_id;
119
- }
120
-
121
- // Ensure that we have the appropriate request data.
122
- if (!params.data && model && (method == 'create' || method == 'update')) {
123
- params.contentType = 'application/json';
124
- params.data = JSON.stringify(model.toJSON());
125
- }
126
-
127
- // For older servers, emulate JSON by encoding the request into an HTML-form.
128
- if (Backbone.emulateJSON) {
129
- params.contentType = 'application/x-www-form-urlencoded';
130
- params.data = params.data ? {model : params.data} : {};
131
- }
132
-
133
- // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
134
- // And an `X-HTTP-Method-Override` header.
135
- if (Backbone.emulateHTTP) {
136
- if (type === 'PUT' || type === 'DELETE') {
137
- if (Backbone.emulateJSON) params.data._method = type;
138
- params.type = 'POST';
139
- params.beforeSend = function(xhr) {
140
- xhr.setRequestHeader('X-HTTP-Method-Override', type);
141
- };
142
- }
143
- }
144
-
145
- // Don't process data on a non-GET request.
146
- if (params.type !== 'GET' && !Backbone.emulateJSON) {
147
- params.processData = false;
148
- }
98
+ options.headers = _.extend(
99
+ { 'X-Pusher-Socket-ID': Backbone.pusher_socket_id },
100
+ options.headers
101
+ );
149
102
 
150
- // Make the request.
151
- return $.ajax(params);
103
+ return origBackboneSync(method, model, options);
152
104
  };
153
105
 
154
106
  // Export:
155
107
  exports.Backpusher = Backpusher;
156
- })((typeof exports !== 'undefined' ? exports : this));
108
+ })((typeof exports !== 'undefined' ? exports : this));
@@ -1,206 +1,211 @@
1
1
  /*!
2
- * Pusher JavaScript Library v1.11.0
2
+ * Pusher JavaScript Library v1.12.0
3
3
  * http://pusherapp.com/
4
4
  *
5
5
  * Copyright 2011, Pusher
6
6
  * Released under the MIT licence.
7
7
  */
8
8
 
9
- if (Function.prototype.scopedTo === undefined) {
10
- Function.prototype.scopedTo = function(context, args) {
11
- var f = this;
12
- return function() {
13
- return f.apply(context, Array.prototype.slice.call(args || [])
14
- .concat(Array.prototype.slice.call(arguments)));
9
+ ;(function() {
10
+ if (Function.prototype.scopedTo === undefined) {
11
+ Function.prototype.scopedTo = function(context, args) {
12
+ var f = this;
13
+ return function() {
14
+ return f.apply(context, Array.prototype.slice.call(args || [])
15
+ .concat(Array.prototype.slice.call(arguments)));
16
+ };
15
17
  };
16
- };
17
- }
18
-
19
- var Pusher = function(app_key, options) {
20
- this.options = options || {};
21
- this.key = app_key;
22
- this.channels = new Pusher.Channels();
23
- this.global_emitter = new Pusher.EventsDispatcher()
24
-
25
- var self = this;
26
-
27
- this.checkAppKey();
28
-
29
- this.connection = new Pusher.Connection(this.key, this.options);
30
-
31
- // Setup / teardown connection
32
- this.connection
33
- .bind('connected', function() {
34
- self.subscribeAll();
35
- })
36
- .bind('message', function(params) {
37
- var internal = (params.event.indexOf('pusher_internal:') === 0);
38
- if (params.channel) {
39
- var channel;
40
- if (channel = self.channel(params.channel)) {
41
- channel.emit(params.event, params.data);
18
+ }
19
+
20
+ var Pusher = function(app_key, options) {
21
+ this.options = options || {};
22
+ this.key = app_key;
23
+ this.channels = new Pusher.Channels();
24
+ this.global_emitter = new Pusher.EventsDispatcher()
25
+
26
+ var self = this;
27
+
28
+ this.checkAppKey();
29
+
30
+ this.connection = new Pusher.Connection(this.key, this.options);
31
+
32
+ // Setup / teardown connection
33
+ this.connection
34
+ .bind('connected', function() {
35
+ self.subscribeAll();
36
+ })
37
+ .bind('message', function(params) {
38
+ var internal = (params.event.indexOf('pusher_internal:') === 0);
39
+ if (params.channel) {
40
+ var channel;
41
+ if (channel = self.channel(params.channel)) {
42
+ channel.emit(params.event, params.data);
43
+ }
42
44
  }
43
- }
44
- // Emit globaly [deprecated]
45
- if (!internal) self.global_emitter.emit(params.event, params.data);
46
- })
47
- .bind('disconnected', function() {
48
- self.channels.disconnect();
49
- })
50
- .bind('error', function(err) {
51
- Pusher.warn('Error', err);
52
- });
45
+ // Emit globaly [deprecated]
46
+ if (!internal) self.global_emitter.emit(params.event, params.data);
47
+ })
48
+ .bind('disconnected', function() {
49
+ self.channels.disconnect();
50
+ })
51
+ .bind('error', function(err) {
52
+ Pusher.warn('Error', err);
53
+ });
53
54
 
54
- Pusher.instances.push(this);
55
+ Pusher.instances.push(this);
55
56
 
56
- if (Pusher.isReady) self.connect();
57
- };
58
- Pusher.instances = [];
59
- Pusher.prototype = {
60
- channel: function(name) {
61
- return this.channels.find(name);
62
- },
57
+ if (Pusher.isReady) self.connect();
58
+ };
59
+ Pusher.instances = [];
60
+ Pusher.prototype = {
61
+ channel: function(name) {
62
+ return this.channels.find(name);
63
+ },
63
64
 
64
- connect: function() {
65
- this.connection.connect();
66
- },
65
+ connect: function() {
66
+ this.connection.connect();
67
+ },
67
68
 
68
- disconnect: function() {
69
- this.connection.disconnect();
70
- },
69
+ disconnect: function() {
70
+ this.connection.disconnect();
71
+ },
71
72
 
72
- bind: function(event_name, callback) {
73
- this.global_emitter.bind(event_name, callback);
74
- return this;
75
- },
73
+ bind: function(event_name, callback) {
74
+ this.global_emitter.bind(event_name, callback);
75
+ return this;
76
+ },
76
77
 
77
- bind_all: function(callback) {
78
- this.global_emitter.bind_all(callback);
79
- return this;
80
- },
78
+ bind_all: function(callback) {
79
+ this.global_emitter.bind_all(callback);
80
+ return this;
81
+ },
81
82
 
82
- subscribeAll: function() {
83
- var channel;
84
- for (channel in this.channels.channels) {
85
- if (this.channels.channels.hasOwnProperty(channel)) {
86
- this.subscribe(channel);
83
+ subscribeAll: function() {
84
+ var channel;
85
+ for (channelName in this.channels.channels) {
86
+ if (this.channels.channels.hasOwnProperty(channelName)) {
87
+ this.subscribe(channelName);
88
+ }
87
89
  }
88
- }
89
- },
90
+ },
90
91
 
91
- subscribe: function(channel_name) {
92
- var self = this;
93
- var channel = this.channels.add(channel_name, this);
94
- if (this.connection.state === 'connected') {
95
- channel.authorize(this, function(err, data) {
96
- if (err) {
97
- channel.emit('pusher:subscription_error', data);
98
- } else {
99
- self.send_event('pusher:subscribe', {
100
- channel: channel_name,
101
- auth: data.auth,
102
- channel_data: data.channel_data
103
- });
104
- }
105
- });
106
- }
107
- return channel;
108
- },
92
+ subscribe: function(channel_name) {
93
+ var self = this;
94
+ var channel = this.channels.add(channel_name, this);
95
+
96
+ if (this.connection.state === 'connected') {
97
+ channel.authorize(this.connection.socket_id, this.options, function(err, data) {
98
+ if (err) {
99
+ channel.emit('pusher:subscription_error', data);
100
+ } else {
101
+ self.send_event('pusher:subscribe', {
102
+ channel: channel_name,
103
+ auth: data.auth,
104
+ channel_data: data.channel_data
105
+ });
106
+ }
107
+ });
108
+ }
109
+ return channel;
110
+ },
109
111
 
110
- unsubscribe: function(channel_name) {
111
- this.channels.remove(channel_name);
112
- if (this.connection.state === 'connected') {
113
- this.send_event('pusher:unsubscribe', {
114
- channel: channel_name
115
- });
116
- }
117
- },
112
+ unsubscribe: function(channel_name) {
113
+ this.channels.remove(channel_name);
114
+ if (this.connection.state === 'connected') {
115
+ this.send_event('pusher:unsubscribe', {
116
+ channel: channel_name
117
+ });
118
+ }
119
+ },
118
120
 
119
- send_event: function(event_name, data, channel) {
120
- return this.connection.send_event(event_name, data, channel);
121
- },
121
+ send_event: function(event_name, data, channel) {
122
+ return this.connection.send_event(event_name, data, channel);
123
+ },
122
124
 
123
- checkAppKey: function() {
124
- if(this.key === null || this.key === undefined) {
125
- Pusher.warn('Warning', 'You must pass your app key when you instantiate Pusher.');
126
- }
127
- }
128
- };
129
-
130
- Pusher.Util = {
131
- extend: function extend(target, extensions) {
132
- for (var property in extensions) {
133
- if (extensions[property] && extensions[property].constructor &&
134
- extensions[property].constructor === Object) {
135
- target[property] = extend(target[property] || {}, extensions[property]);
136
- } else {
137
- target[property] = extensions[property];
125
+ checkAppKey: function() {
126
+ if(this.key === null || this.key === undefined) {
127
+ Pusher.warn('Warning', 'You must pass your app key when you instantiate Pusher.');
138
128
  }
139
129
  }
140
- return target;
141
- },
142
-
143
- stringify: function stringify() {
144
- var m = ["Pusher"]
145
- for (var i = 0; i < arguments.length; i++){
146
- if (typeof arguments[i] === "string") {
147
- m.push(arguments[i])
148
- } else {
149
- if (window['JSON'] == undefined) {
150
- m.push(arguments[i].toString());
130
+ };
131
+
132
+ Pusher.Util = {
133
+ extend: function extend(target, extensions) {
134
+ for (var property in extensions) {
135
+ if (extensions[property] && extensions[property].constructor &&
136
+ extensions[property].constructor === Object) {
137
+ target[property] = extend(target[property] || {}, extensions[property]);
151
138
  } else {
152
- m.push(JSON.stringify(arguments[i]))
139
+ target[property] = extensions[property];
153
140
  }
154
141
  }
155
- };
156
- return m.join(" : ")
157
- },
158
-
159
- arrayIndexOf: function(array, item) { // MSIE doesn't have array.indexOf
160
- var nativeIndexOf = Array.prototype.indexOf;
161
- if (array == null) return -1;
162
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
163
- for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
164
- return -1;
165
- }
166
- };
167
-
168
- // To receive log output provide a Pusher.log function, for example
169
- // Pusher.log = function(m){console.log(m)}
170
- Pusher.debug = function() {
171
- if (!Pusher.log) return
172
- Pusher.log(Pusher.Util.stringify.apply(this, arguments))
173
- }
174
- Pusher.warn = function() {
175
- if (window.console && window.console.warn) {
176
- window.console.warn(Pusher.Util.stringify.apply(this, arguments));
177
- } else {
142
+ return target;
143
+ },
144
+
145
+ stringify: function stringify() {
146
+ var m = ["Pusher"]
147
+ for (var i = 0; i < arguments.length; i++){
148
+ if (typeof arguments[i] === "string") {
149
+ m.push(arguments[i])
150
+ } else {
151
+ if (window['JSON'] == undefined) {
152
+ m.push(arguments[i].toString());
153
+ } else {
154
+ m.push(JSON.stringify(arguments[i]))
155
+ }
156
+ }
157
+ };
158
+ return m.join(" : ")
159
+ },
160
+
161
+ arrayIndexOf: function(array, item) { // MSIE doesn't have array.indexOf
162
+ var nativeIndexOf = Array.prototype.indexOf;
163
+ if (array == null) return -1;
164
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
165
+ for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
166
+ return -1;
167
+ }
168
+ };
169
+
170
+ // To receive log output provide a Pusher.log function, for example
171
+ // Pusher.log = function(m){console.log(m)}
172
+ Pusher.debug = function() {
178
173
  if (!Pusher.log) return
179
- Pusher.log(Pusher.Util.stringify.apply(this, arguments));
174
+ Pusher.log(Pusher.Util.stringify.apply(this, arguments))
180
175
  }
181
- };
182
-
183
- // Pusher defaults
184
- Pusher.VERSION = '1.11.0';
185
-
186
- Pusher.host = 'ws.pusherapp.com';
187
- Pusher.ws_port = 80;
188
- Pusher.wss_port = 443;
189
- Pusher.channel_auth_endpoint = '/pusher/auth';
190
- Pusher.cdn_http = 'http://js.pusher.com/'
191
- Pusher.cdn_https = 'https://d3dy5gmtp8yhk7.cloudfront.net/'
192
- Pusher.dependency_suffix = '';
193
- Pusher.channel_auth_transport = 'ajax';
194
- Pusher.activity_timeout = 120000;
195
- Pusher.pong_timeout = 30000;
196
-
197
- Pusher.isReady = false;
198
- Pusher.ready = function() {
199
- Pusher.isReady = true;
200
- for (var i = 0, l = Pusher.instances.length; i < l; i++) {
201
- Pusher.instances[i].connect();
202
- }
203
- };
176
+ Pusher.warn = function() {
177
+ if (window.console && window.console.warn) {
178
+ window.console.warn(Pusher.Util.stringify.apply(this, arguments));
179
+ } else {
180
+ if (!Pusher.log) return
181
+ Pusher.log(Pusher.Util.stringify.apply(this, arguments));
182
+ }
183
+ };
184
+
185
+ // Pusher defaults
186
+ Pusher.VERSION = '1.12.0';
187
+
188
+ Pusher.host = 'ws.pusherapp.com';
189
+ Pusher.ws_port = 80;
190
+ Pusher.wss_port = 443;
191
+ Pusher.channel_auth_endpoint = '/pusher/auth';
192
+ Pusher.cdn_http = 'http://js.pusher.com/'
193
+ Pusher.cdn_https = 'https://d3dy5gmtp8yhk7.cloudfront.net/'
194
+ Pusher.dependency_suffix = '';
195
+ Pusher.channel_auth_transport = 'ajax';
196
+ Pusher.activity_timeout = 120000;
197
+ Pusher.pong_timeout = 30000;
198
+
199
+ Pusher.isReady = false;
200
+ Pusher.ready = function() {
201
+ Pusher.isReady = true;
202
+ for (var i = 0, l = Pusher.instances.length; i < l; i++) {
203
+ Pusher.instances[i].connect();
204
+ }
205
+ };
206
+
207
+ this.Pusher = Pusher;
208
+ }).call(this);
204
209
 
205
210
  ;(function() {
206
211
  /* Abstract event binding
@@ -230,7 +235,7 @@ Example:
230
235
  this.callbacks[event_name].push(callback);
231
236
  return this;// chainable
232
237
  };
233
-
238
+
234
239
  EventsDispatcher.prototype.unbind = function(eventName, callback) {
235
240
  if(this.callbacks[eventName]) {
236
241
  var index = Pusher.Util.arrayIndexOf(this.callbacks[eventName], callback);
@@ -307,6 +312,11 @@ Example:
307
312
  var stateCallbacks = this.stateActions;
308
313
 
309
314
  if (prevState && (Pusher.Util.arrayIndexOf(this.transitions[prevState], nextState) == -1)) {
315
+ this.emit('invalid_transition_attempt', {
316
+ oldState: prevState,
317
+ newState: nextState
318
+ });
319
+
310
320
  throw new Error('Invalid transition [' + prevState + ' to ' + nextState + ']');
311
321
  }
312
322
 
@@ -350,6 +360,7 @@ Example:
350
360
  A little bauble to interface with window.navigator.onLine,
351
361
  window.ononline and window.onoffline. Easier to mock.
352
362
  */
363
+
353
364
  var NetInfo = function() {
354
365
  var self = this;
355
366
  Pusher.EventsDispatcher.call(this);
@@ -376,7 +387,7 @@ Example:
376
387
  };
377
388
 
378
389
  Pusher.Util.extend(NetInfo.prototype, Pusher.EventsDispatcher.prototype);
379
-
390
+
380
391
  this.Pusher.NetInfo = NetInfo;
381
392
  }).call(this);
382
393
 
@@ -431,7 +442,7 @@ Example:
431
442
  this.netInfo.bind('online', function(){
432
443
  if (self._machine.is('waiting')) {
433
444
  self._machine.transition('connecting');
434
- triggerStateChange('connecting');
445
+ updateState('connecting');
435
446
  }
436
447
  });
437
448
 
@@ -452,8 +463,6 @@ Example:
452
463
 
453
464
  // define the state machine that runs the connection
454
465
  this._machine = new Pusher.Machine('initialized', machineTransitions, {
455
-
456
- // TODO: Use the constructor for this.
457
466
  initializedPre: function() {
458
467
  self.compulsorySecure = self.options.encrypted;
459
468
 
@@ -469,16 +478,18 @@ Example:
469
478
  self.emit('connecting_in', self.connectionWait);
470
479
  }
471
480
 
472
- if (self.netInfo.isOnLine() === false || self.connectionAttempts > 4){
473
- triggerStateChange('unavailable');
481
+ if (self.netInfo.isOnLine() && self.connectionAttempts <= 4) {
482
+ updateState('connecting');
474
483
  } else {
475
- triggerStateChange('connecting');
484
+ updateState('unavailable');
476
485
  }
477
486
 
478
- if (self.netInfo.isOnLine() === true) {
487
+ // When in the unavailable state we attempt to connect, but don't
488
+ // broadcast that fact
489
+ if (self.netInfo.isOnLine()) {
479
490
  self._waitingTimer = setTimeout(function() {
480
491
  self._machine.transition('connecting');
481
- }, self.connectionWait);
492
+ }, connectionDelay());
482
493
  }
483
494
  },
484
495
 
@@ -491,7 +502,7 @@ Example:
491
502
  // state even when offline.
492
503
  if (self.netInfo.isOnLine() === false) {
493
504
  self._machine.transition('waiting');
494
- triggerStateChange('unavailable');
505
+ updateState('unavailable');
495
506
 
496
507
  return;
497
508
  }
@@ -513,6 +524,7 @@ Example:
513
524
 
514
525
  connectingExit: function() {
515
526
  clearTimeout(self._connectingTimer);
527
+ self.socket.onopen = undefined; // unbind to avoid open events that are no longer relevant
516
528
  },
517
529
 
518
530
  connectingToWaiting: function() {
@@ -538,6 +550,7 @@ Example:
538
550
 
539
551
  openExit: function() {
540
552
  clearTimeout(self._openTimer);
553
+ self.socket.onmessage = undefined; // unbind to avoid messages that are no longer relevant
541
554
  },
542
555
 
543
556
  openToWaiting: function() {
@@ -556,17 +569,18 @@ Example:
556
569
  self.socket.onclose = transitionToWaiting;
557
570
 
558
571
  resetConnectionParameters(self);
572
+ self.connectedAt = new Date().getTime();
559
573
 
560
574
  resetActivityCheck();
561
575
  },
562
576
 
563
577
  connectedPost: function() {
564
- triggerStateChange('connected');
578
+ updateState('connected');
565
579
  },
566
580
 
567
581
  connectedExit: function() {
568
582
  stopActivityCheck();
569
- triggerStateChange('disconnected');
583
+ updateState('disconnected');
570
584
  },
571
585
 
572
586
  impermanentlyClosingPost: function() {
@@ -591,8 +605,12 @@ Example:
591
605
  },
592
606
 
593
607
  failedPre: function() {
594
- triggerStateChange('failed');
608
+ updateState('failed');
595
609
  Pusher.debug('WebSockets are not available in this browser.');
610
+ },
611
+
612
+ permanentlyClosedPost: function() {
613
+ updateState('disconnected');
596
614
  }
597
615
  });
598
616
 
@@ -633,7 +651,11 @@ Example:
633
651
  protocol = 'wss://';
634
652
  }
635
653
 
636
- return protocol + Pusher.host + ':' + port + '/app/' + key + '?client=js&version=' + Pusher.VERSION;
654
+ var flash = (Pusher.TransportType === "flash") ? "true" : "false";
655
+
656
+ return protocol + Pusher.host + ':' + port + '/app/' + key + '?protocol=5&client=js'
657
+ + '&version=' + Pusher.VERSION
658
+ + '&flash=' + flash;
637
659
  }
638
660
 
639
661
  // callback for close and retry. Used on timeouts.
@@ -657,6 +679,25 @@ Example:
657
679
  if (self._activityTimer) { clearTimeout(self._activityTimer); }
658
680
  }
659
681
 
682
+ // Returns the delay before the next connection attempt should be made
683
+ //
684
+ // This function guards against attempting to connect more frequently than
685
+ // once every second
686
+ //
687
+ function connectionDelay() {
688
+ var delay = self.connectionWait;
689
+ if (delay === 0) {
690
+ if (self.connectedAt) {
691
+ var t = 1000;
692
+ var connectedFor = new Date().getTime() - self.connectedAt;
693
+ if (connectedFor < t) {
694
+ delay = t - connectedFor;
695
+ }
696
+ }
697
+ }
698
+ return delay;
699
+ }
700
+
660
701
  /*-----------------------------------------------
661
702
  WebSocket Callbacks
662
703
  -----------------------------------------------*/
@@ -666,28 +707,40 @@ Example:
666
707
  self._machine.transition('open');
667
708
  };
668
709
 
710
+ function handleCloseCode(code, message) {
711
+ // first inform the end-developer of this error
712
+ self.emit('error', {type: 'PusherError', data: {code: code, message: message}});
713
+
714
+ if (code === 4000) {
715
+ // SSL only app
716
+ self.compulsorySecure = true;
717
+ self.connectionSecure = true;
718
+ self.options.encrypted = true;
719
+
720
+ self._machine.transition('impermanentlyClosing')
721
+ } else if (code < 4100) {
722
+ // Permentently close connection
723
+ self._machine.transition('permanentlyClosing')
724
+ } else if (code < 4200) {
725
+ // Backoff before reconnecting
726
+ self.connectionWait = 1000;
727
+ self._machine.transition('waiting')
728
+ } else if (code < 4300) {
729
+ // Reconnect immediately
730
+ self._machine.transition('impermanentlyClosing')
731
+ } else {
732
+ // Unknown error
733
+ self._machine.transition('permanentlyClosing')
734
+ }
735
+ }
736
+
669
737
  function ws_onMessageOpen(event) {
670
738
  var params = parseWebSocketEvent(event);
671
739
  if (params !== undefined) {
672
740
  if (params.event === 'pusher:connection_established') {
673
741
  self._machine.transition('connected', params.data.socket_id);
674
742
  } else if (params.event === 'pusher:error') {
675
- // first inform the end-developer of this error
676
- self.emit('error', {type: 'PusherError', data: params.data});
677
-
678
- switch (params.data.code) {
679
- case 4000:
680
- Pusher.warn(params.data.message);
681
-
682
- self.compulsorySecure = true;
683
- self.connectionSecure = true;
684
- self.options.encrypted = true;
685
- break;
686
- case 4001:
687
- // App not found by key - close connection
688
- self._machine.transition('permanentlyClosing');
689
- break;
690
- }
743
+ handleCloseCode(params.data.code, params.data.message)
691
744
  }
692
745
  }
693
746
  }
@@ -753,19 +806,21 @@ Example:
753
806
  self._machine.transition('impermanentlyClosing');
754
807
  }
755
808
 
756
- function triggerStateChange(newState, data) {
757
- // avoid emitting and changing the state
758
- // multiple times when it's the same.
759
- if (self.state === newState) return;
760
-
809
+ // Updates the public state information exposed by connection
810
+ //
811
+ // This is distinct from the internal state information used by _machine
812
+ // to manage the connection
813
+ //
814
+ function updateState(newState, data) {
761
815
  var prevState = self.state;
762
-
763
816
  self.state = newState;
764
817
 
765
- Pusher.debug('State changed', prevState + ' -> ' + newState);
766
-
767
- self.emit('state_change', {previous: prevState, current: newState});
768
- self.emit(newState, data);
818
+ // Only emit when the state changes
819
+ if (prevState !== newState) {
820
+ Pusher.debug('State changed', prevState + ' -> ' + newState);
821
+ self.emit('state_change', {previous: prevState, current: newState});
822
+ self.emit(newState, data);
823
+ }
769
824
  }
770
825
  };
771
826
 
@@ -785,14 +840,24 @@ Example:
785
840
  }
786
841
  // user re-opening connection after closing it
787
842
  else if(this._machine.is("permanentlyClosed")) {
843
+ resetConnectionParameters(this);
788
844
  this._machine.transition('waiting');
789
845
  }
790
846
  };
791
847
 
792
848
  Connection.prototype.send = function(data) {
793
849
  if (this._machine.is('connected')) {
794
- this.socket.send(data);
795
- return true;
850
+ // Bug in iOS (reproduced in 5.0.1) Mobile Safari:
851
+ // 1. Open page/tab, connect WS, get some data.
852
+ // 2. Switch tab or close Mobile Safari and wait for WS connection to get closed (probably by server).
853
+ // 3. Switch back to tab or open Mobile Safari and Mobile Safari crashes.
854
+ // The problem is that WS tries to send data on closed WS connection before it realises it is closed.
855
+ // The timeout means that by the time the send happens, the WS readyState correctly reflects closed state.
856
+ var self = this;
857
+ setTimeout(function() {
858
+ self.socket.send(data);
859
+ }, 0);
860
+ return true; // only a reflection of fact that WS thinks it is open - could get returned before some lower-level failure.
796
861
  } else {
797
862
  return false;
798
863
  }
@@ -823,185 +888,151 @@ Example:
823
888
  this.Pusher.Connection = Connection;
824
889
  }).call(this);
825
890
 
826
- Pusher.Channels = function() {
827
- this.channels = {};
828
- };
891
+ ;(function() {
892
+ Pusher.Channels = function() {
893
+ this.channels = {};
894
+ };
829
895
 
830
- Pusher.Channels.prototype = {
831
- add: function(channel_name, pusher) {
832
- var existing_channel = this.find(channel_name);
833
- if (!existing_channel) {
834
- var channel = Pusher.Channel.factory(channel_name, pusher);
835
- this.channels[channel_name] = channel;
836
- return channel;
837
- } else {
838
- return existing_channel;
839
- }
840
- },
896
+ Pusher.Channels.prototype = {
897
+ add: function(channel_name, pusher) {
898
+ var existing_channel = this.find(channel_name);
899
+ if (!existing_channel) {
900
+ var channel = Pusher.Channel.factory(channel_name, pusher);
901
+ this.channels[channel_name] = channel;
902
+ return channel;
903
+ } else {
904
+ return existing_channel;
905
+ }
906
+ },
841
907
 
842
- find: function(channel_name) {
843
- return this.channels[channel_name];
844
- },
908
+ find: function(channel_name) {
909
+ return this.channels[channel_name];
910
+ },
845
911
 
846
- remove: function(channel_name) {
847
- delete this.channels[channel_name];
848
- },
912
+ remove: function(channel_name) {
913
+ delete this.channels[channel_name];
914
+ },
849
915
 
850
- disconnect: function () {
851
- for(var channel_name in this.channels){
852
- this.channels[channel_name].disconnect()
916
+ disconnect: function () {
917
+ for(var channel_name in this.channels){
918
+ this.channels[channel_name].disconnect()
919
+ }
853
920
  }
854
- }
855
- };
856
-
857
- Pusher.Channel = function(channel_name, pusher) {
858
- var self = this;
859
- Pusher.EventsDispatcher.call(this, function(event_name, event_data) {
860
- Pusher.debug('No callbacks on ' + channel_name + ' for ' + event_name);
861
- });
862
-
863
- this.pusher = pusher;
864
- this.name = channel_name;
865
- this.subscribed = false;
866
-
867
- this.bind('pusher_internal:subscription_succeeded', function(data) {
868
- self.onSubscriptionSucceeded(data);
869
- });
870
- };
871
-
872
- Pusher.Channel.prototype = {
873
- // inheritable constructor
874
- init: function() {},
875
- disconnect: function() {},
876
-
877
- onSubscriptionSucceeded: function(data) {
878
- this.subscribed = true;
879
- this.emit('pusher:subscription_succeeded');
880
- },
881
-
882
- authorize: function(pusher, callback){
883
- callback(false, {}); // normal channels don't require auth
884
- },
885
-
886
- trigger: function(event, data) {
887
- return this.pusher.send_event(event, data, this.name);
888
- }
889
- };
921
+ };
890
922
 
891
- Pusher.Util.extend(Pusher.Channel.prototype, Pusher.EventsDispatcher.prototype);
923
+ Pusher.Channel = function(channel_name, pusher) {
924
+ var self = this;
925
+ Pusher.EventsDispatcher.call(this, function(event_name, event_data) {
926
+ Pusher.debug('No callbacks on ' + channel_name + ' for ' + event_name);
927
+ });
892
928
 
929
+ this.pusher = pusher;
930
+ this.name = channel_name;
931
+ this.subscribed = false;
893
932
 
933
+ this.bind('pusher_internal:subscription_succeeded', function(data) {
934
+ self.onSubscriptionSucceeded(data);
935
+ });
936
+ };
894
937
 
895
- Pusher.auth_callbacks = {};
938
+ Pusher.Channel.prototype = {
939
+ // inheritable constructor
940
+ init: function() {},
941
+ disconnect: function() {
942
+ this.subscribed = false;
943
+ this.emit("pusher_internal:disconnected");
944
+ },
896
945
 
897
- Pusher.authorizers = {
898
- ajax: function(pusher, callback){
899
- var self = this, xhr;
946
+ onSubscriptionSucceeded: function(data) {
947
+ this.subscribed = true;
948
+ this.emit('pusher:subscription_succeeded');
949
+ },
900
950
 
901
- if (Pusher.XHR) {
902
- xhr = new Pusher.XHR();
903
- } else {
904
- xhr = (window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
905
- }
951
+ authorize: function(socketId, options, callback){
952
+ return callback(false, {}); // normal channels don't require auth
953
+ },
906
954
 
907
- xhr.open("POST", Pusher.channel_auth_endpoint, true);
908
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
909
- xhr.onreadystatechange = function() {
910
- if (xhr.readyState == 4) {
911
- if (xhr.status == 200) {
912
- var data, parsed = false;
955
+ trigger: function(event, data) {
956
+ return this.pusher.send_event(event, data, this.name);
957
+ }
958
+ };
913
959
 
914
- try {
915
- data = JSON.parse(xhr.responseText);
916
- parsed = true;
917
- } catch (e) {
918
- callback(true, 'JSON returned from webapp was invalid, yet status code was 200. Data was: ' + xhr.responseText);
919
- }
960
+ Pusher.Util.extend(Pusher.Channel.prototype, Pusher.EventsDispatcher.prototype);
920
961
 
921
- if (parsed) { // prevents double execution.
922
- callback(false, data);
923
- }
924
- } else {
925
- Pusher.warn("Couldn't get auth info from your webapp", status);
926
- callback(true, xhr.status);
962
+ Pusher.Channel.PrivateChannel = {
963
+ authorize: function(socketId, options, callback){
964
+ var self = this;
965
+ var authorizer = new Pusher.Channel.Authorizer(this, Pusher.channel_auth_transport, options);
966
+ return authorizer.authorize(socketId, function(err, authData) {
967
+ if(!err) {
968
+ self.emit('pusher_internal:authorized', authData);
927
969
  }
928
- }
929
- };
930
- xhr.send('socket_id=' + encodeURIComponent(pusher.connection.socket_id) + '&channel_name=' + encodeURIComponent(self.name));
931
- },
932
- jsonp: function(pusher, callback){
933
- var qstring = 'socket_id=' + encodeURIComponent(pusher.connection.socket_id) + '&channel_name=' + encodeURIComponent(this.name);
934
- var script = document.createElement("script");
935
- // Hacked wrapper.
936
- Pusher.auth_callbacks[this.name] = function(data) {
937
- callback(false, data);
938
- };
939
- var callback_name = "Pusher.auth_callbacks['" + this.name + "']";
940
- script.src = Pusher.channel_auth_endpoint+'?callback='+encodeURIComponent(callback_name)+'&'+qstring;
941
- var head = document.getElementsByTagName("head")[0] || document.documentElement;
942
- head.insertBefore( script, head.firstChild );
943
- }
944
- };
945
970
 
946
- Pusher.Channel.PrivateChannel = {
947
- authorize: function(pusher, callback){
948
- Pusher.authorizers[Pusher.channel_auth_transport].scopedTo(this)(pusher, callback);
949
- }
950
- };
951
-
952
- Pusher.Channel.PresenceChannel = {
953
- init: function(){
954
- this.bind('pusher_internal:member_added', function(data){
955
- var member = this.members.add(data.user_id, data.user_info);
956
- this.emit('pusher:member_added', member);
957
- }.scopedTo(this))
958
-
959
- this.bind('pusher_internal:member_removed', function(data){
960
- var member = this.members.remove(data.user_id);
961
- if (member) {
962
- this.emit('pusher:member_removed', member);
963
- }
964
- }.scopedTo(this))
965
- },
971
+ callback(err, authData);
972
+ });
973
+ }
974
+ };
966
975
 
967
- disconnect: function(){
968
- this.members.clear();
969
- },
976
+ Pusher.Channel.PresenceChannel = {
977
+ init: function(){
978
+ this.members = new Members(this); // leeches off channel events
979
+ },
970
980
 
971
- onSubscriptionSucceeded: function(data) {
972
- this.members._members_map = data.presence.hash;
973
- this.members.count = data.presence.count;
974
- this.subscribed = true;
981
+ onSubscriptionSucceeded: function(data) {
982
+ this.subscribed = true;
983
+ // We override this because we want the Members obj to be responsible for
984
+ // emitting the pusher:subscription_succeeded. It will do this after it has done its work.
985
+ }
986
+ };
975
987
 
976
- this.emit('pusher:subscription_succeeded', this.members);
977
- },
988
+ var Members = function(channel) {
989
+ var self = this;
978
990
 
979
- members: {
980
- _members_map: {},
981
- count: 0,
991
+ var reset = function() {
992
+ this._members_map = {};
993
+ this.count = 0;
994
+ this.me = null;
995
+ };
996
+ reset.call(this);
997
+
998
+ channel.bind('pusher_internal:authorized', function(authorizedData) {
999
+ var channelData = JSON.parse(authorizedData.channel_data);
1000
+ channel.bind("pusher_internal:subscription_succeeded", function(subscriptionData) {
1001
+ self._members_map = subscriptionData.presence.hash;
1002
+ self.count = subscriptionData.presence.count;
1003
+ self.me = self.get(channelData.user_id);
1004
+ channel.emit('pusher:subscription_succeeded', self);
1005
+ });
1006
+ });
982
1007
 
983
- each: function(callback) {
984
- for(var i in this._members_map) {
985
- callback({
986
- id: i,
987
- info: this._members_map[i]
988
- });
1008
+ channel.bind('pusher_internal:member_added', function(data) {
1009
+ if(self.get(data.user_id) === null) { // only incr if user_id does not already exist
1010
+ self.count++;
989
1011
  }
990
- },
991
1012
 
992
- add: function(id, info) {
993
- this._members_map[id] = info;
994
- this.count++;
995
- return this.get(id);
996
- },
1013
+ self._members_map[data.user_id] = data.user_info;
1014
+ channel.emit('pusher:member_added', self.get(data.user_id));
1015
+ });
997
1016
 
998
- remove: function(user_id) {
999
- var member = this.get(user_id);
1000
- if (member) {
1001
- delete this._members_map[user_id];
1002
- this.count--;
1017
+ channel.bind('pusher_internal:member_removed', function(data) {
1018
+ var member = self.get(data.user_id);
1019
+ if(member) {
1020
+ delete self._members_map[data.user_id];
1021
+ self.count--;
1022
+ channel.emit('pusher:member_removed', member);
1023
+ }
1024
+ });
1025
+
1026
+ channel.bind('pusher_internal:disconnected', function() {
1027
+ reset.call(self);
1028
+ });
1029
+ };
1030
+
1031
+ Members.prototype = {
1032
+ each: function(callback) {
1033
+ for(var i in this._members_map) {
1034
+ callback(this.get(i));
1003
1035
  }
1004
- return member;
1005
1036
  },
1006
1037
 
1007
1038
  get: function(user_id) {
@@ -1013,27 +1044,114 @@ Pusher.Channel.PresenceChannel = {
1013
1044
  } else { // have never heard of this user
1014
1045
  return null;
1015
1046
  }
1047
+ }
1048
+ };
1049
+
1050
+ Pusher.Channel.factory = function(channel_name, pusher){
1051
+ var channel = new Pusher.Channel(channel_name, pusher);
1052
+ if (channel_name.indexOf('private-') === 0) {
1053
+ Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
1054
+ } else if (channel_name.indexOf('presence-') === 0) {
1055
+ Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
1056
+ Pusher.Util.extend(channel, Pusher.Channel.PresenceChannel);
1057
+ };
1058
+ channel.init();
1059
+ return channel;
1060
+ };
1061
+ }).call(this);
1062
+ ;(function() {
1063
+ Pusher.Channel.Authorizer = function(channel, type, options) {
1064
+ this.channel = channel;
1065
+ this.type = type;
1066
+
1067
+ this.authOptions = (options || {}).auth || {};
1068
+ };
1069
+
1070
+ Pusher.Channel.Authorizer.prototype = {
1071
+ composeQuery: function(socketId) {
1072
+ var query = '&socket_id=' + encodeURIComponent(socketId)
1073
+ + '&channel_name=' + encodeURIComponent(this.channel.name);
1074
+
1075
+ for(var i in this.authOptions.params) {
1076
+ query += "&" + encodeURIComponent(i) + "=" + encodeURIComponent(this.authOptions.params[i]);
1077
+ }
1078
+
1079
+ return query;
1016
1080
  },
1017
1081
 
1018
- clear: function() {
1019
- this._members_map = {};
1020
- this.count = 0;
1082
+ authorize: function(socketId, callback) {
1083
+ return Pusher.authorizers[this.type].call(this, socketId, callback);
1021
1084
  }
1022
- }
1023
- };
1024
-
1025
- Pusher.Channel.factory = function(channel_name, pusher){
1026
- var channel = new Pusher.Channel(channel_name, pusher);
1027
- if (channel_name.indexOf('private-') === 0) {
1028
- Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
1029
- } else if (channel_name.indexOf('presence-') === 0) {
1030
- Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
1031
- Pusher.Util.extend(channel, Pusher.Channel.PresenceChannel);
1032
1085
  };
1033
- channel.init();
1034
- return channel;
1035
- };
1036
1086
 
1087
+
1088
+ Pusher.auth_callbacks = {};
1089
+ Pusher.authorizers = {
1090
+ ajax: function(socketId, callback){
1091
+ var self = this, xhr;
1092
+
1093
+ if (Pusher.XHR) {
1094
+ xhr = new Pusher.XHR();
1095
+ } else {
1096
+ xhr = (window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
1097
+ }
1098
+
1099
+ xhr.open("POST", Pusher.channel_auth_endpoint, true);
1100
+
1101
+ // add request headers
1102
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
1103
+ for(var headerName in this.authOptions.headers) {
1104
+ xhr.setRequestHeader(headerName, this.authOptions.headers[headerName]);
1105
+ }
1106
+
1107
+ xhr.onreadystatechange = function() {
1108
+ if (xhr.readyState == 4) {
1109
+ if (xhr.status == 200) {
1110
+ var data, parsed = false;
1111
+
1112
+ try {
1113
+ data = JSON.parse(xhr.responseText);
1114
+ parsed = true;
1115
+ } catch (e) {
1116
+ callback(true, 'JSON returned from webapp was invalid, yet status code was 200. Data was: ' + xhr.responseText);
1117
+ }
1118
+
1119
+ if (parsed) { // prevents double execution.
1120
+ callback(false, data);
1121
+ }
1122
+ } else {
1123
+ Pusher.warn("Couldn't get auth info from your webapp", xhr.status);
1124
+ callback(true, xhr.status);
1125
+ }
1126
+ }
1127
+ };
1128
+
1129
+ xhr.send(this.composeQuery(socketId));
1130
+ return xhr;
1131
+ },
1132
+
1133
+ jsonp: function(socketId, callback){
1134
+ if(this.authOptions.headers !== undefined) {
1135
+ Pusher.warn("Warn", "To send headers with the auth request, you must use AJAX, rather than JSONP.");
1136
+ }
1137
+
1138
+ var script = document.createElement("script");
1139
+ // Hacked wrapper.
1140
+ Pusher.auth_callbacks[this.channel.name] = function(data) {
1141
+ callback(false, data);
1142
+ };
1143
+
1144
+ var callback_name = "Pusher.auth_callbacks['" + this.channel.name + "']";
1145
+ script.src = Pusher.channel_auth_endpoint
1146
+ + '?callback='
1147
+ + encodeURIComponent(callback_name)
1148
+ + this.composeQuery(socketId);
1149
+
1150
+ var head = document.getElementsByTagName("head")[0] || document.documentElement;
1151
+ head.insertBefore( script, head.firstChild );
1152
+ }
1153
+ };
1154
+ }).call(this);
1037
1155
  var _require = (function () {
1038
1156
 
1039
1157
  var handleScriptLoaded;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pusher_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,19 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-25 00:00:00.000000000 Z
12
+ date: 2012-04-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pusher
16
- requirement: &70209654617200 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.9.1
21
+ version: 0.9.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70209654617200
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.2
25
30
  description: Adds pusher.js/backpusher.js to the asset pipeline and pusher-gem to
26
31
  to your app.
27
32
  email:
@@ -57,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
62
  version: '0'
58
63
  requirements: []
59
64
  rubyforge_project:
60
- rubygems_version: 1.8.10
65
+ rubygems_version: 1.8.21
61
66
  signing_key:
62
67
  specification_version: 3
63
68
  summary: Pusher integration for Rails 3.1+