pusher-fake 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,7 +13,7 @@
13
13
  <ul></ul>
14
14
  </section>
15
15
 
16
- <script src="/javascripts/vendor/pusher-3.0.0.js"></script>
16
+ <script src="/javascripts/vendor/pusher-3.1.0.js"></script>
17
17
  <script>
18
18
  Pusher.instance = <%= PusherFake.javascript %>;
19
19
  Pusher.instance.connection.bind("state_change", function(states) {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pusher-fake
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tristan Dunn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-13 00:00:00.000000000 Z
11
+ date: 2016-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request
@@ -72,56 +72,56 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 1.8.0
75
+ version: 1.11.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 1.8.0
82
+ version: 1.11.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: pusher
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 0.16.0
89
+ version: 1.1.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 0.16.0
96
+ version: 1.1.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: 10.5.0
103
+ version: 11.2.2
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: 10.5.0
110
+ version: 11.2.2
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: 3.4.0
117
+ version: 3.5.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: 3.4.0
124
+ version: 3.5.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: sinatra
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - '='
144
144
  - !ruby/object:Gem::Version
145
- version: 0.8.7.6
145
+ version: 0.9.5
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - '='
151
151
  - !ruby/object:Gem::Version
152
- version: 0.8.7.6
152
+ version: 0.9.5
153
153
  description: A fake Pusher server for development and testing.
154
154
  email: hello@tristandunn.com
155
155
  executables: []
@@ -190,7 +190,7 @@ files:
190
190
  - spec/lib/pusher_fake_spec.rb
191
191
  - spec/spec_helper.rb
192
192
  - spec/support/application.rb
193
- - spec/support/application/public/javascripts/vendor/pusher-3.0.0.js
193
+ - spec/support/application/public/javascripts/vendor/pusher-3.1.0.js
194
194
  - spec/support/application/views/index.erb
195
195
  - spec/support/capybara.rb
196
196
  - spec/support/coveralls.rb
@@ -221,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
221
  version: '0'
222
222
  requirements: []
223
223
  rubyforge_project:
224
- rubygems_version: 2.4.5.1
224
+ rubygems_version: 2.5.1
225
225
  signing_key:
226
226
  specification_version: 4
227
227
  summary: A fake Pusher server for development and testing.
@@ -245,7 +245,7 @@ test_files:
245
245
  - spec/lib/pusher-fake/webhook_spec.rb
246
246
  - spec/lib/pusher_fake_spec.rb
247
247
  - spec/spec_helper.rb
248
- - spec/support/application/public/javascripts/vendor/pusher-3.0.0.js
248
+ - spec/support/application/public/javascripts/vendor/pusher-3.1.0.js
249
249
  - spec/support/application/views/index.erb
250
250
  - spec/support/application.rb
251
251
  - spec/support/capybara.rb
@@ -257,4 +257,3 @@ test_files:
257
257
  - spec/support/matchers/have_configuration_option.rb
258
258
  - spec/support/pusher-fake.rb
259
259
  - spec/support/webhooks.rb
260
- has_rdoc:
@@ -1,3984 +0,0 @@
1
- /*!
2
- * Pusher JavaScript Library v3.0.0
3
- * http://pusher.com/
4
- *
5
- * Copyright 2014, Pusher
6
- * Released under the MIT licence.
7
- */
8
-
9
- // Uses Node, AMD or browser globals to create a module. This example creates
10
- // a global even when AMD is used. This is useful if you have some scripts
11
- // that are loaded by an AMD loader, but they still want access to globals.
12
- // If you do not need to export a global for the AMD case,
13
- // see returnExports.js.
14
-
15
- // If you want something that will work in other stricter CommonJS environments,
16
- // or if you need to create a circular dependency, see commonJsStrictGlobal.js
17
-
18
- // Defines a module "Pusher".
19
-
20
- (function (root, factory) {
21
- if (typeof define === 'function' && define.amd) {
22
- // AMD. Register as an anonymous module.
23
- define([], function () {
24
- return (root.Pusher = factory());
25
- });
26
- } else if (typeof exports === 'object') {
27
- // Node. Does not work with strict CommonJS, but
28
- // only CommonJS-like enviroments that support module.exports,
29
- // like Node.
30
- module.exports = factory();
31
- } else {
32
- // Browser globals
33
- root.Pusher = factory();
34
- }
35
- }(this, function () {
36
-
37
- ;(function() {
38
- function Pusher(app_key, options) {
39
- checkAppKey(app_key);
40
- options = options || {};
41
-
42
- var self = this;
43
-
44
- this.key = app_key;
45
- this.config = Pusher.Util.extend(
46
- Pusher.getGlobalConfig(),
47
- options.cluster ? Pusher.getClusterConfig(options.cluster) : {},
48
- options
49
- );
50
-
51
- this.channels = new Pusher.Channels();
52
- this.global_emitter = new Pusher.EventsDispatcher();
53
- this.sessionID = Math.floor(Math.random() * 1000000000);
54
-
55
- this.timeline = new Pusher.Timeline(this.key, this.sessionID, {
56
- cluster: this.config.cluster,
57
- features: Pusher.Util.getClientFeatures(),
58
- params: this.config.timelineParams || {},
59
- limit: 50,
60
- level: Pusher.Timeline.INFO,
61
- version: Pusher.VERSION
62
- });
63
- if (!this.config.disableStats) {
64
- this.timelineSender = new Pusher.TimelineSender(this.timeline, {
65
- host: this.config.statsHost,
66
- path: "/timeline/v2/jsonp"
67
- });
68
- }
69
-
70
- var getStrategy = function(options) {
71
- var config = Pusher.Util.extend({}, self.config, options);
72
- return Pusher.StrategyBuilder.build(
73
- Pusher.getDefaultStrategy(config), config
74
- );
75
- };
76
-
77
- this.connection = new Pusher.ConnectionManager(
78
- this.key,
79
- Pusher.Util.extend(
80
- { getStrategy: getStrategy,
81
- timeline: this.timeline,
82
- activityTimeout: this.config.activity_timeout,
83
- pongTimeout: this.config.pong_timeout,
84
- unavailableTimeout: this.config.unavailable_timeout
85
- },
86
- this.config,
87
- { encrypted: this.isEncrypted() }
88
- )
89
- );
90
-
91
- this.connection.bind('connected', function() {
92
- self.subscribeAll();
93
- if (self.timelineSender) {
94
- self.timelineSender.send(self.connection.isEncrypted());
95
- }
96
- });
97
- this.connection.bind('message', function(params) {
98
- var internal = (params.event.indexOf('pusher_internal:') === 0);
99
- if (params.channel) {
100
- var channel = self.channel(params.channel);
101
- if (channel) {
102
- channel.handleEvent(params.event, params.data);
103
- }
104
- }
105
- // Emit globally [deprecated]
106
- if (!internal) {
107
- self.global_emitter.emit(params.event, params.data);
108
- }
109
- });
110
- this.connection.bind('disconnected', function() {
111
- self.channels.disconnect();
112
- });
113
- this.connection.bind('error', function(err) {
114
- Pusher.warn('Error', err);
115
- });
116
-
117
- Pusher.instances.push(this);
118
- this.timeline.info({ instances: Pusher.instances.length });
119
-
120
- if (Pusher.isReady) {
121
- self.connect();
122
- }
123
- }
124
- var prototype = Pusher.prototype;
125
-
126
- Pusher.instances = [];
127
- Pusher.isReady = false;
128
-
129
- // To receive log output provide a Pusher.log function, for example
130
- // Pusher.log = function(m){console.log(m)}
131
- Pusher.debug = function() {
132
- if (!Pusher.log) {
133
- return;
134
- }
135
- Pusher.log(Pusher.Util.stringify.apply(this, arguments));
136
- };
137
-
138
- Pusher.warn = function() {
139
- var message = Pusher.Util.stringify.apply(this, arguments);
140
- if (window.console) {
141
- if (window.console.warn) {
142
- window.console.warn(message);
143
- } else if (window.console.log) {
144
- window.console.log(message);
145
- }
146
- }
147
- if (Pusher.log) {
148
- Pusher.log(message);
149
- }
150
- };
151
-
152
- Pusher.ready = function() {
153
- Pusher.isReady = true;
154
- for (var i = 0, l = Pusher.instances.length; i < l; i++) {
155
- Pusher.instances[i].connect();
156
- }
157
- };
158
-
159
- prototype.channel = function(name) {
160
- return this.channels.find(name);
161
- };
162
-
163
- prototype.allChannels = function() {
164
- return this.channels.all();
165
- };
166
-
167
- prototype.connect = function() {
168
- this.connection.connect();
169
-
170
- if (this.timelineSender) {
171
- if (!this.timelineSenderTimer) {
172
- var encrypted = this.connection.isEncrypted();
173
- var timelineSender = this.timelineSender;
174
- this.timelineSenderTimer = new Pusher.PeriodicTimer(60000, function() {
175
- timelineSender.send(encrypted);
176
- });
177
- }
178
- }
179
- };
180
-
181
- prototype.disconnect = function() {
182
- this.connection.disconnect();
183
-
184
- if (this.timelineSenderTimer) {
185
- this.timelineSenderTimer.ensureAborted();
186
- this.timelineSenderTimer = null;
187
- }
188
- };
189
-
190
- prototype.bind = function(event_name, callback) {
191
- this.global_emitter.bind(event_name, callback);
192
- return this;
193
- };
194
-
195
- prototype.bind_all = function(callback) {
196
- this.global_emitter.bind_all(callback);
197
- return this;
198
- };
199
-
200
- prototype.subscribeAll = function() {
201
- var channelName;
202
- for (channelName in this.channels.channels) {
203
- if (this.channels.channels.hasOwnProperty(channelName)) {
204
- this.subscribe(channelName);
205
- }
206
- }
207
- };
208
-
209
- prototype.subscribe = function(channel_name) {
210
- var channel = this.channels.add(channel_name, this);
211
- if (this.connection.state === 'connected') {
212
- channel.subscribe();
213
- }
214
- return channel;
215
- };
216
-
217
- prototype.unsubscribe = function(channel_name) {
218
- var channel = this.channels.remove(channel_name);
219
- if (channel && this.connection.state === 'connected') {
220
- channel.unsubscribe();
221
- }
222
- };
223
-
224
- prototype.send_event = function(event_name, data, channel) {
225
- return this.connection.send_event(event_name, data, channel);
226
- };
227
-
228
- prototype.isEncrypted = function() {
229
- if (Pusher.Util.getDocument().location.protocol === "https:") {
230
- return true;
231
- } else {
232
- return Boolean(this.config.encrypted);
233
- }
234
- };
235
-
236
- function checkAppKey(key) {
237
- if (key === null || key === undefined) {
238
- Pusher.warn(
239
- 'Warning', 'You must pass your app key when you instantiate Pusher.'
240
- );
241
- }
242
- }
243
-
244
- Pusher.HTTP = {};
245
-
246
- this.Pusher = Pusher;
247
- }).call(this);
248
-
249
- ;(function() {
250
- // We need to bind clear functions this way to avoid exceptions on IE8
251
- function clearTimeout(timer) {
252
- window.clearTimeout(timer);
253
- }
254
- function clearInterval(timer) {
255
- window.clearInterval(timer);
256
- }
257
-
258
- function GenericTimer(set, clear, delay, callback) {
259
- var self = this;
260
-
261
- this.clear = clear;
262
- this.timer = set(function() {
263
- if (self.timer !== null) {
264
- self.timer = callback(self.timer);
265
- }
266
- }, delay);
267
- }
268
- var prototype = GenericTimer.prototype;
269
-
270
- /** Returns whether the timer is still running.
271
- *
272
- * @return {Boolean}
273
- */
274
- prototype.isRunning = function() {
275
- return this.timer !== null;
276
- };
277
-
278
- /** Aborts a timer when it's running. */
279
- prototype.ensureAborted = function() {
280
- if (this.timer) {
281
- // Clear function is already bound
282
- this.clear(this.timer);
283
- this.timer = null;
284
- }
285
- };
286
-
287
- /** Cross-browser compatible one-off timer abstraction.
288
- *
289
- * @param {Number} delay
290
- * @param {Function} callback
291
- */
292
- Pusher.Timer = function(delay, callback) {
293
- return new GenericTimer(setTimeout, clearTimeout, delay, function(timer) {
294
- callback();
295
- return null;
296
- });
297
- };
298
- /** Cross-browser compatible periodic timer abstraction.
299
- *
300
- * @param {Number} delay
301
- * @param {Function} callback
302
- */
303
- Pusher.PeriodicTimer = function(delay, callback) {
304
- return new GenericTimer(setInterval, clearInterval, delay, function(timer) {
305
- callback();
306
- return timer;
307
- });
308
- };
309
- }).call(this);
310
-
311
- ;(function() {
312
- Pusher.Util = {
313
- now: function() {
314
- if (Date.now) {
315
- return Date.now();
316
- } else {
317
- return new Date().valueOf();
318
- }
319
- },
320
-
321
- defer: function(callback) {
322
- return new Pusher.Timer(0, callback);
323
- },
324
-
325
- /** Merges multiple objects into the target argument.
326
- *
327
- * For properties that are plain Objects, performs a deep-merge. For the
328
- * rest it just copies the value of the property.
329
- *
330
- * To extend prototypes use it as following:
331
- * Pusher.Util.extend(Target.prototype, Base.prototype)
332
- *
333
- * You can also use it to merge objects without altering them:
334
- * Pusher.Util.extend({}, object1, object2)
335
- *
336
- * @param {Object} target
337
- * @return {Object} the target argument
338
- */
339
- extend: function(target) {
340
- for (var i = 1; i < arguments.length; i++) {
341
- var extensions = arguments[i];
342
- for (var property in extensions) {
343
- if (extensions[property] && extensions[property].constructor &&
344
- extensions[property].constructor === Object) {
345
- target[property] = Pusher.Util.extend(
346
- target[property] || {}, extensions[property]
347
- );
348
- } else {
349
- target[property] = extensions[property];
350
- }
351
- }
352
- }
353
- return target;
354
- },
355
-
356
- stringify: function() {
357
- var m = ["Pusher"];
358
- for (var i = 0; i < arguments.length; i++) {
359
- if (typeof arguments[i] === "string") {
360
- m.push(arguments[i]);
361
- } else {
362
- if (window.JSON === undefined) {
363
- m.push(arguments[i].toString());
364
- } else {
365
- m.push(JSON.stringify(arguments[i]));
366
- }
367
- }
368
- }
369
- return m.join(" : ");
370
- },
371
-
372
- arrayIndexOf: function(array, item) { // MSIE doesn't have array.indexOf
373
- var nativeIndexOf = Array.prototype.indexOf;
374
- if (array === null) {
375
- return -1;
376
- }
377
- if (nativeIndexOf && array.indexOf === nativeIndexOf) {
378
- return array.indexOf(item);
379
- }
380
- for (var i = 0, l = array.length; i < l; i++) {
381
- if (array[i] === item) {
382
- return i;
383
- }
384
- }
385
- return -1;
386
- },
387
-
388
- /** Applies a function f to all properties of an object.
389
- *
390
- * Function f gets 3 arguments passed:
391
- * - element from the object
392
- * - key of the element
393
- * - reference to the object
394
- *
395
- * @param {Object} object
396
- * @param {Function} f
397
- */
398
- objectApply: function(object, f) {
399
- for (var key in object) {
400
- if (Object.prototype.hasOwnProperty.call(object, key)) {
401
- f(object[key], key, object);
402
- }
403
- }
404
- },
405
-
406
- /** Return a list of object's own property keys
407
- *
408
- * @param {Object} object
409
- * @returns {Array}
410
- */
411
- keys: function(object) {
412
- var keys = [];
413
- Pusher.Util.objectApply(object, function(_, key) {
414
- keys.push(key);
415
- });
416
- return keys;
417
- },
418
-
419
- /** Return a list of object's own property values
420
- *
421
- * @param {Object} object
422
- * @returns {Array}
423
- */
424
- values: function(object) {
425
- var values = [];
426
- Pusher.Util.objectApply(object, function(value) {
427
- values.push(value);
428
- });
429
- return values;
430
- },
431
-
432
- /** Applies a function f to all elements of an array.
433
- *
434
- * Function f gets 3 arguments passed:
435
- * - element from the array
436
- * - index of the element
437
- * - reference to the array
438
- *
439
- * @param {Array} array
440
- * @param {Function} f
441
- */
442
- apply: function(array, f, context) {
443
- for (var i = 0; i < array.length; i++) {
444
- f.call(context || window, array[i], i, array);
445
- }
446
- },
447
-
448
- /** Maps all elements of the array and returns the result.
449
- *
450
- * Function f gets 4 arguments passed:
451
- * - element from the array
452
- * - index of the element
453
- * - reference to the source array
454
- * - reference to the destination array
455
- *
456
- * @param {Array} array
457
- * @param {Function} f
458
- */
459
- map: function(array, f) {
460
- var result = [];
461
- for (var i = 0; i < array.length; i++) {
462
- result.push(f(array[i], i, array, result));
463
- }
464
- return result;
465
- },
466
-
467
- /** Maps all elements of the object and returns the result.
468
- *
469
- * Function f gets 4 arguments passed:
470
- * - element from the object
471
- * - key of the element
472
- * - reference to the source object
473
- * - reference to the destination object
474
- *
475
- * @param {Object} object
476
- * @param {Function} f
477
- */
478
- mapObject: function(object, f) {
479
- var result = {};
480
- Pusher.Util.objectApply(object, function(value, key) {
481
- result[key] = f(value);
482
- });
483
- return result;
484
- },
485
-
486
- /** Filters elements of the array using a test function.
487
- *
488
- * Function test gets 4 arguments passed:
489
- * - element from the array
490
- * - index of the element
491
- * - reference to the source array
492
- * - reference to the destination array
493
- *
494
- * @param {Array} array
495
- * @param {Function} f
496
- */
497
- filter: function(array, test) {
498
- test = test || function(value) { return !!value; };
499
-
500
- var result = [];
501
- for (var i = 0; i < array.length; i++) {
502
- if (test(array[i], i, array, result)) {
503
- result.push(array[i]);
504
- }
505
- }
506
- return result;
507
- },
508
-
509
- /** Filters properties of the object using a test function.
510
- *
511
- * Function test gets 4 arguments passed:
512
- * - element from the object
513
- * - key of the element
514
- * - reference to the source object
515
- * - reference to the destination object
516
- *
517
- * @param {Object} object
518
- * @param {Function} f
519
- */
520
- filterObject: function(object, test) {
521
- var result = {};
522
- Pusher.Util.objectApply(object, function(value, key) {
523
- if ((test && test(value, key, object, result)) || Boolean(value)) {
524
- result[key] = value;
525
- }
526
- });
527
- return result;
528
- },
529
-
530
- /** Flattens an object into a two-dimensional array.
531
- *
532
- * @param {Object} object
533
- * @return {Array} resulting array of [key, value] pairs
534
- */
535
- flatten: function(object) {
536
- var result = [];
537
- Pusher.Util.objectApply(object, function(value, key) {
538
- result.push([key, value]);
539
- });
540
- return result;
541
- },
542
-
543
- /** Checks whether any element of the array passes the test.
544
- *
545
- * Function test gets 3 arguments passed:
546
- * - element from the array
547
- * - index of the element
548
- * - reference to the source array
549
- *
550
- * @param {Array} array
551
- * @param {Function} f
552
- */
553
- any: function(array, test) {
554
- for (var i = 0; i < array.length; i++) {
555
- if (test(array[i], i, array)) {
556
- return true;
557
- }
558
- }
559
- return false;
560
- },
561
-
562
- /** Checks whether all elements of the array pass the test.
563
- *
564
- * Function test gets 3 arguments passed:
565
- * - element from the array
566
- * - index of the element
567
- * - reference to the source array
568
- *
569
- * @param {Array} array
570
- * @param {Function} f
571
- */
572
- all: function(array, test) {
573
- for (var i = 0; i < array.length; i++) {
574
- if (!test(array[i], i, array)) {
575
- return false;
576
- }
577
- }
578
- return true;
579
- },
580
-
581
- /** Builds a function that will proxy a method call to its first argument.
582
- *
583
- * Allows partial application of arguments, so additional arguments are
584
- * prepended to the argument list.
585
- *
586
- * @param {String} name method name
587
- * @return {Function} proxy function
588
- */
589
- method: function(name) {
590
- var boundArguments = Array.prototype.slice.call(arguments, 1);
591
- return function(object) {
592
- return object[name].apply(object, boundArguments.concat(arguments));
593
- };
594
- },
595
-
596
- getWindow: function() {
597
- return window;
598
- },
599
-
600
- getDocument: function() {
601
- return document;
602
- },
603
-
604
- getLocalStorage: function() {
605
- try {
606
- return window.localStorage;
607
- } catch (e) {
608
- return undefined;
609
- }
610
- },
611
-
612
- getClientFeatures: function() {
613
- return Pusher.Util.keys(
614
- Pusher.Util.filterObject(
615
- { "ws": Pusher.WSTransport },
616
- function (t) { return t.isSupported({}); }
617
- )
618
- );
619
- },
620
-
621
- addWindowListener: function(event, listener) {
622
- var _window = Pusher.Util.getWindow();
623
- if (_window.addEventListener !== undefined) {
624
- _window.addEventListener(event, listener, false);
625
- } else {
626
- _window.attachEvent("on" + event, listener);
627
- }
628
- },
629
-
630
- removeWindowListener: function(event, listener) {
631
- var _window = Pusher.Util.getWindow();
632
- if (_window.addEventListener !== undefined) {
633
- _window.removeEventListener(event, listener, false);
634
- } else {
635
- _window.detachEvent("on" + event, listener);
636
- }
637
- },
638
-
639
- isXHRSupported: function() {
640
- var XHR = window.XMLHttpRequest;
641
- return Boolean(XHR) && (new XHR()).withCredentials !== undefined;
642
- },
643
-
644
- isXDRSupported: function(encrypted) {
645
- var protocol = encrypted ? "https:" : "http:";
646
- var documentProtocol = Pusher.Util.getDocument().location.protocol;
647
- return Boolean(window.XDomainRequest) && documentProtocol === protocol;
648
- }
649
- };
650
- }).call(this);
651
-
652
- ;(function() {
653
- Pusher.VERSION = '3.0.0';
654
- Pusher.PROTOCOL = 7;
655
-
656
- // DEPRECATED: WS connection parameters
657
- Pusher.host = 'ws.pusherapp.com';
658
- Pusher.ws_port = 80;
659
- Pusher.wss_port = 443;
660
- // DEPRECATED: SockJS fallback parameters
661
- Pusher.sockjs_host = 'sockjs.pusher.com';
662
- Pusher.sockjs_http_port = 80;
663
- Pusher.sockjs_https_port = 443;
664
- Pusher.sockjs_path = "/pusher";
665
- // DEPRECATED: Stats
666
- Pusher.stats_host = 'stats.pusher.com';
667
- // DEPRECATED: Other settings
668
- Pusher.channel_auth_endpoint = '/pusher/auth';
669
- Pusher.channel_auth_transport = 'ajax';
670
- Pusher.activity_timeout = 120000;
671
- Pusher.pong_timeout = 30000;
672
- Pusher.unavailable_timeout = 10000;
673
- // CDN configuration
674
- Pusher.cdn_http = 'http://js.pusher.com/';
675
- Pusher.cdn_https = 'https://js.pusher.com/';
676
- Pusher.dependency_suffix = '';
677
-
678
- Pusher.getDefaultStrategy = function(config) {
679
- var wsStrategy;
680
- if (config.encrypted) {
681
- wsStrategy = [
682
- ":best_connected_ever",
683
- ":ws_loop",
684
- [":delayed", 2000, [":http_fallback_loop"]]
685
- ];
686
- } else {
687
- wsStrategy = [
688
- ":best_connected_ever",
689
- ":ws_loop",
690
- [":delayed", 2000, [":wss_loop"]],
691
- [":delayed", 5000, [":http_fallback_loop"]]
692
- ];
693
- }
694
-
695
- return [
696
- [":def", "ws_options", {
697
- hostUnencrypted: config.wsHost + ":" + config.wsPort,
698
- hostEncrypted: config.wsHost + ":" + config.wssPort
699
- }],
700
- [":def", "wss_options", [":extend", ":ws_options", {
701
- encrypted: true
702
- }]],
703
- [":def", "sockjs_options", {
704
- hostUnencrypted: config.httpHost + ":" + config.httpPort,
705
- hostEncrypted: config.httpHost + ":" + config.httpsPort,
706
- httpPath: config.httpPath
707
- }],
708
- [":def", "timeouts", {
709
- loop: true,
710
- timeout: 15000,
711
- timeoutLimit: 60000
712
- }],
713
-
714
- [":def", "ws_manager", [":transport_manager", {
715
- lives: 2,
716
- minPingDelay: 10000,
717
- maxPingDelay: config.activity_timeout
718
- }]],
719
- [":def", "streaming_manager", [":transport_manager", {
720
- lives: 2,
721
- minPingDelay: 10000,
722
- maxPingDelay: config.activity_timeout
723
- }]],
724
-
725
- [":def_transport", "ws", "ws", 3, ":ws_options", ":ws_manager"],
726
- [":def_transport", "wss", "ws", 3, ":wss_options", ":ws_manager"],
727
- [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"],
728
- [":def_transport", "xhr_streaming", "xhr_streaming", 1, ":sockjs_options", ":streaming_manager"],
729
- [":def_transport", "xdr_streaming", "xdr_streaming", 1, ":sockjs_options", ":streaming_manager"],
730
- [":def_transport", "xhr_polling", "xhr_polling", 1, ":sockjs_options"],
731
- [":def_transport", "xdr_polling", "xdr_polling", 1, ":sockjs_options"],
732
-
733
- [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]],
734
- [":def", "wss_loop", [":sequential", ":timeouts", ":wss"]],
735
- [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]],
736
-
737
- [":def", "streaming_loop", [":sequential", ":timeouts",
738
- [":if", [":is_supported", ":xhr_streaming"],
739
- ":xhr_streaming",
740
- ":xdr_streaming"
741
- ]
742
- ]],
743
- [":def", "polling_loop", [":sequential", ":timeouts",
744
- [":if", [":is_supported", ":xhr_polling"],
745
- ":xhr_polling",
746
- ":xdr_polling"
747
- ]
748
- ]],
749
-
750
- [":def", "http_loop", [":if", [":is_supported", ":streaming_loop"], [
751
- ":best_connected_ever",
752
- ":streaming_loop",
753
- [":delayed", 4000, [":polling_loop"]]
754
- ], [
755
- ":polling_loop"
756
- ]]],
757
-
758
- [":def", "http_fallback_loop",
759
- [":if", [":is_supported", ":http_loop"], [
760
- ":http_loop"
761
- ], [
762
- ":sockjs_loop"
763
- ]]
764
- ],
765
-
766
- [":def", "strategy",
767
- [":cached", 1800000,
768
- [":first_connected",
769
- [":if", [":is_supported", ":ws"],
770
- wsStrategy,
771
- ":http_fallback_loop"
772
- ]
773
- ]
774
- ]
775
- ]
776
- ];
777
- };
778
- }).call(this);
779
-
780
- ;(function() {
781
- Pusher.getGlobalConfig = function() {
782
- return {
783
- wsHost: Pusher.host,
784
- wsPort: Pusher.ws_port,
785
- wssPort: Pusher.wss_port,
786
- httpHost: Pusher.sockjs_host,
787
- httpPort: Pusher.sockjs_http_port,
788
- httpsPort: Pusher.sockjs_https_port,
789
- httpPath: Pusher.sockjs_path,
790
- statsHost: Pusher.stats_host,
791
- authEndpoint: Pusher.channel_auth_endpoint,
792
- authTransport: Pusher.channel_auth_transport,
793
- // TODO make this consistent with other options in next major version
794
- activity_timeout: Pusher.activity_timeout,
795
- pong_timeout: Pusher.pong_timeout,
796
- unavailable_timeout: Pusher.unavailable_timeout
797
- };
798
- };
799
-
800
- Pusher.getClusterConfig = function(clusterName) {
801
- return {
802
- wsHost: "ws-" + clusterName + ".pusher.com",
803
- httpHost: "sockjs-" + clusterName + ".pusher.com"
804
- };
805
- };
806
- }).call(this);
807
-
808
- ;(function() {
809
- function buildExceptionClass(name) {
810
- var constructor = function(message) {
811
- Error.call(this, message);
812
- this.name = name;
813
- };
814
- Pusher.Util.extend(constructor.prototype, Error.prototype);
815
-
816
- return constructor;
817
- }
818
-
819
- /** Error classes used throughout pusher-js library. */
820
- Pusher.Errors = {
821
- BadEventName: buildExceptionClass("BadEventName"),
822
- RequestTimedOut: buildExceptionClass("RequestTimedOut"),
823
- TransportPriorityTooLow: buildExceptionClass("TransportPriorityTooLow"),
824
- TransportClosed: buildExceptionClass("TransportClosed"),
825
- UnsupportedTransport: buildExceptionClass("UnsupportedTransport"),
826
- UnsupportedStrategy: buildExceptionClass("UnsupportedStrategy")
827
- };
828
- }).call(this);
829
-
830
- ;(function() {
831
- /** Manages callback bindings and event emitting.
832
- *
833
- * @param Function failThrough called when no listeners are bound to an event
834
- */
835
- function EventsDispatcher(failThrough) {
836
- this.callbacks = new CallbackRegistry();
837
- this.global_callbacks = [];
838
- this.failThrough = failThrough;
839
- }
840
- var prototype = EventsDispatcher.prototype;
841
-
842
- prototype.bind = function(eventName, callback, context) {
843
- this.callbacks.add(eventName, callback, context);
844
- return this;
845
- };
846
-
847
- prototype.bind_all = function(callback) {
848
- this.global_callbacks.push(callback);
849
- return this;
850
- };
851
-
852
- prototype.unbind = function(eventName, callback, context) {
853
- this.callbacks.remove(eventName, callback, context);
854
- return this;
855
- };
856
-
857
- prototype.unbind_all = function(eventName, callback) {
858
- this.callbacks.remove(eventName, callback);
859
- return this;
860
- };
861
-
862
- prototype.emit = function(eventName, data) {
863
- var i;
864
-
865
- for (i = 0; i < this.global_callbacks.length; i++) {
866
- this.global_callbacks[i](eventName, data);
867
- }
868
-
869
- var callbacks = this.callbacks.get(eventName);
870
- if (callbacks && callbacks.length > 0) {
871
- for (i = 0; i < callbacks.length; i++) {
872
- callbacks[i].fn.call(callbacks[i].context || window, data);
873
- }
874
- } else if (this.failThrough) {
875
- this.failThrough(eventName, data);
876
- }
877
-
878
- return this;
879
- };
880
-
881
- /** Callback registry helper. */
882
-
883
- function CallbackRegistry() {
884
- this._callbacks = {};
885
- }
886
-
887
- CallbackRegistry.prototype.get = function(name) {
888
- return this._callbacks[prefix(name)];
889
- };
890
-
891
- CallbackRegistry.prototype.add = function(name, callback, context) {
892
- var prefixedEventName = prefix(name);
893
- this._callbacks[prefixedEventName] = this._callbacks[prefixedEventName] || [];
894
- this._callbacks[prefixedEventName].push({
895
- fn: callback,
896
- context: context
897
- });
898
- };
899
-
900
- CallbackRegistry.prototype.remove = function(name, callback, context) {
901
- if (!name && !callback && !context) {
902
- this._callbacks = {};
903
- return;
904
- }
905
-
906
- var names = name ? [prefix(name)] : Pusher.Util.keys(this._callbacks);
907
-
908
- if (callback || context) {
909
- Pusher.Util.apply(names, function(name) {
910
- this._callbacks[name] = Pusher.Util.filter(
911
- this._callbacks[name] || [],
912
- function(binding) {
913
- return (callback && callback !== binding.fn) ||
914
- (context && context !== binding.context);
915
- }
916
- );
917
- if (this._callbacks[name].length === 0) {
918
- delete this._callbacks[name];
919
- }
920
- }, this);
921
- } else {
922
- Pusher.Util.apply(names, function(name) {
923
- delete this._callbacks[name];
924
- }, this);
925
- }
926
- };
927
-
928
- function prefix(name) {
929
- return "_" + name;
930
- }
931
-
932
- Pusher.EventsDispatcher = EventsDispatcher;
933
- }).call(this);
934
-
935
- (function() {
936
- /** Builds receivers for JSONP and Script requests.
937
- *
938
- * Each receiver is an object with following fields:
939
- * - number - unique (for the factory instance), numerical id of the receiver
940
- * - id - a string ID that can be used in DOM attributes
941
- * - name - name of the function triggering the receiver
942
- * - callback - callback function
943
- *
944
- * Receivers are triggered only once, on the first callback call.
945
- *
946
- * Receivers can be called by their name or by accessing factory object
947
- * by the number key.
948
- *
949
- * @param {String} prefix the prefix used in ids
950
- * @param {String} name the name of the object
951
- */
952
- function ScriptReceiverFactory(prefix, name) {
953
- this.lastId = 0;
954
- this.prefix = prefix;
955
- this.name = name;
956
- }
957
- var prototype = ScriptReceiverFactory.prototype;
958
-
959
- /** Creates a script receiver.
960
- *
961
- * @param {Function} callback
962
- * @return {ScriptReceiver}
963
- */
964
- prototype.create = function(callback) {
965
- this.lastId++;
966
-
967
- var number = this.lastId;
968
- var id = this.prefix + number;
969
- var name = this.name + "[" + number + "]";
970
-
971
- var called = false;
972
- var callbackWrapper = function() {
973
- if (!called) {
974
- callback.apply(null, arguments);
975
- called = true;
976
- }
977
- };
978
-
979
- this[number] = callbackWrapper;
980
- return { number: number, id: id, name: name, callback: callbackWrapper };
981
- };
982
-
983
- /** Removes the script receiver from the list.
984
- *
985
- * @param {ScriptReceiver} receiver
986
- */
987
- prototype.remove = function(receiver) {
988
- delete this[receiver.number];
989
- };
990
-
991
- Pusher.ScriptReceiverFactory = ScriptReceiverFactory;
992
- Pusher.ScriptReceivers = new ScriptReceiverFactory(
993
- "_pusher_script_", "Pusher.ScriptReceivers"
994
- );
995
- }).call(this);
996
-
997
- (function() {
998
- /** Sends a generic HTTP GET request using a script tag.
999
- *
1000
- * By constructing URL in a specific way, it can be used for loading
1001
- * JavaScript resources or JSONP requests. It can notify about errors, but
1002
- * only in certain environments. Please take care of monitoring the state of
1003
- * the request yourself.
1004
- *
1005
- * @param {String} src
1006
- */
1007
- function ScriptRequest(src) {
1008
- this.src = src;
1009
- }
1010
- var prototype = ScriptRequest.prototype;
1011
-
1012
- /** Sends the actual script request.
1013
- *
1014
- * @param {ScriptReceiver} receiver
1015
- */
1016
- prototype.send = function(receiver) {
1017
- var self = this;
1018
- var errorString = "Error loading " + self.src;
1019
-
1020
- self.script = document.createElement("script");
1021
- self.script.id = receiver.id;
1022
- self.script.src = self.src;
1023
- self.script.type = "text/javascript";
1024
- self.script.charset = "UTF-8";
1025
-
1026
- if (self.script.addEventListener) {
1027
- self.script.onerror = function() {
1028
- receiver.callback(errorString);
1029
- };
1030
- self.script.onload = function() {
1031
- receiver.callback(null);
1032
- };
1033
- } else {
1034
- self.script.onreadystatechange = function() {
1035
- if (self.script.readyState === 'loaded' ||
1036
- self.script.readyState === 'complete') {
1037
- receiver.callback(null);
1038
- }
1039
- };
1040
- }
1041
-
1042
- // Opera<11.6 hack for missing onerror callback
1043
- if (self.script.async === undefined && document.attachEvent &&
1044
- /opera/i.test(navigator.userAgent)) {
1045
- self.errorScript = document.createElement("script");
1046
- self.errorScript.id = receiver.id + "_error";
1047
- self.errorScript.text = receiver.name + "('" + errorString + "');";
1048
- self.script.async = self.errorScript.async = false;
1049
- } else {
1050
- self.script.async = true;
1051
- }
1052
-
1053
- var head = document.getElementsByTagName('head')[0];
1054
- head.insertBefore(self.script, head.firstChild);
1055
- if (self.errorScript) {
1056
- head.insertBefore(self.errorScript, self.script.nextSibling);
1057
- }
1058
- };
1059
-
1060
- /** Cleans up the DOM remains of the script request. */
1061
- prototype.cleanup = function() {
1062
- if (this.script) {
1063
- this.script.onload = this.script.onerror = null;
1064
- this.script.onreadystatechange = null;
1065
- }
1066
- if (this.script && this.script.parentNode) {
1067
- this.script.parentNode.removeChild(this.script);
1068
- }
1069
- if (this.errorScript && this.errorScript.parentNode) {
1070
- this.errorScript.parentNode.removeChild(this.errorScript);
1071
- }
1072
- this.script = null;
1073
- this.errorScript = null;
1074
- };
1075
-
1076
- Pusher.ScriptRequest = ScriptRequest;
1077
- }).call(this);
1078
-
1079
- ;(function() {
1080
- /** Handles loading dependency files.
1081
- *
1082
- * Dependency loaders don't remember whether a resource has been loaded or
1083
- * not. It is caller's responsibility to make sure the resource is not loaded
1084
- * twice. This is because it's impossible to detect resource loading status
1085
- * without knowing its content.
1086
- *
1087
- * Options:
1088
- * - cdn_http - url to HTTP CND
1089
- * - cdn_https - url to HTTPS CDN
1090
- * - version - version of pusher-js
1091
- * - suffix - suffix appended to all names of dependency files
1092
- *
1093
- * @param {Object} options
1094
- */
1095
- function DependencyLoader(options) {
1096
- this.options = options;
1097
- this.receivers = options.receivers || Pusher.ScriptReceivers;
1098
- this.loading = {};
1099
- }
1100
- var prototype = DependencyLoader.prototype;
1101
-
1102
- /** Loads the dependency from CDN.
1103
- *
1104
- * @param {String} name
1105
- * @param {Function} callback
1106
- */
1107
- prototype.load = function(name, options, callback) {
1108
- var self = this;
1109
-
1110
- if (self.loading[name] && self.loading[name].length > 0) {
1111
- self.loading[name].push(callback);
1112
- } else {
1113
- self.loading[name] = [callback];
1114
-
1115
- var request = new Pusher.ScriptRequest(self.getPath(name, options));
1116
- var receiver = self.receivers.create(function(error) {
1117
- self.receivers.remove(receiver);
1118
-
1119
- if (self.loading[name]) {
1120
- var callbacks = self.loading[name];
1121
- delete self.loading[name];
1122
-
1123
- var successCallback = function(wasSuccessful) {
1124
- if (!wasSuccessful) {
1125
- request.cleanup();
1126
- }
1127
- };
1128
- for (var i = 0; i < callbacks.length; i++) {
1129
- callbacks[i](error, successCallback);
1130
- }
1131
- }
1132
- });
1133
- request.send(receiver);
1134
- }
1135
- };
1136
-
1137
- /** Returns a root URL for pusher-js CDN.
1138
- *
1139
- * @returns {String}
1140
- */
1141
- prototype.getRoot = function(options) {
1142
- var cdn;
1143
- var protocol = Pusher.Util.getDocument().location.protocol;
1144
- if ((options && options.encrypted) || protocol === "https:") {
1145
- cdn = this.options.cdn_https;
1146
- } else {
1147
- cdn = this.options.cdn_http;
1148
- }
1149
- // make sure there are no double slashes
1150
- return cdn.replace(/\/*$/, "") + "/" + this.options.version;
1151
- };
1152
-
1153
- /** Returns a full path to a dependency file.
1154
- *
1155
- * @param {String} name
1156
- * @returns {String}
1157
- */
1158
- prototype.getPath = function(name, options) {
1159
- return this.getRoot(options) + '/' + name + this.options.suffix + '.js';
1160
- };
1161
-
1162
- Pusher.DependencyLoader = DependencyLoader;
1163
- }).call(this);
1164
-
1165
- ;(function() {
1166
- Pusher.DependenciesReceivers = new Pusher.ScriptReceiverFactory(
1167
- "_pusher_dependencies", "Pusher.DependenciesReceivers"
1168
- );
1169
- Pusher.Dependencies = new Pusher.DependencyLoader({
1170
- cdn_http: Pusher.cdn_http,
1171
- cdn_https: Pusher.cdn_https,
1172
- version: Pusher.VERSION,
1173
- suffix: Pusher.dependency_suffix,
1174
- receivers: Pusher.DependenciesReceivers
1175
- });
1176
-
1177
- function initialize() {
1178
- Pusher.ready();
1179
- }
1180
-
1181
- // Allows calling a function when the document body is available
1182
- function onDocumentBody(callback) {
1183
- if (document.body) {
1184
- callback();
1185
- } else {
1186
- setTimeout(function() {
1187
- onDocumentBody(callback);
1188
- }, 0);
1189
- }
1190
- }
1191
-
1192
- function initializeOnDocumentBody() {
1193
- onDocumentBody(initialize);
1194
- }
1195
-
1196
- if (!window.JSON) {
1197
- Pusher.Dependencies.load("json2", {}, initializeOnDocumentBody);
1198
- } else {
1199
- initializeOnDocumentBody();
1200
- }
1201
- })();
1202
-
1203
- (function() {
1204
-
1205
- var Base64 = {
1206
- encode: function (s) {
1207
- return btoa(utob(s));
1208
- }
1209
- };
1210
-
1211
- var fromCharCode = String.fromCharCode;
1212
-
1213
- var b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
1214
- var b64tab = {};
1215
-
1216
- for (var i = 0, l = b64chars.length; i < l; i++) {
1217
- b64tab[b64chars.charAt(i)] = i;
1218
- }
1219
-
1220
- var cb_utob = function(c) {
1221
- var cc = c.charCodeAt(0);
1222
- return cc < 0x80 ? c
1223
- : cc < 0x800 ? fromCharCode(0xc0 | (cc >>> 6)) +
1224
- fromCharCode(0x80 | (cc & 0x3f))
1225
- : fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) +
1226
- fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) +
1227
- fromCharCode(0x80 | ( cc & 0x3f));
1228
- };
1229
-
1230
- var utob = function(u) {
1231
- return u.replace(/[^\x00-\x7F]/g, cb_utob);
1232
- };
1233
-
1234
- var cb_encode = function(ccc) {
1235
- var padlen = [0, 2, 1][ccc.length % 3];
1236
- var ord = ccc.charCodeAt(0) << 16
1237
- | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)
1238
- | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0));
1239
- var chars = [
1240
- b64chars.charAt( ord >>> 18),
1241
- b64chars.charAt((ord >>> 12) & 63),
1242
- padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),
1243
- padlen >= 1 ? '=' : b64chars.charAt(ord & 63)
1244
- ];
1245
- return chars.join('');
1246
- };
1247
-
1248
- var btoa = window.btoa || function(b) {
1249
- return b.replace(/[\s\S]{1,3}/g, cb_encode);
1250
- };
1251
-
1252
- Pusher.Base64 = Base64;
1253
-
1254
- }).call(this);
1255
-
1256
- (function() {
1257
- /** Sends data via JSONP.
1258
- *
1259
- * Data is a key-value map. Its values are JSON-encoded and then passed
1260
- * through base64. Finally, keys and encoded values are appended to the query
1261
- * string.
1262
- *
1263
- * The class itself does not guarantee raising errors on failures, as it's not
1264
- * possible to support such feature on all browsers. Instead, JSONP endpoint
1265
- * should call back in a way that's easy to distinguish from browser calls,
1266
- * for example by passing a second argument to the receiver.
1267
- *
1268
- * @param {String} url
1269
- * @param {Object} data key-value map of data to be submitted
1270
- */
1271
- function JSONPRequest(url, data) {
1272
- this.url = url;
1273
- this.data = data;
1274
- }
1275
- var prototype = JSONPRequest.prototype;
1276
-
1277
- /** Sends the actual JSONP request.
1278
- *
1279
- * @param {ScriptReceiver} receiver
1280
- */
1281
- prototype.send = function(receiver) {
1282
- if (this.request) {
1283
- return;
1284
- }
1285
-
1286
- var params = Pusher.Util.filterObject(this.data, function(value) {
1287
- return value !== undefined;
1288
- });
1289
- var query = Pusher.Util.map(
1290
- Pusher.Util.flatten(encodeParamsObject(params)),
1291
- Pusher.Util.method("join", "=")
1292
- ).join("&");
1293
- var url = this.url + "/" + receiver.number + "?" + query;
1294
-
1295
- this.request = new Pusher.ScriptRequest(url);
1296
- this.request.send(receiver);
1297
- };
1298
-
1299
- /** Cleans up the DOM remains of the JSONP request. */
1300
- prototype.cleanup = function() {
1301
- if (this.request) {
1302
- this.request.cleanup();
1303
- }
1304
- };
1305
-
1306
- function encodeParamsObject(data) {
1307
- return Pusher.Util.mapObject(data, function(value) {
1308
- if (typeof value === "object") {
1309
- value = JSON.stringify(value);
1310
- }
1311
- return encodeURIComponent(Pusher.Base64.encode(value.toString()));
1312
- });
1313
- }
1314
-
1315
- Pusher.JSONPRequest = JSONPRequest;
1316
- }).call(this);
1317
-
1318
- (function() {
1319
- function Timeline(key, session, options) {
1320
- this.key = key;
1321
- this.session = session;
1322
- this.events = [];
1323
- this.options = options || {};
1324
- this.sent = 0;
1325
- this.uniqueID = 0;
1326
- }
1327
- var prototype = Timeline.prototype;
1328
-
1329
- // Log levels
1330
- Timeline.ERROR = 3;
1331
- Timeline.INFO = 6;
1332
- Timeline.DEBUG = 7;
1333
-
1334
- prototype.log = function(level, event) {
1335
- if (level <= this.options.level) {
1336
- this.events.push(
1337
- Pusher.Util.extend({}, event, { timestamp: Pusher.Util.now() })
1338
- );
1339
- if (this.options.limit && this.events.length > this.options.limit) {
1340
- this.events.shift();
1341
- }
1342
- }
1343
- };
1344
-
1345
- prototype.error = function(event) {
1346
- this.log(Timeline.ERROR, event);
1347
- };
1348
-
1349
- prototype.info = function(event) {
1350
- this.log(Timeline.INFO, event);
1351
- };
1352
-
1353
- prototype.debug = function(event) {
1354
- this.log(Timeline.DEBUG, event);
1355
- };
1356
-
1357
- prototype.isEmpty = function() {
1358
- return this.events.length === 0;
1359
- };
1360
-
1361
- prototype.send = function(sendJSONP, callback) {
1362
- var self = this;
1363
-
1364
- var data = Pusher.Util.extend({
1365
- session: self.session,
1366
- bundle: self.sent + 1,
1367
- key: self.key,
1368
- lib: "js",
1369
- version: self.options.version,
1370
- cluster: self.options.cluster,
1371
- features: self.options.features,
1372
- timeline: self.events
1373
- }, self.options.params);
1374
-
1375
- self.events = [];
1376
- sendJSONP(data, function(error, result) {
1377
- if (!error) {
1378
- self.sent++;
1379
- }
1380
- if (callback) {
1381
- callback(error, result);
1382
- }
1383
- });
1384
-
1385
- return true;
1386
- };
1387
-
1388
- prototype.generateUniqueID = function() {
1389
- this.uniqueID++;
1390
- return this.uniqueID;
1391
- };
1392
-
1393
- Pusher.Timeline = Timeline;
1394
- }).call(this);
1395
-
1396
- (function() {
1397
- function TimelineSender(timeline, options) {
1398
- this.timeline = timeline;
1399
- this.options = options || {};
1400
- }
1401
- var prototype = TimelineSender.prototype;
1402
-
1403
- prototype.send = function(encrypted, callback) {
1404
- var self = this;
1405
-
1406
- if (self.timeline.isEmpty()) {
1407
- return;
1408
- }
1409
-
1410
- var sendJSONP = function(data, callback) {
1411
- var scheme = "http" + (encrypted ? "s" : "") + "://";
1412
- var url = scheme + (self.host || self.options.host) + self.options.path;
1413
- var request = new Pusher.JSONPRequest(url, data);
1414
-
1415
- var receiver = Pusher.ScriptReceivers.create(function(error, result) {
1416
- Pusher.ScriptReceivers.remove(receiver);
1417
- request.cleanup();
1418
-
1419
- if (result && result.host) {
1420
- self.host = result.host;
1421
- }
1422
- if (callback) {
1423
- callback(error, result);
1424
- }
1425
- });
1426
- request.send(receiver);
1427
- };
1428
- self.timeline.send(sendJSONP, callback);
1429
- };
1430
-
1431
- Pusher.TimelineSender = TimelineSender;
1432
- }).call(this);
1433
-
1434
- ;(function() {
1435
- /** Launches all substrategies and emits prioritized connected transports.
1436
- *
1437
- * @param {Array} strategies
1438
- */
1439
- function BestConnectedEverStrategy(strategies) {
1440
- this.strategies = strategies;
1441
- }
1442
- var prototype = BestConnectedEverStrategy.prototype;
1443
-
1444
- prototype.isSupported = function() {
1445
- return Pusher.Util.any(this.strategies, Pusher.Util.method("isSupported"));
1446
- };
1447
-
1448
- prototype.connect = function(minPriority, callback) {
1449
- return connect(this.strategies, minPriority, function(i, runners) {
1450
- return function(error, handshake) {
1451
- runners[i].error = error;
1452
- if (error) {
1453
- if (allRunnersFailed(runners)) {
1454
- callback(true);
1455
- }
1456
- return;
1457
- }
1458
- Pusher.Util.apply(runners, function(runner) {
1459
- runner.forceMinPriority(handshake.transport.priority);
1460
- });
1461
- callback(null, handshake);
1462
- };
1463
- });
1464
- };
1465
-
1466
- /** Connects to all strategies in parallel.
1467
- *
1468
- * Callback builder should be a function that takes two arguments: index
1469
- * and a list of runners. It should return another function that will be
1470
- * passed to the substrategy with given index. Runners can be aborted using
1471
- * abortRunner(s) functions from this class.
1472
- *
1473
- * @param {Array} strategies
1474
- * @param {Function} callbackBuilder
1475
- * @return {Object} strategy runner
1476
- */
1477
- function connect(strategies, minPriority, callbackBuilder) {
1478
- var runners = Pusher.Util.map(strategies, function(strategy, i, _, rs) {
1479
- return strategy.connect(minPriority, callbackBuilder(i, rs));
1480
- });
1481
- return {
1482
- abort: function() {
1483
- Pusher.Util.apply(runners, abortRunner);
1484
- },
1485
- forceMinPriority: function(p) {
1486
- Pusher.Util.apply(runners, function(runner) {
1487
- runner.forceMinPriority(p);
1488
- });
1489
- }
1490
- };
1491
- }
1492
-
1493
- function allRunnersFailed(runners) {
1494
- return Pusher.Util.all(runners, function(runner) {
1495
- return Boolean(runner.error);
1496
- });
1497
- }
1498
-
1499
- function abortRunner(runner) {
1500
- if (!runner.error && !runner.aborted) {
1501
- runner.abort();
1502
- runner.aborted = true;
1503
- }
1504
- }
1505
-
1506
- Pusher.BestConnectedEverStrategy = BestConnectedEverStrategy;
1507
- }).call(this);
1508
-
1509
- ;(function() {
1510
- /** Caches last successful transport and uses it for following attempts.
1511
- *
1512
- * @param {Strategy} strategy
1513
- * @param {Object} transports
1514
- * @param {Object} options
1515
- */
1516
- function CachedStrategy(strategy, transports, options) {
1517
- this.strategy = strategy;
1518
- this.transports = transports;
1519
- this.ttl = options.ttl || 1800*1000;
1520
- this.encrypted = options.encrypted;
1521
- this.timeline = options.timeline;
1522
- }
1523
- var prototype = CachedStrategy.prototype;
1524
-
1525
- prototype.isSupported = function() {
1526
- return this.strategy.isSupported();
1527
- };
1528
-
1529
- prototype.connect = function(minPriority, callback) {
1530
- var encrypted = this.encrypted;
1531
- var info = fetchTransportCache(encrypted);
1532
-
1533
- var strategies = [this.strategy];
1534
- if (info && info.timestamp + this.ttl >= Pusher.Util.now()) {
1535
- var transport = this.transports[info.transport];
1536
- if (transport) {
1537
- this.timeline.info({
1538
- cached: true,
1539
- transport: info.transport,
1540
- latency: info.latency
1541
- });
1542
- strategies.push(new Pusher.SequentialStrategy([transport], {
1543
- timeout: info.latency * 2 + 1000,
1544
- failFast: true
1545
- }));
1546
- }
1547
- }
1548
-
1549
- var startTimestamp = Pusher.Util.now();
1550
- var runner = strategies.pop().connect(
1551
- minPriority,
1552
- function cb(error, handshake) {
1553
- if (error) {
1554
- flushTransportCache(encrypted);
1555
- if (strategies.length > 0) {
1556
- startTimestamp = Pusher.Util.now();
1557
- runner = strategies.pop().connect(minPriority, cb);
1558
- } else {
1559
- callback(error);
1560
- }
1561
- } else {
1562
- storeTransportCache(
1563
- encrypted,
1564
- handshake.transport.name,
1565
- Pusher.Util.now() - startTimestamp
1566
- );
1567
- callback(null, handshake);
1568
- }
1569
- }
1570
- );
1571
-
1572
- return {
1573
- abort: function() {
1574
- runner.abort();
1575
- },
1576
- forceMinPriority: function(p) {
1577
- minPriority = p;
1578
- if (runner) {
1579
- runner.forceMinPriority(p);
1580
- }
1581
- }
1582
- };
1583
- };
1584
-
1585
- function getTransportCacheKey(encrypted) {
1586
- return "pusherTransport" + (encrypted ? "Encrypted" : "Unencrypted");
1587
- }
1588
-
1589
- function fetchTransportCache(encrypted) {
1590
- var storage = Pusher.Util.getLocalStorage();
1591
- if (storage) {
1592
- try {
1593
- var serializedCache = storage[getTransportCacheKey(encrypted)];
1594
- if (serializedCache) {
1595
- return JSON.parse(serializedCache);
1596
- }
1597
- } catch (e) {
1598
- flushTransportCache(encrypted);
1599
- }
1600
- }
1601
- return null;
1602
- }
1603
-
1604
- function storeTransportCache(encrypted, transport, latency) {
1605
- var storage = Pusher.Util.getLocalStorage();
1606
- if (storage) {
1607
- try {
1608
- storage[getTransportCacheKey(encrypted)] = JSON.stringify({
1609
- timestamp: Pusher.Util.now(),
1610
- transport: transport,
1611
- latency: latency
1612
- });
1613
- } catch (e) {
1614
- // catch over quota exceptions raised by localStorage
1615
- }
1616
- }
1617
- }
1618
-
1619
- function flushTransportCache(encrypted) {
1620
- var storage = Pusher.Util.getLocalStorage();
1621
- if (storage) {
1622
- try {
1623
- delete storage[getTransportCacheKey(encrypted)];
1624
- } catch (e) {
1625
- // catch exceptions raised by localStorage
1626
- }
1627
- }
1628
- }
1629
-
1630
- Pusher.CachedStrategy = CachedStrategy;
1631
- }).call(this);
1632
-
1633
- ;(function() {
1634
- /** Runs substrategy after specified delay.
1635
- *
1636
- * Options:
1637
- * - delay - time in miliseconds to delay the substrategy attempt
1638
- *
1639
- * @param {Strategy} strategy
1640
- * @param {Object} options
1641
- */
1642
- function DelayedStrategy(strategy, options) {
1643
- this.strategy = strategy;
1644
- this.options = { delay: options.delay };
1645
- }
1646
- var prototype = DelayedStrategy.prototype;
1647
-
1648
- prototype.isSupported = function() {
1649
- return this.strategy.isSupported();
1650
- };
1651
-
1652
- prototype.connect = function(minPriority, callback) {
1653
- var strategy = this.strategy;
1654
- var runner;
1655
- var timer = new Pusher.Timer(this.options.delay, function() {
1656
- runner = strategy.connect(minPriority, callback);
1657
- });
1658
-
1659
- return {
1660
- abort: function() {
1661
- timer.ensureAborted();
1662
- if (runner) {
1663
- runner.abort();
1664
- }
1665
- },
1666
- forceMinPriority: function(p) {
1667
- minPriority = p;
1668
- if (runner) {
1669
- runner.forceMinPriority(p);
1670
- }
1671
- }
1672
- };
1673
- };
1674
-
1675
- Pusher.DelayedStrategy = DelayedStrategy;
1676
- }).call(this);
1677
-
1678
- ;(function() {
1679
- /** Launches the substrategy and terminates on the first open connection.
1680
- *
1681
- * @param {Strategy} strategy
1682
- */
1683
- function FirstConnectedStrategy(strategy) {
1684
- this.strategy = strategy;
1685
- }
1686
- var prototype = FirstConnectedStrategy.prototype;
1687
-
1688
- prototype.isSupported = function() {
1689
- return this.strategy.isSupported();
1690
- };
1691
-
1692
- prototype.connect = function(minPriority, callback) {
1693
- var runner = this.strategy.connect(
1694
- minPriority,
1695
- function(error, handshake) {
1696
- if (handshake) {
1697
- runner.abort();
1698
- }
1699
- callback(error, handshake);
1700
- }
1701
- );
1702
- return runner;
1703
- };
1704
-
1705
- Pusher.FirstConnectedStrategy = FirstConnectedStrategy;
1706
- }).call(this);
1707
-
1708
- ;(function() {
1709
- /** Proxies method calls to one of substrategies basing on the test function.
1710
- *
1711
- * @param {Function} test
1712
- * @param {Strategy} trueBranch strategy used when test returns true
1713
- * @param {Strategy} falseBranch strategy used when test returns false
1714
- */
1715
- function IfStrategy(test, trueBranch, falseBranch) {
1716
- this.test = test;
1717
- this.trueBranch = trueBranch;
1718
- this.falseBranch = falseBranch;
1719
- }
1720
- var prototype = IfStrategy.prototype;
1721
-
1722
- prototype.isSupported = function() {
1723
- var branch = this.test() ? this.trueBranch : this.falseBranch;
1724
- return branch.isSupported();
1725
- };
1726
-
1727
- prototype.connect = function(minPriority, callback) {
1728
- var branch = this.test() ? this.trueBranch : this.falseBranch;
1729
- return branch.connect(minPriority, callback);
1730
- };
1731
-
1732
- Pusher.IfStrategy = IfStrategy;
1733
- }).call(this);
1734
-
1735
- ;(function() {
1736
- /** Loops through strategies with optional timeouts.
1737
- *
1738
- * Options:
1739
- * - loop - whether it should loop through the substrategy list
1740
- * - timeout - initial timeout for a single substrategy
1741
- * - timeoutLimit - maximum timeout
1742
- *
1743
- * @param {Strategy[]} strategies
1744
- * @param {Object} options
1745
- */
1746
- function SequentialStrategy(strategies, options) {
1747
- this.strategies = strategies;
1748
- this.loop = Boolean(options.loop);
1749
- this.failFast = Boolean(options.failFast);
1750
- this.timeout = options.timeout;
1751
- this.timeoutLimit = options.timeoutLimit;
1752
- }
1753
- var prototype = SequentialStrategy.prototype;
1754
-
1755
- prototype.isSupported = function() {
1756
- return Pusher.Util.any(this.strategies, Pusher.Util.method("isSupported"));
1757
- };
1758
-
1759
- prototype.connect = function(minPriority, callback) {
1760
- var self = this;
1761
-
1762
- var strategies = this.strategies;
1763
- var current = 0;
1764
- var timeout = this.timeout;
1765
- var runner = null;
1766
-
1767
- var tryNextStrategy = function(error, handshake) {
1768
- if (handshake) {
1769
- callback(null, handshake);
1770
- } else {
1771
- current = current + 1;
1772
- if (self.loop) {
1773
- current = current % strategies.length;
1774
- }
1775
-
1776
- if (current < strategies.length) {
1777
- if (timeout) {
1778
- timeout = timeout * 2;
1779
- if (self.timeoutLimit) {
1780
- timeout = Math.min(timeout, self.timeoutLimit);
1781
- }
1782
- }
1783
- runner = self.tryStrategy(
1784
- strategies[current],
1785
- minPriority,
1786
- { timeout: timeout, failFast: self.failFast },
1787
- tryNextStrategy
1788
- );
1789
- } else {
1790
- callback(true);
1791
- }
1792
- }
1793
- };
1794
-
1795
- runner = this.tryStrategy(
1796
- strategies[current],
1797
- minPriority,
1798
- { timeout: timeout, failFast: this.failFast },
1799
- tryNextStrategy
1800
- );
1801
-
1802
- return {
1803
- abort: function() {
1804
- runner.abort();
1805
- },
1806
- forceMinPriority: function(p) {
1807
- minPriority = p;
1808
- if (runner) {
1809
- runner.forceMinPriority(p);
1810
- }
1811
- }
1812
- };
1813
- };
1814
-
1815
- /** @private */
1816
- prototype.tryStrategy = function(strategy, minPriority, options, callback) {
1817
- var timer = null;
1818
- var runner = null;
1819
-
1820
- if (options.timeout > 0) {
1821
- timer = new Pusher.Timer(options.timeout, function() {
1822
- runner.abort();
1823
- callback(true);
1824
- });
1825
- }
1826
-
1827
- runner = strategy.connect(minPriority, function(error, handshake) {
1828
- if (error && timer && timer.isRunning() && !options.failFast) {
1829
- // advance to the next strategy after the timeout
1830
- return;
1831
- }
1832
- if (timer) {
1833
- timer.ensureAborted();
1834
- }
1835
- callback(error, handshake);
1836
- });
1837
-
1838
- return {
1839
- abort: function() {
1840
- if (timer) {
1841
- timer.ensureAborted();
1842
- }
1843
- runner.abort();
1844
- },
1845
- forceMinPriority: function(p) {
1846
- runner.forceMinPriority(p);
1847
- }
1848
- };
1849
- };
1850
-
1851
- Pusher.SequentialStrategy = SequentialStrategy;
1852
- }).call(this);
1853
-
1854
- ;(function() {
1855
- /** Provides a strategy interface for transports.
1856
- *
1857
- * @param {String} name
1858
- * @param {Number} priority
1859
- * @param {Class} transport
1860
- * @param {Object} options
1861
- */
1862
- function TransportStrategy(name, priority, transport, options) {
1863
- this.name = name;
1864
- this.priority = priority;
1865
- this.transport = transport;
1866
- this.options = options || {};
1867
- }
1868
- var prototype = TransportStrategy.prototype;
1869
-
1870
- /** Returns whether the transport is supported in the browser.
1871
- *
1872
- * @returns {Boolean}
1873
- */
1874
- prototype.isSupported = function() {
1875
- return this.transport.isSupported({
1876
- encrypted: this.options.encrypted
1877
- });
1878
- };
1879
-
1880
- /** Launches a connection attempt and returns a strategy runner.
1881
- *
1882
- * @param {Function} callback
1883
- * @return {Object} strategy runner
1884
- */
1885
- prototype.connect = function(minPriority, callback) {
1886
- if (!this.isSupported()) {
1887
- return failAttempt(new Pusher.Errors.UnsupportedStrategy(), callback);
1888
- } else if (this.priority < minPriority) {
1889
- return failAttempt(new Pusher.Errors.TransportPriorityTooLow(), callback);
1890
- }
1891
-
1892
- var self = this;
1893
- var connected = false;
1894
-
1895
- var transport = this.transport.createConnection(
1896
- this.name, this.priority, this.options.key, this.options
1897
- );
1898
- var handshake = null;
1899
-
1900
- var onInitialized = function() {
1901
- transport.unbind("initialized", onInitialized);
1902
- transport.connect();
1903
- };
1904
- var onOpen = function() {
1905
- handshake = new Pusher.Handshake(transport, function(result) {
1906
- connected = true;
1907
- unbindListeners();
1908
- callback(null, result);
1909
- });
1910
- };
1911
- var onError = function(error) {
1912
- unbindListeners();
1913
- callback(error);
1914
- };
1915
- var onClosed = function() {
1916
- unbindListeners();
1917
- callback(new Pusher.Errors.TransportClosed(transport));
1918
- };
1919
-
1920
- var unbindListeners = function() {
1921
- transport.unbind("initialized", onInitialized);
1922
- transport.unbind("open", onOpen);
1923
- transport.unbind("error", onError);
1924
- transport.unbind("closed", onClosed);
1925
- };
1926
-
1927
- transport.bind("initialized", onInitialized);
1928
- transport.bind("open", onOpen);
1929
- transport.bind("error", onError);
1930
- transport.bind("closed", onClosed);
1931
-
1932
- // connect will be called automatically after initialization
1933
- transport.initialize();
1934
-
1935
- return {
1936
- abort: function() {
1937
- if (connected) {
1938
- return;
1939
- }
1940
- unbindListeners();
1941
- if (handshake) {
1942
- handshake.close();
1943
- } else {
1944
- transport.close();
1945
- }
1946
- },
1947
- forceMinPriority: function(p) {
1948
- if (connected) {
1949
- return;
1950
- }
1951
- if (self.priority < p) {
1952
- if (handshake) {
1953
- handshake.close();
1954
- } else {
1955
- transport.close();
1956
- }
1957
- }
1958
- }
1959
- };
1960
- };
1961
-
1962
- function failAttempt(error, callback) {
1963
- Pusher.Util.defer(function() {
1964
- callback(error);
1965
- });
1966
- return {
1967
- abort: function() {},
1968
- forceMinPriority: function() {}
1969
- };
1970
- }
1971
-
1972
- Pusher.TransportStrategy = TransportStrategy;
1973
- }).call(this);
1974
-
1975
- (function() {
1976
- function getGenericURL(baseScheme, params, path) {
1977
- var scheme = baseScheme + (params.encrypted ? "s" : "");
1978
- var host = params.encrypted ? params.hostEncrypted : params.hostUnencrypted;
1979
- return scheme + "://" + host + path;
1980
- }
1981
-
1982
- function getGenericPath(key, queryString) {
1983
- var path = "/app/" + key;
1984
- var query =
1985
- "?protocol=" + Pusher.PROTOCOL +
1986
- "&client=js" +
1987
- "&version=" + Pusher.VERSION +
1988
- (queryString ? ("&" + queryString) : "");
1989
- return path + query;
1990
- }
1991
-
1992
- /** URL schemes for different transport types. */
1993
- Pusher.URLSchemes = {
1994
- /** Standard WebSocket URL scheme. */
1995
- ws: {
1996
- getInitial: function(key, params) {
1997
- return getGenericURL("ws", params, getGenericPath(key, "flash=false"));
1998
- }
1999
- },
2000
- /** SockJS URL scheme. Supplies the path separately from the initial URL. */
2001
- sockjs: {
2002
- getInitial: function(key, params) {
2003
- return getGenericURL("http", params, params.httpPath || "/pusher", "");
2004
- },
2005
- getPath: function(key, params) {
2006
- return getGenericPath(key);
2007
- }
2008
- },
2009
- /** URL scheme for HTTP transports. Basically, WS scheme with a prefix. */
2010
- http: {
2011
- getInitial: function(key, params) {
2012
- var path = (params.httpPath || "/pusher") + getGenericPath(key);
2013
- return getGenericURL("http", params, path);
2014
- }
2015
- }
2016
- };
2017
- }).call(this);
2018
-
2019
- (function() {
2020
- /** Provides universal API for transport connections.
2021
- *
2022
- * Transport connection is a low-level object that wraps a connection method
2023
- * and exposes a simple evented interface for the connection state and
2024
- * messaging. It does not implement Pusher-specific WebSocket protocol.
2025
- *
2026
- * Additionally, it fetches resources needed for transport to work and exposes
2027
- * an interface for querying transport features.
2028
- *
2029
- * States:
2030
- * - new - initial state after constructing the object
2031
- * - initializing - during initialization phase, usually fetching resources
2032
- * - intialized - ready to establish a connection
2033
- * - connection - when connection is being established
2034
- * - open - when connection ready to be used
2035
- * - closed - after connection was closed be either side
2036
- *
2037
- * Emits:
2038
- * - error - after the connection raised an error
2039
- *
2040
- * Options:
2041
- * - encrypted - whether connection should use ssl
2042
- * - hostEncrypted - host to connect to when connection is encrypted
2043
- * - hostUnencrypted - host to connect to when connection is not encrypted
2044
- *
2045
- * @param {String} key application key
2046
- * @param {Object} options
2047
- */
2048
- function TransportConnection(hooks, name, priority, key, options) {
2049
- Pusher.EventsDispatcher.call(this);
2050
-
2051
- this.hooks = hooks;
2052
- this.name = name;
2053
- this.priority = priority;
2054
- this.key = key;
2055
- this.options = options;
2056
-
2057
- this.state = "new";
2058
- this.timeline = options.timeline;
2059
- this.activityTimeout = options.activityTimeout;
2060
- this.id = this.timeline.generateUniqueID();
2061
- }
2062
- var prototype = TransportConnection.prototype;
2063
- Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
2064
-
2065
- /** Checks whether the transport handles activity checks by itself.
2066
- *
2067
- * @return {Boolean}
2068
- */
2069
- prototype.handlesActivityChecks = function() {
2070
- return Boolean(this.hooks.handlesActivityChecks);
2071
- };
2072
-
2073
- /** Checks whether the transport supports the ping/pong API.
2074
- *
2075
- * @return {Boolean}
2076
- */
2077
- prototype.supportsPing = function() {
2078
- return Boolean(this.hooks.supportsPing);
2079
- };
2080
-
2081
- /** Initializes the transport.
2082
- *
2083
- * Fetches resources if needed and then transitions to initialized.
2084
- */
2085
- prototype.initialize = function() {
2086
- var self = this;
2087
-
2088
- self.timeline.info(self.buildTimelineMessage({
2089
- transport: self.name + (self.options.encrypted ? "s" : "")
2090
- }));
2091
-
2092
- if (self.hooks.isInitialized()) {
2093
- self.changeState("initialized");
2094
- } else if (self.hooks.file) {
2095
- self.changeState("initializing");
2096
- Pusher.Dependencies.load(
2097
- self.hooks.file,
2098
- { encrypted: self.options.encrypted },
2099
- function(error, callback) {
2100
- if (self.hooks.isInitialized()) {
2101
- self.changeState("initialized");
2102
- callback(true);
2103
- } else {
2104
- if (error) {
2105
- self.onError(error);
2106
- }
2107
- self.onClose();
2108
- callback(false);
2109
- }
2110
- }
2111
- );
2112
- } else {
2113
- self.onClose();
2114
- }
2115
- };
2116
-
2117
- /** Tries to establish a connection.
2118
- *
2119
- * @returns {Boolean} false if transport is in invalid state
2120
- */
2121
- prototype.connect = function() {
2122
- var self = this;
2123
-
2124
- if (self.socket || self.state !== "initialized") {
2125
- return false;
2126
- }
2127
-
2128
- var url = self.hooks.urls.getInitial(self.key, self.options);
2129
- try {
2130
- self.socket = self.hooks.getSocket(url, self.options);
2131
- } catch (e) {
2132
- Pusher.Util.defer(function() {
2133
- self.onError(e);
2134
- self.changeState("closed");
2135
- });
2136
- return false;
2137
- }
2138
-
2139
- self.bindListeners();
2140
-
2141
- Pusher.debug("Connecting", { transport: self.name, url: url });
2142
- self.changeState("connecting");
2143
- return true;
2144
- };
2145
-
2146
- /** Closes the connection.
2147
- *
2148
- * @return {Boolean} true if there was a connection to close
2149
- */
2150
- prototype.close = function() {
2151
- if (this.socket) {
2152
- this.socket.close();
2153
- return true;
2154
- } else {
2155
- return false;
2156
- }
2157
- };
2158
-
2159
- /** Sends data over the open connection.
2160
- *
2161
- * @param {String} data
2162
- * @return {Boolean} true only when in the "open" state
2163
- */
2164
- prototype.send = function(data) {
2165
- var self = this;
2166
-
2167
- if (self.state === "open") {
2168
- // Workaround for MobileSafari bug (see https://gist.github.com/2052006)
2169
- Pusher.Util.defer(function() {
2170
- if (self.socket) {
2171
- self.socket.send(data);
2172
- }
2173
- });
2174
- return true;
2175
- } else {
2176
- return false;
2177
- }
2178
- };
2179
-
2180
- /** Sends a ping if the connection is open and transport supports it. */
2181
- prototype.ping = function() {
2182
- if (this.state === "open" && this.supportsPing()) {
2183
- this.socket.ping();
2184
- }
2185
- };
2186
-
2187
- /** @private */
2188
- prototype.onOpen = function() {
2189
- if (this.hooks.beforeOpen) {
2190
- this.hooks.beforeOpen(
2191
- this.socket, this.hooks.urls.getPath(this.key, this.options)
2192
- );
2193
- }
2194
- this.changeState("open");
2195
- this.socket.onopen = undefined;
2196
- };
2197
-
2198
- /** @private */
2199
- prototype.onError = function(error) {
2200
- this.emit("error", { type: 'WebSocketError', error: error });
2201
- this.timeline.error(this.buildTimelineMessage({ error: error.toString() }));
2202
- };
2203
-
2204
- /** @private */
2205
- prototype.onClose = function(closeEvent) {
2206
- if (closeEvent) {
2207
- this.changeState("closed", {
2208
- code: closeEvent.code,
2209
- reason: closeEvent.reason,
2210
- wasClean: closeEvent.wasClean
2211
- });
2212
- } else {
2213
- this.changeState("closed");
2214
- }
2215
- this.unbindListeners();
2216
- this.socket = undefined;
2217
- };
2218
-
2219
- /** @private */
2220
- prototype.onMessage = function(message) {
2221
- this.emit("message", message);
2222
- };
2223
-
2224
- /** @private */
2225
- prototype.onActivity = function() {
2226
- this.emit("activity");
2227
- };
2228
-
2229
- /** @private */
2230
- prototype.bindListeners = function() {
2231
- var self = this;
2232
-
2233
- self.socket.onopen = function() {
2234
- self.onOpen();
2235
- };
2236
- self.socket.onerror = function(error) {
2237
- self.onError(error);
2238
- };
2239
- self.socket.onclose = function(closeEvent) {
2240
- self.onClose(closeEvent);
2241
- };
2242
- self.socket.onmessage = function(message) {
2243
- self.onMessage(message);
2244
- };
2245
-
2246
- if (self.supportsPing()) {
2247
- self.socket.onactivity = function() { self.onActivity(); };
2248
- }
2249
- };
2250
-
2251
- /** @private */
2252
- prototype.unbindListeners = function() {
2253
- if (this.socket) {
2254
- this.socket.onopen = undefined;
2255
- this.socket.onerror = undefined;
2256
- this.socket.onclose = undefined;
2257
- this.socket.onmessage = undefined;
2258
- if (this.supportsPing()) {
2259
- this.socket.onactivity = undefined;
2260
- }
2261
- }
2262
- };
2263
-
2264
- /** @private */
2265
- prototype.changeState = function(state, params) {
2266
- this.state = state;
2267
- this.timeline.info(this.buildTimelineMessage({
2268
- state: state,
2269
- params: params
2270
- }));
2271
- this.emit(state, params);
2272
- };
2273
-
2274
- /** @private */
2275
- prototype.buildTimelineMessage = function(message) {
2276
- return Pusher.Util.extend({ cid: this.id }, message);
2277
- };
2278
-
2279
- Pusher.TransportConnection = TransportConnection;
2280
- }).call(this);
2281
-
2282
- (function() {
2283
- /** Provides interface for transport connection instantiation.
2284
- *
2285
- * Takes transport-specific hooks as the only argument, which allow checking
2286
- * for transport support and creating its connections.
2287
- *
2288
- * Supported hooks:
2289
- * - file - the name of the file to be fetched during initialization
2290
- * - urls - URL scheme to be used by transport
2291
- * - handlesActivityCheck - true when the transport handles activity checks
2292
- * - supportsPing - true when the transport has a ping/activity API
2293
- * - isSupported - tells whether the transport is supported in the environment
2294
- * - getSocket - creates a WebSocket-compatible transport socket
2295
- *
2296
- * See transports.js for specific implementations.
2297
- *
2298
- * @param {Object} hooks object containing all needed transport hooks
2299
- */
2300
- function Transport(hooks) {
2301
- this.hooks = hooks;
2302
- }
2303
- var prototype = Transport.prototype;
2304
-
2305
- /** Returns whether the transport is supported in the environment.
2306
- *
2307
- * @param {Object} environment the environment details (encryption, settings)
2308
- * @returns {Boolean} true when the transport is supported
2309
- */
2310
- prototype.isSupported = function(environment) {
2311
- return this.hooks.isSupported(environment);
2312
- };
2313
-
2314
- /** Creates a transport connection.
2315
- *
2316
- * @param {String} name
2317
- * @param {Number} priority
2318
- * @param {String} key the application key
2319
- * @param {Object} options
2320
- * @returns {TransportConnection}
2321
- */
2322
- prototype.createConnection = function(name, priority, key, options) {
2323
- return new Pusher.TransportConnection(
2324
- this.hooks, name, priority, key, options
2325
- );
2326
- };
2327
-
2328
- Pusher.Transport = Transport;
2329
- }).call(this);
2330
-
2331
- (function() {
2332
- /** WebSocket transport.
2333
- *
2334
- * Uses native WebSocket implementation, including MozWebSocket supported by
2335
- * earlier Firefox versions.
2336
- */
2337
- Pusher.WSTransport = new Pusher.Transport({
2338
- urls: Pusher.URLSchemes.ws,
2339
- handlesActivityChecks: false,
2340
- supportsPing: false,
2341
-
2342
- isInitialized: function() {
2343
- return Boolean(window.WebSocket || window.MozWebSocket);
2344
- },
2345
- isSupported: function() {
2346
- return Boolean(window.WebSocket || window.MozWebSocket);
2347
- },
2348
- getSocket: function(url) {
2349
- var Constructor = window.WebSocket || window.MozWebSocket;
2350
- return new Constructor(url);
2351
- }
2352
- });
2353
-
2354
- /** SockJS transport. */
2355
- Pusher.SockJSTransport = new Pusher.Transport({
2356
- file: "sockjs",
2357
- urls: Pusher.URLSchemes.sockjs,
2358
- handlesActivityChecks: true,
2359
- supportsPing: false,
2360
-
2361
- isSupported: function() {
2362
- return true;
2363
- },
2364
- isInitialized: function() {
2365
- return window.SockJS !== undefined;
2366
- },
2367
- getSocket: function(url, options) {
2368
- return new SockJS(url, null, {
2369
- js_path: Pusher.Dependencies.getPath("sockjs", {
2370
- encrypted: options.encrypted
2371
- }),
2372
- ignore_null_origin: options.ignoreNullOrigin
2373
- });
2374
- },
2375
- beforeOpen: function(socket, path) {
2376
- socket.send(JSON.stringify({
2377
- path: path
2378
- }));
2379
- }
2380
- });
2381
-
2382
- var httpConfiguration = {
2383
- urls: Pusher.URLSchemes.http,
2384
- handlesActivityChecks: false,
2385
- supportsPing: true,
2386
- isInitialized: function() {
2387
- return Boolean(Pusher.HTTP.Socket);
2388
- }
2389
- };
2390
-
2391
- var streamingConfiguration = Pusher.Util.extend(
2392
- { getSocket: function(url) {
2393
- return Pusher.HTTP.getStreamingSocket(url);
2394
- }
2395
- },
2396
- httpConfiguration
2397
- );
2398
- var pollingConfiguration = Pusher.Util.extend(
2399
- { getSocket: function(url) {
2400
- return Pusher.HTTP.getPollingSocket(url);
2401
- }
2402
- },
2403
- httpConfiguration
2404
- );
2405
-
2406
- var xhrConfiguration = {
2407
- file: "xhr",
2408
- isSupported: Pusher.Util.isXHRSupported
2409
- };
2410
- var xdrConfiguration = {
2411
- file: "xdr",
2412
- isSupported: function(environment) {
2413
- return Pusher.Util.isXDRSupported(environment.encrypted);
2414
- }
2415
- };
2416
-
2417
- /** HTTP streaming transport using CORS-enabled XMLHttpRequest. */
2418
- Pusher.XHRStreamingTransport = new Pusher.Transport(
2419
- Pusher.Util.extend({}, streamingConfiguration, xhrConfiguration)
2420
- );
2421
- /** HTTP streaming transport using XDomainRequest (IE 8,9). */
2422
- Pusher.XDRStreamingTransport = new Pusher.Transport(
2423
- Pusher.Util.extend({}, streamingConfiguration, xdrConfiguration)
2424
- );
2425
- /** HTTP long-polling transport using CORS-enabled XMLHttpRequest. */
2426
- Pusher.XHRPollingTransport = new Pusher.Transport(
2427
- Pusher.Util.extend({}, pollingConfiguration, xhrConfiguration)
2428
- );
2429
- /** HTTP long-polling transport using XDomainRequest (IE 8,9). */
2430
- Pusher.XDRPollingTransport = new Pusher.Transport(
2431
- Pusher.Util.extend({}, pollingConfiguration, xdrConfiguration)
2432
- );
2433
- }).call(this);
2434
-
2435
- ;(function() {
2436
- /** Creates transport connections monitored by a transport manager.
2437
- *
2438
- * When a transport is closed, it might mean the environment does not support
2439
- * it. It's possible that messages get stuck in an intermediate buffer or
2440
- * proxies terminate inactive connections. To combat these problems,
2441
- * assistants monitor the connection lifetime, report unclean exits and
2442
- * adjust ping timeouts to keep the connection active. The decision to disable
2443
- * a transport is the manager's responsibility.
2444
- *
2445
- * @param {TransportManager} manager
2446
- * @param {TransportConnection} transport
2447
- * @param {Object} options
2448
- */
2449
- function AssistantToTheTransportManager(manager, transport, options) {
2450
- this.manager = manager;
2451
- this.transport = transport;
2452
- this.minPingDelay = options.minPingDelay;
2453
- this.maxPingDelay = options.maxPingDelay;
2454
- this.pingDelay = undefined;
2455
- }
2456
- var prototype = AssistantToTheTransportManager.prototype;
2457
-
2458
- /** Creates a transport connection.
2459
- *
2460
- * This function has the same API as Transport#createConnection.
2461
- *
2462
- * @param {String} name
2463
- * @param {Number} priority
2464
- * @param {String} key the application key
2465
- * @param {Object} options
2466
- * @returns {TransportConnection}
2467
- */
2468
- prototype.createConnection = function(name, priority, key, options) {
2469
- var self = this;
2470
-
2471
- options = Pusher.Util.extend({}, options, {
2472
- activityTimeout: self.pingDelay
2473
- });
2474
- var connection = self.transport.createConnection(
2475
- name, priority, key, options
2476
- );
2477
-
2478
- var openTimestamp = null;
2479
-
2480
- var onOpen = function() {
2481
- connection.unbind("open", onOpen);
2482
- connection.bind("closed", onClosed);
2483
- openTimestamp = Pusher.Util.now();
2484
- };
2485
- var onClosed = function(closeEvent) {
2486
- connection.unbind("closed", onClosed);
2487
-
2488
- if (closeEvent.code === 1002 || closeEvent.code === 1003) {
2489
- // we don't want to use transports not obeying the protocol
2490
- self.manager.reportDeath();
2491
- } else if (!closeEvent.wasClean && openTimestamp) {
2492
- // report deaths only for short-living transport
2493
- var lifespan = Pusher.Util.now() - openTimestamp;
2494
- if (lifespan < 2 * self.maxPingDelay) {
2495
- self.manager.reportDeath();
2496
- self.pingDelay = Math.max(lifespan / 2, self.minPingDelay);
2497
- }
2498
- }
2499
- };
2500
-
2501
- connection.bind("open", onOpen);
2502
- return connection;
2503
- };
2504
-
2505
- /** Returns whether the transport is supported in the environment.
2506
- *
2507
- * This function has the same API as Transport#isSupported. Might return false
2508
- * when the manager decides to kill the transport.
2509
- *
2510
- * @param {Object} environment the environment details (encryption, settings)
2511
- * @returns {Boolean} true when the transport is supported
2512
- */
2513
- prototype.isSupported = function(environment) {
2514
- return this.manager.isAlive() && this.transport.isSupported(environment);
2515
- };
2516
-
2517
- Pusher.AssistantToTheTransportManager = AssistantToTheTransportManager;
2518
- }).call(this);
2519
-
2520
- ;(function() {
2521
- /** Keeps track of the number of lives left for a transport.
2522
- *
2523
- * In the beginning of a session, transports may be assigned a number of
2524
- * lives. When an AssistantToTheTransportManager instance reports a transport
2525
- * connection closed uncleanly, the transport loses a life. When the number
2526
- * of lives drops to zero, the transport gets disabled by its manager.
2527
- *
2528
- * @param {Object} options
2529
- */
2530
- function TransportManager(options) {
2531
- this.options = options || {};
2532
- this.livesLeft = this.options.lives || Infinity;
2533
- }
2534
- var prototype = TransportManager.prototype;
2535
-
2536
- /** Creates a assistant for the transport.
2537
- *
2538
- * @param {Transport} transport
2539
- * @returns {AssistantToTheTransportManager}
2540
- */
2541
- prototype.getAssistant = function(transport) {
2542
- return new Pusher.AssistantToTheTransportManager(this, transport, {
2543
- minPingDelay: this.options.minPingDelay,
2544
- maxPingDelay: this.options.maxPingDelay
2545
- });
2546
- };
2547
-
2548
- /** Returns whether the transport has any lives left.
2549
- *
2550
- * @returns {Boolean}
2551
- */
2552
- prototype.isAlive = function() {
2553
- return this.livesLeft > 0;
2554
- };
2555
-
2556
- /** Takes one life from the transport. */
2557
- prototype.reportDeath = function() {
2558
- this.livesLeft -= 1;
2559
- };
2560
-
2561
- Pusher.TransportManager = TransportManager;
2562
- }).call(this);
2563
-
2564
- ;(function() {
2565
- var StrategyBuilder = {
2566
- /** Transforms a JSON scheme to a strategy tree.
2567
- *
2568
- * @param {Array} scheme JSON strategy scheme
2569
- * @param {Object} options a hash of symbols to be included in the scheme
2570
- * @returns {Strategy} strategy tree that's represented by the scheme
2571
- */
2572
- build: function(scheme, options) {
2573
- var context = Pusher.Util.extend({}, globalContext, options);
2574
- return evaluate(scheme, context)[1].strategy;
2575
- }
2576
- };
2577
-
2578
- var transports = {
2579
- ws: Pusher.WSTransport,
2580
- sockjs: Pusher.SockJSTransport,
2581
- xhr_streaming: Pusher.XHRStreamingTransport,
2582
- xdr_streaming: Pusher.XDRStreamingTransport,
2583
- xhr_polling: Pusher.XHRPollingTransport,
2584
- xdr_polling: Pusher.XDRPollingTransport
2585
- };
2586
-
2587
- var UnsupportedStrategy = {
2588
- isSupported: function() {
2589
- return false;
2590
- },
2591
- connect: function(_, callback) {
2592
- var deferred = Pusher.Util.defer(function() {
2593
- callback(new Pusher.Errors.UnsupportedStrategy());
2594
- });
2595
- return {
2596
- abort: function() {
2597
- deferred.ensureAborted();
2598
- },
2599
- forceMinPriority: function() {}
2600
- };
2601
- }
2602
- };
2603
-
2604
- // DSL bindings
2605
-
2606
- function returnWithOriginalContext(f) {
2607
- return function(context) {
2608
- return [f.apply(this, arguments), context];
2609
- };
2610
- }
2611
-
2612
- var globalContext = {
2613
- extend: function(context, first, second) {
2614
- return [Pusher.Util.extend({}, first, second), context];
2615
- },
2616
-
2617
- def: function(context, name, value) {
2618
- if (context[name] !== undefined) {
2619
- throw "Redefining symbol " + name;
2620
- }
2621
- context[name] = value;
2622
- return [undefined, context];
2623
- },
2624
-
2625
- def_transport: function(context, name, type, priority, options, manager) {
2626
- var transportClass = transports[type];
2627
- if (!transportClass) {
2628
- throw new Pusher.Errors.UnsupportedTransport(type);
2629
- }
2630
-
2631
- var enabled =
2632
- (!context.enabledTransports ||
2633
- Pusher.Util.arrayIndexOf(context.enabledTransports, name) !== -1) &&
2634
- (!context.disabledTransports ||
2635
- Pusher.Util.arrayIndexOf(context.disabledTransports, name) === -1);
2636
-
2637
- var transport;
2638
- if (enabled) {
2639
- transport = new Pusher.TransportStrategy(
2640
- name,
2641
- priority,
2642
- manager ? manager.getAssistant(transportClass) : transportClass,
2643
- Pusher.Util.extend({
2644
- key: context.key,
2645
- encrypted: context.encrypted,
2646
- timeline: context.timeline,
2647
- ignoreNullOrigin: context.ignoreNullOrigin
2648
- }, options)
2649
- );
2650
- } else {
2651
- transport = UnsupportedStrategy;
2652
- }
2653
-
2654
- var newContext = context.def(context, name, transport)[1];
2655
- newContext.transports = context.transports || {};
2656
- newContext.transports[name] = transport;
2657
- return [undefined, newContext];
2658
- },
2659
-
2660
- transport_manager: returnWithOriginalContext(function(_, options) {
2661
- return new Pusher.TransportManager(options);
2662
- }),
2663
-
2664
- sequential: returnWithOriginalContext(function(_, options) {
2665
- var strategies = Array.prototype.slice.call(arguments, 2);
2666
- return new Pusher.SequentialStrategy(strategies, options);
2667
- }),
2668
-
2669
- cached: returnWithOriginalContext(function(context, ttl, strategy){
2670
- return new Pusher.CachedStrategy(strategy, context.transports, {
2671
- ttl: ttl,
2672
- timeline: context.timeline,
2673
- encrypted: context.encrypted
2674
- });
2675
- }),
2676
-
2677
- first_connected: returnWithOriginalContext(function(_, strategy) {
2678
- return new Pusher.FirstConnectedStrategy(strategy);
2679
- }),
2680
-
2681
- best_connected_ever: returnWithOriginalContext(function() {
2682
- var strategies = Array.prototype.slice.call(arguments, 1);
2683
- return new Pusher.BestConnectedEverStrategy(strategies);
2684
- }),
2685
-
2686
- delayed: returnWithOriginalContext(function(_, delay, strategy) {
2687
- return new Pusher.DelayedStrategy(strategy, { delay: delay });
2688
- }),
2689
-
2690
- "if": returnWithOriginalContext(function(_, test, trueBranch, falseBranch) {
2691
- return new Pusher.IfStrategy(test, trueBranch, falseBranch);
2692
- }),
2693
-
2694
- is_supported: returnWithOriginalContext(function(_, strategy) {
2695
- return function() {
2696
- return strategy.isSupported();
2697
- };
2698
- })
2699
- };
2700
-
2701
- // DSL interpreter
2702
-
2703
- function isSymbol(expression) {
2704
- return (typeof expression === "string") && expression.charAt(0) === ":";
2705
- }
2706
-
2707
- function getSymbolValue(expression, context) {
2708
- return context[expression.slice(1)];
2709
- }
2710
-
2711
- function evaluateListOfExpressions(expressions, context) {
2712
- if (expressions.length === 0) {
2713
- return [[], context];
2714
- }
2715
- var head = evaluate(expressions[0], context);
2716
- var tail = evaluateListOfExpressions(expressions.slice(1), head[1]);
2717
- return [[head[0]].concat(tail[0]), tail[1]];
2718
- }
2719
-
2720
- function evaluateString(expression, context) {
2721
- if (!isSymbol(expression)) {
2722
- return [expression, context];
2723
- }
2724
- var value = getSymbolValue(expression, context);
2725
- if (value === undefined) {
2726
- throw "Undefined symbol " + expression;
2727
- }
2728
- return [value, context];
2729
- }
2730
-
2731
- function evaluateArray(expression, context) {
2732
- if (isSymbol(expression[0])) {
2733
- var f = getSymbolValue(expression[0], context);
2734
- if (expression.length > 1) {
2735
- if (typeof f !== "function") {
2736
- throw "Calling non-function " + expression[0];
2737
- }
2738
- var args = [Pusher.Util.extend({}, context)].concat(
2739
- Pusher.Util.map(expression.slice(1), function(arg) {
2740
- return evaluate(arg, Pusher.Util.extend({}, context))[0];
2741
- })
2742
- );
2743
- return f.apply(this, args);
2744
- } else {
2745
- return [f, context];
2746
- }
2747
- } else {
2748
- return evaluateListOfExpressions(expression, context);
2749
- }
2750
- }
2751
-
2752
- function evaluate(expression, context) {
2753
- var expressionType = typeof expression;
2754
- if (typeof expression === "string") {
2755
- return evaluateString(expression, context);
2756
- } else if (typeof expression === "object") {
2757
- if (expression instanceof Array && expression.length > 0) {
2758
- return evaluateArray(expression, context);
2759
- }
2760
- }
2761
- return [expression, context];
2762
- }
2763
-
2764
- Pusher.StrategyBuilder = StrategyBuilder;
2765
- }).call(this);
2766
-
2767
- ;(function() {
2768
- /**
2769
- * Provides functions for handling Pusher protocol-specific messages.
2770
- */
2771
- var Protocol = {};
2772
-
2773
- /**
2774
- * Decodes a message in a Pusher format.
2775
- *
2776
- * Throws errors when messages are not parse'able.
2777
- *
2778
- * @param {Object} message
2779
- * @return {Object}
2780
- */
2781
- Protocol.decodeMessage = function(message) {
2782
- try {
2783
- var params = JSON.parse(message.data);
2784
- if (typeof params.data === 'string') {
2785
- try {
2786
- params.data = JSON.parse(params.data);
2787
- } catch (e) {
2788
- if (!(e instanceof SyntaxError)) {
2789
- // TODO looks like unreachable code
2790
- // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse
2791
- throw e;
2792
- }
2793
- }
2794
- }
2795
- return params;
2796
- } catch (e) {
2797
- throw { type: 'MessageParseError', error: e, data: message.data};
2798
- }
2799
- };
2800
-
2801
- /**
2802
- * Encodes a message to be sent.
2803
- *
2804
- * @param {Object} message
2805
- * @return {String}
2806
- */
2807
- Protocol.encodeMessage = function(message) {
2808
- return JSON.stringify(message);
2809
- };
2810
-
2811
- /** Processes a handshake message and returns appropriate actions.
2812
- *
2813
- * Returns an object with an 'action' and other action-specific properties.
2814
- *
2815
- * There are three outcomes when calling this function. First is a successful
2816
- * connection attempt, when pusher:connection_established is received, which
2817
- * results in a 'connected' action with an 'id' property. When passed a
2818
- * pusher:error event, it returns a result with action appropriate to the
2819
- * close code and an error. Otherwise, it raises an exception.
2820
- *
2821
- * @param {String} message
2822
- * @result Object
2823
- */
2824
- Protocol.processHandshake = function(message) {
2825
- message = this.decodeMessage(message);
2826
-
2827
- if (message.event === "pusher:connection_established") {
2828
- if (!message.data.activity_timeout) {
2829
- throw "No activity timeout specified in handshake";
2830
- }
2831
- return {
2832
- action: "connected",
2833
- id: message.data.socket_id,
2834
- activityTimeout: message.data.activity_timeout * 1000
2835
- };
2836
- } else if (message.event === "pusher:error") {
2837
- // From protocol 6 close codes are sent only once, so this only
2838
- // happens when connection does not support close codes
2839
- return {
2840
- action: this.getCloseAction(message.data),
2841
- error: this.getCloseError(message.data)
2842
- };
2843
- } else {
2844
- throw "Invalid handshake";
2845
- }
2846
- };
2847
-
2848
- /**
2849
- * Dispatches the close event and returns an appropriate action name.
2850
- *
2851
- * See:
2852
- * 1. https://developer.mozilla.org/en-US/docs/WebSockets/WebSockets_reference/CloseEvent
2853
- * 2. http://pusher.com/docs/pusher_protocol
2854
- *
2855
- * @param {CloseEvent} closeEvent
2856
- * @return {String} close action name
2857
- */
2858
- Protocol.getCloseAction = function(closeEvent) {
2859
- if (closeEvent.code < 4000) {
2860
- // ignore 1000 CLOSE_NORMAL, 1001 CLOSE_GOING_AWAY,
2861
- // 1005 CLOSE_NO_STATUS, 1006 CLOSE_ABNORMAL
2862
- // ignore 1007...3999
2863
- // handle 1002 CLOSE_PROTOCOL_ERROR, 1003 CLOSE_UNSUPPORTED,
2864
- // 1004 CLOSE_TOO_LARGE
2865
- if (closeEvent.code >= 1002 && closeEvent.code <= 1004) {
2866
- return "backoff";
2867
- } else {
2868
- return null;
2869
- }
2870
- } else if (closeEvent.code === 4000) {
2871
- return "ssl_only";
2872
- } else if (closeEvent.code < 4100) {
2873
- return "refused";
2874
- } else if (closeEvent.code < 4200) {
2875
- return "backoff";
2876
- } else if (closeEvent.code < 4300) {
2877
- return "retry";
2878
- } else {
2879
- // unknown error
2880
- return "refused";
2881
- }
2882
- };
2883
-
2884
- /**
2885
- * Returns an error or null basing on the close event.
2886
- *
2887
- * Null is returned when connection was closed cleanly. Otherwise, an object
2888
- * with error details is returned.
2889
- *
2890
- * @param {CloseEvent} closeEvent
2891
- * @return {Object} error object
2892
- */
2893
- Protocol.getCloseError = function(closeEvent) {
2894
- if (closeEvent.code !== 1000 && closeEvent.code !== 1001) {
2895
- return {
2896
- type: 'PusherError',
2897
- data: {
2898
- code: closeEvent.code,
2899
- message: closeEvent.reason || closeEvent.message
2900
- }
2901
- };
2902
- } else {
2903
- return null;
2904
- }
2905
- };
2906
-
2907
- Pusher.Protocol = Protocol;
2908
- }).call(this);
2909
-
2910
- ;(function() {
2911
- /**
2912
- * Provides Pusher protocol interface for transports.
2913
- *
2914
- * Emits following events:
2915
- * - message - on received messages
2916
- * - ping - on ping requests
2917
- * - pong - on pong responses
2918
- * - error - when the transport emits an error
2919
- * - closed - after closing the transport
2920
- *
2921
- * It also emits more events when connection closes with a code.
2922
- * See Protocol.getCloseAction to get more details.
2923
- *
2924
- * @param {Number} id
2925
- * @param {AbstractTransport} transport
2926
- */
2927
- function Connection(id, transport) {
2928
- Pusher.EventsDispatcher.call(this);
2929
-
2930
- this.id = id;
2931
- this.transport = transport;
2932
- this.activityTimeout = transport.activityTimeout;
2933
- this.bindListeners();
2934
- }
2935
- var prototype = Connection.prototype;
2936
- Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
2937
-
2938
- /** Returns whether used transport handles activity checks by itself
2939
- *
2940
- * @returns {Boolean} true if activity checks are handled by the transport
2941
- */
2942
- prototype.handlesActivityChecks = function() {
2943
- return this.transport.handlesActivityChecks();
2944
- };
2945
-
2946
- /** Sends raw data.
2947
- *
2948
- * @param {String} data
2949
- */
2950
- prototype.send = function(data) {
2951
- return this.transport.send(data);
2952
- };
2953
-
2954
- /** Sends an event.
2955
- *
2956
- * @param {String} name
2957
- * @param {String} data
2958
- * @param {String} [channel]
2959
- * @returns {Boolean} whether message was sent or not
2960
- */
2961
- prototype.send_event = function(name, data, channel) {
2962
- var message = { event: name, data: data };
2963
- if (channel) {
2964
- message.channel = channel;
2965
- }
2966
- Pusher.debug('Event sent', message);
2967
- return this.send(Pusher.Protocol.encodeMessage(message));
2968
- };
2969
-
2970
- /** Sends a ping message to the server.
2971
- *
2972
- * Basing on the underlying transport, it might send either transport's
2973
- * protocol-specific ping or pusher:ping event.
2974
- */
2975
- prototype.ping = function() {
2976
- if (this.transport.supportsPing()) {
2977
- this.transport.ping();
2978
- } else {
2979
- this.send_event('pusher:ping', {});
2980
- }
2981
- };
2982
-
2983
- /** Closes the connection. */
2984
- prototype.close = function() {
2985
- this.transport.close();
2986
- };
2987
-
2988
- /** @private */
2989
- prototype.bindListeners = function() {
2990
- var self = this;
2991
-
2992
- var listeners = {
2993
- message: function(m) {
2994
- var message;
2995
- try {
2996
- message = Pusher.Protocol.decodeMessage(m);
2997
- } catch(e) {
2998
- self.emit('error', {
2999
- type: 'MessageParseError',
3000
- error: e,
3001
- data: m.data
3002
- });
3003
- }
3004
-
3005
- if (message !== undefined) {
3006
- Pusher.debug('Event recd', message);
3007
-
3008
- switch (message.event) {
3009
- case 'pusher:error':
3010
- self.emit('error', { type: 'PusherError', data: message.data });
3011
- break;
3012
- case 'pusher:ping':
3013
- self.emit("ping");
3014
- break;
3015
- case 'pusher:pong':
3016
- self.emit("pong");
3017
- break;
3018
- }
3019
- self.emit('message', message);
3020
- }
3021
- },
3022
- activity: function() {
3023
- self.emit("activity");
3024
- },
3025
- error: function(error) {
3026
- self.emit("error", { type: "WebSocketError", error: error });
3027
- },
3028
- closed: function(closeEvent) {
3029
- unbindListeners();
3030
-
3031
- if (closeEvent && closeEvent.code) {
3032
- self.handleCloseEvent(closeEvent);
3033
- }
3034
-
3035
- self.transport = null;
3036
- self.emit("closed");
3037
- }
3038
- };
3039
-
3040
- var unbindListeners = function() {
3041
- Pusher.Util.objectApply(listeners, function(listener, event) {
3042
- self.transport.unbind(event, listener);
3043
- });
3044
- };
3045
-
3046
- Pusher.Util.objectApply(listeners, function(listener, event) {
3047
- self.transport.bind(event, listener);
3048
- });
3049
- };
3050
-
3051
- /** @private */
3052
- prototype.handleCloseEvent = function(closeEvent) {
3053
- var action = Pusher.Protocol.getCloseAction(closeEvent);
3054
- var error = Pusher.Protocol.getCloseError(closeEvent);
3055
- if (error) {
3056
- this.emit('error', error);
3057
- }
3058
- if (action) {
3059
- this.emit(action);
3060
- }
3061
- };
3062
-
3063
- Pusher.Connection = Connection;
3064
- }).call(this);
3065
-
3066
- ;(function() {
3067
- /**
3068
- * Handles Pusher protocol handshakes for transports.
3069
- *
3070
- * Calls back with a result object after handshake is completed. Results
3071
- * always have two fields:
3072
- * - action - string describing action to be taken after the handshake
3073
- * - transport - the transport object passed to the constructor
3074
- *
3075
- * Different actions can set different additional properties on the result.
3076
- * In the case of 'connected' action, there will be a 'connection' property
3077
- * containing a Connection object for the transport. Other actions should
3078
- * carry an 'error' property.
3079
- *
3080
- * @param {AbstractTransport} transport
3081
- * @param {Function} callback
3082
- */
3083
- function Handshake(transport, callback) {
3084
- this.transport = transport;
3085
- this.callback = callback;
3086
- this.bindListeners();
3087
- }
3088
- var prototype = Handshake.prototype;
3089
-
3090
- prototype.close = function() {
3091
- this.unbindListeners();
3092
- this.transport.close();
3093
- };
3094
-
3095
- /** @private */
3096
- prototype.bindListeners = function() {
3097
- var self = this;
3098
-
3099
- self.onMessage = function(m) {
3100
- self.unbindListeners();
3101
-
3102
- try {
3103
- var result = Pusher.Protocol.processHandshake(m);
3104
- if (result.action === "connected") {
3105
- self.finish("connected", {
3106
- connection: new Pusher.Connection(result.id, self.transport),
3107
- activityTimeout: result.activityTimeout
3108
- });
3109
- } else {
3110
- self.finish(result.action, { error: result.error });
3111
- self.transport.close();
3112
- }
3113
- } catch (e) {
3114
- self.finish("error", { error: e });
3115
- self.transport.close();
3116
- }
3117
- };
3118
-
3119
- self.onClosed = function(closeEvent) {
3120
- self.unbindListeners();
3121
-
3122
- var action = Pusher.Protocol.getCloseAction(closeEvent) || "backoff";
3123
- var error = Pusher.Protocol.getCloseError(closeEvent);
3124
- self.finish(action, { error: error });
3125
- };
3126
-
3127
- self.transport.bind("message", self.onMessage);
3128
- self.transport.bind("closed", self.onClosed);
3129
- };
3130
-
3131
- /** @private */
3132
- prototype.unbindListeners = function() {
3133
- this.transport.unbind("message", this.onMessage);
3134
- this.transport.unbind("closed", this.onClosed);
3135
- };
3136
-
3137
- /** @private */
3138
- prototype.finish = function(action, params) {
3139
- this.callback(
3140
- Pusher.Util.extend({ transport: this.transport, action: action }, params)
3141
- );
3142
- };
3143
-
3144
- Pusher.Handshake = Handshake;
3145
- }).call(this);
3146
-
3147
- ;(function() {
3148
- /** Manages connection to Pusher.
3149
- *
3150
- * Uses a strategy (currently only default), timers and network availability
3151
- * info to establish a connection and export its state. In case of failures,
3152
- * manages reconnection attempts.
3153
- *
3154
- * Exports state changes as following events:
3155
- * - "state_change", { previous: p, current: state }
3156
- * - state
3157
- *
3158
- * States:
3159
- * - initialized - initial state, never transitioned to
3160
- * - connecting - connection is being established
3161
- * - connected - connection has been fully established
3162
- * - disconnected - on requested disconnection
3163
- * - unavailable - after connection timeout or when there's no network
3164
- * - failed - when the connection strategy is not supported
3165
- *
3166
- * Options:
3167
- * - unavailableTimeout - time to transition to unavailable state
3168
- * - activityTimeout - time after which ping message should be sent
3169
- * - pongTimeout - time for Pusher to respond with pong before reconnecting
3170
- *
3171
- * @param {String} key application key
3172
- * @param {Object} options
3173
- */
3174
- function ConnectionManager(key, options) {
3175
- Pusher.EventsDispatcher.call(this);
3176
-
3177
- this.key = key;
3178
- this.options = options || {};
3179
- this.state = "initialized";
3180
- this.connection = null;
3181
- this.encrypted = !!options.encrypted;
3182
- this.timeline = this.options.timeline;
3183
-
3184
- this.connectionCallbacks = this.buildConnectionCallbacks();
3185
- this.errorCallbacks = this.buildErrorCallbacks();
3186
- this.handshakeCallbacks = this.buildHandshakeCallbacks(this.errorCallbacks);
3187
-
3188
- var self = this;
3189
-
3190
- Pusher.Network.bind("online", function() {
3191
- self.timeline.info({ netinfo: "online" });
3192
- if (self.state === "connecting" || self.state === "unavailable") {
3193
- self.retryIn(0);
3194
- }
3195
- });
3196
- Pusher.Network.bind("offline", function() {
3197
- self.timeline.info({ netinfo: "offline" });
3198
- if (self.connection) {
3199
- self.sendActivityCheck();
3200
- }
3201
- });
3202
-
3203
- this.updateStrategy();
3204
- }
3205
- var prototype = ConnectionManager.prototype;
3206
-
3207
- Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
3208
-
3209
- /** Establishes a connection to Pusher.
3210
- *
3211
- * Does nothing when connection is already established. See top-level doc
3212
- * to find events emitted on connection attempts.
3213
- */
3214
- prototype.connect = function() {
3215
- if (this.connection || this.runner) {
3216
- return;
3217
- }
3218
- if (!this.strategy.isSupported()) {
3219
- this.updateState("failed");
3220
- return;
3221
- }
3222
- this.updateState("connecting");
3223
- this.startConnecting();
3224
- this.setUnavailableTimer();
3225
- };
3226
-
3227
- /** Sends raw data.
3228
- *
3229
- * @param {String} data
3230
- */
3231
- prototype.send = function(data) {
3232
- if (this.connection) {
3233
- return this.connection.send(data);
3234
- } else {
3235
- return false;
3236
- }
3237
- };
3238
-
3239
- /** Sends an event.
3240
- *
3241
- * @param {String} name
3242
- * @param {String} data
3243
- * @param {String} [channel]
3244
- * @returns {Boolean} whether message was sent or not
3245
- */
3246
- prototype.send_event = function(name, data, channel) {
3247
- if (this.connection) {
3248
- return this.connection.send_event(name, data, channel);
3249
- } else {
3250
- return false;
3251
- }
3252
- };
3253
-
3254
- /** Closes the connection. */
3255
- prototype.disconnect = function() {
3256
- this.disconnectInternally();
3257
- this.updateState("disconnected");
3258
- };
3259
-
3260
- prototype.isEncrypted = function() {
3261
- return this.encrypted;
3262
- };
3263
-
3264
- /** @private */
3265
- prototype.startConnecting = function() {
3266
- var self = this;
3267
- var callback = function(error, handshake) {
3268
- if (error) {
3269
- self.runner = self.strategy.connect(0, callback);
3270
- } else {
3271
- if (handshake.action === "error") {
3272
- self.emit("error", { type: "HandshakeError", error: handshake.error });
3273
- self.timeline.error({ handshakeError: handshake.error });
3274
- } else {
3275
- self.abortConnecting(); // we don't support switching connections yet
3276
- self.handshakeCallbacks[handshake.action](handshake);
3277
- }
3278
- }
3279
- };
3280
- self.runner = self.strategy.connect(0, callback);
3281
- };
3282
-
3283
- /** @private */
3284
- prototype.abortConnecting = function() {
3285
- if (this.runner) {
3286
- this.runner.abort();
3287
- this.runner = null;
3288
- }
3289
- };
3290
-
3291
- /** @private */
3292
- prototype.disconnectInternally = function() {
3293
- this.abortConnecting();
3294
- this.clearRetryTimer();
3295
- this.clearUnavailableTimer();
3296
- if (this.connection) {
3297
- var connection = this.abandonConnection();
3298
- connection.close();
3299
- }
3300
- };
3301
-
3302
- /** @private */
3303
- prototype.updateStrategy = function() {
3304
- this.strategy = this.options.getStrategy({
3305
- key: this.key,
3306
- timeline: this.timeline,
3307
- encrypted: this.encrypted
3308
- });
3309
- };
3310
-
3311
- /** @private */
3312
- prototype.retryIn = function(delay) {
3313
- var self = this;
3314
- self.timeline.info({ action: "retry", delay: delay });
3315
- if (delay > 0) {
3316
- self.emit("connecting_in", Math.round(delay / 1000));
3317
- }
3318
- self.retryTimer = new Pusher.Timer(delay || 0, function() {
3319
- self.disconnectInternally();
3320
- self.connect();
3321
- });
3322
- };
3323
-
3324
- /** @private */
3325
- prototype.clearRetryTimer = function() {
3326
- if (this.retryTimer) {
3327
- this.retryTimer.ensureAborted();
3328
- this.retryTimer = null;
3329
- }
3330
- };
3331
-
3332
- /** @private */
3333
- prototype.setUnavailableTimer = function() {
3334
- var self = this;
3335
- self.unavailableTimer = new Pusher.Timer(
3336
- self.options.unavailableTimeout,
3337
- function() {
3338
- self.updateState("unavailable");
3339
- }
3340
- );
3341
- };
3342
-
3343
- /** @private */
3344
- prototype.clearUnavailableTimer = function() {
3345
- if (this.unavailableTimer) {
3346
- this.unavailableTimer.ensureAborted();
3347
- }
3348
- };
3349
-
3350
- /** @private */
3351
- prototype.sendActivityCheck = function() {
3352
- var self = this;
3353
- self.stopActivityCheck();
3354
- self.connection.ping();
3355
- // wait for pong response
3356
- self.activityTimer = new Pusher.Timer(
3357
- self.options.pongTimeout,
3358
- function() {
3359
- self.timeline.error({ pong_timed_out: self.options.pongTimeout });
3360
- self.retryIn(0);
3361
- }
3362
- );
3363
- };
3364
-
3365
- /** @private */
3366
- prototype.resetActivityCheck = function() {
3367
- var self = this;
3368
- self.stopActivityCheck();
3369
- // send ping after inactivity
3370
- if (!self.connection.handlesActivityChecks()) {
3371
- self.activityTimer = new Pusher.Timer(self.activityTimeout, function() {
3372
- self.sendActivityCheck();
3373
- });
3374
- }
3375
- };
3376
-
3377
- /** @private */
3378
- prototype.stopActivityCheck = function() {
3379
- if (this.activityTimer) {
3380
- this.activityTimer.ensureAborted();
3381
- }
3382
- };
3383
-
3384
- /** @private */
3385
- prototype.buildConnectionCallbacks = function() {
3386
- var self = this;
3387
- return {
3388
- message: function(message) {
3389
- // includes pong messages from server
3390
- self.resetActivityCheck();
3391
- self.emit('message', message);
3392
- },
3393
- ping: function() {
3394
- self.send_event('pusher:pong', {});
3395
- },
3396
- activity: function() {
3397
- self.resetActivityCheck();
3398
- },
3399
- error: function(error) {
3400
- // just emit error to user - socket will already be closed by browser
3401
- self.emit("error", { type: "WebSocketError", error: error });
3402
- },
3403
- closed: function() {
3404
- self.abandonConnection();
3405
- if (self.shouldRetry()) {
3406
- self.retryIn(1000);
3407
- }
3408
- }
3409
- };
3410
- };
3411
-
3412
- /** @private */
3413
- prototype.buildHandshakeCallbacks = function(errorCallbacks) {
3414
- var self = this;
3415
- return Pusher.Util.extend({}, errorCallbacks, {
3416
- connected: function(handshake) {
3417
- self.activityTimeout = Math.min(
3418
- self.options.activityTimeout,
3419
- handshake.activityTimeout,
3420
- handshake.connection.activityTimeout || Infinity
3421
- );
3422
- self.clearUnavailableTimer();
3423
- self.setConnection(handshake.connection);
3424
- self.socket_id = self.connection.id;
3425
- self.updateState("connected", { socket_id: self.socket_id });
3426
- }
3427
- });
3428
- };
3429
-
3430
- /** @private */
3431
- prototype.buildErrorCallbacks = function() {
3432
- var self = this;
3433
-
3434
- function withErrorEmitted(callback) {
3435
- return function(result) {
3436
- if (result.error) {
3437
- self.emit("error", { type: "WebSocketError", error: result.error });
3438
- }
3439
- callback(result);
3440
- };
3441
- }
3442
-
3443
- return {
3444
- ssl_only: withErrorEmitted(function() {
3445
- self.encrypted = true;
3446
- self.updateStrategy();
3447
- self.retryIn(0);
3448
- }),
3449
- refused: withErrorEmitted(function() {
3450
- self.disconnect();
3451
- }),
3452
- backoff: withErrorEmitted(function() {
3453
- self.retryIn(1000);
3454
- }),
3455
- retry: withErrorEmitted(function() {
3456
- self.retryIn(0);
3457
- })
3458
- };
3459
- };
3460
-
3461
- /** @private */
3462
- prototype.setConnection = function(connection) {
3463
- this.connection = connection;
3464
- for (var event in this.connectionCallbacks) {
3465
- this.connection.bind(event, this.connectionCallbacks[event]);
3466
- }
3467
- this.resetActivityCheck();
3468
- };
3469
-
3470
- /** @private */
3471
- prototype.abandonConnection = function() {
3472
- if (!this.connection) {
3473
- return;
3474
- }
3475
- this.stopActivityCheck();
3476
- for (var event in this.connectionCallbacks) {
3477
- this.connection.unbind(event, this.connectionCallbacks[event]);
3478
- }
3479
- var connection = this.connection;
3480
- this.connection = null;
3481
- return connection;
3482
- };
3483
-
3484
- /** @private */
3485
- prototype.updateState = function(newState, data) {
3486
- var previousState = this.state;
3487
- this.state = newState;
3488
- if (previousState !== newState) {
3489
- Pusher.debug('State changed', previousState + ' -> ' + newState);
3490
- this.timeline.info({ state: newState, params: data });
3491
- this.emit('state_change', { previous: previousState, current: newState });
3492
- this.emit(newState, data);
3493
- }
3494
- };
3495
-
3496
- /** @private */
3497
- prototype.shouldRetry = function() {
3498
- return this.state === "connecting" || this.state === "connected";
3499
- };
3500
-
3501
- Pusher.ConnectionManager = ConnectionManager;
3502
- }).call(this);
3503
-
3504
- ;(function() {
3505
- /** Really basic interface providing network availability info.
3506
- *
3507
- * Emits:
3508
- * - online - when browser goes online
3509
- * - offline - when browser goes offline
3510
- */
3511
- function NetInfo() {
3512
- Pusher.EventsDispatcher.call(this);
3513
-
3514
- var self = this;
3515
- // This is okay, as IE doesn't support this stuff anyway.
3516
- if (window.addEventListener !== undefined) {
3517
- window.addEventListener("online", function() {
3518
- self.emit('online');
3519
- }, false);
3520
- window.addEventListener("offline", function() {
3521
- self.emit('offline');
3522
- }, false);
3523
- }
3524
- }
3525
- Pusher.Util.extend(NetInfo.prototype, Pusher.EventsDispatcher.prototype);
3526
-
3527
- var prototype = NetInfo.prototype;
3528
-
3529
- /** Returns whether browser is online or not
3530
- *
3531
- * Offline means definitely offline (no connection to router).
3532
- * Inverse does NOT mean definitely online (only currently supported in Safari
3533
- * and even there only means the device has a connection to the router).
3534
- *
3535
- * @return {Boolean}
3536
- */
3537
- prototype.isOnline = function() {
3538
- if (window.navigator.onLine === undefined) {
3539
- return true;
3540
- } else {
3541
- return window.navigator.onLine;
3542
- }
3543
- };
3544
-
3545
- Pusher.NetInfo = NetInfo;
3546
- Pusher.Network = new NetInfo();
3547
- }).call(this);
3548
-
3549
- ;(function() {
3550
- /** Represents a collection of members of a presence channel. */
3551
- function Members() {
3552
- this.reset();
3553
- }
3554
- var prototype = Members.prototype;
3555
-
3556
- /** Returns member's info for given id.
3557
- *
3558
- * Resulting object containts two fields - id and info.
3559
- *
3560
- * @param {Number} id
3561
- * @return {Object} member's info or null
3562
- */
3563
- prototype.get = function(id) {
3564
- if (Object.prototype.hasOwnProperty.call(this.members, id)) {
3565
- return {
3566
- id: id,
3567
- info: this.members[id]
3568
- };
3569
- } else {
3570
- return null;
3571
- }
3572
- };
3573
-
3574
- /** Calls back for each member in unspecified order.
3575
- *
3576
- * @param {Function} callback
3577
- */
3578
- prototype.each = function(callback) {
3579
- var self = this;
3580
- Pusher.Util.objectApply(self.members, function(member, id) {
3581
- callback(self.get(id));
3582
- });
3583
- };
3584
-
3585
- /** Updates the id for connected member. For internal use only. */
3586
- prototype.setMyID = function(id) {
3587
- this.myID = id;
3588
- };
3589
-
3590
- /** Handles subscription data. For internal use only. */
3591
- prototype.onSubscription = function(subscriptionData) {
3592
- this.members = subscriptionData.presence.hash;
3593
- this.count = subscriptionData.presence.count;
3594
- this.me = this.get(this.myID);
3595
- };
3596
-
3597
- /** Adds a new member to the collection. For internal use only. */
3598
- prototype.addMember = function(memberData) {
3599
- if (this.get(memberData.user_id) === null) {
3600
- this.count++;
3601
- }
3602
- this.members[memberData.user_id] = memberData.user_info;
3603
- return this.get(memberData.user_id);
3604
- };
3605
-
3606
- /** Adds a member from the collection. For internal use only. */
3607
- prototype.removeMember = function(memberData) {
3608
- var member = this.get(memberData.user_id);
3609
- if (member) {
3610
- delete this.members[memberData.user_id];
3611
- this.count--;
3612
- }
3613
- return member;
3614
- };
3615
-
3616
- /** Resets the collection to the initial state. For internal use only. */
3617
- prototype.reset = function() {
3618
- this.members = {};
3619
- this.count = 0;
3620
- this.myID = null;
3621
- this.me = null;
3622
- };
3623
-
3624
- Pusher.Members = Members;
3625
- }).call(this);
3626
-
3627
- ;(function() {
3628
- /** Provides base public channel interface with an event emitter.
3629
- *
3630
- * Emits:
3631
- * - pusher:subscription_succeeded - after subscribing successfully
3632
- * - other non-internal events
3633
- *
3634
- * @param {String} name
3635
- * @param {Pusher} pusher
3636
- */
3637
- function Channel(name, pusher) {
3638
- Pusher.EventsDispatcher.call(this, function(event, data) {
3639
- Pusher.debug('No callbacks on ' + name + ' for ' + event);
3640
- });
3641
-
3642
- this.name = name;
3643
- this.pusher = pusher;
3644
- this.subscribed = false;
3645
- }
3646
- var prototype = Channel.prototype;
3647
- Pusher.Util.extend(prototype, Pusher.EventsDispatcher.prototype);
3648
-
3649
- /** Skips authorization, since public channels don't require it.
3650
- *
3651
- * @param {Function} callback
3652
- */
3653
- prototype.authorize = function(socketId, callback) {
3654
- return callback(false, {});
3655
- };
3656
-
3657
- /** Triggers an event */
3658
- prototype.trigger = function(event, data) {
3659
- if (event.indexOf("client-") !== 0) {
3660
- throw new Pusher.Errors.BadEventName(
3661
- "Event '" + event + "' does not start with 'client-'"
3662
- );
3663
- }
3664
- return this.pusher.send_event(event, data, this.name);
3665
- };
3666
-
3667
- /** Signals disconnection to the channel. For internal use only. */
3668
- prototype.disconnect = function() {
3669
- this.subscribed = false;
3670
- };
3671
-
3672
- /** Handles an event. For internal use only.
3673
- *
3674
- * @param {String} event
3675
- * @param {*} data
3676
- */
3677
- prototype.handleEvent = function(event, data) {
3678
- if (event.indexOf("pusher_internal:") === 0) {
3679
- if (event === "pusher_internal:subscription_succeeded") {
3680
- this.subscribed = true;
3681
- this.emit("pusher:subscription_succeeded", data);
3682
- }
3683
- } else {
3684
- this.emit(event, data);
3685
- }
3686
- };
3687
-
3688
- /** Sends a subscription request. For internal use only. */
3689
- prototype.subscribe = function() {
3690
- var self = this;
3691
-
3692
- self.authorize(self.pusher.connection.socket_id, function(error, data) {
3693
- if (error) {
3694
- self.handleEvent('pusher:subscription_error', data);
3695
- } else {
3696
- self.pusher.send_event('pusher:subscribe', {
3697
- auth: data.auth,
3698
- channel_data: data.channel_data,
3699
- channel: self.name
3700
- });
3701
- }
3702
- });
3703
- };
3704
-
3705
- /** Sends an unsubscription request. For internal use only. */
3706
- prototype.unsubscribe = function() {
3707
- this.pusher.send_event('pusher:unsubscribe', {
3708
- channel: this.name
3709
- });
3710
- };
3711
-
3712
- Pusher.Channel = Channel;
3713
- }).call(this);
3714
-
3715
- ;(function() {
3716
- /** Extends public channels to provide private channel interface.
3717
- *
3718
- * @param {String} name
3719
- * @param {Pusher} pusher
3720
- */
3721
- function PrivateChannel(name, pusher) {
3722
- Pusher.Channel.call(this, name, pusher);
3723
- }
3724
- var prototype = PrivateChannel.prototype;
3725
- Pusher.Util.extend(prototype, Pusher.Channel.prototype);
3726
-
3727
- /** Authorizes the connection to use the channel.
3728
- *
3729
- * @param {String} socketId
3730
- * @param {Function} callback
3731
- */
3732
- prototype.authorize = function(socketId, callback) {
3733
- var authorizer = new Pusher.Channel.Authorizer(this, this.pusher.config);
3734
- return authorizer.authorize(socketId, callback);
3735
- };
3736
-
3737
- Pusher.PrivateChannel = PrivateChannel;
3738
- }).call(this);
3739
-
3740
- ;(function() {
3741
- /** Adds presence channel functionality to private channels.
3742
- *
3743
- * @param {String} name
3744
- * @param {Pusher} pusher
3745
- */
3746
- function PresenceChannel(name, pusher) {
3747
- Pusher.PrivateChannel.call(this, name, pusher);
3748
- this.members = new Pusher.Members();
3749
- }
3750
- var prototype = PresenceChannel.prototype;
3751
- Pusher.Util.extend(prototype, Pusher.PrivateChannel.prototype);
3752
-
3753
- /** Authenticates the connection as a member of the channel.
3754
- *
3755
- * @param {String} socketId
3756
- * @param {Function} callback
3757
- */
3758
- prototype.authorize = function(socketId, callback) {
3759
- var _super = Pusher.PrivateChannel.prototype.authorize;
3760
- var self = this;
3761
- _super.call(self, socketId, function(error, authData) {
3762
- if (!error) {
3763
- if (authData.channel_data === undefined) {
3764
- Pusher.warn(
3765
- "Invalid auth response for channel '" +
3766
- self.name +
3767
- "', expected 'channel_data' field"
3768
- );
3769
- callback("Invalid auth response");
3770
- return;
3771
- }
3772
- var channelData = JSON.parse(authData.channel_data);
3773
- self.members.setMyID(channelData.user_id);
3774
- }
3775
- callback(error, authData);
3776
- });
3777
- };
3778
-
3779
- /** Handles presence and subscription events. For internal use only.
3780
- *
3781
- * @param {String} event
3782
- * @param {*} data
3783
- */
3784
- prototype.handleEvent = function(event, data) {
3785
- switch (event) {
3786
- case "pusher_internal:subscription_succeeded":
3787
- this.members.onSubscription(data);
3788
- this.subscribed = true;
3789
- this.emit("pusher:subscription_succeeded", this.members);
3790
- break;
3791
- case "pusher_internal:member_added":
3792
- var addedMember = this.members.addMember(data);
3793
- this.emit('pusher:member_added', addedMember);
3794
- break;
3795
- case "pusher_internal:member_removed":
3796
- var removedMember = this.members.removeMember(data);
3797
- if (removedMember) {
3798
- this.emit('pusher:member_removed', removedMember);
3799
- }
3800
- break;
3801
- default:
3802
- Pusher.PrivateChannel.prototype.handleEvent.call(this, event, data);
3803
- }
3804
- };
3805
-
3806
- /** Resets the channel state, including members map. For internal use only. */
3807
- prototype.disconnect = function() {
3808
- this.members.reset();
3809
- Pusher.PrivateChannel.prototype.disconnect.call(this);
3810
- };
3811
-
3812
- Pusher.PresenceChannel = PresenceChannel;
3813
- }).call(this);
3814
-
3815
- ;(function() {
3816
- /** Handles a channel map. */
3817
- function Channels() {
3818
- this.channels = {};
3819
- }
3820
- var prototype = Channels.prototype;
3821
-
3822
- /** Creates or retrieves an existing channel by its name.
3823
- *
3824
- * @param {String} name
3825
- * @param {Pusher} pusher
3826
- * @return {Channel}
3827
- */
3828
- prototype.add = function(name, pusher) {
3829
- if (!this.channels[name]) {
3830
- this.channels[name] = createChannel(name, pusher);
3831
- }
3832
- return this.channels[name];
3833
- };
3834
-
3835
- /** Returns a list of all channels
3836
- *
3837
- * @return {Array}
3838
- */
3839
- prototype.all = function(name) {
3840
- return Pusher.Util.values(this.channels);
3841
- };
3842
-
3843
- /** Finds a channel by its name.
3844
- *
3845
- * @param {String} name
3846
- * @return {Channel} channel or null if it doesn't exist
3847
- */
3848
- prototype.find = function(name) {
3849
- return this.channels[name];
3850
- };
3851
-
3852
- /** Removes a channel from the map.
3853
- *
3854
- * @param {String} name
3855
- */
3856
- prototype.remove = function(name) {
3857
- var channel = this.channels[name];
3858
- delete this.channels[name];
3859
- return channel;
3860
- };
3861
-
3862
- /** Proxies disconnection signal to all channels. */
3863
- prototype.disconnect = function() {
3864
- Pusher.Util.objectApply(this.channels, function(channel) {
3865
- channel.disconnect();
3866
- });
3867
- };
3868
-
3869
- function createChannel(name, pusher) {
3870
- if (name.indexOf('private-') === 0) {
3871
- return new Pusher.PrivateChannel(name, pusher);
3872
- } else if (name.indexOf('presence-') === 0) {
3873
- return new Pusher.PresenceChannel(name, pusher);
3874
- } else {
3875
- return new Pusher.Channel(name, pusher);
3876
- }
3877
- }
3878
-
3879
- Pusher.Channels = Channels;
3880
- }).call(this);
3881
-
3882
- ;(function() {
3883
- Pusher.Channel.Authorizer = function(channel, options) {
3884
- this.channel = channel;
3885
- this.type = options.authTransport;
3886
-
3887
- this.options = options;
3888
- this.authOptions = (options || {}).auth || {};
3889
- };
3890
-
3891
- Pusher.Channel.Authorizer.prototype = {
3892
- composeQuery: function(socketId) {
3893
- var query = 'socket_id=' + encodeURIComponent(socketId) +
3894
- '&channel_name=' + encodeURIComponent(this.channel.name);
3895
-
3896
- for(var i in this.authOptions.params) {
3897
- query += "&" + encodeURIComponent(i) + "=" + encodeURIComponent(this.authOptions.params[i]);
3898
- }
3899
-
3900
- return query;
3901
- },
3902
-
3903
- authorize: function(socketId, callback) {
3904
- return Pusher.authorizers[this.type].call(this, socketId, callback);
3905
- }
3906
- };
3907
-
3908
- var nextAuthCallbackID = 1;
3909
-
3910
- Pusher.auth_callbacks = {};
3911
- Pusher.authorizers = {
3912
- ajax: function(socketId, callback){
3913
- var self = this, xhr;
3914
-
3915
- if (Pusher.XHR) {
3916
- xhr = new Pusher.XHR();
3917
- } else {
3918
- xhr = (window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
3919
- }
3920
-
3921
- xhr.open("POST", self.options.authEndpoint, true);
3922
-
3923
- // add request headers
3924
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
3925
- for(var headerName in this.authOptions.headers) {
3926
- xhr.setRequestHeader(headerName, this.authOptions.headers[headerName]);
3927
- }
3928
-
3929
- xhr.onreadystatechange = function() {
3930
- if (xhr.readyState === 4) {
3931
- if (xhr.status === 200) {
3932
- var data, parsed = false;
3933
-
3934
- try {
3935
- data = JSON.parse(xhr.responseText);
3936
- parsed = true;
3937
- } catch (e) {
3938
- callback(true, 'JSON returned from webapp was invalid, yet status code was 200. Data was: ' + xhr.responseText);
3939
- }
3940
-
3941
- if (parsed) { // prevents double execution.
3942
- callback(false, data);
3943
- }
3944
- } else {
3945
- Pusher.warn("Couldn't get auth info from your webapp", xhr.status);
3946
- callback(true, xhr.status);
3947
- }
3948
- }
3949
- };
3950
-
3951
- xhr.send(this.composeQuery(socketId));
3952
- return xhr;
3953
- },
3954
-
3955
- jsonp: function(socketId, callback){
3956
- if(this.authOptions.headers !== undefined) {
3957
- Pusher.warn("Warn", "To send headers with the auth request, you must use AJAX, rather than JSONP.");
3958
- }
3959
-
3960
- var callbackName = nextAuthCallbackID.toString();
3961
- nextAuthCallbackID++;
3962
-
3963
- var document = Pusher.Util.getDocument();
3964
- var script = document.createElement("script");
3965
- // Hacked wrapper.
3966
- Pusher.auth_callbacks[callbackName] = function(data) {
3967
- callback(false, data);
3968
- };
3969
-
3970
- var callback_name = "Pusher.auth_callbacks['" + callbackName + "']";
3971
- script.src = this.options.authEndpoint +
3972
- '?callback=' +
3973
- encodeURIComponent(callback_name) +
3974
- '&' +
3975
- this.composeQuery(socketId);
3976
-
3977
- var head = document.getElementsByTagName("head")[0] || document.documentElement;
3978
- head.insertBefore( script, head.firstChild );
3979
- }
3980
- };
3981
- }).call(this);
3982
-
3983
- return Pusher;
3984
- }));