pusher_rails 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -0
- data/README.md +3 -3
- data/pusher_rails.gemspec +1 -1
- data/vendor/assets/javascripts/pusher.js +2723 -828
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3179ba51d8e8259fdcbd446b60e6e5c87a18886e
|
4
|
+
data.tar.gz: 2a672bd542b91f62a5ab8c26aba5ad1650bf5ea7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7eec94660281c2fa773cc2a5d6d0fa4cd5dfbfb73d98abfd6703de63b0ca6b81d568b2b340c3194421e80b255a77b9d6d4c5de95443be85b3cf5efee01f27209
|
7
|
+
data.tar.gz: 13b1878f0357078485f6d3e8108d9f77e0c103ab3c1f39a108083973a17bca67f0c7a9fbcc0f903ad00d66cb84a8ff01fbbd054ac4958a7c17751f265b573adb
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
=====================
|
3
3
|
|
4
4
|
Adds:
|
5
|
-
- [pusher-gem v0.9.
|
6
|
-
- [pusher.js v1.12.
|
5
|
+
- [pusher-gem v0.9.4](https://github.com/pusher/pusher-gem/tree/v0.9.4)
|
6
|
+
- [pusher.js v1.12.2](https://github.com/pusher/pusher-js/tree/v1.12.2)
|
7
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.
|
@@ -21,7 +21,7 @@ Licenses
|
|
21
21
|
========
|
22
22
|
|
23
23
|
/*!
|
24
|
-
* Pusher JavaScript Library v1.12.
|
24
|
+
* Pusher JavaScript Library v1.12.2
|
25
25
|
* http://pusherapp.com/
|
26
26
|
*
|
27
27
|
* Copyright 2011, Pusher
|
data/pusher_rails.gemspec
CHANGED
@@ -1,924 +1,2946 @@
|
|
1
1
|
/*!
|
2
|
-
* Pusher JavaScript Library
|
2
|
+
* Pusher JavaScript Library v2.0.0
|
3
3
|
* http://pusherapp.com/
|
4
4
|
*
|
5
|
-
* Copyright
|
5
|
+
* Copyright 2013, Pusher
|
6
6
|
* Released under the MIT licence.
|
7
7
|
*/
|
8
8
|
|
9
9
|
;(function() {
|
10
|
-
|
11
|
-
|
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
|
-
};
|
17
|
-
};
|
18
|
-
}
|
10
|
+
function Pusher(app_key, options) {
|
11
|
+
var self = this;
|
19
12
|
|
20
|
-
var Pusher = function(app_key, options) {
|
21
13
|
this.options = options || {};
|
22
14
|
this.key = app_key;
|
23
15
|
this.channels = new Pusher.Channels();
|
24
|
-
this.global_emitter = new Pusher.EventsDispatcher()
|
16
|
+
this.global_emitter = new Pusher.EventsDispatcher();
|
17
|
+
this.sessionID = Math.floor(Math.random() * 1000000000);
|
25
18
|
|
26
|
-
|
19
|
+
checkAppKey(this.key);
|
20
|
+
|
21
|
+
var getStrategy = function(options) {
|
22
|
+
return Pusher.StrategyBuilder.build(
|
23
|
+
Pusher.getDefaultStrategy(),
|
24
|
+
Pusher.Util.extend({}, self.options, options)
|
25
|
+
);
|
26
|
+
};
|
27
|
+
var getTimeline = function() {
|
28
|
+
return new Pusher.Timeline(self.key, self.sessionID, {
|
29
|
+
features: Pusher.Util.getClientFeatures(),
|
30
|
+
params: self.options.timelineParams || {},
|
31
|
+
limit: 25,
|
32
|
+
level: Pusher.Timeline.INFO,
|
33
|
+
version: Pusher.VERSION
|
34
|
+
});
|
35
|
+
};
|
36
|
+
var getTimelineSender = function(timeline, options) {
|
37
|
+
if (self.options.disableStats) {
|
38
|
+
return null;
|
39
|
+
}
|
40
|
+
return new Pusher.TimelineSender(timeline, {
|
41
|
+
encrypted: self.isEncrypted() || !!options.encrypted,
|
42
|
+
host: Pusher.stats_host,
|
43
|
+
path: "/timeline"
|
44
|
+
});
|
45
|
+
};
|
46
|
+
|
47
|
+
this.connection = new Pusher.ConnectionManager(
|
48
|
+
this.key,
|
49
|
+
Pusher.Util.extend(
|
50
|
+
{ getStrategy: getStrategy,
|
51
|
+
getTimeline: getTimeline,
|
52
|
+
getTimelineSender: getTimelineSender,
|
53
|
+
activityTimeout: Pusher.activity_timeout,
|
54
|
+
pongTimeout: Pusher.pong_timeout,
|
55
|
+
unavailableTimeout: Pusher.unavailable_timeout
|
56
|
+
},
|
57
|
+
this.options,
|
58
|
+
{ encrypted: this.isEncrypted() }
|
59
|
+
)
|
60
|
+
);
|
61
|
+
|
62
|
+
this.connection.bind('connected', function() {
|
63
|
+
self.subscribeAll();
|
64
|
+
});
|
65
|
+
this.connection.bind('message', function(params) {
|
66
|
+
var internal = (params.event.indexOf('pusher_internal:') === 0);
|
67
|
+
if (params.channel) {
|
68
|
+
var channel = self.channel(params.channel);
|
69
|
+
if (channel) {
|
70
|
+
channel.emit(params.event, params.data);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
// Emit globaly [deprecated]
|
74
|
+
if (!internal) self.global_emitter.emit(params.event, params.data);
|
75
|
+
});
|
76
|
+
this.connection.bind('disconnected', function() {
|
77
|
+
self.channels.disconnect();
|
78
|
+
});
|
79
|
+
this.connection.bind('error', function(err) {
|
80
|
+
Pusher.warn('Error', err);
|
81
|
+
});
|
82
|
+
|
83
|
+
Pusher.instances.push(this);
|
27
84
|
|
28
|
-
|
85
|
+
if (Pusher.isReady) self.connect();
|
86
|
+
}
|
87
|
+
var prototype = Pusher.prototype;
|
29
88
|
|
30
|
-
|
89
|
+
Pusher.instances = [];
|
90
|
+
Pusher.isReady = false;
|
31
91
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
92
|
+
// To receive log output provide a Pusher.log function, for example
|
93
|
+
// Pusher.log = function(m){console.log(m)}
|
94
|
+
Pusher.debug = function() {
|
95
|
+
if (!Pusher.log) {
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
Pusher.log(Pusher.Util.stringify.apply(this, arguments));
|
99
|
+
};
|
100
|
+
|
101
|
+
Pusher.warn = function() {
|
102
|
+
if (window.console && window.console.warn) {
|
103
|
+
window.console.warn(Pusher.Util.stringify.apply(this, arguments));
|
104
|
+
} else {
|
105
|
+
if (!Pusher.log) {
|
106
|
+
return;
|
107
|
+
}
|
108
|
+
Pusher.log(Pusher.Util.stringify.apply(this, arguments));
|
109
|
+
}
|
110
|
+
};
|
111
|
+
|
112
|
+
Pusher.ready = function() {
|
113
|
+
Pusher.isReady = true;
|
114
|
+
for (var i = 0, l = Pusher.instances.length; i < l; i++) {
|
115
|
+
Pusher.instances[i].connect();
|
116
|
+
}
|
117
|
+
};
|
118
|
+
|
119
|
+
prototype.channel = function(name) {
|
120
|
+
return this.channels.find(name);
|
121
|
+
};
|
122
|
+
|
123
|
+
prototype.connect = function() {
|
124
|
+
this.connection.connect();
|
125
|
+
};
|
126
|
+
|
127
|
+
prototype.disconnect = function() {
|
128
|
+
this.connection.disconnect();
|
129
|
+
};
|
130
|
+
|
131
|
+
prototype.bind = function(event_name, callback) {
|
132
|
+
this.global_emitter.bind(event_name, callback);
|
133
|
+
return this;
|
134
|
+
};
|
135
|
+
|
136
|
+
prototype.bind_all = function(callback) {
|
137
|
+
this.global_emitter.bind_all(callback);
|
138
|
+
return this;
|
139
|
+
};
|
140
|
+
|
141
|
+
prototype.subscribeAll = function() {
|
142
|
+
var channelName;
|
143
|
+
for (channelName in this.channels.channels) {
|
144
|
+
if (this.channels.channels.hasOwnProperty(channelName)) {
|
145
|
+
this.subscribe(channelName);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
};
|
149
|
+
|
150
|
+
prototype.subscribe = function(channel_name) {
|
151
|
+
var self = this;
|
152
|
+
var channel = this.channels.add(channel_name, this);
|
153
|
+
|
154
|
+
if (this.connection.state === 'connected') {
|
155
|
+
channel.authorize(
|
156
|
+
this.connection.socket_id,
|
157
|
+
this.options,
|
158
|
+
function(err, data) {
|
159
|
+
if (err) {
|
160
|
+
channel.emit('pusher:subscription_error', data);
|
161
|
+
} else {
|
162
|
+
self.send_event('pusher:subscribe', {
|
163
|
+
channel: channel_name,
|
164
|
+
auth: data.auth,
|
165
|
+
channel_data: data.channel_data
|
166
|
+
});
|
43
167
|
}
|
44
168
|
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
169
|
+
);
|
170
|
+
}
|
171
|
+
return channel;
|
172
|
+
};
|
173
|
+
|
174
|
+
prototype.unsubscribe = function(channel_name) {
|
175
|
+
this.channels.remove(channel_name);
|
176
|
+
if (this.connection.state === 'connected') {
|
177
|
+
this.send_event('pusher:unsubscribe', {
|
178
|
+
channel: channel_name
|
53
179
|
});
|
180
|
+
}
|
181
|
+
};
|
54
182
|
|
55
|
-
|
183
|
+
prototype.send_event = function(event_name, data, channel) {
|
184
|
+
return this.connection.send_event(event_name, data, channel);
|
185
|
+
};
|
56
186
|
|
57
|
-
|
187
|
+
prototype.isEncrypted = function() {
|
188
|
+
if (Pusher.Util.getDocumentLocation().protocol === "https:") {
|
189
|
+
return true;
|
190
|
+
} else {
|
191
|
+
return !!this.options.encrypted;
|
192
|
+
}
|
58
193
|
};
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
194
|
+
|
195
|
+
function checkAppKey(key) {
|
196
|
+
if (key === null || key === undefined) {
|
197
|
+
Pusher.warn(
|
198
|
+
'Warning', 'You must pass your app key when you instantiate Pusher.'
|
199
|
+
);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
this.Pusher = Pusher;
|
204
|
+
}).call(this);
|
205
|
+
|
206
|
+
;(function() {
|
207
|
+
Pusher.Util = {
|
208
|
+
now: function() {
|
209
|
+
if (Date.now) {
|
210
|
+
return Date.now();
|
211
|
+
} else {
|
212
|
+
return new Date().valueOf();
|
213
|
+
}
|
63
214
|
},
|
64
215
|
|
65
|
-
|
66
|
-
|
216
|
+
/** Merges multiple objects into the target argument.
|
217
|
+
*
|
218
|
+
* For properties that are plain Objects, performs a deep-merge. For the
|
219
|
+
* rest it just copies the value of the property.
|
220
|
+
*
|
221
|
+
* To extend prototypes use it as following:
|
222
|
+
* Pusher.Util.extend(Target.prototype, Base.prototype)
|
223
|
+
*
|
224
|
+
* You can also use it to merge objects without altering them:
|
225
|
+
* Pusher.Util.extend({}, object1, object2)
|
226
|
+
*
|
227
|
+
* @param {Object} target
|
228
|
+
* @return {Object} the target argument
|
229
|
+
*/
|
230
|
+
extend: function(target) {
|
231
|
+
for (var i = 1; i < arguments.length; i++) {
|
232
|
+
var extensions = arguments[i];
|
233
|
+
for (var property in extensions) {
|
234
|
+
if (extensions[property] && extensions[property].constructor &&
|
235
|
+
extensions[property].constructor === Object) {
|
236
|
+
target[property] = Pusher.Util.extend(
|
237
|
+
target[property] || {}, extensions[property]
|
238
|
+
);
|
239
|
+
} else {
|
240
|
+
target[property] = extensions[property];
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return target;
|
67
245
|
},
|
68
246
|
|
69
|
-
|
70
|
-
|
247
|
+
stringify: function() {
|
248
|
+
var m = ["Pusher"];
|
249
|
+
for (var i = 0; i < arguments.length; i++) {
|
250
|
+
if (typeof arguments[i] === "string") {
|
251
|
+
m.push(arguments[i]);
|
252
|
+
} else {
|
253
|
+
if (window.JSON === undefined) {
|
254
|
+
m.push(arguments[i].toString());
|
255
|
+
} else {
|
256
|
+
m.push(JSON.stringify(arguments[i]));
|
257
|
+
}
|
258
|
+
}
|
259
|
+
}
|
260
|
+
return m.join(" : ");
|
261
|
+
},
|
262
|
+
|
263
|
+
arrayIndexOf: function(array, item) { // MSIE doesn't have array.indexOf
|
264
|
+
var nativeIndexOf = Array.prototype.indexOf;
|
265
|
+
if (array === null) {
|
266
|
+
return -1;
|
267
|
+
}
|
268
|
+
if (nativeIndexOf && array.indexOf === nativeIndexOf) {
|
269
|
+
return array.indexOf(item);
|
270
|
+
}
|
271
|
+
for (var i = 0, l = array.length; i < l; i++) {
|
272
|
+
if (array[i] === item) {
|
273
|
+
return i;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
return -1;
|
71
277
|
},
|
72
278
|
|
73
|
-
|
74
|
-
|
75
|
-
|
279
|
+
keys: function(object) {
|
280
|
+
var result = [];
|
281
|
+
for (var key in object) {
|
282
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
283
|
+
result.push(key);
|
284
|
+
}
|
285
|
+
}
|
286
|
+
return result;
|
287
|
+
},
|
288
|
+
|
289
|
+
/** Applies a function f to all elements of an array.
|
290
|
+
*
|
291
|
+
* Function f gets 3 arguments passed:
|
292
|
+
* - element from the array
|
293
|
+
* - index of the element
|
294
|
+
* - reference to the array
|
295
|
+
*
|
296
|
+
* @param {Array} array
|
297
|
+
* @param {Function} f
|
298
|
+
*/
|
299
|
+
apply: function(array, f) {
|
300
|
+
for (var i = 0; i < array.length; i++) {
|
301
|
+
f(array[i], i, array);
|
302
|
+
}
|
303
|
+
},
|
304
|
+
|
305
|
+
/** Applies a function f to all properties of an object.
|
306
|
+
*
|
307
|
+
* Function f gets 3 arguments passed:
|
308
|
+
* - element from the object
|
309
|
+
* - key of the element
|
310
|
+
* - reference to the object
|
311
|
+
*
|
312
|
+
* @param {Object} object
|
313
|
+
* @param {Function} f
|
314
|
+
*/
|
315
|
+
objectApply: function(object, f) {
|
316
|
+
for (var key in object) {
|
317
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
318
|
+
f(object[key], key, object);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
},
|
322
|
+
|
323
|
+
/** Maps all elements of the array and returns the result.
|
324
|
+
*
|
325
|
+
* Function f gets 4 arguments passed:
|
326
|
+
* - element from the array
|
327
|
+
* - index of the element
|
328
|
+
* - reference to the source array
|
329
|
+
* - reference to the destination array
|
330
|
+
*
|
331
|
+
* @param {Array} array
|
332
|
+
* @param {Function} f
|
333
|
+
*/
|
334
|
+
map: function(array, f) {
|
335
|
+
var result = [];
|
336
|
+
for (var i = 0; i < array.length; i++) {
|
337
|
+
result.push(f(array[i], i, array, result));
|
338
|
+
}
|
339
|
+
return result;
|
340
|
+
},
|
341
|
+
|
342
|
+
/** Maps all elements of the object and returns the result.
|
343
|
+
*
|
344
|
+
* Function f gets 4 arguments passed:
|
345
|
+
* - element from the object
|
346
|
+
* - key of the element
|
347
|
+
* - reference to the source object
|
348
|
+
* - reference to the destination object
|
349
|
+
*
|
350
|
+
* @param {Object} object
|
351
|
+
* @param {Function} f
|
352
|
+
*/
|
353
|
+
mapObject: function(object, f) {
|
354
|
+
var result = {};
|
355
|
+
for (var key in object) {
|
356
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
357
|
+
result[key] = f(object[key]);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
return result;
|
361
|
+
},
|
362
|
+
|
363
|
+
/** Filters elements of the array using a test function.
|
364
|
+
*
|
365
|
+
* Function test gets 4 arguments passed:
|
366
|
+
* - element from the array
|
367
|
+
* - index of the element
|
368
|
+
* - reference to the source array
|
369
|
+
* - reference to the destination array
|
370
|
+
*
|
371
|
+
* @param {Array} array
|
372
|
+
* @param {Function} f
|
373
|
+
*/
|
374
|
+
filter: function(array, test) {
|
375
|
+
test = test || function(value) { return !!value; };
|
376
|
+
|
377
|
+
var result = [];
|
378
|
+
for (var i = 0; i < array.length; i++) {
|
379
|
+
if (test(array[i], i, array, result)) {
|
380
|
+
result.push(array[i]);
|
381
|
+
}
|
382
|
+
}
|
383
|
+
return result;
|
384
|
+
},
|
385
|
+
|
386
|
+
/** Filters properties of the object using a test function.
|
387
|
+
*
|
388
|
+
* Function test gets 4 arguments passed:
|
389
|
+
* - element from the object
|
390
|
+
* - key of the element
|
391
|
+
* - reference to the source object
|
392
|
+
* - reference to the destination object
|
393
|
+
*
|
394
|
+
* @param {Object} object
|
395
|
+
* @param {Function} f
|
396
|
+
*/
|
397
|
+
filterObject: function(object, test) {
|
398
|
+
test = test || function(value) { return !!value; };
|
399
|
+
|
400
|
+
var result = {};
|
401
|
+
for (var key in object) {
|
402
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
403
|
+
if (test(object[key], key, object, result)) {
|
404
|
+
result[key] = object[key];
|
405
|
+
}
|
406
|
+
}
|
407
|
+
}
|
408
|
+
return result;
|
409
|
+
},
|
410
|
+
|
411
|
+
/** Flattens an object into a two-dimensional array.
|
412
|
+
*
|
413
|
+
* @param {Object} object
|
414
|
+
* @return {Array} resulting array of [key, value] pairs
|
415
|
+
*/
|
416
|
+
flatten: function(object) {
|
417
|
+
var result = [];
|
418
|
+
for (var key in object) {
|
419
|
+
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
420
|
+
result.push([key, object[key]]);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
return result;
|
424
|
+
},
|
425
|
+
|
426
|
+
/** Checks whether any element of the array passes the test.
|
427
|
+
*
|
428
|
+
* Function test gets 3 arguments passed:
|
429
|
+
* - element from the array
|
430
|
+
* - index of the element
|
431
|
+
* - reference to the source array
|
432
|
+
*
|
433
|
+
* @param {Array} array
|
434
|
+
* @param {Function} f
|
435
|
+
*/
|
436
|
+
any: function(array, test) {
|
437
|
+
for (var i = 0; i < array.length; i++) {
|
438
|
+
if (test(array[i], i, array)) {
|
439
|
+
return true;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
return false;
|
443
|
+
},
|
444
|
+
|
445
|
+
/** Checks whether all elements of the array pass the test.
|
446
|
+
*
|
447
|
+
* Function test gets 3 arguments passed:
|
448
|
+
* - element from the array
|
449
|
+
* - index of the element
|
450
|
+
* - reference to the source array
|
451
|
+
*
|
452
|
+
* @param {Array} array
|
453
|
+
* @param {Function} f
|
454
|
+
*/
|
455
|
+
all: function(array, test) {
|
456
|
+
for (var i = 0; i < array.length; i++) {
|
457
|
+
if (!test(array[i], i, array)) {
|
458
|
+
return false;
|
459
|
+
}
|
460
|
+
}
|
461
|
+
return true;
|
462
|
+
},
|
463
|
+
|
464
|
+
/** Builds a function that will proxy a method call to its first argument.
|
465
|
+
*
|
466
|
+
* Allows partial application of arguments, so additional arguments are
|
467
|
+
* prepended to the argument list.
|
468
|
+
*
|
469
|
+
* @param {String} name method name
|
470
|
+
* @return {Function} proxy function
|
471
|
+
*/
|
472
|
+
method: function(name) {
|
473
|
+
var boundArguments = Array.prototype.slice.call(arguments, 1);
|
474
|
+
return function(object) {
|
475
|
+
return object[name].apply(object, boundArguments.concat(arguments));
|
476
|
+
};
|
477
|
+
},
|
478
|
+
|
479
|
+
getDocument: function() {
|
480
|
+
return document;
|
481
|
+
},
|
482
|
+
|
483
|
+
getDocumentLocation: function() {
|
484
|
+
return Pusher.Util.getDocument().location;
|
76
485
|
},
|
77
486
|
|
78
|
-
|
79
|
-
|
80
|
-
return this;
|
487
|
+
getLocalStorage: function() {
|
488
|
+
return window.localStorage;
|
81
489
|
},
|
82
490
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
491
|
+
getClientFeatures: function() {
|
492
|
+
return Pusher.Util.keys(
|
493
|
+
Pusher.Util.filterObject(
|
494
|
+
{ "ws": Pusher.WSTransport, "flash": Pusher.FlashTransport },
|
495
|
+
function (t) { return t.isSupported(); }
|
496
|
+
)
|
497
|
+
);
|
498
|
+
}
|
499
|
+
};
|
500
|
+
}).call(this);
|
501
|
+
|
502
|
+
;(function() {
|
503
|
+
Pusher.VERSION = '2.0.0';
|
504
|
+
Pusher.PROTOCOL = 6;
|
505
|
+
|
506
|
+
// WS connection parameters
|
507
|
+
Pusher.host = 'ws.pusherapp.com';
|
508
|
+
Pusher.ws_port = 80;
|
509
|
+
Pusher.wss_port = 443;
|
510
|
+
// SockJS fallback parameters
|
511
|
+
Pusher.sockjs_host = 'sockjs.pusher.com';
|
512
|
+
Pusher.sockjs_http_port = 80;
|
513
|
+
Pusher.sockjs_https_port = 443;
|
514
|
+
Pusher.sockjs_path = "/pusher";
|
515
|
+
// Stats
|
516
|
+
Pusher.stats_host = 'stats.pusher.com';
|
517
|
+
// Other settings
|
518
|
+
Pusher.channel_auth_endpoint = '/pusher/auth';
|
519
|
+
Pusher.cdn_http = 'http://js.pusher.com/';
|
520
|
+
Pusher.cdn_https = 'https://d3dy5gmtp8yhk7.cloudfront.net/';
|
521
|
+
Pusher.dependency_suffix = '';
|
522
|
+
Pusher.channel_auth_transport = 'ajax';
|
523
|
+
Pusher.activity_timeout = 120000;
|
524
|
+
Pusher.pong_timeout = 30000;
|
525
|
+
Pusher.unavailable_timeout = 10000;
|
526
|
+
|
527
|
+
Pusher.getDefaultStrategy = function() {
|
528
|
+
return [
|
529
|
+
[":def", "ws_options", {
|
530
|
+
hostUnencrypted: Pusher.host + ":" + Pusher.ws_port,
|
531
|
+
hostEncrypted: Pusher.host + ":" + Pusher.wss_port
|
532
|
+
}],
|
533
|
+
[":def", "sockjs_options", {
|
534
|
+
hostUnencrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_http_port,
|
535
|
+
hostEncrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_https_port
|
536
|
+
}],
|
537
|
+
[":def", "timeouts", {
|
538
|
+
loop: true,
|
539
|
+
timeout: 15000,
|
540
|
+
timeoutLimit: 60000
|
541
|
+
}],
|
542
|
+
|
543
|
+
[":def", "ws_manager", [":transport_manager", { lives: 2 }]],
|
544
|
+
[":def_transport", "ws", "ws", 3, ":ws_options", ":ws_manager"],
|
545
|
+
[":def_transport", "flash", "flash", 2, ":ws_options", ":ws_manager"],
|
546
|
+
[":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"],
|
547
|
+
[":def", "ws_loop", [":sequential", ":timeouts", ":ws"]],
|
548
|
+
[":def", "flash_loop", [":sequential", ":timeouts", ":flash"]],
|
549
|
+
[":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]],
|
550
|
+
|
551
|
+
[":def", "strategy",
|
552
|
+
[":cached", 1800000,
|
553
|
+
[":first_connected",
|
554
|
+
[":if", [":is_supported", ":ws"], [
|
555
|
+
":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]]
|
556
|
+
], [":if", [":is_supported", ":flash"], [
|
557
|
+
":best_connected_ever", ":flash_loop", [":delayed", 2000, [":sockjs_loop"]]
|
558
|
+
], [
|
559
|
+
":sockjs_loop"
|
560
|
+
]
|
561
|
+
]]
|
562
|
+
]
|
563
|
+
]
|
564
|
+
]
|
565
|
+
];
|
566
|
+
};
|
567
|
+
}).call(this);
|
568
|
+
|
569
|
+
;(function() {
|
570
|
+
function buildExceptionClass(name) {
|
571
|
+
var klass = function(message) {
|
572
|
+
Error.call(this, message);
|
573
|
+
this.name = name;
|
574
|
+
};
|
575
|
+
Pusher.Util.extend(klass.prototype, Error.prototype);
|
576
|
+
|
577
|
+
return klass;
|
578
|
+
}
|
579
|
+
|
580
|
+
/** Error classes used throughout pusher-js library. */
|
581
|
+
Pusher.Errors = {
|
582
|
+
UnsupportedTransport: buildExceptionClass("UnsupportedTransport"),
|
583
|
+
UnsupportedStrategy: buildExceptionClass("UnsupportedStrategy"),
|
584
|
+
TransportPriorityTooLow: buildExceptionClass("TransportPriorityTooLow"),
|
585
|
+
TransportClosed: buildExceptionClass("TransportClosed")
|
586
|
+
};
|
587
|
+
}).call(this);
|
588
|
+
|
589
|
+
;(function() {
|
590
|
+
/** Manages callback bindings and event emitting.
|
591
|
+
*
|
592
|
+
* @param Function failThrough called when no listeners are bound to an event
|
593
|
+
*/
|
594
|
+
function EventsDispatcher(failThrough) {
|
595
|
+
this.callbacks = new CallbackRegistry();
|
596
|
+
this.global_callbacks = [];
|
597
|
+
this.failThrough = failThrough;
|
598
|
+
}
|
599
|
+
var prototype = EventsDispatcher.prototype;
|
600
|
+
|
601
|
+
prototype.bind = function(eventName, callback) {
|
602
|
+
this.callbacks.add(eventName, callback);
|
603
|
+
return this;
|
604
|
+
};
|
605
|
+
|
606
|
+
prototype.bind_all = function(callback) {
|
607
|
+
this.global_callbacks.push(callback);
|
608
|
+
return this;
|
609
|
+
};
|
610
|
+
|
611
|
+
prototype.unbind = function(eventName, callback) {
|
612
|
+
this.callbacks.remove(eventName, callback);
|
613
|
+
return this;
|
614
|
+
};
|
615
|
+
|
616
|
+
prototype.emit = function(eventName, data) {
|
617
|
+
var i;
|
618
|
+
|
619
|
+
for (i = 0; i < this.global_callbacks.length; i++) {
|
620
|
+
this.global_callbacks[i](eventName, data);
|
621
|
+
}
|
622
|
+
|
623
|
+
var callbacks = this.callbacks.get(eventName);
|
624
|
+
if (callbacks && callbacks.length > 0) {
|
625
|
+
for (i = 0; i < callbacks.length; i++) {
|
626
|
+
callbacks[i](data);
|
627
|
+
}
|
628
|
+
} else if (this.failThrough) {
|
629
|
+
this.failThrough(eventName, data);
|
630
|
+
}
|
631
|
+
|
632
|
+
return this;
|
633
|
+
};
|
634
|
+
|
635
|
+
/** Callback registry helper. */
|
636
|
+
|
637
|
+
function CallbackRegistry() {
|
638
|
+
this._callbacks = {};
|
639
|
+
}
|
640
|
+
|
641
|
+
CallbackRegistry.prototype.get = function(eventName) {
|
642
|
+
return this._callbacks[this._prefix(eventName)];
|
643
|
+
};
|
644
|
+
|
645
|
+
CallbackRegistry.prototype.add = function(eventName, callback) {
|
646
|
+
var prefixedEventName = this._prefix(eventName);
|
647
|
+
this._callbacks[prefixedEventName] = this._callbacks[prefixedEventName] || [];
|
648
|
+
this._callbacks[prefixedEventName].push(callback);
|
649
|
+
};
|
650
|
+
|
651
|
+
CallbackRegistry.prototype.remove = function(eventName, callback) {
|
652
|
+
if(this.get(eventName)) {
|
653
|
+
var index = Pusher.Util.arrayIndexOf(this.get(eventName), callback);
|
654
|
+
var callbacksCopy = this._callbacks[this._prefix(eventName)].slice(0);
|
655
|
+
callbacksCopy.splice(index, 1);
|
656
|
+
this._callbacks[this._prefix(eventName)] = callbacksCopy;
|
657
|
+
}
|
658
|
+
};
|
659
|
+
|
660
|
+
CallbackRegistry.prototype._prefix = function(eventName) {
|
661
|
+
return "_" + eventName;
|
662
|
+
};
|
663
|
+
|
664
|
+
Pusher.EventsDispatcher = EventsDispatcher;
|
665
|
+
}).call(this);
|
666
|
+
|
667
|
+
;(function() {
|
668
|
+
/** Handles loading dependency files.
|
669
|
+
*
|
670
|
+
* Options:
|
671
|
+
* - cdn_http - url to HTTP CND
|
672
|
+
* - cdn_https - url to HTTPS CDN
|
673
|
+
* - version - version of pusher-js
|
674
|
+
* - suffix - suffix appended to all names of dependency files
|
675
|
+
*
|
676
|
+
* @param {Object} options
|
677
|
+
*/
|
678
|
+
function DependencyLoader(options) {
|
679
|
+
this.options = options;
|
680
|
+
this.loading = {};
|
681
|
+
this.loaded = {};
|
682
|
+
}
|
683
|
+
var prototype = DependencyLoader.prototype;
|
684
|
+
|
685
|
+
/** Loads the dependency from CDN.
|
686
|
+
*
|
687
|
+
* @param {String} name
|
688
|
+
* @param {Function} callback
|
689
|
+
*/
|
690
|
+
prototype.load = function(name, callback) {
|
691
|
+
var self = this;
|
692
|
+
|
693
|
+
if (this.loaded[name]) {
|
694
|
+
callback();
|
695
|
+
return;
|
696
|
+
}
|
697
|
+
|
698
|
+
if (!this.loading[name]) {
|
699
|
+
this.loading[name] = [];
|
700
|
+
}
|
701
|
+
this.loading[name].push(callback);
|
702
|
+
if (this.loading[name].length > 1) {
|
703
|
+
return;
|
704
|
+
}
|
705
|
+
|
706
|
+
require(this.getPath(name), function() {
|
707
|
+
for (var i = 0; i < self.loading[name].length; i++) {
|
708
|
+
self.loading[name][i]();
|
709
|
+
}
|
710
|
+
delete self.loading[name];
|
711
|
+
self.loaded[name] = true;
|
712
|
+
});
|
713
|
+
};
|
714
|
+
|
715
|
+
/** Returns a root URL for pusher-js CDN.
|
716
|
+
*
|
717
|
+
* @returns {String}
|
718
|
+
*/
|
719
|
+
prototype.getRoot = function(options) {
|
720
|
+
var cdn;
|
721
|
+
var protocol = Pusher.Util.getDocumentLocation().protocol;
|
722
|
+
if ((options && options.encrypted) || protocol === "https:") {
|
723
|
+
cdn = this.options.cdn_https;
|
724
|
+
} else {
|
725
|
+
cdn = this.options.cdn_http;
|
726
|
+
}
|
727
|
+
// make sure there are no double slashes
|
728
|
+
return cdn.replace(/\/*$/, "") + "/" + this.options.version;
|
729
|
+
};
|
730
|
+
|
731
|
+
/** Returns a full path to a dependency file.
|
732
|
+
*
|
733
|
+
* @param {String} name
|
734
|
+
* @returns {String}
|
735
|
+
*/
|
736
|
+
prototype.getPath = function(name, options) {
|
737
|
+
return this.getRoot(options) + '/' + name + this.options.suffix + '.js';
|
738
|
+
};
|
739
|
+
|
740
|
+
function handleScriptLoaded(elem, callback) {
|
741
|
+
if (Pusher.Util.getDocument().addEventListener) {
|
742
|
+
elem.addEventListener('load', callback, false);
|
743
|
+
} else {
|
744
|
+
elem.attachEvent('onreadystatechange', function () {
|
745
|
+
if (elem.readyState === 'loaded' || elem.readyState === 'complete') {
|
746
|
+
callback();
|
747
|
+
}
|
748
|
+
});
|
749
|
+
}
|
750
|
+
}
|
751
|
+
|
752
|
+
function require(src, callback) {
|
753
|
+
var document = Pusher.Util.getDocument();
|
754
|
+
var head = document.getElementsByTagName('head')[0];
|
755
|
+
var script = document.createElement('script');
|
756
|
+
|
757
|
+
script.setAttribute('src', src);
|
758
|
+
script.setAttribute("type","text/javascript");
|
759
|
+
script.setAttribute('async', true);
|
760
|
+
|
761
|
+
handleScriptLoaded(script, function() {
|
762
|
+
// workaround for an Opera issue
|
763
|
+
setTimeout(callback, 0);
|
764
|
+
});
|
765
|
+
|
766
|
+
head.appendChild(script);
|
767
|
+
}
|
768
|
+
|
769
|
+
Pusher.DependencyLoader = DependencyLoader;
|
770
|
+
}).call(this);
|
771
|
+
|
772
|
+
;(function() {
|
773
|
+
Pusher.Dependencies = new Pusher.DependencyLoader({
|
774
|
+
cdn_http: Pusher.cdn_http,
|
775
|
+
cdn_https: Pusher.cdn_https,
|
776
|
+
version: Pusher.VERSION,
|
777
|
+
suffix: Pusher.dependency_suffix
|
778
|
+
});
|
779
|
+
|
780
|
+
// Support Firefox versions which prefix WebSocket
|
781
|
+
if (!window.WebSocket && window.MozWebSocket) {
|
782
|
+
window.WebSocket = window.MozWebSocket;
|
783
|
+
}
|
784
|
+
|
785
|
+
function initialize() {
|
786
|
+
Pusher.ready();
|
787
|
+
}
|
788
|
+
|
789
|
+
// Allows calling a function when the document body is available
|
790
|
+
function onDocumentBody(callback) {
|
791
|
+
if (document.body) {
|
792
|
+
callback();
|
793
|
+
} else {
|
794
|
+
setTimeout(function() {
|
795
|
+
onDocumentBody(callback);
|
796
|
+
}, 0);
|
797
|
+
}
|
798
|
+
}
|
799
|
+
|
800
|
+
function initializeOnDocumentBody() {
|
801
|
+
onDocumentBody(initialize);
|
802
|
+
}
|
803
|
+
|
804
|
+
if (!window.JSON) {
|
805
|
+
Pusher.Dependencies.load("json2", initializeOnDocumentBody);
|
806
|
+
} else {
|
807
|
+
initializeOnDocumentBody();
|
808
|
+
}
|
809
|
+
})();
|
810
|
+
|
811
|
+
;(function() {
|
812
|
+
/** Cross-browser compatible timer abstraction.
|
813
|
+
*
|
814
|
+
* @param {Number} delay
|
815
|
+
* @param {Function} callback
|
816
|
+
*/
|
817
|
+
function Timer(delay, callback) {
|
818
|
+
var self = this;
|
819
|
+
|
820
|
+
this.timeout = setTimeout(function() {
|
821
|
+
if (self.timeout !== null) {
|
822
|
+
callback();
|
823
|
+
self.timeout = null;
|
824
|
+
}
|
825
|
+
}, delay);
|
826
|
+
}
|
827
|
+
var prototype = Timer.prototype;
|
828
|
+
|
829
|
+
/** Returns whether the timer is still running.
|
830
|
+
*
|
831
|
+
* @return {Boolean}
|
832
|
+
*/
|
833
|
+
prototype.isRunning = function() {
|
834
|
+
return this.timeout !== null;
|
835
|
+
};
|
836
|
+
|
837
|
+
/** Aborts a timer when it's running. */
|
838
|
+
prototype.ensureAborted = function() {
|
839
|
+
if (this.timeout) {
|
840
|
+
clearTimeout(this.timeout);
|
841
|
+
this.timeout = null;
|
842
|
+
}
|
843
|
+
};
|
844
|
+
|
845
|
+
Pusher.Timer = Timer;
|
846
|
+
}).call(this);
|
847
|
+
|
848
|
+
(function() {
|
849
|
+
|
850
|
+
var Base64 = {
|
851
|
+
encode: function (s) {
|
852
|
+
return btoa(utob(s));
|
853
|
+
}
|
854
|
+
};
|
855
|
+
|
856
|
+
var fromCharCode = String.fromCharCode;
|
857
|
+
|
858
|
+
var b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
859
|
+
var b64tab = {};
|
860
|
+
|
861
|
+
for (var i = 0, l = b64chars.length; i < l; i++) {
|
862
|
+
b64tab[b64chars.charAt(i)] = i;
|
863
|
+
}
|
864
|
+
|
865
|
+
var cb_utob = function(c) {
|
866
|
+
var cc = c.charCodeAt(0);
|
867
|
+
return cc < 0x80 ? c
|
868
|
+
: cc < 0x800 ? fromCharCode(0xc0 | (cc >>> 6)) +
|
869
|
+
fromCharCode(0x80 | (cc & 0x3f))
|
870
|
+
: fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) +
|
871
|
+
fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) +
|
872
|
+
fromCharCode(0x80 | ( cc & 0x3f));
|
873
|
+
};
|
874
|
+
|
875
|
+
var utob = function(u) {
|
876
|
+
return u.replace(/[^\x00-\x7F]/g, cb_utob);
|
877
|
+
};
|
878
|
+
|
879
|
+
var cb_encode = function(ccc) {
|
880
|
+
var padlen = [0, 2, 1][ccc.length % 3];
|
881
|
+
var ord = ccc.charCodeAt(0) << 16
|
882
|
+
| ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)
|
883
|
+
| ((ccc.length > 2 ? ccc.charCodeAt(2) : 0));
|
884
|
+
var chars = [
|
885
|
+
b64chars.charAt( ord >>> 18),
|
886
|
+
b64chars.charAt((ord >>> 12) & 63),
|
887
|
+
padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),
|
888
|
+
padlen >= 1 ? '=' : b64chars.charAt(ord & 63)
|
889
|
+
];
|
890
|
+
return chars.join('');
|
891
|
+
};
|
892
|
+
|
893
|
+
var btoa = window.btoa || function(b) {
|
894
|
+
return b.replace(/[\s\S]{1,3}/g, cb_encode);
|
895
|
+
};
|
896
|
+
|
897
|
+
Pusher.Base64 = Base64;
|
898
|
+
|
899
|
+
}).call(this);
|
900
|
+
|
901
|
+
(function() {
|
902
|
+
|
903
|
+
function JSONPRequest(options) {
|
904
|
+
this.options = options;
|
905
|
+
}
|
906
|
+
|
907
|
+
JSONPRequest.send = function(options, callback) {
|
908
|
+
var request = new Pusher.JSONPRequest({
|
909
|
+
url: options.url,
|
910
|
+
receiver: options.receiverName,
|
911
|
+
tagPrefix: options.tagPrefix
|
912
|
+
});
|
913
|
+
var id = options.receiver.register(function(error, result) {
|
914
|
+
request.cleanup();
|
915
|
+
callback(error, result);
|
916
|
+
});
|
917
|
+
|
918
|
+
return request.send(id, options.data, function(error) {
|
919
|
+
var callback = options.receiver.unregister(id);
|
920
|
+
if (callback) {
|
921
|
+
callback(error);
|
922
|
+
}
|
923
|
+
});
|
924
|
+
};
|
925
|
+
|
926
|
+
var prototype = JSONPRequest.prototype;
|
927
|
+
|
928
|
+
prototype.send = function(id, data, callback) {
|
929
|
+
if (this.script) {
|
930
|
+
return false;
|
931
|
+
}
|
932
|
+
|
933
|
+
var tagPrefix = this.options.tagPrefix || "_pusher_jsonp_";
|
934
|
+
|
935
|
+
var params = Pusher.Util.extend(
|
936
|
+
{}, data, { receiver: this.options.receiver }
|
937
|
+
);
|
938
|
+
var query = Pusher.Util.map(
|
939
|
+
Pusher.Util.flatten(
|
940
|
+
encodeData(
|
941
|
+
Pusher.Util.filterObject(params, function(value) {
|
942
|
+
return value !== undefined;
|
943
|
+
})
|
944
|
+
)
|
945
|
+
),
|
946
|
+
Pusher.Util.method("join", "=")
|
947
|
+
).join("&");
|
948
|
+
|
949
|
+
this.script = document.createElement("script");
|
950
|
+
this.script.id = tagPrefix + id;
|
951
|
+
this.script.src = this.options.url + "/" + id + "?" + query;
|
952
|
+
this.script.type = "text/javascript";
|
953
|
+
this.script.charset = "UTF-8";
|
954
|
+
this.script.onerror = this.script.onload = callback;
|
955
|
+
|
956
|
+
// Opera<11.6 hack for missing onerror callback
|
957
|
+
if (this.script.async === undefined && document.attachEvent) {
|
958
|
+
if (/opera/i.test(navigator.userAgent)) {
|
959
|
+
var receiverName = this.options.receiver || "Pusher.JSONP.receive";
|
960
|
+
this.errorScript = document.createElement("script");
|
961
|
+
this.errorScript.text = receiverName + "(" + id + ", true);";
|
962
|
+
this.script.async = this.errorScript.async = false;
|
963
|
+
}
|
964
|
+
}
|
965
|
+
|
966
|
+
var self = this;
|
967
|
+
this.script.onreadystatechange = function() {
|
968
|
+
if (self.script && /loaded|complete/.test(self.script.readyState)) {
|
969
|
+
callback(true);
|
970
|
+
}
|
971
|
+
};
|
972
|
+
|
973
|
+
var head = document.getElementsByTagName('head')[0];
|
974
|
+
head.insertBefore(this.script, head.firstChild);
|
975
|
+
if (this.errorScript) {
|
976
|
+
head.insertBefore(this.errorScript, this.script.nextSibling);
|
977
|
+
}
|
978
|
+
|
979
|
+
return true;
|
980
|
+
};
|
981
|
+
|
982
|
+
prototype.cleanup = function() {
|
983
|
+
if (this.script && this.script.parentNode) {
|
984
|
+
this.script.parentNode.removeChild(this.script);
|
985
|
+
this.script = null;
|
986
|
+
}
|
987
|
+
if (this.errorScript && this.errorScript.parentNode) {
|
988
|
+
this.errorScript.parentNode.removeChild(this.errorScript);
|
989
|
+
this.errorScript = null;
|
990
|
+
}
|
991
|
+
};
|
992
|
+
|
993
|
+
function encodeData(data) {
|
994
|
+
return Pusher.Util.mapObject(data, function(value) {
|
995
|
+
if (typeof value === "object") {
|
996
|
+
value = JSON.stringify(value);
|
997
|
+
}
|
998
|
+
return encodeURIComponent(Pusher.Base64.encode(value.toString()));
|
999
|
+
});
|
1000
|
+
}
|
1001
|
+
|
1002
|
+
Pusher.JSONPRequest = JSONPRequest;
|
1003
|
+
|
1004
|
+
}).call(this);
|
1005
|
+
|
1006
|
+
(function() {
|
1007
|
+
|
1008
|
+
function JSONPReceiver() {
|
1009
|
+
this.lastId = 0;
|
1010
|
+
this.callbacks = {};
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
var prototype = JSONPReceiver.prototype;
|
1014
|
+
|
1015
|
+
prototype.register = function(callback) {
|
1016
|
+
this.lastId++;
|
1017
|
+
var id = this.lastId;
|
1018
|
+
this.callbacks[id] = callback;
|
1019
|
+
return id;
|
1020
|
+
};
|
1021
|
+
|
1022
|
+
prototype.unregister = function(id) {
|
1023
|
+
if (this.callbacks[id]) {
|
1024
|
+
var callback = this.callbacks[id];
|
1025
|
+
delete this.callbacks[id];
|
1026
|
+
return callback;
|
1027
|
+
} else {
|
1028
|
+
return null;
|
1029
|
+
}
|
1030
|
+
};
|
1031
|
+
|
1032
|
+
prototype.receive = function(id, error, data) {
|
1033
|
+
var callback = this.unregister(id);
|
1034
|
+
if (callback) {
|
1035
|
+
callback(error, data);
|
1036
|
+
}
|
1037
|
+
};
|
1038
|
+
|
1039
|
+
Pusher.JSONPReceiver = JSONPReceiver;
|
1040
|
+
Pusher.JSONP = new JSONPReceiver();
|
1041
|
+
|
1042
|
+
}).call(this);
|
1043
|
+
|
1044
|
+
(function() {
|
1045
|
+
function Timeline(key, session, options) {
|
1046
|
+
this.key = key;
|
1047
|
+
this.session = session;
|
1048
|
+
this.events = [];
|
1049
|
+
this.options = options || {};
|
1050
|
+
this.sent = 0;
|
1051
|
+
this.uniqueID = 0;
|
1052
|
+
}
|
1053
|
+
var prototype = Timeline.prototype;
|
1054
|
+
|
1055
|
+
// Log levels
|
1056
|
+
Timeline.ERROR = 3;
|
1057
|
+
Timeline.INFO = 6;
|
1058
|
+
Timeline.DEBUG = 7;
|
1059
|
+
|
1060
|
+
prototype.log = function(level, event) {
|
1061
|
+
if (this.options.level === undefined || level <= this.options.level) {
|
1062
|
+
this.events.push(
|
1063
|
+
Pusher.Util.extend({}, event, {
|
1064
|
+
timestamp: Pusher.Util.now(),
|
1065
|
+
level: level
|
1066
|
+
})
|
1067
|
+
);
|
1068
|
+
if (this.options.limit && this.events.length > this.options.limit) {
|
1069
|
+
this.events.shift();
|
1070
|
+
}
|
1071
|
+
}
|
1072
|
+
};
|
1073
|
+
|
1074
|
+
prototype.error = function(event) {
|
1075
|
+
this.log(Timeline.ERROR, event);
|
1076
|
+
};
|
1077
|
+
|
1078
|
+
prototype.info = function(event) {
|
1079
|
+
this.log(Timeline.INFO, event);
|
1080
|
+
};
|
1081
|
+
|
1082
|
+
prototype.debug = function(event) {
|
1083
|
+
this.log(Timeline.DEBUG, event);
|
1084
|
+
};
|
1085
|
+
|
1086
|
+
prototype.isEmpty = function() {
|
1087
|
+
return this.events.length === 0;
|
1088
|
+
};
|
1089
|
+
|
1090
|
+
prototype.send = function(sendJSONP, callback) {
|
1091
|
+
var self = this;
|
1092
|
+
|
1093
|
+
var data = {};
|
1094
|
+
if (this.sent === 0) {
|
1095
|
+
data = Pusher.Util.extend({
|
1096
|
+
key: this.key,
|
1097
|
+
features: this.options.features,
|
1098
|
+
version: this.options.version
|
1099
|
+
}, this.options.params || {});
|
1100
|
+
}
|
1101
|
+
data.session = this.session;
|
1102
|
+
data.timeline = this.events;
|
1103
|
+
data = Pusher.Util.filterObject(data, function(v) {
|
1104
|
+
return v !== undefined;
|
1105
|
+
});
|
1106
|
+
|
1107
|
+
this.events = [];
|
1108
|
+
sendJSONP(data, function(error, result) {
|
1109
|
+
if (!error) {
|
1110
|
+
self.sent++;
|
1111
|
+
}
|
1112
|
+
callback(error, result);
|
1113
|
+
});
|
1114
|
+
|
1115
|
+
return true;
|
1116
|
+
};
|
1117
|
+
|
1118
|
+
prototype.generateUniqueID = function() {
|
1119
|
+
this.uniqueID++;
|
1120
|
+
return this.uniqueID;
|
1121
|
+
};
|
1122
|
+
|
1123
|
+
Pusher.Timeline = Timeline;
|
1124
|
+
}).call(this);
|
1125
|
+
|
1126
|
+
(function() {
|
1127
|
+
function TimelineSender(timeline, options) {
|
1128
|
+
this.timeline = timeline;
|
1129
|
+
this.options = options || {};
|
1130
|
+
}
|
1131
|
+
var prototype = TimelineSender.prototype;
|
1132
|
+
|
1133
|
+
prototype.send = function(callback) {
|
1134
|
+
if (this.timeline.isEmpty()) {
|
1135
|
+
return;
|
1136
|
+
}
|
1137
|
+
|
1138
|
+
var options = this.options;
|
1139
|
+
var scheme = "http" + (this.isEncrypted() ? "s" : "") + "://";
|
1140
|
+
|
1141
|
+
var sendJSONP = function(data, callback) {
|
1142
|
+
return Pusher.JSONPRequest.send({
|
1143
|
+
data: data,
|
1144
|
+
url: scheme + options.host + options.path,
|
1145
|
+
receiver: Pusher.JSONP
|
1146
|
+
}, callback);
|
1147
|
+
};
|
1148
|
+
this.timeline.send(sendJSONP, callback);
|
1149
|
+
};
|
1150
|
+
|
1151
|
+
prototype.isEncrypted = function() {
|
1152
|
+
return !!this.options.encrypted;
|
1153
|
+
};
|
1154
|
+
|
1155
|
+
Pusher.TimelineSender = TimelineSender;
|
1156
|
+
}).call(this);
|
1157
|
+
|
1158
|
+
;(function() {
|
1159
|
+
/** Launches all substrategies and emits prioritized connected transports.
|
1160
|
+
*
|
1161
|
+
* @param {Array} strategies
|
1162
|
+
*/
|
1163
|
+
function BestConnectedEverStrategy(strategies) {
|
1164
|
+
this.strategies = strategies;
|
1165
|
+
}
|
1166
|
+
var prototype = BestConnectedEverStrategy.prototype;
|
1167
|
+
|
1168
|
+
prototype.isSupported = function() {
|
1169
|
+
return Pusher.Util.any(this.strategies, Pusher.Util.method("isSupported"));
|
1170
|
+
};
|
1171
|
+
|
1172
|
+
prototype.connect = function(minPriority, callback) {
|
1173
|
+
return connect(this.strategies, minPriority, function(i, runners) {
|
1174
|
+
return function(error, connection) {
|
1175
|
+
runners[i].error = error;
|
1176
|
+
if (error) {
|
1177
|
+
if (allRunnersFailed(runners)) {
|
1178
|
+
callback(true);
|
1179
|
+
}
|
1180
|
+
return;
|
1181
|
+
}
|
1182
|
+
Pusher.Util.apply(runners, function(runner) {
|
1183
|
+
runner.forceMinPriority(connection.priority);
|
1184
|
+
});
|
1185
|
+
callback(null, connection);
|
1186
|
+
};
|
1187
|
+
});
|
1188
|
+
};
|
1189
|
+
|
1190
|
+
/** Connects to all strategies in parallel.
|
1191
|
+
*
|
1192
|
+
* Callback builder should be a function that takes two arguments: index
|
1193
|
+
* and a list of runners. It should return another function that will be
|
1194
|
+
* passed to the substrategy with given index. Runners can be aborted using
|
1195
|
+
* abortRunner(s) functions from this class.
|
1196
|
+
*
|
1197
|
+
* @param {Array} strategies
|
1198
|
+
* @param {Function} callbackBuilder
|
1199
|
+
* @return {Object} strategy runner
|
1200
|
+
*/
|
1201
|
+
function connect(strategies, minPriority, callbackBuilder) {
|
1202
|
+
var runners = Pusher.Util.map(strategies, function(strategy, i, _, rs) {
|
1203
|
+
return strategy.connect(minPriority, callbackBuilder(i, rs));
|
1204
|
+
});
|
1205
|
+
return {
|
1206
|
+
abort: function() {
|
1207
|
+
Pusher.Util.apply(runners, abortRunner);
|
1208
|
+
},
|
1209
|
+
forceMinPriority: function(p) {
|
1210
|
+
Pusher.Util.apply(runners, function(runner) {
|
1211
|
+
runner.forceMinPriority(p);
|
1212
|
+
});
|
1213
|
+
}
|
1214
|
+
};
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
function allRunnersFailed(runners) {
|
1218
|
+
return Pusher.Util.all(runners, function(runner) {
|
1219
|
+
return Boolean(runner.error);
|
1220
|
+
});
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
function abortRunner(runner) {
|
1224
|
+
if (!runner.error && !runner.aborted) {
|
1225
|
+
runner.abort();
|
1226
|
+
runner.aborted = true;
|
1227
|
+
}
|
1228
|
+
}
|
1229
|
+
|
1230
|
+
Pusher.BestConnectedEverStrategy = BestConnectedEverStrategy;
|
1231
|
+
}).call(this);
|
1232
|
+
|
1233
|
+
;(function() {
|
1234
|
+
/** Caches last successful transport and uses it for following attempts.
|
1235
|
+
*
|
1236
|
+
* @param {Strategy} strategy
|
1237
|
+
* @param {Object} transports
|
1238
|
+
* @param {Object} options
|
1239
|
+
*/
|
1240
|
+
function CachedStrategy(strategy, transports, options) {
|
1241
|
+
this.strategy = strategy;
|
1242
|
+
this.transports = transports;
|
1243
|
+
this.ttl = options.ttl || 1800*1000;
|
1244
|
+
this.timeline = options.timeline;
|
1245
|
+
}
|
1246
|
+
var prototype = CachedStrategy.prototype;
|
1247
|
+
|
1248
|
+
prototype.isSupported = function() {
|
1249
|
+
return this.strategy.isSupported();
|
1250
|
+
};
|
1251
|
+
|
1252
|
+
prototype.connect = function(minPriority, callback) {
|
1253
|
+
var info = fetchTransportInfo();
|
1254
|
+
|
1255
|
+
var strategies = [this.strategy];
|
1256
|
+
if (info && info.timestamp + this.ttl >= Pusher.Util.now()) {
|
1257
|
+
var transport = this.transports[info.transport];
|
1258
|
+
if (transport) {
|
1259
|
+
this.timeline.info({ cached: true, transport: info.transport });
|
1260
|
+
strategies.push(new Pusher.SequentialStrategy([transport], {
|
1261
|
+
timeout: info.latency * 2,
|
1262
|
+
failFast: true
|
1263
|
+
}));
|
1264
|
+
}
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
var startTimestamp = Pusher.Util.now();
|
1268
|
+
var runner = strategies.pop().connect(
|
1269
|
+
minPriority,
|
1270
|
+
function cb(error, connection) {
|
1271
|
+
if (error) {
|
1272
|
+
flushTransportInfo();
|
1273
|
+
if (strategies.length > 0) {
|
1274
|
+
startTimestamp = Pusher.Util.now();
|
1275
|
+
runner = strategies.pop().connect(minPriority, cb);
|
1276
|
+
} else {
|
1277
|
+
callback(error);
|
1278
|
+
}
|
1279
|
+
} else {
|
1280
|
+
var latency = Pusher.Util.now() - startTimestamp;
|
1281
|
+
storeTransportInfo(connection.name, latency);
|
1282
|
+
callback(null, connection);
|
1283
|
+
}
|
1284
|
+
}
|
1285
|
+
);
|
1286
|
+
|
1287
|
+
return {
|
1288
|
+
abort: function() {
|
1289
|
+
runner.abort();
|
1290
|
+
},
|
1291
|
+
forceMinPriority: function(p) {
|
1292
|
+
minPriority = p;
|
1293
|
+
if (runner) {
|
1294
|
+
runner.forceMinPriority(p);
|
1295
|
+
}
|
1296
|
+
}
|
1297
|
+
};
|
1298
|
+
};
|
1299
|
+
|
1300
|
+
function fetchTransportInfo() {
|
1301
|
+
var storage = Pusher.Util.getLocalStorage();
|
1302
|
+
if (storage) {
|
1303
|
+
var info = storage.pusherTransport;
|
1304
|
+
if (info) {
|
1305
|
+
return JSON.parse(storage.pusherTransport);
|
1306
|
+
}
|
1307
|
+
}
|
1308
|
+
return null;
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
function storeTransportInfo(transport, latency) {
|
1312
|
+
var storage = Pusher.Util.getLocalStorage();
|
1313
|
+
if (storage) {
|
1314
|
+
storage.pusherTransport = JSON.stringify({
|
1315
|
+
timestamp: Pusher.Util.now(),
|
1316
|
+
transport: transport,
|
1317
|
+
latency: latency
|
1318
|
+
});
|
1319
|
+
}
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
function flushTransportInfo() {
|
1323
|
+
var storage = Pusher.Util.getLocalStorage();
|
1324
|
+
if (storage) {
|
1325
|
+
delete storage.pusherTransport;
|
1326
|
+
}
|
1327
|
+
}
|
1328
|
+
|
1329
|
+
Pusher.CachedStrategy = CachedStrategy;
|
1330
|
+
}).call(this);
|
1331
|
+
|
1332
|
+
;(function() {
|
1333
|
+
/** Runs substrategy after specified delay.
|
1334
|
+
*
|
1335
|
+
* Options:
|
1336
|
+
* - delay - time in miliseconds to delay the substrategy attempt
|
1337
|
+
*
|
1338
|
+
* @param {Strategy} strategy
|
1339
|
+
* @param {Object} options
|
1340
|
+
*/
|
1341
|
+
function DelayedStrategy(strategy, options) {
|
1342
|
+
this.strategy = strategy;
|
1343
|
+
this.options = { delay: options.delay };
|
1344
|
+
}
|
1345
|
+
var prototype = DelayedStrategy.prototype;
|
1346
|
+
|
1347
|
+
prototype.isSupported = function() {
|
1348
|
+
return this.strategy.isSupported();
|
1349
|
+
};
|
1350
|
+
|
1351
|
+
prototype.connect = function(minPriority, callback) {
|
1352
|
+
var strategy = this.strategy;
|
1353
|
+
var runner;
|
1354
|
+
var timer = new Pusher.Timer(this.options.delay, function() {
|
1355
|
+
runner = strategy.connect(minPriority, callback);
|
1356
|
+
});
|
1357
|
+
|
1358
|
+
return {
|
1359
|
+
abort: function() {
|
1360
|
+
timer.ensureAborted();
|
1361
|
+
if (runner) {
|
1362
|
+
runner.abort();
|
1363
|
+
}
|
1364
|
+
},
|
1365
|
+
forceMinPriority: function(p) {
|
1366
|
+
minPriority = p;
|
1367
|
+
if (runner) {
|
1368
|
+
runner.forceMinPriority(p);
|
1369
|
+
}
|
1370
|
+
}
|
1371
|
+
};
|
1372
|
+
};
|
1373
|
+
|
1374
|
+
Pusher.DelayedStrategy = DelayedStrategy;
|
1375
|
+
}).call(this);
|
1376
|
+
|
1377
|
+
;(function() {
|
1378
|
+
/** Launches the substrategy and terminates on the first open connection.
|
1379
|
+
*
|
1380
|
+
* @param {Strategy} strategy
|
1381
|
+
*/
|
1382
|
+
function FirstConnectedStrategy(strategy) {
|
1383
|
+
this.strategy = strategy;
|
1384
|
+
}
|
1385
|
+
var prototype = FirstConnectedStrategy.prototype;
|
1386
|
+
|
1387
|
+
prototype.isSupported = function() {
|
1388
|
+
return this.strategy.isSupported();
|
1389
|
+
};
|
1390
|
+
|
1391
|
+
prototype.connect = function(minPriority, callback) {
|
1392
|
+
var runner = this.strategy.connect(
|
1393
|
+
minPriority,
|
1394
|
+
function(error, connection) {
|
1395
|
+
if (connection) {
|
1396
|
+
runner.abort();
|
1397
|
+
}
|
1398
|
+
callback(error, connection);
|
1399
|
+
}
|
1400
|
+
);
|
1401
|
+
return runner;
|
1402
|
+
};
|
1403
|
+
|
1404
|
+
Pusher.FirstConnectedStrategy = FirstConnectedStrategy;
|
1405
|
+
}).call(this);
|
1406
|
+
|
1407
|
+
;(function() {
|
1408
|
+
/** Proxies method calls to one of substrategies basing on the test function.
|
1409
|
+
*
|
1410
|
+
* @param {Function} test
|
1411
|
+
* @param {Strategy} trueBranch strategy used when test returns true
|
1412
|
+
* @param {Strategy} falseBranch strategy used when test returns false
|
1413
|
+
*/
|
1414
|
+
function IfStrategy(test, trueBranch, falseBranch) {
|
1415
|
+
this.test = test;
|
1416
|
+
this.trueBranch = trueBranch;
|
1417
|
+
this.falseBranch = falseBranch;
|
1418
|
+
}
|
1419
|
+
var prototype = IfStrategy.prototype;
|
1420
|
+
|
1421
|
+
prototype.isSupported = function() {
|
1422
|
+
var branch = this.test() ? this.trueBranch : this.falseBranch;
|
1423
|
+
return branch.isSupported();
|
1424
|
+
};
|
1425
|
+
|
1426
|
+
prototype.connect = function(minPriority, callback) {
|
1427
|
+
var branch = this.test() ? this.trueBranch : this.falseBranch;
|
1428
|
+
return branch.connect(minPriority, callback);
|
1429
|
+
};
|
1430
|
+
|
1431
|
+
Pusher.IfStrategy = IfStrategy;
|
1432
|
+
}).call(this);
|
1433
|
+
|
1434
|
+
;(function() {
|
1435
|
+
/** Loops through strategies with optional timeouts.
|
1436
|
+
*
|
1437
|
+
* Options:
|
1438
|
+
* - loop - whether it should loop through the substrategy list
|
1439
|
+
* - timeout - initial timeout for a single substrategy
|
1440
|
+
* - timeoutLimit - maximum timeout
|
1441
|
+
*
|
1442
|
+
* @param {Strategy[]} strategies
|
1443
|
+
* @param {Object} options
|
1444
|
+
*/
|
1445
|
+
function SequentialStrategy(strategies, options) {
|
1446
|
+
this.strategies = strategies;
|
1447
|
+
this.loop = Boolean(options.loop);
|
1448
|
+
this.failFast = Boolean(options.failFast);
|
1449
|
+
this.timeout = options.timeout;
|
1450
|
+
this.timeoutLimit = options.timeoutLimit;
|
1451
|
+
}
|
1452
|
+
var prototype = SequentialStrategy.prototype;
|
1453
|
+
|
1454
|
+
prototype.isSupported = function() {
|
1455
|
+
return Pusher.Util.any(this.strategies, Pusher.Util.method("isSupported"));
|
1456
|
+
};
|
1457
|
+
|
1458
|
+
prototype.connect = function(minPriority, callback) {
|
1459
|
+
var self = this;
|
1460
|
+
|
1461
|
+
var strategies = this.strategies;
|
1462
|
+
var current = 0;
|
1463
|
+
var timeout = this.timeout;
|
1464
|
+
var runner = null;
|
1465
|
+
|
1466
|
+
var tryNextStrategy = function(error, connection) {
|
1467
|
+
if (connection) {
|
1468
|
+
callback(null, connection);
|
1469
|
+
} else {
|
1470
|
+
current = current + 1;
|
1471
|
+
if (self.loop) {
|
1472
|
+
current = current % strategies.length;
|
1473
|
+
}
|
1474
|
+
|
1475
|
+
if (current < strategies.length) {
|
1476
|
+
if (timeout) {
|
1477
|
+
timeout = timeout * 2;
|
1478
|
+
if (self.timeoutLimit) {
|
1479
|
+
timeout = Math.min(timeout, self.timeoutLimit);
|
1480
|
+
}
|
1481
|
+
}
|
1482
|
+
runner = self.tryStrategy(
|
1483
|
+
strategies[current],
|
1484
|
+
minPriority,
|
1485
|
+
{ timeout: timeout, failFast: self.failFast },
|
1486
|
+
tryNextStrategy
|
1487
|
+
);
|
1488
|
+
} else {
|
1489
|
+
callback(true);
|
1490
|
+
}
|
1491
|
+
}
|
1492
|
+
};
|
1493
|
+
|
1494
|
+
runner = this.tryStrategy(
|
1495
|
+
strategies[current],
|
1496
|
+
minPriority,
|
1497
|
+
{ timeout: timeout, failFast: this.failFast },
|
1498
|
+
tryNextStrategy
|
1499
|
+
);
|
1500
|
+
|
1501
|
+
return {
|
1502
|
+
abort: function() {
|
1503
|
+
runner.abort();
|
1504
|
+
},
|
1505
|
+
forceMinPriority: function(p) {
|
1506
|
+
minPriority = p;
|
1507
|
+
if (runner) {
|
1508
|
+
runner.forceMinPriority(p);
|
1509
|
+
}
|
1510
|
+
}
|
1511
|
+
};
|
1512
|
+
};
|
1513
|
+
|
1514
|
+
/** @private */
|
1515
|
+
prototype.tryStrategy = function(strategy, minPriority, options, callback) {
|
1516
|
+
var timer = null;
|
1517
|
+
var runner = null;
|
1518
|
+
|
1519
|
+
runner = strategy.connect(minPriority, function(error, connection) {
|
1520
|
+
if (error && timer && timer.isRunning() && !options.failFast) {
|
1521
|
+
// advance to the next strategy after the timeout
|
1522
|
+
return;
|
1523
|
+
}
|
1524
|
+
if (timer) {
|
1525
|
+
timer.ensureAborted();
|
1526
|
+
}
|
1527
|
+
callback(error, connection);
|
1528
|
+
});
|
1529
|
+
|
1530
|
+
if (options.timeout > 0) {
|
1531
|
+
timer = new Pusher.Timer(options.timeout, function() {
|
1532
|
+
runner.abort();
|
1533
|
+
callback(true);
|
1534
|
+
});
|
1535
|
+
}
|
1536
|
+
|
1537
|
+
return {
|
1538
|
+
abort: function() {
|
1539
|
+
if (timer) {
|
1540
|
+
timer.ensureAborted();
|
1541
|
+
}
|
1542
|
+
runner.abort();
|
1543
|
+
},
|
1544
|
+
forceMinPriority: function(p) {
|
1545
|
+
runner.forceMinPriority(p);
|
1546
|
+
}
|
1547
|
+
};
|
1548
|
+
};
|
1549
|
+
|
1550
|
+
Pusher.SequentialStrategy = SequentialStrategy;
|
1551
|
+
}).call(this);
|
1552
|
+
|
1553
|
+
;(function() {
|
1554
|
+
/** Provides a strategy interface for transports.
|
1555
|
+
*
|
1556
|
+
* @param {String} name
|
1557
|
+
* @param {Number} priority
|
1558
|
+
* @param {Class} transport
|
1559
|
+
* @param {Object} options
|
1560
|
+
*/
|
1561
|
+
function TransportStrategy(name, priority, transport, options) {
|
1562
|
+
this.name = name;
|
1563
|
+
this.priority = priority;
|
1564
|
+
this.transport = transport;
|
1565
|
+
this.options = options || {};
|
1566
|
+
}
|
1567
|
+
var prototype = TransportStrategy.prototype;
|
1568
|
+
|
1569
|
+
/** Returns whether the transport is supported in the browser.
|
1570
|
+
*
|
1571
|
+
* @returns {Boolean}
|
1572
|
+
*/
|
1573
|
+
prototype.isSupported = function() {
|
1574
|
+
return this.transport.isSupported({
|
1575
|
+
disableFlash: !!this.options.disableFlash
|
1576
|
+
});
|
1577
|
+
};
|
1578
|
+
|
1579
|
+
/** Launches a connection attempt and returns a strategy runner.
|
1580
|
+
*
|
1581
|
+
* @param {Function} callback
|
1582
|
+
* @return {Object} strategy runner
|
1583
|
+
*/
|
1584
|
+
prototype.connect = function(minPriority, callback) {
|
1585
|
+
if (!this.transport.isSupported()) {
|
1586
|
+
return failAttempt(new Pusher.Errors.UnsupportedStrategy(), callback);
|
1587
|
+
} else if (this.priority < minPriority) {
|
1588
|
+
return failAttempt(new Pusher.Errors.TransportPriorityTooLow(), callback);
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
var self = this;
|
1592
|
+
var connection = this.transport.createConnection(
|
1593
|
+
this.name, this.priority, this.options.key, this.options
|
1594
|
+
);
|
1595
|
+
|
1596
|
+
var onInitialized = function() {
|
1597
|
+
connection.unbind("initialized", onInitialized);
|
1598
|
+
connection.connect();
|
1599
|
+
};
|
1600
|
+
var onOpen = function() {
|
1601
|
+
unbindListeners();
|
1602
|
+
callback(null, connection);
|
1603
|
+
};
|
1604
|
+
var onError = function(error) {
|
1605
|
+
unbindListeners();
|
1606
|
+
callback(error);
|
1607
|
+
};
|
1608
|
+
var onClosed = function() {
|
1609
|
+
unbindListeners();
|
1610
|
+
callback(new Pusher.Errors.TransportClosed(this.transport));
|
1611
|
+
};
|
1612
|
+
|
1613
|
+
var unbindListeners = function() {
|
1614
|
+
connection.unbind("initialized", onInitialized);
|
1615
|
+
connection.unbind("open", onOpen);
|
1616
|
+
connection.unbind("error", onError);
|
1617
|
+
connection.unbind("closed", onClosed);
|
1618
|
+
};
|
1619
|
+
|
1620
|
+
connection.bind("initialized", onInitialized);
|
1621
|
+
connection.bind("open", onOpen);
|
1622
|
+
connection.bind("error", onError);
|
1623
|
+
connection.bind("closed", onClosed);
|
1624
|
+
|
1625
|
+
// connect will be called automatically after initialization
|
1626
|
+
connection.initialize();
|
1627
|
+
|
1628
|
+
return {
|
1629
|
+
abort: function() {
|
1630
|
+
if (connection.state === "open") {
|
1631
|
+
return;
|
1632
|
+
}
|
1633
|
+
unbindListeners();
|
1634
|
+
connection.close();
|
1635
|
+
},
|
1636
|
+
forceMinPriority: function(p) {
|
1637
|
+
if (connection.state === "open") {
|
1638
|
+
return;
|
1639
|
+
}
|
1640
|
+
if (self.priority < p) {
|
1641
|
+
// TODO close connection in a nicer way
|
1642
|
+
connection.close();
|
88
1643
|
}
|
89
1644
|
}
|
90
|
-
}
|
91
|
-
|
92
|
-
subscribe: function(channel_name) {
|
93
|
-
var self = this;
|
94
|
-
var channel = this.channels.add(channel_name, this);
|
1645
|
+
};
|
1646
|
+
};
|
95
1647
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
});
|
106
|
-
}
|
107
|
-
});
|
108
|
-
}
|
109
|
-
return channel;
|
110
|
-
},
|
1648
|
+
function failAttempt(error, callback) {
|
1649
|
+
new Pusher.Timer(0, function() {
|
1650
|
+
callback(error);
|
1651
|
+
});
|
1652
|
+
return {
|
1653
|
+
abort: function() {},
|
1654
|
+
forceMinPriority: function() {}
|
1655
|
+
};
|
1656
|
+
}
|
111
1657
|
|
112
|
-
|
113
|
-
|
114
|
-
if (this.connection.state === 'connected') {
|
115
|
-
this.send_event('pusher:unsubscribe', {
|
116
|
-
channel: channel_name
|
117
|
-
});
|
118
|
-
}
|
119
|
-
},
|
1658
|
+
Pusher.TransportStrategy = TransportStrategy;
|
1659
|
+
}).call(this);
|
120
1660
|
|
121
|
-
|
122
|
-
|
123
|
-
|
1661
|
+
;(function() {
|
1662
|
+
/** Handles common logic for all transports.
|
1663
|
+
*
|
1664
|
+
* Transport is a low-level connection object that wraps a connection method
|
1665
|
+
* and exposes a simple evented interface for the connection state and
|
1666
|
+
* messaging. It does not implement Pusher-specific WebSocket protocol.
|
1667
|
+
*
|
1668
|
+
* Additionally, it fetches resources needed for transport to work and exposes
|
1669
|
+
* an interface for querying transport support and its features.
|
1670
|
+
*
|
1671
|
+
* This is an abstract class, please do not instantiate it.
|
1672
|
+
*
|
1673
|
+
* States:
|
1674
|
+
* - new - initial state after constructing the object
|
1675
|
+
* - initializing - during initialization phase, usually fetching resources
|
1676
|
+
* - intialized - ready to establish a connection
|
1677
|
+
* - connection - when connection is being established
|
1678
|
+
* - open - when connection ready to be used
|
1679
|
+
* - closed - after connection was closed be either side
|
1680
|
+
*
|
1681
|
+
* Emits:
|
1682
|
+
* - error - after the connection raised an error
|
1683
|
+
*
|
1684
|
+
* Options:
|
1685
|
+
* - encrypted - whether connection should use ssl
|
1686
|
+
* - hostEncrypted - host to connect to when connection is encrypted
|
1687
|
+
* - hostUnencrypted - host to connect to when connection is not encrypted
|
1688
|
+
*
|
1689
|
+
* @param {String} key application key
|
1690
|
+
* @param {Object} options
|
1691
|
+
*/
|
1692
|
+
function AbstractTransport(name, priority, key, options) {
|
1693
|
+
Pusher.EventsDispatcher.call(this);
|
124
1694
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
1695
|
+
this.name = name;
|
1696
|
+
this.priority = priority;
|
1697
|
+
this.key = key;
|
1698
|
+
this.state = "new";
|
1699
|
+
this.timeline = options.timeline;
|
1700
|
+
this.id = this.timeline.generateUniqueID();
|
1701
|
+
|
1702
|
+
this.options = {
|
1703
|
+
encrypted: Boolean(options.encrypted),
|
1704
|
+
hostUnencrypted: options.hostUnencrypted,
|
1705
|
+
hostEncrypted: options.hostEncrypted
|
1706
|
+
};
|
1707
|
+
}
|
1708
|
+
var prototype = AbstractTransport.prototype;
|
1709
|
+
Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
|
1710
|
+
|
1711
|
+
/** Checks whether the transport is supported in the browser.
|
1712
|
+
*
|
1713
|
+
* @returns {Boolean}
|
1714
|
+
*/
|
1715
|
+
AbstractTransport.isSupported = function() {
|
1716
|
+
return false;
|
131
1717
|
};
|
132
1718
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
target[property] = extensions[property];
|
141
|
-
}
|
142
|
-
}
|
143
|
-
return target;
|
144
|
-
},
|
1719
|
+
/** Checks whether the transport handles ping/pong on itself.
|
1720
|
+
*
|
1721
|
+
* @return {Boolean}
|
1722
|
+
*/
|
1723
|
+
prototype.supportsPing = function() {
|
1724
|
+
return false;
|
1725
|
+
};
|
145
1726
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
};
|
159
|
-
return m.join(" : ")
|
160
|
-
},
|
1727
|
+
/** Initializes the transport.
|
1728
|
+
*
|
1729
|
+
* Fetches resources if needed and then transitions to initialized.
|
1730
|
+
*/
|
1731
|
+
prototype.initialize = function() {
|
1732
|
+
this.timeline.info(this.buildTimelineMessage({
|
1733
|
+
transport: this.name + (this.options.encrypted ? "s" : "")
|
1734
|
+
}));
|
1735
|
+
this.timeline.debug(this.buildTimelineMessage({ method: "initialize" }));
|
1736
|
+
|
1737
|
+
this.changeState("initialized");
|
1738
|
+
};
|
161
1739
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
1740
|
+
/** Tries to establish a connection.
|
1741
|
+
*
|
1742
|
+
* @returns {Boolean} false if transport is in invalid state
|
1743
|
+
*/
|
1744
|
+
prototype.connect = function() {
|
1745
|
+
var url = this.getURL(this.key, this.options);
|
1746
|
+
this.timeline.debug(this.buildTimelineMessage({
|
1747
|
+
method: "connect",
|
1748
|
+
url: url
|
1749
|
+
}));
|
1750
|
+
|
1751
|
+
if (this.socket || this.state !== "initialized") {
|
1752
|
+
return false;
|
168
1753
|
}
|
1754
|
+
|
1755
|
+
this.socket = this.createSocket(url);
|
1756
|
+
this.bindListeners();
|
1757
|
+
|
1758
|
+
Pusher.debug("Connecting", { transport: this.name, url: url });
|
1759
|
+
this.changeState("connecting");
|
1760
|
+
return true;
|
169
1761
|
};
|
170
1762
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
if (
|
179
|
-
|
1763
|
+
/** Closes the connection.
|
1764
|
+
*
|
1765
|
+
* @return {Boolean} true if there was a connection to close
|
1766
|
+
*/
|
1767
|
+
prototype.close = function() {
|
1768
|
+
this.timeline.debug(this.buildTimelineMessage({ method: "close" }));
|
1769
|
+
|
1770
|
+
if (this.socket) {
|
1771
|
+
this.socket.close();
|
1772
|
+
return true;
|
180
1773
|
} else {
|
181
|
-
|
182
|
-
Pusher.log(Pusher.Util.stringify.apply(this, arguments));
|
1774
|
+
return false;
|
183
1775
|
}
|
184
1776
|
};
|
185
1777
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
Pusher.sockjs_path = "/pusher"
|
197
|
-
// Other settings
|
198
|
-
Pusher.channel_auth_endpoint = '/pusher/auth';
|
199
|
-
Pusher.cdn_http = 'http://js.pusher.com/'
|
200
|
-
Pusher.cdn_https = 'https://d3dy5gmtp8yhk7.cloudfront.net/'
|
201
|
-
Pusher.dependency_suffix = '';
|
202
|
-
Pusher.channel_auth_transport = 'ajax';
|
203
|
-
Pusher.activity_timeout = 120000;
|
204
|
-
Pusher.pong_timeout = 30000;
|
1778
|
+
/** Sends data over the open connection.
|
1779
|
+
*
|
1780
|
+
* @param {String} data
|
1781
|
+
* @return {Boolean} true only when in the "open" state
|
1782
|
+
*/
|
1783
|
+
prototype.send = function(data) {
|
1784
|
+
this.timeline.debug(this.buildTimelineMessage({
|
1785
|
+
method: "send",
|
1786
|
+
data: data
|
1787
|
+
}));
|
205
1788
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
1789
|
+
if (this.state === "open") {
|
1790
|
+
// Workaround for MobileSafari bug (see https://gist.github.com/2052006)
|
1791
|
+
var self = this;
|
1792
|
+
setTimeout(function() {
|
1793
|
+
self.socket.send(data);
|
1794
|
+
}, 0);
|
1795
|
+
return true;
|
1796
|
+
} else {
|
1797
|
+
return false;
|
211
1798
|
}
|
212
1799
|
};
|
213
1800
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
;(function() {
|
218
|
-
/* Abstract event binding
|
219
|
-
Example:
|
1801
|
+
prototype.requestPing = function() {
|
1802
|
+
this.emit("ping_request");
|
1803
|
+
};
|
220
1804
|
|
221
|
-
|
222
|
-
|
1805
|
+
/** @protected */
|
1806
|
+
prototype.onOpen = function() {
|
1807
|
+
this.changeState("open");
|
1808
|
+
this.socket.onopen = undefined;
|
1809
|
+
};
|
223
1810
|
|
224
|
-
|
1811
|
+
/** @protected */
|
1812
|
+
prototype.onError = function(error) {
|
1813
|
+
this.emit("error", { type: 'WebSocketError', error: error });
|
1814
|
+
this.timeline.error(this.buildTimelineMessage({
|
1815
|
+
error: getErrorDetails(error)
|
1816
|
+
}));
|
1817
|
+
};
|
225
1818
|
|
226
|
-
|
227
|
-
|
1819
|
+
/** @protected */
|
1820
|
+
prototype.onClose = function(closeEvent) {
|
1821
|
+
this.changeState("closed", closeEvent);
|
1822
|
+
this.socket = undefined;
|
1823
|
+
};
|
228
1824
|
|
229
|
-
|
230
|
-
|
1825
|
+
/** @protected */
|
1826
|
+
prototype.onMessage = function(message) {
|
1827
|
+
this.timeline.debug(this.buildTimelineMessage({ message: message.data }));
|
1828
|
+
this.emit("message", message);
|
1829
|
+
};
|
231
1830
|
|
232
|
-
|
1831
|
+
/** @protected */
|
1832
|
+
prototype.bindListeners = function() {
|
1833
|
+
var self = this;
|
233
1834
|
|
234
|
-
|
235
|
-
this.
|
1835
|
+
this.socket.onopen = function() { self.onOpen(); };
|
1836
|
+
this.socket.onerror = function(error) { self.onError(error); };
|
1837
|
+
this.socket.onclose = function(closeEvent) { self.onClose(closeEvent); };
|
1838
|
+
this.socket.onmessage = function(message) { self.onMessage(message); };
|
236
1839
|
};
|
237
1840
|
|
238
|
-
|
239
|
-
|
1841
|
+
/** @protected */
|
1842
|
+
prototype.createSocket = function(url) {
|
1843
|
+
return null;
|
240
1844
|
};
|
241
1845
|
|
242
|
-
|
243
|
-
|
244
|
-
this.
|
245
|
-
this._callbacks[prefixedEventName].push(callback);
|
1846
|
+
/** @protected */
|
1847
|
+
prototype.getScheme = function() {
|
1848
|
+
return this.options.encrypted ? "wss" : "ws";
|
246
1849
|
};
|
247
1850
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
1851
|
+
/** @protected */
|
1852
|
+
prototype.getBaseURL = function() {
|
1853
|
+
var host;
|
1854
|
+
if (this.options.encrypted) {
|
1855
|
+
host = this.options.hostEncrypted;
|
1856
|
+
} else {
|
1857
|
+
host = this.options.hostUnencrypted;
|
252
1858
|
}
|
1859
|
+
return this.getScheme() + "://" + host;
|
253
1860
|
};
|
254
1861
|
|
255
|
-
|
256
|
-
|
1862
|
+
/** @protected */
|
1863
|
+
prototype.getPath = function() {
|
1864
|
+
return "/app/" + this.key;
|
257
1865
|
};
|
258
1866
|
|
1867
|
+
/** @protected */
|
1868
|
+
prototype.getQueryString = function() {
|
1869
|
+
return "?protocol=" + Pusher.PROTOCOL +
|
1870
|
+
"&client=js&version=" + Pusher.VERSION;
|
1871
|
+
};
|
259
1872
|
|
260
|
-
|
261
|
-
|
262
|
-
this.
|
263
|
-
|
264
|
-
this.failThrough = failThrough;
|
265
|
-
}
|
1873
|
+
/** @protected */
|
1874
|
+
prototype.getURL = function() {
|
1875
|
+
return this.getBaseURL() + this.getPath() + this.getQueryString();
|
1876
|
+
};
|
266
1877
|
|
267
|
-
|
268
|
-
|
269
|
-
|
1878
|
+
/** @protected */
|
1879
|
+
prototype.changeState = function(state, params) {
|
1880
|
+
this.state = state;
|
1881
|
+
this.timeline.info(this.buildTimelineMessage({
|
1882
|
+
state: state,
|
1883
|
+
params: params
|
1884
|
+
}));
|
1885
|
+
this.emit(state, params);
|
270
1886
|
};
|
271
1887
|
|
272
|
-
|
273
|
-
|
274
|
-
return this;
|
1888
|
+
/** @protected */
|
1889
|
+
prototype.buildTimelineMessage = function(message) {
|
1890
|
+
return Pusher.Util.extend({ cid: this.id }, message);
|
275
1891
|
};
|
276
1892
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
this.global_callbacks[i](eventName, data);
|
1893
|
+
function getErrorDetails(error) {
|
1894
|
+
if (typeof error === "string") {
|
1895
|
+
return error;
|
281
1896
|
}
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
this.failThrough(eventName, data)
|
1897
|
+
if (typeof error === "object") {
|
1898
|
+
return Pusher.Util.mapObject(error, function(value) {
|
1899
|
+
var valueType = typeof value;
|
1900
|
+
if (valueType === "object" || valueType == "function") {
|
1901
|
+
return valueType;
|
1902
|
+
}
|
1903
|
+
return value;
|
1904
|
+
});
|
291
1905
|
}
|
1906
|
+
return typeof error;
|
1907
|
+
}
|
292
1908
|
|
293
|
-
|
294
|
-
};
|
295
|
-
|
296
|
-
EventsDispatcher.prototype.bind_all = function(callback) {
|
297
|
-
this.global_callbacks.push(callback);
|
298
|
-
return this;
|
299
|
-
};
|
300
|
-
|
301
|
-
this.Pusher.EventsDispatcher = EventsDispatcher;
|
1909
|
+
Pusher.AbstractTransport = AbstractTransport;
|
302
1910
|
}).call(this);
|
303
1911
|
|
304
1912
|
;(function() {
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
function capitalize(str) {
|
312
|
-
return str.substr(0, 1).toUpperCase() + str.substr(1);
|
1913
|
+
/** Transport using Flash to emulate WebSockets.
|
1914
|
+
*
|
1915
|
+
* @see AbstractTransport
|
1916
|
+
*/
|
1917
|
+
function FlashTransport(name, priority, key, options) {
|
1918
|
+
Pusher.AbstractTransport.call(this, name, priority, key, options);
|
313
1919
|
}
|
1920
|
+
var prototype = FlashTransport.prototype;
|
1921
|
+
Pusher.Util.extend(prototype, Pusher.AbstractTransport.prototype);
|
1922
|
+
|
1923
|
+
/** Creates a new instance of FlashTransport.
|
1924
|
+
*
|
1925
|
+
* @param {String} key
|
1926
|
+
* @param {Object} options
|
1927
|
+
* @return {FlashTransport}
|
1928
|
+
*/
|
1929
|
+
FlashTransport.createConnection = function(name, priority, key, options) {
|
1930
|
+
return new FlashTransport(name, priority, key, options);
|
1931
|
+
};
|
314
1932
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
1933
|
+
/** Checks whether Flash is supported in the browser.
|
1934
|
+
*
|
1935
|
+
* It is possible to disable flash by passing an envrionment object with the
|
1936
|
+
* disableFlash property set to true.
|
1937
|
+
*
|
1938
|
+
* @see AbstractTransport.isSupported
|
1939
|
+
* @param {Object} environment
|
1940
|
+
* @returns {Boolean}
|
1941
|
+
*/
|
1942
|
+
FlashTransport.isSupported = function(environment) {
|
1943
|
+
if (environment && environment.disableFlash) {
|
1944
|
+
return false;
|
319
1945
|
}
|
320
|
-
|
1946
|
+
try {
|
1947
|
+
return !!(new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
|
1948
|
+
} catch (e) {
|
1949
|
+
return navigator.mimeTypes["application/x-shockwave-flash"] !== undefined;
|
1950
|
+
}
|
1951
|
+
};
|
321
1952
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
1953
|
+
/** Fetches flashfallback dependency if needed.
|
1954
|
+
*
|
1955
|
+
* Sets WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR to true (if not set before)
|
1956
|
+
* and WEB_SOCKET_SWF_LOCATION to Pusher's cdn before loading Flash resources.
|
1957
|
+
*
|
1958
|
+
* @see AbstractTransport.prototype.initialize
|
1959
|
+
*/
|
1960
|
+
prototype.initialize = function() {
|
1961
|
+
var self = this;
|
327
1962
|
|
328
|
-
this.
|
329
|
-
|
1963
|
+
this.timeline.info(this.buildTimelineMessage({
|
1964
|
+
transport: this.name + (this.options.encrypted ? "s" : "")
|
1965
|
+
}));
|
1966
|
+
this.timeline.debug(this.buildTimelineMessage({ method: "initialize" }));
|
1967
|
+
this.changeState("initializing");
|
330
1968
|
|
331
|
-
|
332
|
-
|
1969
|
+
if (window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR === undefined) {
|
1970
|
+
window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
|
1971
|
+
}
|
1972
|
+
window.WEB_SOCKET_SWF_LOCATION = Pusher.Dependencies.getRoot() +
|
1973
|
+
"/WebSocketMain.swf";
|
1974
|
+
Pusher.Dependencies.load("flashfallback", function() {
|
1975
|
+
self.changeState("initialized");
|
1976
|
+
});
|
1977
|
+
};
|
333
1978
|
|
334
|
-
|
335
|
-
|
1979
|
+
/** @protected */
|
1980
|
+
prototype.createSocket = function(url) {
|
1981
|
+
return new FlashWebSocket(url);
|
1982
|
+
};
|
336
1983
|
|
337
|
-
|
1984
|
+
/** @protected */
|
1985
|
+
prototype.getQueryString = function() {
|
1986
|
+
return Pusher.AbstractTransport.prototype.getQueryString.call(this) +
|
1987
|
+
"&flash=true";
|
338
1988
|
};
|
339
1989
|
|
340
|
-
|
341
|
-
|
342
|
-
var stateCallbacks = this.stateActions;
|
1990
|
+
Pusher.FlashTransport = FlashTransport;
|
1991
|
+
}).call(this);
|
343
1992
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
1993
|
+
;(function() {
|
1994
|
+
/** Fallback transport using SockJS.
|
1995
|
+
*
|
1996
|
+
* @see AbstractTransport
|
1997
|
+
*/
|
1998
|
+
function SockJSTransport(name, priority, key, options) {
|
1999
|
+
Pusher.AbstractTransport.call(this, name, priority, key, options);
|
2000
|
+
}
|
2001
|
+
var prototype = SockJSTransport.prototype;
|
2002
|
+
Pusher.Util.extend(prototype, Pusher.AbstractTransport.prototype);
|
2003
|
+
|
2004
|
+
/** Creates a new instance of SockJSTransport.
|
2005
|
+
*
|
2006
|
+
* @param {String} key
|
2007
|
+
* @param {Object} options
|
2008
|
+
* @return {SockJSTransport}
|
2009
|
+
*/
|
2010
|
+
SockJSTransport.createConnection = function(name, priority, key, options) {
|
2011
|
+
return new SockJSTransport(name, priority, key, options);
|
2012
|
+
};
|
349
2013
|
|
350
|
-
|
351
|
-
|
2014
|
+
/** Assumes that SockJS is always supported.
|
2015
|
+
*
|
2016
|
+
* @returns {Boolean} always true
|
2017
|
+
*/
|
2018
|
+
SockJSTransport.isSupported = function() {
|
2019
|
+
return true;
|
2020
|
+
};
|
352
2021
|
|
353
|
-
|
354
|
-
|
2022
|
+
/** Fetches sockjs dependency if needed.
|
2023
|
+
*
|
2024
|
+
* @see AbstractTransport.prototype.initialize
|
2025
|
+
*/
|
2026
|
+
prototype.initialize = function() {
|
2027
|
+
var self = this;
|
355
2028
|
|
356
|
-
|
357
|
-
|
2029
|
+
this.timeline.info(this.buildTimelineMessage({
|
2030
|
+
transport: this.name + (this.options.encrypted ? "s" : "")
|
2031
|
+
}));
|
2032
|
+
this.timeline.debug(this.buildTimelineMessage({ method: "initialize" }));
|
358
2033
|
|
359
|
-
|
360
|
-
|
2034
|
+
this.changeState("initializing");
|
2035
|
+
Pusher.Dependencies.load("sockjs", function() {
|
2036
|
+
self.changeState("initialized");
|
2037
|
+
});
|
2038
|
+
};
|
361
2039
|
|
362
|
-
|
363
|
-
|
2040
|
+
/** Always returns true, since SockJS handles ping on its own.
|
2041
|
+
*
|
2042
|
+
* @returns {Boolean} always true
|
2043
|
+
*/
|
2044
|
+
prototype.supportsPing = function() {
|
2045
|
+
return true;
|
2046
|
+
};
|
364
2047
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
2048
|
+
/** @protected */
|
2049
|
+
prototype.createSocket = function(url) {
|
2050
|
+
// exclude iframe transports until we link to correct SockJS version
|
2051
|
+
// inside the iframe
|
2052
|
+
return new SockJS(url, null, {
|
2053
|
+
js_path: Pusher.Dependencies.getPath("sockjs", {
|
2054
|
+
encrypted: this.options.encrypted
|
2055
|
+
})
|
369
2056
|
});
|
2057
|
+
};
|
370
2058
|
|
371
|
-
|
372
|
-
|
2059
|
+
/** @protected */
|
2060
|
+
prototype.getScheme = function() {
|
2061
|
+
return this.options.encrypted ? "https" : "http";
|
373
2062
|
};
|
374
2063
|
|
375
|
-
|
376
|
-
|
2064
|
+
/** @protected */
|
2065
|
+
prototype.getPath = function() {
|
2066
|
+
return "/pusher";
|
377
2067
|
};
|
378
2068
|
|
379
|
-
|
380
|
-
|
2069
|
+
/** @protected */
|
2070
|
+
prototype.getQueryString = function() {
|
2071
|
+
return "";
|
381
2072
|
};
|
382
2073
|
|
383
|
-
|
2074
|
+
/** Handles opening a SockJS connection to Pusher.
|
2075
|
+
*
|
2076
|
+
* Since SockJS does not handle custom paths, we send it immediately after
|
2077
|
+
* establishing the connection.
|
2078
|
+
*
|
2079
|
+
* @protected
|
2080
|
+
*/
|
2081
|
+
prototype.onOpen = function() {
|
2082
|
+
this.socket.send(JSON.stringify({
|
2083
|
+
path: Pusher.AbstractTransport.prototype.getPath.call(this) +
|
2084
|
+
Pusher.AbstractTransport.prototype.getQueryString.call(this)
|
2085
|
+
}));
|
2086
|
+
this.changeState("open");
|
2087
|
+
this.socket.onopen = undefined;
|
2088
|
+
};
|
384
2089
|
|
385
|
-
|
2090
|
+
Pusher.SockJSTransport = SockJSTransport;
|
386
2091
|
}).call(this);
|
387
2092
|
|
388
2093
|
;(function() {
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
2094
|
+
/** WebSocket transport.
|
2095
|
+
*
|
2096
|
+
* @see AbstractTransport
|
2097
|
+
*/
|
2098
|
+
function WSTransport(name, priority, key, options) {
|
2099
|
+
Pusher.AbstractTransport.call(this, name, priority, key, options);
|
2100
|
+
}
|
2101
|
+
var prototype = WSTransport.prototype;
|
2102
|
+
Pusher.Util.extend(prototype, Pusher.AbstractTransport.prototype);
|
2103
|
+
|
2104
|
+
/** Creates a new instance of WSTransport.
|
2105
|
+
*
|
2106
|
+
* @param {String} key
|
2107
|
+
* @param {Object} options
|
2108
|
+
* @return {WSTransport}
|
2109
|
+
*/
|
2110
|
+
WSTransport.createConnection = function(name, priority, key, options) {
|
2111
|
+
return new WSTransport(name, priority, key, options);
|
2112
|
+
};
|
393
2113
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
self.emit('online', null);
|
401
|
-
}, false);
|
402
|
-
window.addEventListener("offline", function() {
|
403
|
-
self.emit('offline', null);
|
404
|
-
}, false);
|
405
|
-
}
|
2114
|
+
/** Checks whether the browser supports WebSockets in any form.
|
2115
|
+
*
|
2116
|
+
* @returns {Boolean} true if browser supports WebSockets
|
2117
|
+
*/
|
2118
|
+
WSTransport.isSupported = function() {
|
2119
|
+
return window.WebSocket !== undefined || window.MozWebSocket !== undefined;
|
406
2120
|
};
|
407
2121
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
if (window.navigator.onLine === undefined) {
|
413
|
-
return true;
|
414
|
-
} else {
|
415
|
-
return window.navigator.onLine;
|
416
|
-
}
|
2122
|
+
/** @protected */
|
2123
|
+
prototype.createSocket = function(url) {
|
2124
|
+
var constructor = window.WebSocket || window.MozWebSocket;
|
2125
|
+
return new constructor(url);
|
417
2126
|
};
|
418
2127
|
|
419
|
-
|
2128
|
+
/** @protected */
|
2129
|
+
prototype.getQueryString = function() {
|
2130
|
+
return Pusher.AbstractTransport.prototype.getQueryString.call(this) +
|
2131
|
+
"&flash=false";
|
2132
|
+
};
|
420
2133
|
|
421
|
-
|
2134
|
+
Pusher.WSTransport = WSTransport;
|
422
2135
|
}).call(this);
|
423
2136
|
|
424
2137
|
;(function() {
|
425
|
-
|
2138
|
+
function AssistantToTheTransportManager(manager, transport, options) {
|
2139
|
+
this.manager = manager;
|
2140
|
+
this.transport = transport;
|
2141
|
+
this.minPingDelay = options.minPingDelay || 10000;
|
2142
|
+
this.maxPingDelay = options.maxPingDelay || Pusher.activity_timeout;
|
2143
|
+
this.pingDelay = null;
|
2144
|
+
}
|
2145
|
+
var prototype = AssistantToTheTransportManager.prototype;
|
426
2146
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
'open': ['connected', 'permanentlyClosing', 'impermanentlyClosing', 'waiting'],
|
432
|
-
'connected': ['permanentlyClosing', 'waiting'],
|
433
|
-
'impermanentlyClosing': ['waiting', 'permanentlyClosing'],
|
434
|
-
'permanentlyClosing': ['permanentlyClosed'],
|
435
|
-
'permanentlyClosed': ['waiting', 'failed'],
|
436
|
-
'failed': ['permanentlyClosed']
|
437
|
-
};
|
2147
|
+
prototype.createConnection = function(name, priority, key, options) {
|
2148
|
+
var connection = this.transport.createConnection(
|
2149
|
+
name, priority, key, options
|
2150
|
+
);
|
438
2151
|
|
2152
|
+
var self = this;
|
2153
|
+
var openTimestamp = null;
|
2154
|
+
var pingTimer = null;
|
439
2155
|
|
440
|
-
|
441
|
-
|
2156
|
+
var onOpen = function() {
|
2157
|
+
connection.unbind("open", onOpen);
|
442
2158
|
|
443
|
-
|
444
|
-
|
2159
|
+
openTimestamp = Pusher.Util.now();
|
2160
|
+
if (self.pingDelay) {
|
2161
|
+
pingTimer = setInterval(function() {
|
2162
|
+
if (pingTimer) {
|
2163
|
+
connection.requestPing();
|
2164
|
+
}
|
2165
|
+
}, self.pingDelay);
|
2166
|
+
}
|
445
2167
|
|
446
|
-
|
447
|
-
|
2168
|
+
connection.bind("closed", onClosed);
|
2169
|
+
};
|
2170
|
+
var onClosed = function(closeEvent) {
|
2171
|
+
connection.unbind("closed", onClosed);
|
2172
|
+
if (pingTimer) {
|
2173
|
+
clearInterval(pingTimer);
|
2174
|
+
pingTimer = null;
|
2175
|
+
}
|
448
2176
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
connection.openTimeout = 7000;
|
453
|
-
} else { // SockJS
|
454
|
-
connection.openTimeout = 6000;
|
455
|
-
}
|
456
|
-
connection.connectedTimeout = 2000;
|
457
|
-
connection.connectionSecure = connection.compulsorySecure;
|
458
|
-
connection.failedAttempts = 0;
|
459
|
-
}
|
2177
|
+
if (closeEvent.wasClean) {
|
2178
|
+
return;
|
2179
|
+
}
|
460
2180
|
|
461
|
-
|
462
|
-
|
2181
|
+
if (openTimestamp) {
|
2182
|
+
var lifespan = Pusher.Util.now() - openTimestamp;
|
2183
|
+
if (lifespan < 2 * self.maxPingDelay) {
|
2184
|
+
self.manager.reportDeath();
|
2185
|
+
self.pingDelay = Math.max(lifespan / 2, self.minPingDelay);
|
2186
|
+
}
|
2187
|
+
}
|
2188
|
+
};
|
463
2189
|
|
464
|
-
|
2190
|
+
connection.bind("open", onOpen);
|
2191
|
+
return connection;
|
2192
|
+
};
|
465
2193
|
|
466
|
-
|
467
|
-
this.
|
2194
|
+
prototype.isSupported = function() {
|
2195
|
+
return this.manager.isAlive() && this.transport.isSupported();
|
2196
|
+
};
|
468
2197
|
|
469
|
-
|
2198
|
+
Pusher.AssistantToTheTransportManager = AssistantToTheTransportManager;
|
2199
|
+
}).call(this);
|
470
2200
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
2201
|
+
;(function() {
|
2202
|
+
function TransportManager(options) {
|
2203
|
+
this.options = options || {};
|
2204
|
+
this.livesLeft = this.options.lives || Infinity;
|
2205
|
+
}
|
2206
|
+
var prototype = TransportManager.prototype;
|
2207
|
+
|
2208
|
+
prototype.getAssistant = function(transport) {
|
2209
|
+
return new Pusher.AssistantToTheTransportManager(this, transport, {
|
2210
|
+
minPingDelay: this.options.minPingDelay,
|
2211
|
+
maxPingDelay: this.options.maxPingDelay
|
476
2212
|
});
|
2213
|
+
};
|
477
2214
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
// having two sockets hanging around.
|
482
|
-
self.socket.onclose = undefined;
|
483
|
-
self.socket.onmessage = undefined;
|
484
|
-
self.socket.onerror = undefined;
|
485
|
-
self.socket.onopen = undefined;
|
2215
|
+
prototype.isAlive = function() {
|
2216
|
+
return this.livesLeft > 0;
|
2217
|
+
};
|
486
2218
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
2219
|
+
prototype.reportDeath = function() {
|
2220
|
+
this.livesLeft -= 1;
|
2221
|
+
};
|
2222
|
+
|
2223
|
+
Pusher.TransportManager = TransportManager;
|
2224
|
+
}).call(this);
|
492
2225
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
2226
|
+
;(function() {
|
2227
|
+
var StrategyBuilder = {
|
2228
|
+
/** Transforms a JSON scheme to a strategy tree.
|
2229
|
+
*
|
2230
|
+
* @param {Array} scheme JSON strategy scheme
|
2231
|
+
* @param {Object} options a hash of symbols to be included in the scheme
|
2232
|
+
* @returns {Strategy} strategy tree that's represented by the scheme
|
2233
|
+
*/
|
2234
|
+
build: function(scheme, options) {
|
2235
|
+
var context = Pusher.Util.extend({}, globalContext, options);
|
2236
|
+
return evaluate(scheme, context)[1].strategy;
|
2237
|
+
}
|
2238
|
+
};
|
497
2239
|
|
498
|
-
|
499
|
-
|
500
|
-
|
2240
|
+
var transports = {
|
2241
|
+
ws: Pusher.WSTransport,
|
2242
|
+
flash: Pusher.FlashTransport,
|
2243
|
+
sockjs: Pusher.SockJSTransport
|
2244
|
+
};
|
501
2245
|
|
502
|
-
|
503
|
-
},
|
2246
|
+
// DSL bindings
|
504
2247
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
updateState('unavailable');
|
511
|
-
// Delay 10s between connection attempts on entering unavailable
|
512
|
-
self.connectionWait = 10000;
|
513
|
-
}
|
2248
|
+
function returnWithOriginalContext(f) {
|
2249
|
+
return function(context) {
|
2250
|
+
return [f.apply(this, arguments), context];
|
2251
|
+
};
|
2252
|
+
}
|
514
2253
|
|
515
|
-
|
516
|
-
|
517
|
-
|
2254
|
+
var globalContext = {
|
2255
|
+
def: function(context, name, value) {
|
2256
|
+
if (context[name] !== undefined) {
|
2257
|
+
throw "Redefining symbol " + name;
|
2258
|
+
}
|
2259
|
+
context[name] = value;
|
2260
|
+
return [undefined, context];
|
2261
|
+
},
|
518
2262
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
2263
|
+
def_transport: function(context, name, type, priority, options, manager) {
|
2264
|
+
var transportClass = transports[type];
|
2265
|
+
if (!transportClass) {
|
2266
|
+
throw new Pusher.Errors.UnsupportedTransport(type);
|
2267
|
+
}
|
2268
|
+
var transportOptions = Pusher.Util.extend({}, {
|
2269
|
+
key: context.key,
|
2270
|
+
encrypted: context.encrypted,
|
2271
|
+
timeline: context.timeline,
|
2272
|
+
disableFlash: context.disableFlash
|
2273
|
+
}, options);
|
2274
|
+
if (manager) {
|
2275
|
+
transportClass = manager.getAssistant(transportClass);
|
2276
|
+
}
|
2277
|
+
var transport = new Pusher.TransportStrategy(
|
2278
|
+
name, priority, transportClass, transportOptions
|
2279
|
+
);
|
2280
|
+
var newContext = context.def(context, name, transport)[1];
|
2281
|
+
newContext.transports = context.transports || {};
|
2282
|
+
newContext.transports[name] = transport;
|
2283
|
+
return [undefined, newContext];
|
2284
|
+
},
|
527
2285
|
|
528
|
-
|
529
|
-
|
530
|
-
|
2286
|
+
transport_manager: returnWithOriginalContext(function(_, options) {
|
2287
|
+
return new Pusher.TransportManager(options);
|
2288
|
+
}),
|
531
2289
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
self._machine.transition('waiting');
|
537
|
-
updateState('unavailable');
|
2290
|
+
sequential: returnWithOriginalContext(function(_, options) {
|
2291
|
+
var strategies = Array.prototype.slice.call(arguments, 2);
|
2292
|
+
return new Pusher.SequentialStrategy(strategies, options);
|
2293
|
+
}),
|
538
2294
|
|
539
|
-
|
540
|
-
|
2295
|
+
cached: returnWithOriginalContext(function(context, ttl, strategy){
|
2296
|
+
return new Pusher.CachedStrategy(strategy, context.transports, {
|
2297
|
+
ttl: ttl,
|
2298
|
+
timeline: context.timeline
|
2299
|
+
});
|
2300
|
+
}),
|
541
2301
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
2302
|
+
first_connected: returnWithOriginalContext(function(_, strategy) {
|
2303
|
+
return new Pusher.FirstConnectedStrategy(strategy);
|
2304
|
+
}),
|
2305
|
+
|
2306
|
+
best_connected_ever: returnWithOriginalContext(function() {
|
2307
|
+
var strategies = Array.prototype.slice.call(arguments, 1);
|
2308
|
+
return new Pusher.BestConnectedEverStrategy(strategies);
|
2309
|
+
}),
|
2310
|
+
|
2311
|
+
delayed: returnWithOriginalContext(function(_, delay, strategy) {
|
2312
|
+
return new Pusher.DelayedStrategy(strategy, { delay: delay });
|
2313
|
+
}),
|
2314
|
+
|
2315
|
+
"if": returnWithOriginalContext(function(_, test, trueBranch, falseBranch) {
|
2316
|
+
return new Pusher.IfStrategy(test, trueBranch, falseBranch);
|
2317
|
+
}),
|
2318
|
+
|
2319
|
+
is_supported: returnWithOriginalContext(function(_, strategy) {
|
2320
|
+
return function() {
|
2321
|
+
return strategy.isSupported();
|
2322
|
+
};
|
2323
|
+
})
|
2324
|
+
};
|
562
2325
|
|
563
|
-
|
564
|
-
self.socket.onerror = ws_onError;
|
2326
|
+
// DSL interpreter
|
565
2327
|
|
566
|
-
|
567
|
-
|
568
|
-
|
2328
|
+
function isSymbol(expression) {
|
2329
|
+
return (typeof expression === "string") && expression.charAt(0) === ":";
|
2330
|
+
}
|
569
2331
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
},
|
2332
|
+
function getSymbolValue(expression, context) {
|
2333
|
+
return context[expression.slice(1)];
|
2334
|
+
}
|
574
2335
|
|
575
|
-
|
576
|
-
|
2336
|
+
function evaluateListOfExpressions(expressions, context) {
|
2337
|
+
if (expressions.length === 0) {
|
2338
|
+
return [[], context];
|
2339
|
+
}
|
2340
|
+
var head = evaluate(expressions[0], context);
|
2341
|
+
var tail = evaluateListOfExpressions(expressions.slice(1), head[1]);
|
2342
|
+
return [[head[0]].concat(tail[0]), tail[1]];
|
2343
|
+
}
|
577
2344
|
|
578
|
-
|
579
|
-
|
2345
|
+
function evaluateString(expression, context) {
|
2346
|
+
if (!isSymbol(expression)) {
|
2347
|
+
return [expression, context];
|
2348
|
+
}
|
2349
|
+
var value = getSymbolValue(expression, context);
|
2350
|
+
if (value === undefined) {
|
2351
|
+
throw "Undefined symbol " + expression;
|
2352
|
+
}
|
2353
|
+
return [value, context];
|
2354
|
+
}
|
580
2355
|
|
581
|
-
|
582
|
-
|
2356
|
+
function evaluateArray(expression, context) {
|
2357
|
+
if (isSymbol(expression[0])) {
|
2358
|
+
var f = getSymbolValue(expression[0], context);
|
2359
|
+
if (expression.length > 1) {
|
2360
|
+
if (typeof f !== "function") {
|
2361
|
+
throw "Calling non-function " + expression[0];
|
2362
|
+
}
|
2363
|
+
var args = [Pusher.Util.extend({}, context)].concat(
|
2364
|
+
Pusher.Util.map(expression.slice(1), function(arg) {
|
2365
|
+
return evaluate(arg, Pusher.Util.extend({}, context))[0];
|
2366
|
+
})
|
2367
|
+
);
|
2368
|
+
return f.apply(this, args);
|
2369
|
+
} else {
|
2370
|
+
return [f, context];
|
2371
|
+
}
|
2372
|
+
} else {
|
2373
|
+
return evaluateListOfExpressions(expression, context);
|
2374
|
+
}
|
2375
|
+
}
|
583
2376
|
|
584
|
-
|
585
|
-
|
2377
|
+
function evaluate(expression, context) {
|
2378
|
+
var expressionType = typeof expression;
|
2379
|
+
if (typeof expression === "string") {
|
2380
|
+
return evaluateString(expression, context);
|
2381
|
+
} else if (typeof expression === "object") {
|
2382
|
+
if (expression instanceof Array && expression.length > 0) {
|
2383
|
+
return evaluateArray(expression, context);
|
2384
|
+
}
|
2385
|
+
}
|
2386
|
+
return [expression, context];
|
2387
|
+
}
|
586
2388
|
|
587
|
-
|
588
|
-
|
589
|
-
self.socket.onerror = ws_onError;
|
590
|
-
self.socket.onclose = transitionToWaiting;
|
2389
|
+
Pusher.StrategyBuilder = StrategyBuilder;
|
2390
|
+
}).call(this);
|
591
2391
|
|
592
|
-
|
593
|
-
|
594
|
-
|
2392
|
+
;(function() {
|
2393
|
+
/**
|
2394
|
+
* Provides Pusher protocol interface for transports.
|
2395
|
+
*
|
2396
|
+
* Emits following events:
|
2397
|
+
* - connected - after establishing connection and receiving a socket id
|
2398
|
+
* - message - on received messages
|
2399
|
+
* - ping - on ping requests
|
2400
|
+
* - pong - on pong responses
|
2401
|
+
* - error - when the transport emits an error
|
2402
|
+
* - closed - after closing the transport
|
2403
|
+
* - ssl_only - after trying to connect without ssl to a ssl-only app
|
2404
|
+
* - retry - when closed connection should be retried immediately
|
2405
|
+
* - backoff - when closed connection should be retried with a delay
|
2406
|
+
* - refused - when closed connection should not be retried
|
2407
|
+
*
|
2408
|
+
* @param {AbstractTransport} transport
|
2409
|
+
*/
|
2410
|
+
function ProtocolWrapper(transport) {
|
2411
|
+
Pusher.EventsDispatcher.call(this);
|
2412
|
+
this.transport = transport;
|
2413
|
+
this.bindListeners();
|
2414
|
+
}
|
2415
|
+
var prototype = ProtocolWrapper.prototype;
|
595
2416
|
|
596
|
-
|
597
|
-
clearTimeout(self._openTimer);
|
598
|
-
self.socket.onmessage = undefined; // unbind to avoid messages that are no longer relevant
|
599
|
-
},
|
2417
|
+
Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
|
600
2418
|
|
601
|
-
|
602
|
-
|
603
|
-
|
2419
|
+
/** Returns whether used transport handles ping/pong by itself
|
2420
|
+
*
|
2421
|
+
* @returns {Boolean} true if ping is handled by the transport
|
2422
|
+
*/
|
2423
|
+
prototype.supportsPing = function() {
|
2424
|
+
return this.transport.supportsPing();
|
2425
|
+
};
|
604
2426
|
|
605
|
-
|
606
|
-
|
607
|
-
|
2427
|
+
/** Sends raw data.
|
2428
|
+
*
|
2429
|
+
* @param {String} data
|
2430
|
+
*/
|
2431
|
+
prototype.send = function(data) {
|
2432
|
+
return this.transport.send(data);
|
2433
|
+
};
|
608
2434
|
|
609
|
-
|
610
|
-
|
2435
|
+
/** Sends an event.
|
2436
|
+
*
|
2437
|
+
* @param {String} name
|
2438
|
+
* @param {String} data
|
2439
|
+
* @param {String} [channel]
|
2440
|
+
* @returns {Boolean} whether message was sent or not
|
2441
|
+
*/
|
2442
|
+
prototype.send_event = function(name, data, channel) {
|
2443
|
+
var payload = {
|
2444
|
+
event: name,
|
2445
|
+
data: data
|
2446
|
+
};
|
2447
|
+
if (channel) {
|
2448
|
+
payload.channel = channel;
|
2449
|
+
}
|
611
2450
|
|
612
|
-
|
613
|
-
|
614
|
-
|
2451
|
+
Pusher.debug('Event sent', payload);
|
2452
|
+
return this.send(JSON.stringify(payload));
|
2453
|
+
};
|
615
2454
|
|
616
|
-
|
617
|
-
|
2455
|
+
/** Closes the transport. */
|
2456
|
+
prototype.close = function() {
|
2457
|
+
this.transport.close();
|
2458
|
+
};
|
618
2459
|
|
619
|
-
|
620
|
-
|
2460
|
+
/** @private */
|
2461
|
+
prototype.bindListeners = function() {
|
2462
|
+
var self = this;
|
621
2463
|
|
622
|
-
|
623
|
-
|
624
|
-
|
2464
|
+
var onMessageOpen = function(message) {
|
2465
|
+
message = self.parseMessage(message);
|
2466
|
+
|
2467
|
+
if (message !== undefined) {
|
2468
|
+
if (message.event === 'pusher:connection_established') {
|
2469
|
+
self.id = message.data.socket_id;
|
2470
|
+
self.transport.unbind("message", onMessageOpen);
|
2471
|
+
self.transport.bind("message", onMessageConnected);
|
2472
|
+
self.transport.bind("ping_request", onPingRequest);
|
2473
|
+
self.emit("connected", self.id);
|
2474
|
+
} else if (message.event === "pusher:error") {
|
2475
|
+
// From protocol 6 close codes are sent only once, so this only
|
2476
|
+
// happens when connection does not support close codes
|
2477
|
+
self.handleCloseCode(message.data.code, message.data.message);
|
2478
|
+
self.transport.close();
|
2479
|
+
}
|
2480
|
+
}
|
2481
|
+
};
|
2482
|
+
var onMessageConnected = function(message) {
|
2483
|
+
message = self.parseMessage(message);
|
625
2484
|
|
626
|
-
|
627
|
-
|
628
|
-
updateState('disconnected');
|
629
|
-
},
|
2485
|
+
if (message !== undefined) {
|
2486
|
+
Pusher.debug('Event recd', message);
|
630
2487
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
2488
|
+
switch (message.event) {
|
2489
|
+
case 'pusher:error':
|
2490
|
+
self.emit('error', { type: 'PusherError', data: message.data });
|
2491
|
+
break;
|
2492
|
+
case 'pusher:ping':
|
2493
|
+
self.emit("ping");
|
2494
|
+
break;
|
2495
|
+
case 'pusher:pong':
|
2496
|
+
self.emit("pong");
|
2497
|
+
break;
|
635
2498
|
}
|
636
|
-
|
2499
|
+
self.emit('message', message);
|
2500
|
+
}
|
2501
|
+
};
|
2502
|
+
var onPingRequest = function() {
|
2503
|
+
self.emit("ping_request");
|
2504
|
+
};
|
2505
|
+
var onError = function(error) {
|
2506
|
+
self.emit("error", { type: "WebSocketError", error: error });
|
2507
|
+
};
|
2508
|
+
var onClosed = function(error) {
|
2509
|
+
if (error && error.code) {
|
2510
|
+
self.handleCloseCode(error.code, error.reason);
|
2511
|
+
}
|
2512
|
+
self.transport.unbind("message", onMessageOpen);
|
2513
|
+
self.transport.unbind("message", onMessageConnected);
|
2514
|
+
self.transport.unbind("ping_request", onPingRequest);
|
2515
|
+
self.transport.unbind("error", onError);
|
2516
|
+
self.transport.unbind("closed", onClosed);
|
2517
|
+
self.transport = null;
|
2518
|
+
self.emit("closed");
|
2519
|
+
};
|
637
2520
|
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
self._machine.transition('permanentlyClosed');
|
643
|
-
};
|
2521
|
+
this.transport.bind("message", onMessageOpen);
|
2522
|
+
this.transport.bind("error", onError);
|
2523
|
+
this.transport.bind("closed", onClosed);
|
2524
|
+
};
|
644
2525
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
2526
|
+
/** @private */
|
2527
|
+
prototype.parseMessage = function(message) {
|
2528
|
+
try {
|
2529
|
+
var params = JSON.parse(message.data);
|
2530
|
+
|
2531
|
+
if (typeof params.data === 'string') {
|
2532
|
+
try {
|
2533
|
+
params.data = JSON.parse(params.data);
|
2534
|
+
} catch (e) {
|
2535
|
+
if (!(e instanceof SyntaxError)) {
|
2536
|
+
throw e;
|
2537
|
+
}
|
649
2538
|
}
|
650
|
-
}
|
2539
|
+
}
|
651
2540
|
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
2541
|
+
return params;
|
2542
|
+
} catch (e) {
|
2543
|
+
this.emit(
|
2544
|
+
'error', { type: 'MessageParseError', error: e, data: message.data}
|
2545
|
+
);
|
2546
|
+
}
|
2547
|
+
};
|
656
2548
|
|
657
|
-
|
658
|
-
|
2549
|
+
/** @private */
|
2550
|
+
prototype.handleCloseCode = function(code, message) {
|
2551
|
+
this.emit(
|
2552
|
+
'error', { type: 'PusherError', data: { code: code, message: message } }
|
2553
|
+
);
|
2554
|
+
|
2555
|
+
// See:
|
2556
|
+
// 1. https://developer.mozilla.org/en-US/docs/WebSockets/WebSockets_reference/CloseEvent
|
2557
|
+
// 2. http://pusher.com/docs/pusher_protocol
|
2558
|
+
if (code < 4000) {
|
2559
|
+
// ignore 1000 CLOSE_NORMAL, 1001 CLOSE_GOING_AWAY,
|
2560
|
+
// 1005 CLOSE_NO_STATUS, 1006 CLOSE_ABNORMAL
|
2561
|
+
// ignore 1007...3999
|
2562
|
+
// handle 1002 CLOSE_PROTOCOL_ERROR, 1003 CLOSE_UNSUPPORTED,
|
2563
|
+
// 1004 CLOSE_TOO_LARGE
|
2564
|
+
if (code >= 1002 && code <= 1004) {
|
2565
|
+
this.emit("backoff");
|
659
2566
|
}
|
660
|
-
})
|
2567
|
+
} else if (code === 4000) {
|
2568
|
+
this.emit("ssl_only");
|
2569
|
+
} else if (code < 4100) {
|
2570
|
+
this.emit("refused");
|
2571
|
+
} else if (code < 4200) {
|
2572
|
+
this.emit("backoff");
|
2573
|
+
} else if (code < 4300) {
|
2574
|
+
this.emit("retry");
|
2575
|
+
} else {
|
2576
|
+
// unknown error
|
2577
|
+
this.emit("refused");
|
2578
|
+
}
|
2579
|
+
};
|
661
2580
|
|
662
|
-
|
663
|
-
|
2581
|
+
Pusher.ProtocolWrapper = ProtocolWrapper;
|
2582
|
+
}).call(this);
|
664
2583
|
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
2584
|
+
;(function() {
|
2585
|
+
/** Manages connection to Pusher.
|
2586
|
+
*
|
2587
|
+
* Uses a strategy (currently only default), timers and network availability
|
2588
|
+
* info to establish a connection and export its state. In case of failures,
|
2589
|
+
* manages reconnection attempts.
|
2590
|
+
*
|
2591
|
+
* Exports state changes as following events:
|
2592
|
+
* - "state_change", { previous: p, current: state }
|
2593
|
+
* - state
|
2594
|
+
*
|
2595
|
+
* States:
|
2596
|
+
* - initialized - initial state, never transitioned to
|
2597
|
+
* - connecting - connection is being established
|
2598
|
+
* - connected - connection has been fully established
|
2599
|
+
* - disconnected - on requested disconnection or before reconnecting
|
2600
|
+
* - unavailable - after connection timeout or when there's no network
|
2601
|
+
*
|
2602
|
+
* Options:
|
2603
|
+
* - unavailableTimeout - time to transition to unavailable state
|
2604
|
+
* - activityTimeout - time after which ping message should be sent
|
2605
|
+
* - pongTimeout - time for Pusher to respond with pong before reconnecting
|
2606
|
+
*
|
2607
|
+
* @param {String} key application key
|
2608
|
+
* @param {Object} options
|
2609
|
+
*/
|
2610
|
+
function ConnectionManager(key, options) {
|
2611
|
+
Pusher.EventsDispatcher.call(this);
|
669
2612
|
|
670
|
-
|
671
|
-
|
672
|
-
|
2613
|
+
this.key = key;
|
2614
|
+
this.options = options || {};
|
2615
|
+
this.state = "initialized";
|
2616
|
+
this.connection = null;
|
2617
|
+
this.encrypted = !!options.encrypted;
|
2618
|
+
this.timeline = this.options.getTimeline();
|
673
2619
|
|
674
|
-
|
675
|
-
|
676
|
-
|
2620
|
+
var self = this;
|
2621
|
+
|
2622
|
+
Pusher.Network.bind("online", function() {
|
2623
|
+
if (self.state === "unavailable") {
|
2624
|
+
self.connect();
|
2625
|
+
}
|
2626
|
+
});
|
2627
|
+
Pusher.Network.bind("offline", function() {
|
2628
|
+
if (self.shouldRetry()) {
|
2629
|
+
self.disconnect();
|
2630
|
+
self.updateState("unavailable");
|
677
2631
|
}
|
2632
|
+
});
|
678
2633
|
|
679
|
-
|
2634
|
+
var sendTimeline = function() {
|
2635
|
+
if (self.timelineSender) {
|
2636
|
+
self.timelineSender.send(function() {});
|
2637
|
+
}
|
2638
|
+
};
|
2639
|
+
this.bind("connected", sendTimeline);
|
2640
|
+
setInterval(sendTimeline, 60000);
|
2641
|
+
}
|
2642
|
+
var prototype = ConnectionManager.prototype;
|
2643
|
+
|
2644
|
+
Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
|
2645
|
+
|
2646
|
+
/** Establishes a connection to Pusher.
|
2647
|
+
*
|
2648
|
+
* Does nothing when connection is already established. See top-level doc
|
2649
|
+
* to find events emitted on connection attempts.
|
2650
|
+
*/
|
2651
|
+
prototype.connect = function() {
|
2652
|
+
if (this.connection) {
|
2653
|
+
return;
|
2654
|
+
}
|
2655
|
+
if (this.state === "connecting") {
|
2656
|
+
return;
|
680
2657
|
}
|
681
2658
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
2659
|
+
var strategy = this.options.getStrategy({
|
2660
|
+
key: this.key,
|
2661
|
+
timeline: this.timeline,
|
2662
|
+
encrypted: this.encrypted
|
2663
|
+
});
|
687
2664
|
|
688
|
-
|
2665
|
+
if (!strategy.isSupported()) {
|
2666
|
+
this.updateState("failed");
|
2667
|
+
return;
|
689
2668
|
}
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
var path = '/app/' + key + '?protocol=5&client=js'
|
694
|
-
+ '&version=' + Pusher.VERSION
|
695
|
-
+ '&flash=' + flash;
|
696
|
-
return path;
|
2669
|
+
if (Pusher.Network.isOnline() === false) {
|
2670
|
+
this.updateState("unavailable");
|
2671
|
+
return;
|
697
2672
|
}
|
698
2673
|
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
2674
|
+
this.updateState("connecting");
|
2675
|
+
this.timelineSender = this.options.getTimelineSender(
|
2676
|
+
this.timeline,
|
2677
|
+
{ encrypted: this.encrypted },
|
2678
|
+
this
|
2679
|
+
);
|
703
2680
|
|
704
|
-
|
705
|
-
|
2681
|
+
var self = this;
|
2682
|
+
var callback = function(error, transport) {
|
2683
|
+
if (error) {
|
2684
|
+
self.runner = strategy.connect(0, callback);
|
2685
|
+
} else {
|
2686
|
+
// we don't support switching connections yet
|
2687
|
+
self.runner.abort();
|
2688
|
+
self.setConnection(self.wrapTransport(transport));
|
2689
|
+
}
|
2690
|
+
};
|
2691
|
+
this.runner = strategy.connect(0, callback);
|
706
2692
|
|
707
|
-
|
708
|
-
|
709
|
-
|
2693
|
+
this.setUnavailableTimer();
|
2694
|
+
};
|
2695
|
+
|
2696
|
+
/** Sends raw data.
|
2697
|
+
*
|
2698
|
+
* @param {String} data
|
2699
|
+
*/
|
2700
|
+
prototype.send = function(data) {
|
2701
|
+
if (this.connection) {
|
2702
|
+
return this.connection.send(data);
|
2703
|
+
} else {
|
2704
|
+
return false;
|
710
2705
|
}
|
2706
|
+
};
|
711
2707
|
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
2708
|
+
/** Sends an event.
|
2709
|
+
*
|
2710
|
+
* @param {String} name
|
2711
|
+
* @param {String} data
|
2712
|
+
* @param {String} [channel]
|
2713
|
+
* @returns {Boolean} whether message was sent or not
|
2714
|
+
*/
|
2715
|
+
prototype.send_event = function(name, data, channel) {
|
2716
|
+
if (this.connection) {
|
2717
|
+
return this.connection.send_event(name, data, channel);
|
2718
|
+
} else {
|
2719
|
+
return false;
|
724
2720
|
}
|
2721
|
+
};
|
725
2722
|
|
726
|
-
|
727
|
-
|
2723
|
+
/** Closes the connection. */
|
2724
|
+
prototype.disconnect = function() {
|
2725
|
+
if (this.runner) {
|
2726
|
+
this.runner.abort();
|
2727
|
+
}
|
2728
|
+
this.clearRetryTimer();
|
2729
|
+
this.clearUnavailableTimer();
|
2730
|
+
this.stopActivityCheck();
|
2731
|
+
this.updateState("disconnected");
|
2732
|
+
// we're in disconnected state, so closing will not cause reconnecting
|
2733
|
+
if (this.connection) {
|
2734
|
+
this.connection.close();
|
2735
|
+
this.connection = null;
|
728
2736
|
}
|
2737
|
+
};
|
729
2738
|
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
var delay = self.connectionWait;
|
737
|
-
if (delay === 0) {
|
738
|
-
if (self.connectedAt) {
|
739
|
-
var t = 1000;
|
740
|
-
var connectedFor = new Date().getTime() - self.connectedAt;
|
741
|
-
if (connectedFor < t) {
|
742
|
-
delay = t - connectedFor;
|
743
|
-
}
|
744
|
-
}
|
745
|
-
}
|
746
|
-
return delay;
|
747
|
-
}
|
748
|
-
|
749
|
-
/*-----------------------------------------------
|
750
|
-
WebSocket Callbacks
|
751
|
-
-----------------------------------------------*/
|
752
|
-
|
753
|
-
function handleCloseCode(code, message) {
|
754
|
-
// first inform the end-developer of this error
|
755
|
-
self.emit('error', {type: 'PusherError', data: {code: code, message: message}});
|
756
|
-
|
757
|
-
if (code === 4000) {
|
758
|
-
// SSL only app
|
759
|
-
self.compulsorySecure = true;
|
760
|
-
self.connectionSecure = true;
|
761
|
-
self.options.encrypted = true;
|
762
|
-
|
763
|
-
TransitionToImpermanentlyClosing();
|
764
|
-
} else if (code < 4100) {
|
765
|
-
// Permentently close connection
|
766
|
-
self._machine.transition('permanentlyClosing')
|
767
|
-
} else if (code < 4200) {
|
768
|
-
// Backoff before reconnecting
|
769
|
-
self.connectionWait = 1000;
|
770
|
-
self._machine.transition('waiting')
|
771
|
-
} else if (code < 4300) {
|
772
|
-
// Reconnect immediately
|
773
|
-
TransitionToImpermanentlyClosing();
|
774
|
-
} else {
|
775
|
-
// Unknown error
|
776
|
-
self._machine.transition('permanentlyClosing')
|
2739
|
+
/** @private */
|
2740
|
+
prototype.retryIn = function(delay) {
|
2741
|
+
var self = this;
|
2742
|
+
this.retryTimer = setTimeout(function() {
|
2743
|
+
if (self.retryTimer === null) {
|
2744
|
+
return;
|
777
2745
|
}
|
2746
|
+
self.retryTimer = null;
|
2747
|
+
self.disconnect();
|
2748
|
+
self.connect();
|
2749
|
+
}, delay || 0);
|
2750
|
+
};
|
2751
|
+
|
2752
|
+
/** @private */
|
2753
|
+
prototype.clearRetryTimer = function() {
|
2754
|
+
if (this.retryTimer) {
|
2755
|
+
clearTimeout(this.retryTimer);
|
2756
|
+
this.retryTimer = null;
|
778
2757
|
}
|
2758
|
+
};
|
779
2759
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
handleCloseCode(params.data.code, params.data.message)
|
787
|
-
}
|
2760
|
+
/** @private */
|
2761
|
+
prototype.setUnavailableTimer = function() {
|
2762
|
+
var self = this;
|
2763
|
+
this.unavailableTimer = setTimeout(function() {
|
2764
|
+
if (!self.unavailableTimer) {
|
2765
|
+
return;
|
788
2766
|
}
|
2767
|
+
self.updateState("unavailable");
|
2768
|
+
self.unavailableTimer = null;
|
2769
|
+
}, this.options.unavailableTimeout);
|
2770
|
+
};
|
2771
|
+
|
2772
|
+
/** @private */
|
2773
|
+
prototype.clearUnavailableTimer = function() {
|
2774
|
+
if (this.unavailableTimer) {
|
2775
|
+
clearTimeout(this.unavailableTimer);
|
2776
|
+
this.unavailableTimer = null;
|
789
2777
|
}
|
2778
|
+
};
|
790
2779
|
|
791
|
-
|
792
|
-
|
2780
|
+
/** @private */
|
2781
|
+
prototype.resetActivityCheck = function() {
|
2782
|
+
this.stopActivityCheck();
|
2783
|
+
// send ping after inactivity
|
2784
|
+
if (!this.connection.supportsPing()) {
|
2785
|
+
var self = this;
|
2786
|
+
this.activityTimer = setTimeout(function() {
|
2787
|
+
self.send_event('pusher:ping', {});
|
2788
|
+
// wait for pong response
|
2789
|
+
self.activityTimer = setTimeout(function() {
|
2790
|
+
self.connection.close();
|
2791
|
+
}, (self.options.pongTimeout));
|
2792
|
+
}, (this.options.activityTimeout));
|
2793
|
+
}
|
2794
|
+
};
|
793
2795
|
|
794
|
-
|
795
|
-
|
796
|
-
|
2796
|
+
/** @private */
|
2797
|
+
prototype.stopActivityCheck = function() {
|
2798
|
+
if (this.activityTimer) {
|
2799
|
+
clearTimeout(this.activityTimer);
|
2800
|
+
this.activityTimer = null;
|
2801
|
+
}
|
2802
|
+
};
|
797
2803
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
break;
|
802
|
-
case 'pusher:ping':
|
803
|
-
self.send_event('pusher:pong', {})
|
804
|
-
break;
|
805
|
-
}
|
2804
|
+
/** @private */
|
2805
|
+
prototype.setConnection = function(connection) {
|
2806
|
+
this.connection = connection;
|
806
2807
|
|
807
|
-
|
2808
|
+
var self = this;
|
2809
|
+
var onConnected = function(id) {
|
2810
|
+
self.clearUnavailableTimer();
|
2811
|
+
self.socket_id = id;
|
2812
|
+
self.updateState("connected");
|
2813
|
+
self.resetActivityCheck();
|
2814
|
+
};
|
2815
|
+
var onMessage = function(message) {
|
2816
|
+
// includes pong messages from server
|
2817
|
+
self.resetActivityCheck();
|
2818
|
+
self.emit('message', message);
|
2819
|
+
};
|
2820
|
+
var onPing = function() {
|
2821
|
+
self.send_event('pusher:pong', {});
|
2822
|
+
};
|
2823
|
+
var onPingRequest = function() {
|
2824
|
+
self.send_event('pusher:ping', {});
|
2825
|
+
};
|
2826
|
+
var onError = function(error) {
|
2827
|
+
// just emit error to user - socket will already be closed by browser
|
2828
|
+
self.emit("error", { type: "WebSocketError", error: error });
|
2829
|
+
};
|
2830
|
+
var onClosed = function() {
|
2831
|
+
connection.unbind("connected", onConnected);
|
2832
|
+
connection.unbind("message", onMessage);
|
2833
|
+
connection.unbind("ping", onPing);
|
2834
|
+
connection.unbind("ping_request", onPingRequest);
|
2835
|
+
connection.unbind("error", onError);
|
2836
|
+
connection.unbind("closed", onClosed);
|
2837
|
+
self.connection = null;
|
2838
|
+
|
2839
|
+
if (self.shouldRetry()) {
|
2840
|
+
self.retryIn(1000);
|
808
2841
|
}
|
809
|
-
}
|
2842
|
+
};
|
810
2843
|
|
2844
|
+
// handling close conditions
|
2845
|
+
var onSSLOnly = function() {
|
2846
|
+
self.encrypted = true;
|
2847
|
+
self.retryIn(0);
|
2848
|
+
};
|
2849
|
+
var onRefused = function() {
|
2850
|
+
self.disconnect();
|
2851
|
+
};
|
2852
|
+
var onBackoff = function() {
|
2853
|
+
self.retryIn(1000);
|
2854
|
+
};
|
2855
|
+
var onRetry = function() {
|
2856
|
+
self.retryIn(0);
|
2857
|
+
};
|
811
2858
|
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
function parseWebSocketEvent(event) {
|
819
|
-
try {
|
820
|
-
var params = JSON.parse(event.data);
|
821
|
-
|
822
|
-
if (typeof params.data === 'string') {
|
823
|
-
try {
|
824
|
-
params.data = JSON.parse(params.data);
|
825
|
-
} catch (e) {
|
826
|
-
if (!(e instanceof SyntaxError)) {
|
827
|
-
throw e;
|
828
|
-
}
|
829
|
-
}
|
830
|
-
}
|
2859
|
+
connection.bind("connected", onConnected);
|
2860
|
+
connection.bind("message", onMessage);
|
2861
|
+
connection.bind("ping", onPing);
|
2862
|
+
connection.bind("ping_request", onPingRequest);
|
2863
|
+
connection.bind("error", onError);
|
2864
|
+
connection.bind("closed", onClosed);
|
831
2865
|
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
}
|
2866
|
+
connection.bind("ssl_only", onSSLOnly);
|
2867
|
+
connection.bind("refused", onRefused);
|
2868
|
+
connection.bind("backoff", onBackoff);
|
2869
|
+
connection.bind("retry", onRetry);
|
837
2870
|
|
838
|
-
|
839
|
-
|
840
|
-
}
|
2871
|
+
this.resetActivityCheck();
|
2872
|
+
};
|
841
2873
|
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
}
|
2874
|
+
/** @private */
|
2875
|
+
prototype.updateState = function(newState, data) {
|
2876
|
+
var previousState = this.state;
|
846
2877
|
|
847
|
-
|
848
|
-
//
|
849
|
-
|
850
|
-
|
851
|
-
//
|
852
|
-
function updateState(newState, data) {
|
853
|
-
var prevState = self.state;
|
854
|
-
self.state = newState;
|
2878
|
+
this.state = newState;
|
2879
|
+
// Only emit when the state changes
|
2880
|
+
if (previousState !== newState) {
|
2881
|
+
Pusher.debug('State changed', previousState + ' -> ' + newState);
|
855
2882
|
|
856
|
-
|
857
|
-
|
858
|
-
Pusher.debug('State changed', prevState + ' -> ' + newState);
|
859
|
-
self.emit('state_change', {previous: prevState, current: newState});
|
860
|
-
self.emit(newState, data);
|
861
|
-
}
|
2883
|
+
this.emit('state_change', { previous: previousState, current: newState });
|
2884
|
+
this.emit(newState, data);
|
862
2885
|
}
|
863
2886
|
};
|
864
2887
|
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
this._machine.transition('failed');
|
869
|
-
}
|
870
|
-
// initial open of connection
|
871
|
-
else if(this._machine.is('initialized')) {
|
872
|
-
resetConnectionParameters(this);
|
873
|
-
this._machine.transition('waiting');
|
874
|
-
}
|
875
|
-
// user skipping connection wait
|
876
|
-
else if (this._machine.is('waiting') && this.netInfo.isOnLine() === true) {
|
877
|
-
this._machine.transition('connecting');
|
878
|
-
}
|
879
|
-
// user re-opening connection after closing it
|
880
|
-
else if(this._machine.is("permanentlyClosed")) {
|
881
|
-
resetConnectionParameters(this);
|
882
|
-
this._machine.transition('waiting');
|
883
|
-
}
|
2888
|
+
/** @private */
|
2889
|
+
prototype.shouldRetry = function() {
|
2890
|
+
return this.state === "connecting" || this.state === "connected";
|
884
2891
|
};
|
885
2892
|
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
var self = this;
|
890
|
-
setTimeout(function() {
|
891
|
-
self.socket.send(data);
|
892
|
-
}, 0);
|
893
|
-
return true;
|
894
|
-
} else {
|
895
|
-
return false;
|
896
|
-
}
|
2893
|
+
/** @private */
|
2894
|
+
prototype.wrapTransport = function(transport) {
|
2895
|
+
return new Pusher.ProtocolWrapper(transport);
|
897
2896
|
};
|
898
2897
|
|
899
|
-
|
900
|
-
|
901
|
-
event: event_name,
|
902
|
-
data: data
|
903
|
-
};
|
904
|
-
if (channel) payload['channel'] = channel;
|
2898
|
+
Pusher.ConnectionManager = ConnectionManager;
|
2899
|
+
}).call(this);
|
905
2900
|
|
906
|
-
|
907
|
-
|
908
|
-
|
2901
|
+
;(function() {
|
2902
|
+
/** Really basic interface providing network availability info.
|
2903
|
+
*
|
2904
|
+
* Emits:
|
2905
|
+
* - online - when browser goes online
|
2906
|
+
* - offline - when browser goes offline
|
2907
|
+
*/
|
2908
|
+
function NetInfo() {
|
2909
|
+
Pusher.EventsDispatcher.call(this);
|
909
2910
|
|
910
|
-
|
911
|
-
|
2911
|
+
var self = this;
|
2912
|
+
// This is okay, as IE doesn't support this stuff anyway.
|
2913
|
+
if (window.addEventListener !== undefined) {
|
2914
|
+
window.addEventListener("online", function() {
|
2915
|
+
self.emit('online');
|
2916
|
+
}, false);
|
2917
|
+
window.addEventListener("offline", function() {
|
2918
|
+
self.emit('offline');
|
2919
|
+
}, false);
|
2920
|
+
}
|
2921
|
+
}
|
2922
|
+
Pusher.Util.extend(NetInfo.prototype, Pusher.EventsDispatcher.prototype);
|
912
2923
|
|
913
|
-
|
914
|
-
|
2924
|
+
var prototype = NetInfo.prototype;
|
2925
|
+
|
2926
|
+
/** Returns whether browser is online or not
|
2927
|
+
*
|
2928
|
+
* Offline means definitely offline (no connection to router).
|
2929
|
+
* Inverse does NOT mean definitely online (only currently supported in Safari
|
2930
|
+
* and even there only means the device has a connection to the router).
|
2931
|
+
*
|
2932
|
+
* @return {Boolean}
|
2933
|
+
*/
|
2934
|
+
prototype.isOnline = function() {
|
2935
|
+
if (window.navigator.onLine === undefined) {
|
2936
|
+
return true;
|
915
2937
|
} else {
|
916
|
-
|
2938
|
+
return window.navigator.onLine;
|
917
2939
|
}
|
918
2940
|
};
|
919
2941
|
|
920
|
-
Pusher.
|
921
|
-
|
2942
|
+
Pusher.NetInfo = NetInfo;
|
2943
|
+
Pusher.Network = new NetInfo();
|
922
2944
|
}).call(this);
|
923
2945
|
|
924
2946
|
;(function() {
|
@@ -1191,130 +3213,3 @@ Example:
|
|
1191
3213
|
}
|
1192
3214
|
};
|
1193
3215
|
}).call(this);
|
1194
|
-
// _require(dependencies, callback) takes an array of dependency urls and a
|
1195
|
-
// callback to call when all the dependecies have finished loading
|
1196
|
-
var _require = (function() {
|
1197
|
-
function handleScriptLoaded(elem, callback) {
|
1198
|
-
if (document.addEventListener) {
|
1199
|
-
elem.addEventListener('load', callback, false);
|
1200
|
-
} else {
|
1201
|
-
elem.attachEvent('onreadystatechange', function () {
|
1202
|
-
if (elem.readyState == 'loaded' || elem.readyState == 'complete') {
|
1203
|
-
callback();
|
1204
|
-
}
|
1205
|
-
});
|
1206
|
-
}
|
1207
|
-
}
|
1208
|
-
|
1209
|
-
function addScript(src, callback) {
|
1210
|
-
var head = document.getElementsByTagName('head')[0];
|
1211
|
-
var script = document.createElement('script');
|
1212
|
-
script.setAttribute('src', src);
|
1213
|
-
script.setAttribute("type","text/javascript");
|
1214
|
-
script.setAttribute('async', true);
|
1215
|
-
|
1216
|
-
handleScriptLoaded(script, function() {
|
1217
|
-
callback();
|
1218
|
-
});
|
1219
|
-
|
1220
|
-
head.appendChild(script);
|
1221
|
-
}
|
1222
|
-
|
1223
|
-
return function(deps, callback) {
|
1224
|
-
var deps_loaded = 0;
|
1225
|
-
for (var i = 0; i < deps.length; i++) {
|
1226
|
-
addScript(deps[i], function() {
|
1227
|
-
if (deps.length == ++deps_loaded) {
|
1228
|
-
// This setTimeout is a workaround for an Opera issue
|
1229
|
-
setTimeout(callback, 0);
|
1230
|
-
}
|
1231
|
-
});
|
1232
|
-
}
|
1233
|
-
}
|
1234
|
-
})();
|
1235
|
-
|
1236
|
-
;(function() {
|
1237
|
-
// Support Firefox versions which prefix WebSocket
|
1238
|
-
if (!window['WebSocket'] && window['MozWebSocket']) {
|
1239
|
-
window['WebSocket'] = window['MozWebSocket']
|
1240
|
-
}
|
1241
|
-
|
1242
|
-
if (window['WebSocket']) {
|
1243
|
-
Pusher.Transport = window['WebSocket'];
|
1244
|
-
Pusher.TransportType = 'native';
|
1245
|
-
}
|
1246
|
-
|
1247
|
-
var cdn = (document.location.protocol == 'http:') ? Pusher.cdn_http : Pusher.cdn_https;
|
1248
|
-
var root = cdn + Pusher.VERSION;
|
1249
|
-
var deps = [];
|
1250
|
-
|
1251
|
-
if (!window['JSON']) {
|
1252
|
-
deps.push(root + '/json2' + Pusher.dependency_suffix + '.js');
|
1253
|
-
}
|
1254
|
-
if (!window['WebSocket']) {
|
1255
|
-
var flashSupported;
|
1256
|
-
try {
|
1257
|
-
flashSupported = Boolean(new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
|
1258
|
-
} catch (e) {
|
1259
|
-
flashSupported = navigator.mimeTypes["application/x-shockwave-flash"] !== undefined;
|
1260
|
-
}
|
1261
|
-
|
1262
|
-
if (flashSupported) {
|
1263
|
-
// Try to use web-socket-js (flash WebSocket emulation)
|
1264
|
-
window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
|
1265
|
-
window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
|
1266
|
-
deps.push(root + '/flashfallback' + Pusher.dependency_suffix + '.js');
|
1267
|
-
} else {
|
1268
|
-
// Use SockJS when Flash is not available
|
1269
|
-
deps.push(root + '/sockjs' + Pusher.dependency_suffix + '.js');
|
1270
|
-
}
|
1271
|
-
}
|
1272
|
-
|
1273
|
-
var initialize = function() {
|
1274
|
-
if (window['WebSocket']) {
|
1275
|
-
// Initialize function in the case that we have native WebSocket support
|
1276
|
-
return function() {
|
1277
|
-
Pusher.ready();
|
1278
|
-
}
|
1279
|
-
} else {
|
1280
|
-
// Initialize function for fallback case
|
1281
|
-
return function() {
|
1282
|
-
if (window['WebSocket']) {
|
1283
|
-
// window['WebSocket'] is a flash emulation of WebSocket
|
1284
|
-
Pusher.Transport = window['WebSocket'];
|
1285
|
-
Pusher.TransportType = 'flash';
|
1286
|
-
|
1287
|
-
window.WEB_SOCKET_SWF_LOCATION = root + "/WebSocketMain.swf";
|
1288
|
-
WebSocket.__addTask(function() {
|
1289
|
-
Pusher.ready();
|
1290
|
-
})
|
1291
|
-
WebSocket.__initialize();
|
1292
|
-
} else {
|
1293
|
-
// Flash fallback was not loaded, using SockJS
|
1294
|
-
Pusher.Transport = window.SockJS;
|
1295
|
-
Pusher.TransportType = 'sockjs';
|
1296
|
-
Pusher.ready();
|
1297
|
-
}
|
1298
|
-
}
|
1299
|
-
}
|
1300
|
-
}();
|
1301
|
-
|
1302
|
-
// Allows calling a function when the document body is available
|
1303
|
-
var ondocumentbody = function(callback) {
|
1304
|
-
var load_body = function() {
|
1305
|
-
document.body ? callback() : setTimeout(load_body, 0);
|
1306
|
-
}
|
1307
|
-
load_body();
|
1308
|
-
};
|
1309
|
-
|
1310
|
-
var initializeOnDocumentBody = function() {
|
1311
|
-
ondocumentbody(initialize);
|
1312
|
-
}
|
1313
|
-
|
1314
|
-
if (deps.length > 0) {
|
1315
|
-
_require(deps, initializeOnDocumentBody);
|
1316
|
-
} else {
|
1317
|
-
initializeOnDocumentBody();
|
1318
|
-
}
|
1319
|
-
})();
|
1320
|
-
|