rfaye 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2765 @@
1
+ (function() {
2
+ 'use strict';
3
+
4
+ var Faye = {
5
+ VERSION: '1.1.1',
6
+
7
+ BAYEUX_VERSION: '1.0',
8
+ ID_LENGTH: 160,
9
+ JSONP_CALLBACK: 'jsonpcallback',
10
+ CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'],
11
+
12
+ MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'],
13
+
14
+ ENV: (typeof window !== 'undefined') ? window : global,
15
+
16
+ extend: function(dest, source, overwrite) {
17
+ if (!source) return dest;
18
+ for (var key in source) {
19
+ if (!source.hasOwnProperty(key)) continue;
20
+ if (dest.hasOwnProperty(key) && overwrite === false) continue;
21
+ if (dest[key] !== source[key])
22
+ dest[key] = source[key];
23
+ }
24
+ return dest;
25
+ },
26
+
27
+ random: function(bitlength) {
28
+ bitlength = bitlength || this.ID_LENGTH;
29
+ var maxLength = Math.ceil(bitlength * Math.log(2) / Math.log(36));
30
+ var string = csprng(bitlength, 36);
31
+ while (string.length < maxLength) string = '0' + string;
32
+ return string;
33
+ },
34
+
35
+ validateOptions: function(options, validKeys) {
36
+ for (var key in options) {
37
+ if (this.indexOf(validKeys, key) < 0)
38
+ throw new Error('Unrecognized option: ' + key);
39
+ }
40
+ },
41
+
42
+ clientIdFromMessages: function(messages) {
43
+ var connect = this.filter([].concat(messages), function(message) {
44
+ return message.channel === '/meta/connect';
45
+ });
46
+ return connect[0] && connect[0].clientId;
47
+ },
48
+
49
+ copyObject: function(object) {
50
+ var clone, i, key;
51
+ if (object instanceof Array) {
52
+ clone = [];
53
+ i = object.length;
54
+ while (i--) clone[i] = Faye.copyObject(object[i]);
55
+ return clone;
56
+ } else if (typeof object === 'object') {
57
+ clone = (object === null) ? null : {};
58
+ for (key in object) clone[key] = Faye.copyObject(object[key]);
59
+ return clone;
60
+ } else {
61
+ return object;
62
+ }
63
+ },
64
+
65
+ commonElement: function(lista, listb) {
66
+ for (var i = 0, n = lista.length; i < n; i++) {
67
+ if (this.indexOf(listb, lista[i]) !== -1)
68
+ return lista[i];
69
+ }
70
+ return null;
71
+ },
72
+
73
+ indexOf: function(list, needle) {
74
+ if (list.indexOf) return list.indexOf(needle);
75
+
76
+ for (var i = 0, n = list.length; i < n; i++) {
77
+ if (list[i] === needle) return i;
78
+ }
79
+ return -1;
80
+ },
81
+
82
+ map: function(object, callback, context) {
83
+ if (object.map) return object.map(callback, context);
84
+ var result = [];
85
+
86
+ if (object instanceof Array) {
87
+ for (var i = 0, n = object.length; i < n; i++) {
88
+ result.push(callback.call(context || null, object[i], i));
89
+ }
90
+ } else {
91
+ for (var key in object) {
92
+ if (!object.hasOwnProperty(key)) continue;
93
+ result.push(callback.call(context || null, key, object[key]));
94
+ }
95
+ }
96
+ return result;
97
+ },
98
+
99
+ filter: function(array, callback, context) {
100
+ if (array.filter) return array.filter(callback, context);
101
+ var result = [];
102
+ for (var i = 0, n = array.length; i < n; i++) {
103
+ if (callback.call(context || null, array[i], i))
104
+ result.push(array[i]);
105
+ }
106
+ return result;
107
+ },
108
+
109
+ asyncEach: function(list, iterator, callback, context) {
110
+ var n = list.length,
111
+ i = -1,
112
+ calls = 0,
113
+ looping = false;
114
+
115
+ var iterate = function() {
116
+ calls -= 1;
117
+ i += 1;
118
+ if (i === n) return callback && callback.call(context);
119
+ iterator(list[i], resume);
120
+ };
121
+
122
+ var loop = function() {
123
+ if (looping) return;
124
+ looping = true;
125
+ while (calls > 0) iterate();
126
+ looping = false;
127
+ };
128
+
129
+ var resume = function() {
130
+ calls += 1;
131
+ loop();
132
+ };
133
+ resume();
134
+ },
135
+
136
+ // http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/
137
+ toJSON: function(object) {
138
+ if (!this.stringify) return JSON.stringify(object);
139
+
140
+ return this.stringify(object, function(key, value) {
141
+ return (this[key] instanceof Array) ? this[key] : value;
142
+ });
143
+ }
144
+ };
145
+
146
+ if (typeof module !== 'undefined')
147
+ module.exports = Faye;
148
+ else if (typeof window !== 'undefined')
149
+ window.Faye = Faye;
150
+
151
+ Faye.Class = function(parent, methods) {
152
+ if (typeof parent !== 'function') {
153
+ methods = parent;
154
+ parent = Object;
155
+ }
156
+
157
+ var klass = function() {
158
+ if (!this.initialize) return this;
159
+ return this.initialize.apply(this, arguments) || this;
160
+ };
161
+
162
+ var bridge = function() {};
163
+ bridge.prototype = parent.prototype;
164
+
165
+ klass.prototype = new bridge();
166
+ Faye.extend(klass.prototype, methods);
167
+
168
+ return klass;
169
+ };
170
+
171
+ (function() {
172
+ var EventEmitter = Faye.EventEmitter = function() {};
173
+
174
+ /*
175
+ Copyright Joyent, Inc. and other Node contributors. All rights reserved.
176
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
177
+ this software and associated documentation files (the "Software"), to deal in
178
+ the Software without restriction, including without limitation the rights to
179
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
180
+ of the Software, and to permit persons to whom the Software is furnished to do
181
+ so, subject to the following conditions:
182
+
183
+ The above copyright notice and this permission notice shall be included in all
184
+ copies or substantial portions of the Software.
185
+
186
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
189
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
190
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
191
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
192
+ SOFTWARE.
193
+ */
194
+
195
+ var isArray = typeof Array.isArray === 'function'
196
+ ? Array.isArray
197
+ : function (xs) {
198
+ return Object.prototype.toString.call(xs) === '[object Array]'
199
+ }
200
+ ;
201
+ function indexOf (xs, x) {
202
+ if (xs.indexOf) return xs.indexOf(x);
203
+ for (var i = 0; i < xs.length; i++) {
204
+ if (x === xs[i]) return i;
205
+ }
206
+ return -1;
207
+ }
208
+
209
+
210
+ EventEmitter.prototype.emit = function(type) {
211
+ // If there is no 'error' event listener then throw.
212
+ if (type === 'error') {
213
+ if (!this._events || !this._events.error ||
214
+ (isArray(this._events.error) && !this._events.error.length))
215
+ {
216
+ if (arguments[1] instanceof Error) {
217
+ throw arguments[1]; // Unhandled 'error' event
218
+ } else {
219
+ throw new Error("Uncaught, unspecified 'error' event.");
220
+ }
221
+ return false;
222
+ }
223
+ }
224
+
225
+ if (!this._events) return false;
226
+ var handler = this._events[type];
227
+ if (!handler) return false;
228
+
229
+ if (typeof handler == 'function') {
230
+ switch (arguments.length) {
231
+ // fast cases
232
+ case 1:
233
+ handler.call(this);
234
+ break;
235
+ case 2:
236
+ handler.call(this, arguments[1]);
237
+ break;
238
+ case 3:
239
+ handler.call(this, arguments[1], arguments[2]);
240
+ break;
241
+ // slower
242
+ default:
243
+ var args = Array.prototype.slice.call(arguments, 1);
244
+ handler.apply(this, args);
245
+ }
246
+ return true;
247
+
248
+ } else if (isArray(handler)) {
249
+ var args = Array.prototype.slice.call(arguments, 1);
250
+
251
+ var listeners = handler.slice();
252
+ for (var i = 0, l = listeners.length; i < l; i++) {
253
+ listeners[i].apply(this, args);
254
+ }
255
+ return true;
256
+
257
+ } else {
258
+ return false;
259
+ }
260
+ };
261
+
262
+ // EventEmitter is defined in src/node_events.cc
263
+ // EventEmitter.prototype.emit() is also defined there.
264
+ EventEmitter.prototype.addListener = function(type, listener) {
265
+ if ('function' !== typeof listener) {
266
+ throw new Error('addListener only takes instances of Function');
267
+ }
268
+
269
+ if (!this._events) this._events = {};
270
+
271
+ // To avoid recursion in the case that type == "newListeners"! Before
272
+ // adding it to the listeners, first emit "newListeners".
273
+ this.emit('newListener', type, listener);
274
+
275
+ if (!this._events[type]) {
276
+ // Optimize the case of one listener. Don't need the extra array object.
277
+ this._events[type] = listener;
278
+ } else if (isArray(this._events[type])) {
279
+ // If we've already got an array, just append.
280
+ this._events[type].push(listener);
281
+ } else {
282
+ // Adding the second element, need to change to array.
283
+ this._events[type] = [this._events[type], listener];
284
+ }
285
+
286
+ return this;
287
+ };
288
+
289
+ EventEmitter.prototype.on = EventEmitter.prototype.addListener;
290
+
291
+ EventEmitter.prototype.once = function(type, listener) {
292
+ var self = this;
293
+ self.on(type, function g() {
294
+ self.removeListener(type, g);
295
+ listener.apply(this, arguments);
296
+ });
297
+
298
+ return this;
299
+ };
300
+
301
+ EventEmitter.prototype.removeListener = function(type, listener) {
302
+ if ('function' !== typeof listener) {
303
+ throw new Error('removeListener only takes instances of Function');
304
+ }
305
+
306
+ // does not use listeners(), so no side effect of creating _events[type]
307
+ if (!this._events || !this._events[type]) return this;
308
+
309
+ var list = this._events[type];
310
+
311
+ if (isArray(list)) {
312
+ var i = indexOf(list, listener);
313
+ if (i < 0) return this;
314
+ list.splice(i, 1);
315
+ if (list.length == 0)
316
+ delete this._events[type];
317
+ } else if (this._events[type] === listener) {
318
+ delete this._events[type];
319
+ }
320
+
321
+ return this;
322
+ };
323
+
324
+ EventEmitter.prototype.removeAllListeners = function(type) {
325
+ if (arguments.length === 0) {
326
+ this._events = {};
327
+ return this;
328
+ }
329
+
330
+ // does not use listeners(), so no side effect of creating _events[type]
331
+ if (type && this._events && this._events[type]) this._events[type] = null;
332
+ return this;
333
+ };
334
+
335
+ EventEmitter.prototype.listeners = function(type) {
336
+ if (!this._events) this._events = {};
337
+ if (!this._events[type]) this._events[type] = [];
338
+ if (!isArray(this._events[type])) {
339
+ this._events[type] = [this._events[type]];
340
+ }
341
+ return this._events[type];
342
+ };
343
+
344
+ })();
345
+
346
+ Faye.Namespace = Faye.Class({
347
+ initialize: function() {
348
+ this._used = {};
349
+ },
350
+
351
+ exists: function(id) {
352
+ return this._used.hasOwnProperty(id);
353
+ },
354
+
355
+ generate: function() {
356
+ var name = Faye.random();
357
+ while (this._used.hasOwnProperty(name))
358
+ name = Faye.random();
359
+ return this._used[name] = name;
360
+ },
361
+
362
+ release: function(id) {
363
+ delete this._used[id];
364
+ }
365
+ });
366
+
367
+ (function() {
368
+ 'use strict';
369
+
370
+ var timeout = setTimeout, defer;
371
+
372
+ if (typeof setImmediate === 'function')
373
+ defer = function(fn) { setImmediate(fn) };
374
+ else if (typeof process === 'object' && process.nextTick)
375
+ defer = function(fn) { process.nextTick(fn) };
376
+ else
377
+ defer = function(fn) { timeout(fn, 0) };
378
+
379
+ var PENDING = 0,
380
+ FULFILLED = 1,
381
+ REJECTED = 2;
382
+
383
+ var RETURN = function(x) { return x },
384
+ THROW = function(x) { throw x };
385
+
386
+ var Promise = function(task) {
387
+ this._state = PENDING;
388
+ this._onFulfilled = [];
389
+ this._onRejected = [];
390
+
391
+ if (typeof task !== 'function') return;
392
+ var self = this;
393
+
394
+ task(function(value) { fulfill(self, value) },
395
+ function(reason) { reject(self, reason) });
396
+ };
397
+
398
+ Promise.prototype.then = function(onFulfilled, onRejected) {
399
+ var next = new Promise();
400
+ registerOnFulfilled(this, onFulfilled, next);
401
+ registerOnRejected(this, onRejected, next);
402
+ return next;
403
+ };
404
+
405
+ var registerOnFulfilled = function(promise, onFulfilled, next) {
406
+ if (typeof onFulfilled !== 'function') onFulfilled = RETURN;
407
+ var handler = function(value) { invoke(onFulfilled, value, next) };
408
+
409
+ if (promise._state === PENDING) {
410
+ promise._onFulfilled.push(handler);
411
+ } else if (promise._state === FULFILLED) {
412
+ handler(promise._value);
413
+ }
414
+ };
415
+
416
+ var registerOnRejected = function(promise, onRejected, next) {
417
+ if (typeof onRejected !== 'function') onRejected = THROW;
418
+ var handler = function(reason) { invoke(onRejected, reason, next) };
419
+
420
+ if (promise._state === PENDING) {
421
+ promise._onRejected.push(handler);
422
+ } else if (promise._state === REJECTED) {
423
+ handler(promise._reason);
424
+ }
425
+ };
426
+
427
+ var invoke = function(fn, value, next) {
428
+ defer(function() { _invoke(fn, value, next) });
429
+ };
430
+
431
+ var _invoke = function(fn, value, next) {
432
+ var outcome;
433
+
434
+ try {
435
+ outcome = fn(value);
436
+ } catch (error) {
437
+ return reject(next, error);
438
+ }
439
+
440
+ if (outcome === next) {
441
+ reject(next, new TypeError('Recursive promise chain detected'));
442
+ } else {
443
+ fulfill(next, outcome);
444
+ }
445
+ };
446
+
447
+ var fulfill = Promise.fulfill = Promise.resolve = function(promise, value) {
448
+ var called = false, type, then;
449
+
450
+ try {
451
+ type = typeof value;
452
+ then = value !== null && (type === 'function' || type === 'object') && value.then;
453
+
454
+ if (typeof then !== 'function') return _fulfill(promise, value);
455
+
456
+ then.call(value, function(v) {
457
+ if (!(called ^ (called = true))) return;
458
+ fulfill(promise, v);
459
+ }, function(r) {
460
+ if (!(called ^ (called = true))) return;
461
+ reject(promise, r);
462
+ });
463
+ } catch (error) {
464
+ if (!(called ^ (called = true))) return;
465
+ reject(promise, error);
466
+ }
467
+ };
468
+
469
+ var _fulfill = function(promise, value) {
470
+ if (promise._state !== PENDING) return;
471
+
472
+ promise._state = FULFILLED;
473
+ promise._value = value;
474
+ promise._onRejected = [];
475
+
476
+ var onFulfilled = promise._onFulfilled, fn;
477
+ while (fn = onFulfilled.shift()) fn(value);
478
+ };
479
+
480
+ var reject = Promise.reject = function(promise, reason) {
481
+ if (promise._state !== PENDING) return;
482
+
483
+ promise._state = REJECTED;
484
+ promise._reason = reason;
485
+ promise._onFulfilled = [];
486
+
487
+ var onRejected = promise._onRejected, fn;
488
+ while (fn = onRejected.shift()) fn(reason);
489
+ };
490
+
491
+ Promise.all = function(promises) {
492
+ return new Promise(function(fulfill, reject) {
493
+ var list = [],
494
+ n = promises.length,
495
+ i;
496
+
497
+ if (n === 0) return fulfill(list);
498
+
499
+ for (i = 0; i < n; i++) (function(promise, i) {
500
+ Promise.fulfilled(promise).then(function(value) {
501
+ list[i] = value;
502
+ if (--n === 0) fulfill(list);
503
+ }, reject);
504
+ })(promises[i], i);
505
+ });
506
+ };
507
+
508
+ Promise.defer = defer;
509
+
510
+ Promise.deferred = Promise.pending = function() {
511
+ var tuple = {};
512
+
513
+ tuple.promise = new Promise(function(fulfill, reject) {
514
+ tuple.fulfill = tuple.resolve = fulfill;
515
+ tuple.reject = reject;
516
+ });
517
+ return tuple;
518
+ };
519
+
520
+ Promise.fulfilled = Promise.resolved = function(value) {
521
+ return new Promise(function(fulfill, reject) { fulfill(value) });
522
+ };
523
+
524
+ Promise.rejected = function(reason) {
525
+ return new Promise(function(fulfill, reject) { reject(reason) });
526
+ };
527
+
528
+ if (typeof Faye === 'undefined')
529
+ module.exports = Promise;
530
+ else
531
+ Faye.Promise = Promise;
532
+
533
+ })();
534
+
535
+ Faye.Set = Faye.Class({
536
+ initialize: function() {
537
+ this._index = {};
538
+ },
539
+
540
+ add: function(item) {
541
+ var key = (item.id !== undefined) ? item.id : item;
542
+ if (this._index.hasOwnProperty(key)) return false;
543
+ this._index[key] = item;
544
+ return true;
545
+ },
546
+
547
+ forEach: function(block, context) {
548
+ for (var key in this._index) {
549
+ if (this._index.hasOwnProperty(key))
550
+ block.call(context, this._index[key]);
551
+ }
552
+ },
553
+
554
+ isEmpty: function() {
555
+ for (var key in this._index) {
556
+ if (this._index.hasOwnProperty(key)) return false;
557
+ }
558
+ return true;
559
+ },
560
+
561
+ member: function(item) {
562
+ for (var key in this._index) {
563
+ if (this._index[key] === item) return true;
564
+ }
565
+ return false;
566
+ },
567
+
568
+ remove: function(item) {
569
+ var key = (item.id !== undefined) ? item.id : item;
570
+ var removed = this._index[key];
571
+ delete this._index[key];
572
+ return removed;
573
+ },
574
+
575
+ toArray: function() {
576
+ var array = [];
577
+ this.forEach(function(item) { array.push(item) });
578
+ return array;
579
+ }
580
+ });
581
+
582
+ Faye.URI = {
583
+ isURI: function(uri) {
584
+ return uri && uri.protocol && uri.host && uri.path;
585
+ },
586
+
587
+ isSameOrigin: function(uri) {
588
+ var location = Faye.ENV.location;
589
+ return uri.protocol === location.protocol &&
590
+ uri.hostname === location.hostname &&
591
+ uri.port === location.port;
592
+ },
593
+
594
+ parse: function(url) {
595
+ if (typeof url !== 'string') return url;
596
+ var uri = {}, parts, query, pairs, i, n, data;
597
+
598
+ var consume = function(name, pattern) {
599
+ url = url.replace(pattern, function(match) {
600
+ uri[name] = match;
601
+ return '';
602
+ });
603
+ uri[name] = uri[name] || '';
604
+ };
605
+
606
+ consume('protocol', /^[a-z]+\:/i);
607
+ consume('host', /^\/\/[^\/\?#]+/);
608
+
609
+ if (!/^\//.test(url) && !uri.host)
610
+ url = Faye.ENV.location.pathname.replace(/[^\/]*$/, '') + url;
611
+
612
+ consume('pathname', /^[^\?#]*/);
613
+ consume('search', /^\?[^#]*/);
614
+ consume('hash', /^#.*/);
615
+
616
+ uri.protocol = uri.protocol || Faye.ENV.location.protocol;
617
+
618
+ if (uri.host) {
619
+ uri.host = uri.host.substr(2);
620
+ parts = uri.host.split(':');
621
+ uri.hostname = parts[0];
622
+ uri.port = parts[1] || '';
623
+ } else {
624
+ uri.host = Faye.ENV.location.host;
625
+ uri.hostname = Faye.ENV.location.hostname;
626
+ uri.port = Faye.ENV.location.port;
627
+ }
628
+
629
+ uri.pathname = uri.pathname || '/';
630
+ uri.path = uri.pathname + uri.search;
631
+
632
+ query = uri.search.replace(/^\?/, '');
633
+ pairs = query ? query.split('&') : [];
634
+ data = {};
635
+
636
+ for (i = 0, n = pairs.length; i < n; i++) {
637
+ parts = pairs[i].split('=');
638
+ data[decodeURIComponent(parts[0] || '')] = decodeURIComponent(parts[1] || '');
639
+ }
640
+
641
+ uri.query = data;
642
+
643
+ uri.href = this.stringify(uri);
644
+ return uri;
645
+ },
646
+
647
+ stringify: function(uri) {
648
+ var string = uri.protocol + '//' + uri.hostname;
649
+ if (uri.port) string += ':' + uri.port;
650
+ string += uri.pathname + this.queryString(uri.query) + (uri.hash || '');
651
+ return string;
652
+ },
653
+
654
+ queryString: function(query) {
655
+ var pairs = [];
656
+ for (var key in query) {
657
+ if (!query.hasOwnProperty(key)) continue;
658
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(query[key]));
659
+ }
660
+ if (pairs.length === 0) return '';
661
+ return '?' + pairs.join('&');
662
+ }
663
+ };
664
+
665
+ Faye.Error = Faye.Class({
666
+ initialize: function(code, params, message) {
667
+ this.code = code;
668
+ this.params = Array.prototype.slice.call(params);
669
+ this.message = message;
670
+ },
671
+
672
+ toString: function() {
673
+ return this.code + ':' +
674
+ this.params.join(',') + ':' +
675
+ this.message;
676
+ }
677
+ });
678
+
679
+ Faye.Error.parse = function(message) {
680
+ message = message || '';
681
+ if (!Faye.Grammar.ERROR.test(message)) return new this(null, [], message);
682
+
683
+ var parts = message.split(':'),
684
+ code = parseInt(parts[0]),
685
+ params = parts[1].split(','),
686
+ message = parts[2];
687
+
688
+ return new this(code, params, message);
689
+ };
690
+
691
+
692
+
693
+
694
+ Faye.Error.versionMismatch = function() {
695
+ return new this(300, arguments, 'Version mismatch').toString();
696
+ };
697
+
698
+ Faye.Error.conntypeMismatch = function() {
699
+ return new this(301, arguments, 'Connection types not supported').toString();
700
+ };
701
+
702
+ Faye.Error.extMismatch = function() {
703
+ return new this(302, arguments, 'Extension mismatch').toString();
704
+ };
705
+
706
+ Faye.Error.badRequest = function() {
707
+ return new this(400, arguments, 'Bad request').toString();
708
+ };
709
+
710
+ Faye.Error.clientUnknown = function() {
711
+ return new this(401, arguments, 'Unknown client').toString();
712
+ };
713
+
714
+ Faye.Error.parameterMissing = function() {
715
+ return new this(402, arguments, 'Missing required parameter').toString();
716
+ };
717
+
718
+ Faye.Error.channelForbidden = function() {
719
+ return new this(403, arguments, 'Forbidden channel').toString();
720
+ };
721
+
722
+ Faye.Error.channelUnknown = function() {
723
+ return new this(404, arguments, 'Unknown channel').toString();
724
+ };
725
+
726
+ Faye.Error.channelInvalid = function() {
727
+ return new this(405, arguments, 'Invalid channel').toString();
728
+ };
729
+
730
+ Faye.Error.extUnknown = function() {
731
+ return new this(406, arguments, 'Unknown extension').toString();
732
+ };
733
+
734
+ Faye.Error.publishFailed = function() {
735
+ return new this(407, arguments, 'Failed to publish').toString();
736
+ };
737
+
738
+ Faye.Error.serverError = function() {
739
+ return new this(500, arguments, 'Internal server error').toString();
740
+ };
741
+
742
+
743
+ Faye.Deferrable = {
744
+ then: function(callback, errback) {
745
+ var self = this;
746
+ if (!this._promise)
747
+ this._promise = new Faye.Promise(function(fulfill, reject) {
748
+ self._fulfill = fulfill;
749
+ self._reject = reject;
750
+ });
751
+
752
+ if (arguments.length === 0)
753
+ return this._promise;
754
+ else
755
+ return this._promise.then(callback, errback);
756
+ },
757
+
758
+ callback: function(callback, context) {
759
+ return this.then(function(value) { callback.call(context, value) });
760
+ },
761
+
762
+ errback: function(callback, context) {
763
+ return this.then(null, function(reason) { callback.call(context, reason) });
764
+ },
765
+
766
+ timeout: function(seconds, message) {
767
+ this.then();
768
+ var self = this;
769
+ this._timer = Faye.ENV.setTimeout(function() {
770
+ self._reject(message);
771
+ }, seconds * 1000);
772
+ },
773
+
774
+ setDeferredStatus: function(status, value) {
775
+ if (this._timer) Faye.ENV.clearTimeout(this._timer);
776
+
777
+ this.then();
778
+
779
+ if (status === 'succeeded')
780
+ this._fulfill(value);
781
+ else if (status === 'failed')
782
+ this._reject(value);
783
+ else
784
+ delete this._promise;
785
+ }
786
+ };
787
+
788
+ Faye.Publisher = {
789
+ countListeners: function(eventType) {
790
+ return this.listeners(eventType).length;
791
+ },
792
+
793
+ bind: function(eventType, listener, context) {
794
+ var slice = Array.prototype.slice,
795
+ handler = function() { listener.apply(context, slice.call(arguments)) };
796
+
797
+ this._listeners = this._listeners || [];
798
+ this._listeners.push([eventType, listener, context, handler]);
799
+ return this.on(eventType, handler);
800
+ },
801
+
802
+ unbind: function(eventType, listener, context) {
803
+ this._listeners = this._listeners || [];
804
+ var n = this._listeners.length, tuple;
805
+
806
+ while (n--) {
807
+ tuple = this._listeners[n];
808
+ if (tuple[0] !== eventType) continue;
809
+ if (listener && (tuple[1] !== listener || tuple[2] !== context)) continue;
810
+ this._listeners.splice(n, 1);
811
+ this.removeListener(eventType, tuple[3]);
812
+ }
813
+ }
814
+ };
815
+
816
+ Faye.extend(Faye.Publisher, Faye.EventEmitter.prototype);
817
+ Faye.Publisher.trigger = Faye.Publisher.emit;
818
+
819
+ Faye.Timeouts = {
820
+ addTimeout: function(name, delay, callback, context) {
821
+ this._timeouts = this._timeouts || {};
822
+ if (this._timeouts.hasOwnProperty(name)) return;
823
+ var self = this;
824
+ this._timeouts[name] = Faye.ENV.setTimeout(function() {
825
+ delete self._timeouts[name];
826
+ callback.call(context);
827
+ }, 1000 * delay);
828
+ },
829
+
830
+ removeTimeout: function(name) {
831
+ this._timeouts = this._timeouts || {};
832
+ var timeout = this._timeouts[name];
833
+ if (!timeout) return;
834
+ Faye.ENV.clearTimeout(timeout);
835
+ delete this._timeouts[name];
836
+ },
837
+
838
+ removeAllTimeouts: function() {
839
+ this._timeouts = this._timeouts || {};
840
+ for (var name in this._timeouts) this.removeTimeout(name);
841
+ }
842
+ };
843
+
844
+ Faye.Logging = {
845
+ LOG_LEVELS: {
846
+ fatal: 4,
847
+ error: 3,
848
+ warn: 2,
849
+ info: 1,
850
+ debug: 0
851
+ },
852
+
853
+ writeLog: function(messageArgs, level) {
854
+ if (!Faye.logger) return;
855
+
856
+ var args = Array.prototype.slice.apply(messageArgs),
857
+ banner = '[Faye',
858
+ klass = this.className,
859
+
860
+ message = args.shift().replace(/\?/g, function() {
861
+ try {
862
+ return Faye.toJSON(args.shift());
863
+ } catch (e) {
864
+ return '[Object]';
865
+ }
866
+ });
867
+
868
+ for (var key in Faye) {
869
+ if (klass) continue;
870
+ if (typeof Faye[key] !== 'function') continue;
871
+ if (this instanceof Faye[key]) klass = key;
872
+ }
873
+ if (klass) banner += '.' + klass;
874
+ banner += '] ';
875
+
876
+ if (typeof Faye.logger[level] === 'function')
877
+ Faye.logger[level](banner + message);
878
+ else if (typeof Faye.logger === 'function')
879
+ Faye.logger(banner + message);
880
+ }
881
+ };
882
+
883
+ (function() {
884
+ for (var key in Faye.Logging.LOG_LEVELS)
885
+ (function(level) {
886
+ Faye.Logging[level] = function() {
887
+ this.writeLog(arguments, level);
888
+ };
889
+ })(key);
890
+ })();
891
+
892
+ Faye.Grammar = {
893
+ CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
894
+ CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
895
+ ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/,
896
+ VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/
897
+ };
898
+
899
+ Faye.Extensible = {
900
+ addExtension: function(extension) {
901
+ this._extensions = this._extensions || [];
902
+ this._extensions.push(extension);
903
+ if (extension.added) extension.added(this);
904
+ },
905
+
906
+ removeExtension: function(extension) {
907
+ if (!this._extensions) return;
908
+ var i = this._extensions.length;
909
+ while (i--) {
910
+ if (this._extensions[i] !== extension) continue;
911
+ this._extensions.splice(i,1);
912
+ if (extension.removed) extension.removed(this);
913
+ }
914
+ },
915
+
916
+ pipeThroughExtensions: function(stage, message, request, callback, context) {
917
+ this.debug('Passing through ? extensions: ?', stage, message);
918
+
919
+ if (!this._extensions) return callback.call(context, message);
920
+ var extensions = this._extensions.slice();
921
+
922
+ var pipe = function(message) {
923
+ if (!message) return callback.call(context, message);
924
+
925
+ var extension = extensions.shift();
926
+ if (!extension) return callback.call(context, message);
927
+
928
+ var fn = extension[stage];
929
+ if (!fn) return pipe(message);
930
+
931
+ if (fn.length >= 3) extension[stage](message, request, pipe);
932
+ else extension[stage](message, pipe);
933
+ };
934
+ pipe(message);
935
+ }
936
+ };
937
+
938
+ Faye.extend(Faye.Extensible, Faye.Logging);
939
+
940
+ Faye.Channel = Faye.Class({
941
+ initialize: function(name) {
942
+ this.id = this.name = name;
943
+ },
944
+
945
+ push: function(message) {
946
+ this.trigger('message', message);
947
+ },
948
+
949
+ isUnused: function() {
950
+ return this.countListeners('message') === 0;
951
+ }
952
+ });
953
+
954
+ Faye.extend(Faye.Channel.prototype, Faye.Publisher);
955
+
956
+ Faye.extend(Faye.Channel, {
957
+ HANDSHAKE: '/meta/handshake',
958
+ CONNECT: '/meta/connect',
959
+ SUBSCRIBE: '/meta/subscribe',
960
+ UNSUBSCRIBE: '/meta/unsubscribe',
961
+ DISCONNECT: '/meta/disconnect',
962
+
963
+ META: 'meta',
964
+ SERVICE: 'service',
965
+
966
+ expand: function(name) {
967
+ var segments = this.parse(name),
968
+ channels = ['/**', name];
969
+
970
+ var copy = segments.slice();
971
+ copy[copy.length - 1] = '*';
972
+ channels.push(this.unparse(copy));
973
+
974
+ for (var i = 1, n = segments.length; i < n; i++) {
975
+ copy = segments.slice(0, i);
976
+ copy.push('**');
977
+ channels.push(this.unparse(copy));
978
+ }
979
+
980
+ return channels;
981
+ },
982
+
983
+ isValid: function(name) {
984
+ return Faye.Grammar.CHANNEL_NAME.test(name) ||
985
+ Faye.Grammar.CHANNEL_PATTERN.test(name);
986
+ },
987
+
988
+ parse: function(name) {
989
+ if (!this.isValid(name)) return null;
990
+ return name.split('/').slice(1);
991
+ },
992
+
993
+ unparse: function(segments) {
994
+ return '/' + segments.join('/');
995
+ },
996
+
997
+ isMeta: function(name) {
998
+ var segments = this.parse(name);
999
+ return segments ? (segments[0] === this.META) : null;
1000
+ },
1001
+
1002
+ isService: function(name) {
1003
+ var segments = this.parse(name);
1004
+ return segments ? (segments[0] === this.SERVICE) : null;
1005
+ },
1006
+
1007
+ isSubscribable: function(name) {
1008
+ if (!this.isValid(name)) return null;
1009
+ return !this.isMeta(name) && !this.isService(name);
1010
+ },
1011
+
1012
+ Set: Faye.Class({
1013
+ initialize: function() {
1014
+ this._channels = {};
1015
+ },
1016
+
1017
+ getKeys: function() {
1018
+ var keys = [];
1019
+ for (var key in this._channels) keys.push(key);
1020
+ return keys;
1021
+ },
1022
+
1023
+ remove: function(name) {
1024
+ delete this._channels[name];
1025
+ },
1026
+
1027
+ hasSubscription: function(name) {
1028
+ return this._channels.hasOwnProperty(name);
1029
+ },
1030
+
1031
+ subscribe: function(names, callback, context) {
1032
+ var name;
1033
+ for (var i = 0, n = names.length; i < n; i++) {
1034
+ name = names[i];
1035
+ var channel = this._channels[name] = this._channels[name] || new Faye.Channel(name);
1036
+ if (callback) channel.bind('message', callback, context);
1037
+ }
1038
+ },
1039
+
1040
+ unsubscribe: function(name, callback, context) {
1041
+ var channel = this._channels[name];
1042
+ if (!channel) return false;
1043
+ channel.unbind('message', callback, context);
1044
+
1045
+ if (channel.isUnused()) {
1046
+ this.remove(name);
1047
+ return true;
1048
+ } else {
1049
+ return false;
1050
+ }
1051
+ },
1052
+
1053
+ distributeMessage: function(message) {
1054
+ var channels = Faye.Channel.expand(message.channel);
1055
+
1056
+ for (var i = 0, n = channels.length; i < n; i++) {
1057
+ var channel = this._channels[channels[i]];
1058
+ if (channel) channel.trigger('message', message.data);
1059
+ }
1060
+ }
1061
+ })
1062
+ });
1063
+
1064
+ Faye.Publication = Faye.Class(Faye.Deferrable);
1065
+
1066
+ Faye.Subscription = Faye.Class({
1067
+ initialize: function(client, channels, callback, context) {
1068
+ this._client = client;
1069
+ this._channels = channels;
1070
+ this._callback = callback;
1071
+ this._context = context;
1072
+ this._cancelled = false;
1073
+ },
1074
+
1075
+ cancel: function() {
1076
+ if (this._cancelled) return;
1077
+ this._client.unsubscribe(this._channels, this._callback, this._context);
1078
+ this._cancelled = true;
1079
+ },
1080
+
1081
+ unsubscribe: function() {
1082
+ this.cancel();
1083
+ }
1084
+ });
1085
+
1086
+ Faye.extend(Faye.Subscription.prototype, Faye.Deferrable);
1087
+
1088
+ Faye.Client = Faye.Class({
1089
+ UNCONNECTED: 1,
1090
+ CONNECTING: 2,
1091
+ CONNECTED: 3,
1092
+ DISCONNECTED: 4,
1093
+
1094
+ HANDSHAKE: 'handshake',
1095
+ RETRY: 'retry',
1096
+ NONE: 'none',
1097
+
1098
+ CONNECTION_TIMEOUT: 60,
1099
+
1100
+ DEFAULT_ENDPOINT: '/bayeux',
1101
+ INTERVAL: 0,
1102
+
1103
+ initialize: function(endpoint, options) {
1104
+ this.info('New client created for ?', endpoint);
1105
+ options = options || {};
1106
+
1107
+ Faye.validateOptions(options, ['interval', 'timeout', 'endpoints', 'proxy', 'retry', 'scheduler', 'websocketExtensions', 'tls', 'ca']);
1108
+
1109
+ this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
1110
+ this._channels = new Faye.Channel.Set();
1111
+ this._dispatcher = new Faye.Dispatcher(this, this._endpoint, options);
1112
+
1113
+ this._messageId = 0;
1114
+ this._state = this.UNCONNECTED;
1115
+
1116
+ this._responseCallbacks = {};
1117
+
1118
+ this._advice = {
1119
+ reconnect: this.RETRY,
1120
+ interval: 1000 * (options.interval || this.INTERVAL),
1121
+ timeout: 1000 * (options.timeout || this.CONNECTION_TIMEOUT)
1122
+ };
1123
+ this._dispatcher.timeout = this._advice.timeout / 1000;
1124
+
1125
+ this._dispatcher.bind('message', this._receiveMessage, this);
1126
+
1127
+ if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
1128
+ Faye.Event.on(Faye.ENV, 'beforeunload', function() {
1129
+ if (Faye.indexOf(this._dispatcher._disabled, 'autodisconnect') < 0)
1130
+ this.disconnect();
1131
+ }, this);
1132
+ },
1133
+
1134
+ addWebsocketExtension: function(extension) {
1135
+ return this._dispatcher.addWebsocketExtension(extension);
1136
+ },
1137
+
1138
+ disable: function(feature) {
1139
+ return this._dispatcher.disable(feature);
1140
+ },
1141
+
1142
+ setHeader: function(name, value) {
1143
+ return this._dispatcher.setHeader(name, value);
1144
+ },
1145
+
1146
+ // Request
1147
+ // MUST include: * channel
1148
+ // * version
1149
+ // * supportedConnectionTypes
1150
+ // MAY include: * minimumVersion
1151
+ // * ext
1152
+ // * id
1153
+ //
1154
+ // Success Response Failed Response
1155
+ // MUST include: * channel MUST include: * channel
1156
+ // * version * successful
1157
+ // * supportedConnectionTypes * error
1158
+ // * clientId MAY include: * supportedConnectionTypes
1159
+ // * successful * advice
1160
+ // MAY include: * minimumVersion * version
1161
+ // * advice * minimumVersion
1162
+ // * ext * ext
1163
+ // * id * id
1164
+ // * authSuccessful
1165
+ handshake: function(callback, context) {
1166
+ if (this._advice.reconnect === this.NONE) return;
1167
+ if (this._state !== this.UNCONNECTED) return;
1168
+
1169
+ this._state = this.CONNECTING;
1170
+ var self = this;
1171
+
1172
+ this.info('Initiating handshake with ?', Faye.URI.stringify(this._endpoint));
1173
+ this._dispatcher.selectTransport(Faye.MANDATORY_CONNECTION_TYPES);
1174
+
1175
+ this._sendMessage({
1176
+ channel: Faye.Channel.HANDSHAKE,
1177
+ version: Faye.BAYEUX_VERSION,
1178
+ supportedConnectionTypes: this._dispatcher.getConnectionTypes()
1179
+
1180
+ }, {}, function(response) {
1181
+
1182
+ if (response.successful) {
1183
+ this._state = this.CONNECTED;
1184
+ this._dispatcher.clientId = response.clientId;
1185
+
1186
+ this._dispatcher.selectTransport(response.supportedConnectionTypes);
1187
+
1188
+ this.info('Handshake successful: ?', this._dispatcher.clientId);
1189
+
1190
+ this.subscribe(this._channels.getKeys(), true);
1191
+ if (callback) Faye.Promise.defer(function() { callback.call(context) });
1192
+
1193
+ } else {
1194
+ this.info('Handshake unsuccessful');
1195
+ Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._dispatcher.retry * 1000);
1196
+ this._state = this.UNCONNECTED;
1197
+ }
1198
+ }, this);
1199
+ },
1200
+
1201
+ // Request Response
1202
+ // MUST include: * channel MUST include: * channel
1203
+ // * clientId * successful
1204
+ // * connectionType * clientId
1205
+ // MAY include: * ext MAY include: * error
1206
+ // * id * advice
1207
+ // * ext
1208
+ // * id
1209
+ // * timestamp
1210
+ connect: function(callback, context) {
1211
+ if (this._advice.reconnect === this.NONE) return;
1212
+ if (this._state === this.DISCONNECTED) return;
1213
+
1214
+ if (this._state === this.UNCONNECTED)
1215
+ return this.handshake(function() { this.connect(callback, context) }, this);
1216
+
1217
+ this.callback(callback, context);
1218
+ if (this._state !== this.CONNECTED) return;
1219
+
1220
+ this.info('Calling deferred actions for ?', this._dispatcher.clientId);
1221
+ this.setDeferredStatus('succeeded');
1222
+ this.setDeferredStatus('unknown');
1223
+
1224
+ if (this._connectRequest) return;
1225
+ this._connectRequest = true;
1226
+
1227
+ this.info('Initiating connection for ?', this._dispatcher.clientId);
1228
+
1229
+ this._sendMessage({
1230
+ channel: Faye.Channel.CONNECT,
1231
+ clientId: this._dispatcher.clientId,
1232
+ connectionType: this._dispatcher.connectionType
1233
+
1234
+ }, {}, this._cycleConnection, this);
1235
+ },
1236
+
1237
+ // Request Response
1238
+ // MUST include: * channel MUST include: * channel
1239
+ // * clientId * successful
1240
+ // MAY include: * ext * clientId
1241
+ // * id MAY include: * error
1242
+ // * ext
1243
+ // * id
1244
+ disconnect: function() {
1245
+ if (this._state !== this.CONNECTED) return;
1246
+ this._state = this.DISCONNECTED;
1247
+
1248
+ this.info('Disconnecting ?', this._dispatcher.clientId);
1249
+ var promise = new Faye.Publication();
1250
+
1251
+ this._sendMessage({
1252
+ channel: Faye.Channel.DISCONNECT,
1253
+ clientId: this._dispatcher.clientId
1254
+
1255
+ }, {}, function(response) {
1256
+ if (response.successful) {
1257
+ this._dispatcher.close();
1258
+ promise.setDeferredStatus('succeeded');
1259
+ } else {
1260
+ promise.setDeferredStatus('failed', Faye.Error.parse(response.error));
1261
+ }
1262
+ }, this);
1263
+
1264
+ this.info('Clearing channel listeners for ?', this._dispatcher.clientId);
1265
+ this._channels = new Faye.Channel.Set();
1266
+
1267
+ return promise;
1268
+ },
1269
+
1270
+ // Request Response
1271
+ // MUST include: * channel MUST include: * channel
1272
+ // * clientId * successful
1273
+ // * subscription * clientId
1274
+ // MAY include: * ext * subscription
1275
+ // * id MAY include: * error
1276
+ // * advice
1277
+ // * ext
1278
+ // * id
1279
+ // * timestamp
1280
+ subscribe: function(channel, callback, context) {
1281
+ if (channel instanceof Array)
1282
+ return Faye.map(channel, function(c) {
1283
+ return this.subscribe(c, callback, context);
1284
+ }, this);
1285
+
1286
+ var subscription = new Faye.Subscription(this, channel, callback, context),
1287
+ force = (callback === true),
1288
+ hasSubscribe = this._channels.hasSubscription(channel);
1289
+
1290
+ if (hasSubscribe && !force) {
1291
+ this._channels.subscribe([channel], callback, context);
1292
+ subscription.setDeferredStatus('succeeded');
1293
+ return subscription;
1294
+ }
1295
+
1296
+ this.connect(function() {
1297
+ this.info('Client ? attempting to subscribe to ?', this._dispatcher.clientId, channel);
1298
+ if (!force) this._channels.subscribe([channel], callback, context);
1299
+
1300
+ this._sendMessage({
1301
+ channel: Faye.Channel.SUBSCRIBE,
1302
+ clientId: this._dispatcher.clientId,
1303
+ subscription: channel
1304
+
1305
+ }, {}, function(response) {
1306
+ if (!response.successful) {
1307
+ subscription.setDeferredStatus('failed', Faye.Error.parse(response.error));
1308
+ return this._channels.unsubscribe(channel, callback, context);
1309
+ }
1310
+
1311
+ var channels = [].concat(response.subscription);
1312
+ this.info('Subscription acknowledged for ? to ?', this._dispatcher.clientId, channels);
1313
+ subscription.setDeferredStatus('succeeded');
1314
+ }, this);
1315
+ }, this);
1316
+
1317
+ return subscription;
1318
+ },
1319
+
1320
+ // Request Response
1321
+ // MUST include: * channel MUST include: * channel
1322
+ // * clientId * successful
1323
+ // * subscription * clientId
1324
+ // MAY include: * ext * subscription
1325
+ // * id MAY include: * error
1326
+ // * advice
1327
+ // * ext
1328
+ // * id
1329
+ // * timestamp
1330
+ unsubscribe: function(channel, callback, context) {
1331
+ if (channel instanceof Array)
1332
+ return Faye.map(channel, function(c) {
1333
+ return this.unsubscribe(c, callback, context);
1334
+ }, this);
1335
+
1336
+ var dead = this._channels.unsubscribe(channel, callback, context);
1337
+ if (!dead) return;
1338
+
1339
+ this.connect(function() {
1340
+ this.info('Client ? attempting to unsubscribe from ?', this._dispatcher.clientId, channel);
1341
+
1342
+ this._sendMessage({
1343
+ channel: Faye.Channel.UNSUBSCRIBE,
1344
+ clientId: this._dispatcher.clientId,
1345
+ subscription: channel
1346
+
1347
+ }, {}, function(response) {
1348
+ if (!response.successful) return;
1349
+
1350
+ var channels = [].concat(response.subscription);
1351
+ this.info('Unsubscription acknowledged for ? from ?', this._dispatcher.clientId, channels);
1352
+ }, this);
1353
+ }, this);
1354
+ },
1355
+
1356
+ // Request Response
1357
+ // MUST include: * channel MUST include: * channel
1358
+ // * data * successful
1359
+ // MAY include: * clientId MAY include: * id
1360
+ // * id * error
1361
+ // * ext * ext
1362
+ publish: function(channel, data, options) {
1363
+ Faye.validateOptions(options || {}, ['attempts', 'deadline']);
1364
+ var publication = new Faye.Publication();
1365
+
1366
+ this.connect(function() {
1367
+ this.info('Client ? queueing published message to ?: ?', this._dispatcher.clientId, channel, data);
1368
+
1369
+ this._sendMessage({
1370
+ channel: channel,
1371
+ data: data,
1372
+ clientId: this._dispatcher.clientId
1373
+
1374
+ }, options, function(response) {
1375
+ if (response.successful)
1376
+ publication.setDeferredStatus('succeeded');
1377
+ else
1378
+ publication.setDeferredStatus('failed', Faye.Error.parse(response.error));
1379
+ }, this);
1380
+ }, this);
1381
+
1382
+ return publication;
1383
+ },
1384
+
1385
+ _sendMessage: function(message, options, callback, context) {
1386
+ message.id = this._generateMessageId();
1387
+
1388
+ var timeout = this._advice.timeout
1389
+ ? 1.2 * this._advice.timeout / 1000
1390
+ : 1.2 * this._dispatcher.retry;
1391
+
1392
+ this.pipeThroughExtensions('outgoing', message, null, function(message) {
1393
+ if (!message) return;
1394
+ if (callback) this._responseCallbacks[message.id] = [callback, context];
1395
+ this._dispatcher.sendMessage(message, timeout, options || {});
1396
+ }, this);
1397
+ },
1398
+
1399
+ _generateMessageId: function() {
1400
+ this._messageId += 1;
1401
+ if (this._messageId >= Math.pow(2,32)) this._messageId = 0;
1402
+ return this._messageId.toString(36);
1403
+ },
1404
+
1405
+ _receiveMessage: function(message) {
1406
+ var id = message.id, callback;
1407
+
1408
+ if (message.successful !== undefined) {
1409
+ callback = this._responseCallbacks[id];
1410
+ delete this._responseCallbacks[id];
1411
+ }
1412
+
1413
+ this.pipeThroughExtensions('incoming', message, null, function(message) {
1414
+ if (!message) return;
1415
+ if (message.advice) this._handleAdvice(message.advice);
1416
+ this._deliverMessage(message);
1417
+ if (callback) callback[0].call(callback[1], message);
1418
+ }, this);
1419
+ },
1420
+
1421
+ _handleAdvice: function(advice) {
1422
+ Faye.extend(this._advice, advice);
1423
+ this._dispatcher.timeout = this._advice.timeout / 1000;
1424
+
1425
+ if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
1426
+ this._state = this.UNCONNECTED;
1427
+ this._dispatcher.clientId = null;
1428
+ this._cycleConnection();
1429
+ }
1430
+ },
1431
+
1432
+ _deliverMessage: function(message) {
1433
+ if (!message.channel || message.data === undefined) return;
1434
+ this.info('Client ? calling listeners for ? with ?', this._dispatcher.clientId, message.channel, message.data);
1435
+ this._channels.distributeMessage(message);
1436
+ },
1437
+
1438
+ _cycleConnection: function() {
1439
+ if (this._connectRequest) {
1440
+ this._connectRequest = null;
1441
+ this.info('Closed connection for ?', this._dispatcher.clientId);
1442
+ }
1443
+ var self = this;
1444
+ Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
1445
+ }
1446
+ });
1447
+
1448
+ Faye.extend(Faye.Client.prototype, Faye.Deferrable);
1449
+ Faye.extend(Faye.Client.prototype, Faye.Publisher);
1450
+ Faye.extend(Faye.Client.prototype, Faye.Logging);
1451
+ Faye.extend(Faye.Client.prototype, Faye.Extensible);
1452
+
1453
+ Faye.Dispatcher = Faye.Class({
1454
+ MAX_REQUEST_SIZE: 2048,
1455
+ DEFAULT_RETRY: 5,
1456
+
1457
+ UP: 1,
1458
+ DOWN: 2,
1459
+
1460
+ initialize: function(client, endpoint, options) {
1461
+ this._client = client;
1462
+ this.endpoint = Faye.URI.parse(endpoint);
1463
+ this._alternates = options.endpoints || {};
1464
+
1465
+ this.cookies = Faye.Cookies && new Faye.Cookies.CookieJar();
1466
+ this._disabled = [];
1467
+ this._envelopes = {};
1468
+ this.headers = {};
1469
+ this.retry = options.retry || this.DEFAULT_RETRY;
1470
+ this._scheduler = options.scheduler || Faye.Scheduler;
1471
+ this._state = 0;
1472
+ this.transports = {};
1473
+ this.wsExtensions = [];
1474
+
1475
+ this.proxy = options.proxy || {};
1476
+ if (typeof this._proxy === 'string') this._proxy = {origin: this._proxy};
1477
+
1478
+ var exts = options.websocketExtensions;
1479
+ if (exts) {
1480
+ exts = [].concat(exts);
1481
+ for (var i = 0, n = exts.length; i < n; i++)
1482
+ this.addWebsocketExtension(exts[i]);
1483
+ }
1484
+
1485
+ this.tls = options.tls || {};
1486
+ this.tls.ca = this.tls.ca || options.ca;
1487
+
1488
+ for (var type in this._alternates)
1489
+ this._alternates[type] = Faye.URI.parse(this._alternates[type]);
1490
+
1491
+ this.maxRequestSize = this.MAX_REQUEST_SIZE;
1492
+ },
1493
+
1494
+ endpointFor: function(connectionType) {
1495
+ return this._alternates[connectionType] || this.endpoint;
1496
+ },
1497
+
1498
+ addWebsocketExtension: function(extension) {
1499
+ this.wsExtensions.push(extension);
1500
+ },
1501
+
1502
+ disable: function(feature) {
1503
+ this._disabled.push(feature);
1504
+ },
1505
+
1506
+ setHeader: function(name, value) {
1507
+ this.headers[name] = value;
1508
+ },
1509
+
1510
+ close: function() {
1511
+ var transport = this._transport;
1512
+ delete this._transport;
1513
+ if (transport) transport.close();
1514
+ },
1515
+
1516
+ getConnectionTypes: function() {
1517
+ return Faye.Transport.getConnectionTypes();
1518
+ },
1519
+
1520
+ selectTransport: function(transportTypes) {
1521
+ Faye.Transport.get(this, transportTypes, this._disabled, function(transport) {
1522
+ this.debug('Selected ? transport for ?', transport.connectionType, Faye.URI.stringify(transport.endpoint));
1523
+
1524
+ if (transport === this._transport) return;
1525
+ if (this._transport) this._transport.close();
1526
+
1527
+ this._transport = transport;
1528
+ this.connectionType = transport.connectionType;
1529
+ }, this);
1530
+ },
1531
+
1532
+ sendMessage: function(message, timeout, options) {
1533
+ options = options || {};
1534
+
1535
+ var id = message.id,
1536
+ attempts = options.attempts,
1537
+ deadline = options.deadline && new Date().getTime() + (options.deadline * 1000),
1538
+ envelope = this._envelopes[id],
1539
+ scheduler;
1540
+
1541
+ if (!envelope) {
1542
+ scheduler = new this._scheduler(message, {timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline});
1543
+ envelope = this._envelopes[id] = {message: message, scheduler: scheduler};
1544
+ }
1545
+
1546
+ this._sendEnvelope(envelope);
1547
+ },
1548
+
1549
+ _sendEnvelope: function(envelope) {
1550
+ if (!this._transport) return;
1551
+ if (envelope.request || envelope.timer) return;
1552
+
1553
+ var message = envelope.message,
1554
+ scheduler = envelope.scheduler,
1555
+ self = this;
1556
+
1557
+ if (!scheduler.isDeliverable()) {
1558
+ scheduler.abort();
1559
+ delete this._envelopes[message.id];
1560
+ return;
1561
+ }
1562
+
1563
+ envelope.timer = Faye.ENV.setTimeout(function() {
1564
+ self.handleError(message);
1565
+ }, scheduler.getTimeout() * 1000);
1566
+
1567
+ scheduler.send();
1568
+ envelope.request = this._transport.sendMessage(message);
1569
+ },
1570
+
1571
+ handleResponse: function(reply) {
1572
+ var envelope = this._envelopes[reply.id];
1573
+
1574
+ if (reply.successful !== undefined && envelope) {
1575
+ envelope.scheduler.succeed();
1576
+ delete this._envelopes[reply.id];
1577
+ Faye.ENV.clearTimeout(envelope.timer);
1578
+ }
1579
+
1580
+ this.trigger('message', reply);
1581
+
1582
+ if (this._state === this.UP) return;
1583
+ this._state = this.UP;
1584
+ this._client.trigger('transport:up');
1585
+ },
1586
+
1587
+ handleError: function(message, immediate) {
1588
+ var envelope = this._envelopes[message.id],
1589
+ request = envelope && envelope.request,
1590
+ self = this;
1591
+
1592
+ if (!request) return;
1593
+
1594
+ request.then(function(req) {
1595
+ if (req && req.abort) req.abort();
1596
+ });
1597
+
1598
+ var scheduler = envelope.scheduler;
1599
+ scheduler.fail();
1600
+
1601
+ Faye.ENV.clearTimeout(envelope.timer);
1602
+ envelope.request = envelope.timer = null;
1603
+
1604
+ if (immediate) {
1605
+ this._sendEnvelope(envelope);
1606
+ } else {
1607
+ envelope.timer = Faye.ENV.setTimeout(function() {
1608
+ envelope.timer = null;
1609
+ self._sendEnvelope(envelope);
1610
+ }, scheduler.getInterval() * 1000);
1611
+ }
1612
+
1613
+ if (this._state === this.DOWN) return;
1614
+ this._state = this.DOWN;
1615
+ this._client.trigger('transport:down');
1616
+ }
1617
+ });
1618
+
1619
+ Faye.extend(Faye.Dispatcher.prototype, Faye.Publisher);
1620
+ Faye.extend(Faye.Dispatcher.prototype, Faye.Logging);
1621
+
1622
+ Faye.Scheduler = function(message, options) {
1623
+ this.message = message;
1624
+ this.options = options;
1625
+ this.attempts = 0;
1626
+ };
1627
+
1628
+ Faye.extend(Faye.Scheduler.prototype, {
1629
+ getTimeout: function() {
1630
+ return this.options.timeout;
1631
+ },
1632
+
1633
+ getInterval: function() {
1634
+ return this.options.interval;
1635
+ },
1636
+
1637
+ isDeliverable: function() {
1638
+ var attempts = this.options.attempts,
1639
+ made = this.attempts,
1640
+ deadline = this.options.deadline,
1641
+ now = new Date().getTime();
1642
+
1643
+ if (attempts !== undefined && made >= attempts)
1644
+ return false;
1645
+
1646
+ if (deadline !== undefined && now > deadline)
1647
+ return false;
1648
+
1649
+ return true;
1650
+ },
1651
+
1652
+ send: function() {
1653
+ this.attempts += 1;
1654
+ },
1655
+
1656
+ succeed: function() {},
1657
+
1658
+ fail: function() {},
1659
+
1660
+ abort: function() {}
1661
+ });
1662
+
1663
+ Faye.Transport = Faye.extend(Faye.Class({
1664
+ DEFAULT_PORTS: {'http:': 80, 'https:': 443, 'ws:': 80, 'wss:': 443},
1665
+ SECURE_PROTOCOLS: ['https:', 'wss:'],
1666
+ MAX_DELAY: 0,
1667
+
1668
+ batching: true,
1669
+
1670
+ initialize: function(dispatcher, endpoint) {
1671
+ this._dispatcher = dispatcher;
1672
+ this.endpoint = endpoint;
1673
+ this._outbox = [];
1674
+ this._proxy = Faye.extend({}, this._dispatcher.proxy);
1675
+
1676
+ if (!this._proxy.origin && Faye.NodeAdapter) {
1677
+ this._proxy.origin = Faye.indexOf(this.SECURE_PROTOCOLS, this.endpoint.protocol) >= 0
1678
+ ? (process.env.HTTPS_PROXY || process.env.https_proxy)
1679
+ : (process.env.HTTP_PROXY || process.env.http_proxy);
1680
+ }
1681
+ },
1682
+
1683
+ close: function() {},
1684
+
1685
+ encode: function(messages) {
1686
+ return '';
1687
+ },
1688
+
1689
+ sendMessage: function(message) {
1690
+ this.debug('Client ? sending message to ?: ?',
1691
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), message);
1692
+
1693
+ if (!this.batching) return Faye.Promise.fulfilled(this.request([message]));
1694
+
1695
+ this._outbox.push(message);
1696
+ this._flushLargeBatch();
1697
+ this._promise = this._promise || new Faye.Promise();
1698
+
1699
+ if (message.channel === Faye.Channel.HANDSHAKE) {
1700
+ this.addTimeout('publish', 0.01, this._flush, this);
1701
+ return this._promise;
1702
+ }
1703
+
1704
+ if (message.channel === Faye.Channel.CONNECT)
1705
+ this._connectMessage = message;
1706
+
1707
+ this.addTimeout('publish', this.MAX_DELAY, this._flush, this);
1708
+ return this._promise;
1709
+ },
1710
+
1711
+ _flush: function() {
1712
+ this.removeTimeout('publish');
1713
+
1714
+ if (this._outbox.length > 1 && this._connectMessage)
1715
+ this._connectMessage.advice = {timeout: 0};
1716
+
1717
+ Faye.Promise.fulfill(this._promise, this.request(this._outbox));
1718
+ delete this._promise;
1719
+
1720
+ this._connectMessage = null;
1721
+ this._outbox = [];
1722
+ },
1723
+
1724
+ _flushLargeBatch: function() {
1725
+ var string = this.encode(this._outbox);
1726
+ if (string.length < this._dispatcher.maxRequestSize) return;
1727
+ var last = this._outbox.pop();
1728
+ this._flush();
1729
+ if (last) this._outbox.push(last);
1730
+ },
1731
+
1732
+ _receive: function(replies) {
1733
+ if (!replies) return;
1734
+ replies = [].concat(replies);
1735
+
1736
+ this.debug('Client ? received from ? via ?: ?',
1737
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, replies);
1738
+
1739
+ for (var i = 0, n = replies.length; i < n; i++)
1740
+ this._dispatcher.handleResponse(replies[i]);
1741
+ },
1742
+
1743
+ _handleError: function(messages, immediate) {
1744
+ messages = [].concat(messages);
1745
+
1746
+ this.debug('Client ? failed to send to ? via ?: ?',
1747
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, messages);
1748
+
1749
+ for (var i = 0, n = messages.length; i < n; i++)
1750
+ this._dispatcher.handleError(messages[i]);
1751
+ },
1752
+
1753
+ _getCookies: function() {
1754
+ var cookies = this._dispatcher.cookies,
1755
+ url = Faye.URI.stringify(this.endpoint);
1756
+
1757
+ if (!cookies) return '';
1758
+
1759
+ return Faye.map(cookies.getCookiesSync(url), function(cookie) {
1760
+ return cookie.cookieString();
1761
+ }).join('; ');
1762
+ },
1763
+
1764
+ _storeCookies: function(setCookie) {
1765
+ var cookies = this._dispatcher.cookies,
1766
+ url = Faye.URI.stringify(this.endpoint),
1767
+ cookie;
1768
+
1769
+ if (!setCookie || !cookies) return;
1770
+ setCookie = [].concat(setCookie);
1771
+
1772
+ for (var i = 0, n = setCookie.length; i < n; i++) {
1773
+ cookie = Faye.Cookies.Cookie.parse(setCookie[i]);
1774
+ cookies.setCookieSync(cookie, url);
1775
+ }
1776
+ }
1777
+
1778
+ }), {
1779
+ get: function(dispatcher, allowed, disabled, callback, context) {
1780
+ var endpoint = dispatcher.endpoint;
1781
+
1782
+ Faye.asyncEach(this._transports, function(pair, resume) {
1783
+ var connType = pair[0], klass = pair[1],
1784
+ connEndpoint = dispatcher.endpointFor(connType);
1785
+
1786
+ if (Faye.indexOf(disabled, connType) >= 0)
1787
+ return resume();
1788
+
1789
+ if (Faye.indexOf(allowed, connType) < 0) {
1790
+ klass.isUsable(dispatcher, connEndpoint, function() {});
1791
+ return resume();
1792
+ }
1793
+
1794
+ klass.isUsable(dispatcher, connEndpoint, function(isUsable) {
1795
+ if (!isUsable) return resume();
1796
+ var transport = klass.hasOwnProperty('create') ? klass.create(dispatcher, connEndpoint) : new klass(dispatcher, connEndpoint);
1797
+ callback.call(context, transport);
1798
+ });
1799
+ }, function() {
1800
+ throw new Error('Could not find a usable connection type for ' + Faye.URI.stringify(endpoint));
1801
+ });
1802
+ },
1803
+
1804
+ register: function(type, klass) {
1805
+ this._transports.push([type, klass]);
1806
+ klass.prototype.connectionType = type;
1807
+ },
1808
+
1809
+ getConnectionTypes: function() {
1810
+ return Faye.map(this._transports, function(t) { return t[0] });
1811
+ },
1812
+
1813
+ _transports: []
1814
+ });
1815
+
1816
+ Faye.extend(Faye.Transport.prototype, Faye.Logging);
1817
+ Faye.extend(Faye.Transport.prototype, Faye.Timeouts);
1818
+
1819
+ Faye.Event = {
1820
+ _registry: [],
1821
+
1822
+ on: function(element, eventName, callback, context) {
1823
+ var wrapped = function() { callback.call(context) };
1824
+
1825
+ if (element.addEventListener)
1826
+ element.addEventListener(eventName, wrapped, false);
1827
+ else
1828
+ element.attachEvent('on' + eventName, wrapped);
1829
+
1830
+ this._registry.push({
1831
+ _element: element,
1832
+ _type: eventName,
1833
+ _callback: callback,
1834
+ _context: context,
1835
+ _handler: wrapped
1836
+ });
1837
+ },
1838
+
1839
+ detach: function(element, eventName, callback, context) {
1840
+ var i = this._registry.length, register;
1841
+ while (i--) {
1842
+ register = this._registry[i];
1843
+
1844
+ if ((element && element !== register._element) ||
1845
+ (eventName && eventName !== register._type) ||
1846
+ (callback && callback !== register._callback) ||
1847
+ (context && context !== register._context))
1848
+ continue;
1849
+
1850
+ if (register._element.removeEventListener)
1851
+ register._element.removeEventListener(register._type, register._handler, false);
1852
+ else
1853
+ register._element.detachEvent('on' + register._type, register._handler);
1854
+
1855
+ this._registry.splice(i,1);
1856
+ register = null;
1857
+ }
1858
+ }
1859
+ };
1860
+
1861
+ if (Faye.ENV.onunload !== undefined) Faye.Event.on(Faye.ENV, 'unload', Faye.Event.detach, Faye.Event);
1862
+
1863
+ /*
1864
+ json2.js
1865
+ 2013-05-26
1866
+
1867
+ Public Domain.
1868
+
1869
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
1870
+
1871
+ See http://www.JSON.org/js.html
1872
+
1873
+
1874
+ This code should be minified before deployment.
1875
+ See http://javascript.crockford.com/jsmin.html
1876
+
1877
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
1878
+ NOT CONTROL.
1879
+
1880
+
1881
+ This file creates a global JSON object containing two methods: stringify
1882
+ and parse.
1883
+
1884
+ JSON.stringify(value, replacer, space)
1885
+ value any JavaScript value, usually an object or array.
1886
+
1887
+ replacer an optional parameter that determines how object
1888
+ values are stringified for objects. It can be a
1889
+ function or an array of strings.
1890
+
1891
+ space an optional parameter that specifies the indentation
1892
+ of nested structures. If it is omitted, the text will
1893
+ be packed without extra whitespace. If it is a number,
1894
+ it will specify the number of spaces to indent at each
1895
+ level. If it is a string (such as '\t' or '&nbsp;'),
1896
+ it contains the characters used to indent at each level.
1897
+
1898
+ This method produces a JSON text from a JavaScript value.
1899
+
1900
+ When an object value is found, if the object contains a toJSON
1901
+ method, its toJSON method will be called and the result will be
1902
+ stringified. A toJSON method does not serialize: it returns the
1903
+ value represented by the name/value pair that should be serialized,
1904
+ or undefined if nothing should be serialized. The toJSON method
1905
+ will be passed the key associated with the value, and this will be
1906
+ bound to the value
1907
+
1908
+ For example, this would serialize Dates as ISO strings.
1909
+
1910
+ Date.prototype.toJSON = function (key) {
1911
+ function f(n) {
1912
+ // Format integers to have at least two digits.
1913
+ return n < 10 ? '0' + n : n;
1914
+ }
1915
+
1916
+ return this.getUTCFullYear() + '-' +
1917
+ f(this.getUTCMonth() + 1) + '-' +
1918
+ f(this.getUTCDate()) + 'T' +
1919
+ f(this.getUTCHours()) + ':' +
1920
+ f(this.getUTCMinutes()) + ':' +
1921
+ f(this.getUTCSeconds()) + 'Z';
1922
+ };
1923
+
1924
+ You can provide an optional replacer method. It will be passed the
1925
+ key and value of each member, with this bound to the containing
1926
+ object. The value that is returned from your method will be
1927
+ serialized. If your method returns undefined, then the member will
1928
+ be excluded from the serialization.
1929
+
1930
+ If the replacer parameter is an array of strings, then it will be
1931
+ used to select the members to be serialized. It filters the results
1932
+ such that only members with keys listed in the replacer array are
1933
+ stringified.
1934
+
1935
+ Values that do not have JSON representations, such as undefined or
1936
+ functions, will not be serialized. Such values in objects will be
1937
+ dropped; in arrays they will be replaced with null. You can use
1938
+ a replacer function to replace those with JSON values.
1939
+ JSON.stringify(undefined) returns undefined.
1940
+
1941
+ The optional space parameter produces a stringification of the
1942
+ value that is filled with line breaks and indentation to make it
1943
+ easier to read.
1944
+
1945
+ If the space parameter is a non-empty string, then that string will
1946
+ be used for indentation. If the space parameter is a number, then
1947
+ the indentation will be that many spaces.
1948
+
1949
+ Example:
1950
+
1951
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
1952
+ // text is '["e",{"pluribus":"unum"}]'
1953
+
1954
+
1955
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
1956
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
1957
+
1958
+ text = JSON.stringify([new Date()], function (key, value) {
1959
+ return this[key] instanceof Date ?
1960
+ 'Date(' + this[key] + ')' : value;
1961
+ });
1962
+ // text is '["Date(---current time---)"]'
1963
+
1964
+
1965
+ JSON.parse(text, reviver)
1966
+ This method parses a JSON text to produce an object or array.
1967
+ It can throw a SyntaxError exception.
1968
+
1969
+ The optional reviver parameter is a function that can filter and
1970
+ transform the results. It receives each of the keys and values,
1971
+ and its return value is used instead of the original value.
1972
+ If it returns what it received, then the structure is not modified.
1973
+ If it returns undefined then the member is deleted.
1974
+
1975
+ Example:
1976
+
1977
+ // Parse the text. Values that look like ISO date strings will
1978
+ // be converted to Date objects.
1979
+
1980
+ myData = JSON.parse(text, function (key, value) {
1981
+ var a;
1982
+ if (typeof value === 'string') {
1983
+ a =
1984
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
1985
+ if (a) {
1986
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
1987
+ +a[5], +a[6]));
1988
+ }
1989
+ }
1990
+ return value;
1991
+ });
1992
+
1993
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
1994
+ var d;
1995
+ if (typeof value === 'string' &&
1996
+ value.slice(0, 5) === 'Date(' &&
1997
+ value.slice(-1) === ')') {
1998
+ d = new Date(value.slice(5, -1));
1999
+ if (d) {
2000
+ return d;
2001
+ }
2002
+ }
2003
+ return value;
2004
+ });
2005
+
2006
+
2007
+ This is a reference implementation. You are free to copy, modify, or
2008
+ redistribute.
2009
+ */
2010
+
2011
+ /*jslint evil: true, regexp: true */
2012
+
2013
+ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
2014
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
2015
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
2016
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
2017
+ test, toJSON, toString, valueOf
2018
+ */
2019
+
2020
+
2021
+ // Create a JSON object only if one does not already exist. We create the
2022
+ // methods in a closure to avoid creating global variables.
2023
+
2024
+ if (typeof JSON !== 'object') {
2025
+ JSON = {};
2026
+ }
2027
+
2028
+ (function () {
2029
+ 'use strict';
2030
+
2031
+ function f(n) {
2032
+ // Format integers to have at least two digits.
2033
+ return n < 10 ? '0' + n : n;
2034
+ }
2035
+
2036
+ if (typeof Date.prototype.toJSON !== 'function') {
2037
+
2038
+ Date.prototype.toJSON = function () {
2039
+
2040
+ return isFinite(this.valueOf())
2041
+ ? this.getUTCFullYear() + '-' +
2042
+ f(this.getUTCMonth() + 1) + '-' +
2043
+ f(this.getUTCDate()) + 'T' +
2044
+ f(this.getUTCHours()) + ':' +
2045
+ f(this.getUTCMinutes()) + ':' +
2046
+ f(this.getUTCSeconds()) + 'Z'
2047
+ : null;
2048
+ };
2049
+
2050
+ String.prototype.toJSON =
2051
+ Number.prototype.toJSON =
2052
+ Boolean.prototype.toJSON = function () {
2053
+ return this.valueOf();
2054
+ };
2055
+ }
2056
+
2057
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
2058
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
2059
+ gap,
2060
+ indent,
2061
+ meta = { // table of character substitutions
2062
+ '\b': '\\b',
2063
+ '\t': '\\t',
2064
+ '\n': '\\n',
2065
+ '\f': '\\f',
2066
+ '\r': '\\r',
2067
+ '"' : '\\"',
2068
+ '\\': '\\\\'
2069
+ },
2070
+ rep;
2071
+
2072
+
2073
+ function quote(string) {
2074
+
2075
+ // If the string contains no control characters, no quote characters, and no
2076
+ // backslash characters, then we can safely slap some quotes around it.
2077
+ // Otherwise we must also replace the offending characters with safe escape
2078
+ // sequences.
2079
+
2080
+ escapable.lastIndex = 0;
2081
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
2082
+ var c = meta[a];
2083
+ return typeof c === 'string'
2084
+ ? c
2085
+ : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
2086
+ }) + '"' : '"' + string + '"';
2087
+ }
2088
+
2089
+
2090
+ function str(key, holder) {
2091
+
2092
+ // Produce a string from holder[key].
2093
+
2094
+ var i, // The loop counter.
2095
+ k, // The member key.
2096
+ v, // The member value.
2097
+ length,
2098
+ mind = gap,
2099
+ partial,
2100
+ value = holder[key];
2101
+
2102
+ // If the value has a toJSON method, call it to obtain a replacement value.
2103
+
2104
+ if (value && typeof value === 'object' &&
2105
+ typeof value.toJSON === 'function') {
2106
+ value = value.toJSON(key);
2107
+ }
2108
+
2109
+ // If we were called with a replacer function, then call the replacer to
2110
+ // obtain a replacement value.
2111
+
2112
+ if (typeof rep === 'function') {
2113
+ value = rep.call(holder, key, value);
2114
+ }
2115
+
2116
+ // What happens next depends on the value's type.
2117
+
2118
+ switch (typeof value) {
2119
+ case 'string':
2120
+ return quote(value);
2121
+
2122
+ case 'number':
2123
+
2124
+ // JSON numbers must be finite. Encode non-finite numbers as null.
2125
+
2126
+ return isFinite(value) ? String(value) : 'null';
2127
+
2128
+ case 'boolean':
2129
+ case 'null':
2130
+
2131
+ // If the value is a boolean or null, convert it to a string. Note:
2132
+ // typeof null does not produce 'null'. The case is included here in
2133
+ // the remote chance that this gets fixed someday.
2134
+
2135
+ return String(value);
2136
+
2137
+ // If the type is 'object', we might be dealing with an object or an array or
2138
+ // null.
2139
+
2140
+ case 'object':
2141
+
2142
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
2143
+ // so watch out for that case.
2144
+
2145
+ if (!value) {
2146
+ return 'null';
2147
+ }
2148
+
2149
+ // Make an array to hold the partial results of stringifying this object value.
2150
+
2151
+ gap += indent;
2152
+ partial = [];
2153
+
2154
+ // Is the value an array?
2155
+
2156
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
2157
+
2158
+ // The value is an array. Stringify every element. Use null as a placeholder
2159
+ // for non-JSON values.
2160
+
2161
+ length = value.length;
2162
+ for (i = 0; i < length; i += 1) {
2163
+ partial[i] = str(i, value) || 'null';
2164
+ }
2165
+
2166
+ // Join all of the elements together, separated with commas, and wrap them in
2167
+ // brackets.
2168
+
2169
+ v = partial.length === 0
2170
+ ? '[]'
2171
+ : gap
2172
+ ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
2173
+ : '[' + partial.join(',') + ']';
2174
+ gap = mind;
2175
+ return v;
2176
+ }
2177
+
2178
+ // If the replacer is an array, use it to select the members to be stringified.
2179
+
2180
+ if (rep && typeof rep === 'object') {
2181
+ length = rep.length;
2182
+ for (i = 0; i < length; i += 1) {
2183
+ if (typeof rep[i] === 'string') {
2184
+ k = rep[i];
2185
+ v = str(k, value);
2186
+ if (v) {
2187
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
2188
+ }
2189
+ }
2190
+ }
2191
+ } else {
2192
+
2193
+ // Otherwise, iterate through all of the keys in the object.
2194
+
2195
+ for (k in value) {
2196
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
2197
+ v = str(k, value);
2198
+ if (v) {
2199
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
2200
+ }
2201
+ }
2202
+ }
2203
+ }
2204
+
2205
+ // Join all of the member texts together, separated with commas,
2206
+ // and wrap them in braces.
2207
+
2208
+ v = partial.length === 0
2209
+ ? '{}'
2210
+ : gap
2211
+ ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
2212
+ : '{' + partial.join(',') + '}';
2213
+ gap = mind;
2214
+ return v;
2215
+ }
2216
+ }
2217
+
2218
+ // If the JSON object does not yet have a stringify method, give it one.
2219
+
2220
+ Faye.stringify = function (value, replacer, space) {
2221
+
2222
+ // The stringify method takes a value and an optional replacer, and an optional
2223
+ // space parameter, and returns a JSON text. The replacer can be a function
2224
+ // that can replace values, or an array of strings that will select the keys.
2225
+ // A default replacer method can be provided. Use of the space parameter can
2226
+ // produce text that is more easily readable.
2227
+
2228
+ var i;
2229
+ gap = '';
2230
+ indent = '';
2231
+
2232
+ // If the space parameter is a number, make an indent string containing that
2233
+ // many spaces.
2234
+
2235
+ if (typeof space === 'number') {
2236
+ for (i = 0; i < space; i += 1) {
2237
+ indent += ' ';
2238
+ }
2239
+
2240
+ // If the space parameter is a string, it will be used as the indent string.
2241
+
2242
+ } else if (typeof space === 'string') {
2243
+ indent = space;
2244
+ }
2245
+
2246
+ // If there is a replacer, it must be a function or an array.
2247
+ // Otherwise, throw an error.
2248
+
2249
+ rep = replacer;
2250
+ if (replacer && typeof replacer !== 'function' &&
2251
+ (typeof replacer !== 'object' ||
2252
+ typeof replacer.length !== 'number')) {
2253
+ throw new Error('JSON.stringify');
2254
+ }
2255
+
2256
+ // Make a fake root object containing our value under the key of ''.
2257
+ // Return the result of stringifying the value.
2258
+
2259
+ return str('', {'': value});
2260
+ };
2261
+
2262
+ if (typeof JSON.stringify !== 'function') {
2263
+ JSON.stringify = Faye.stringify;
2264
+ }
2265
+
2266
+ // If the JSON object does not yet have a parse method, give it one.
2267
+
2268
+ if (typeof JSON.parse !== 'function') {
2269
+ JSON.parse = function (text, reviver) {
2270
+
2271
+ // The parse method takes a text and an optional reviver function, and returns
2272
+ // a JavaScript value if the text is a valid JSON text.
2273
+
2274
+ var j;
2275
+
2276
+ function walk(holder, key) {
2277
+
2278
+ // The walk method is used to recursively walk the resulting structure so
2279
+ // that modifications can be made.
2280
+
2281
+ var k, v, value = holder[key];
2282
+ if (value && typeof value === 'object') {
2283
+ for (k in value) {
2284
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
2285
+ v = walk(value, k);
2286
+ if (v !== undefined) {
2287
+ value[k] = v;
2288
+ } else {
2289
+ delete value[k];
2290
+ }
2291
+ }
2292
+ }
2293
+ }
2294
+ return reviver.call(holder, key, value);
2295
+ }
2296
+
2297
+
2298
+ // Parsing happens in four stages. In the first stage, we replace certain
2299
+ // Unicode characters with escape sequences. JavaScript handles many characters
2300
+ // incorrectly, either silently deleting them, or treating them as line endings.
2301
+
2302
+ text = String(text);
2303
+ cx.lastIndex = 0;
2304
+ if (cx.test(text)) {
2305
+ text = text.replace(cx, function (a) {
2306
+ return '\\u' +
2307
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
2308
+ });
2309
+ }
2310
+
2311
+ // In the second stage, we run the text against regular expressions that look
2312
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
2313
+ // because they can cause invocation, and '=' because it can cause mutation.
2314
+ // But just to be safe, we want to reject all unexpected forms.
2315
+
2316
+ // We split the second stage into 4 regexp operations in order to work around
2317
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
2318
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
2319
+ // replace all simple value tokens with ']' characters. Third, we delete all
2320
+ // open brackets that follow a colon or comma or that begin the text. Finally,
2321
+ // we look to see that the remaining characters are only whitespace or ']' or
2322
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
2323
+
2324
+ if (/^[\],:{}\s]*$/
2325
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
2326
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
2327
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
2328
+
2329
+ // In the third stage we use the eval function to compile the text into a
2330
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
2331
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
2332
+ // in parens to eliminate the ambiguity.
2333
+
2334
+ j = eval('(' + text + ')');
2335
+
2336
+ // In the optional fourth stage, we recursively walk the new structure, passing
2337
+ // each name/value pair to a reviver function for possible transformation.
2338
+
2339
+ return typeof reviver === 'function'
2340
+ ? walk({'': j}, '')
2341
+ : j;
2342
+ }
2343
+
2344
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
2345
+
2346
+ throw new SyntaxError('JSON.parse');
2347
+ };
2348
+ }
2349
+ }());
2350
+
2351
+ Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, {
2352
+ UNCONNECTED: 1,
2353
+ CONNECTING: 2,
2354
+ CONNECTED: 3,
2355
+
2356
+ batching: false,
2357
+
2358
+ isUsable: function(callback, context) {
2359
+ this.callback(function() { callback.call(context, true) });
2360
+ this.errback(function() { callback.call(context, false) });
2361
+ this.connect();
2362
+ },
2363
+
2364
+ request: function(messages) {
2365
+ this._pending = this._pending || new Faye.Set();
2366
+ for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]);
2367
+
2368
+ var promise = new Faye.Promise();
2369
+
2370
+ this.callback(function(socket) {
2371
+ if (!socket) return;
2372
+ socket.send(Faye.toJSON(messages));
2373
+ Faye.Promise.fulfill(promise, socket);
2374
+ }, this);
2375
+
2376
+ this.connect();
2377
+
2378
+ return {
2379
+ abort: function() { promise.then(function(ws) { ws.close() }) }
2380
+ };
2381
+ },
2382
+
2383
+ connect: function() {
2384
+ if (Faye.Transport.WebSocket._unloaded) return;
2385
+
2386
+ this._state = this._state || this.UNCONNECTED;
2387
+ if (this._state !== this.UNCONNECTED) return;
2388
+ this._state = this.CONNECTING;
2389
+
2390
+ var socket = this._createSocket();
2391
+ if (!socket) return this.setDeferredStatus('failed');
2392
+
2393
+ var self = this;
2394
+
2395
+ socket.onopen = function() {
2396
+ if (socket.headers) self._storeCookies(socket.headers['set-cookie']);
2397
+ self._socket = socket;
2398
+ self._state = self.CONNECTED;
2399
+ self._everConnected = true;
2400
+ self._ping();
2401
+ self.setDeferredStatus('succeeded', socket);
2402
+ };
2403
+
2404
+ var closed = false;
2405
+ socket.onclose = socket.onerror = function() {
2406
+ if (closed) return;
2407
+ closed = true;
2408
+
2409
+ var wasConnected = (self._state === self.CONNECTED);
2410
+ socket.onopen = socket.onclose = socket.onerror = socket.onmessage = null;
2411
+
2412
+ delete self._socket;
2413
+ self._state = self.UNCONNECTED;
2414
+ self.removeTimeout('ping');
2415
+ self.setDeferredStatus('unknown');
2416
+
2417
+ var pending = self._pending ? self._pending.toArray() : [];
2418
+ delete self._pending;
2419
+
2420
+ if (wasConnected) {
2421
+ self._handleError(pending, true);
2422
+ } else if (self._everConnected) {
2423
+ self._handleError(pending);
2424
+ } else {
2425
+ self.setDeferredStatus('failed');
2426
+ }
2427
+ };
2428
+
2429
+ socket.onmessage = function(event) {
2430
+ var replies = JSON.parse(event.data);
2431
+ if (!replies) return;
2432
+
2433
+ replies = [].concat(replies);
2434
+
2435
+ for (var i = 0, n = replies.length; i < n; i++) {
2436
+ if (replies[i].successful === undefined) continue;
2437
+ self._pending.remove(replies[i]);
2438
+ }
2439
+ self._receive(replies);
2440
+ };
2441
+ },
2442
+
2443
+ close: function() {
2444
+ if (!this._socket) return;
2445
+ this._socket.close();
2446
+ },
2447
+
2448
+ _createSocket: function() {
2449
+ var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint),
2450
+ headers = this._dispatcher.headers,
2451
+ extensions = this._dispatcher.wsExtensions,
2452
+ cookie = this._getCookies(),
2453
+ tls = this._dispatcher.tls,
2454
+ options = {extensions: extensions, headers: headers, proxy: this._proxy, tls: tls};
2455
+
2456
+ if (cookie !== '') options.headers['Cookie'] = cookie;
2457
+
2458
+ if (Faye.WebSocket) return new Faye.WebSocket.Client(url, [], options);
2459
+ if (Faye.ENV.MozWebSocket) return new MozWebSocket(url);
2460
+ if (Faye.ENV.WebSocket) return new WebSocket(url);
2461
+ },
2462
+
2463
+ _ping: function() {
2464
+ if (!this._socket) return;
2465
+ this._socket.send('[]');
2466
+ this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this);
2467
+ }
2468
+
2469
+ }), {
2470
+ PROTOCOLS: {
2471
+ 'http:': 'ws:',
2472
+ 'https:': 'wss:'
2473
+ },
2474
+
2475
+ create: function(dispatcher, endpoint) {
2476
+ var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {};
2477
+ sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint);
2478
+ return sockets[endpoint.href];
2479
+ },
2480
+
2481
+ getSocketUrl: function(endpoint) {
2482
+ endpoint = Faye.copyObject(endpoint);
2483
+ endpoint.protocol = this.PROTOCOLS[endpoint.protocol];
2484
+ return Faye.URI.stringify(endpoint);
2485
+ },
2486
+
2487
+ isUsable: function(dispatcher, endpoint, callback, context) {
2488
+ this.create(dispatcher, endpoint).isUsable(callback, context);
2489
+ }
2490
+ });
2491
+
2492
+ Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable);
2493
+ Faye.Transport.register('websocket', Faye.Transport.WebSocket);
2494
+
2495
+ if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
2496
+ Faye.Event.on(Faye.ENV, 'beforeunload', function() {
2497
+ Faye.Transport.WebSocket._unloaded = true;
2498
+ });
2499
+
2500
+ Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
2501
+ initialize: function(dispatcher, endpoint) {
2502
+ Faye.Transport.prototype.initialize.call(this, dispatcher, endpoint);
2503
+ if (!Faye.ENV.EventSource) return this.setDeferredStatus('failed');
2504
+
2505
+ this._xhr = new Faye.Transport.XHR(dispatcher, endpoint);
2506
+
2507
+ endpoint = Faye.copyObject(endpoint);
2508
+ endpoint.pathname += '/' + dispatcher.clientId;
2509
+
2510
+ var socket = new EventSource(Faye.URI.stringify(endpoint)),
2511
+ self = this;
2512
+
2513
+ socket.onopen = function() {
2514
+ self._everConnected = true;
2515
+ self.setDeferredStatus('succeeded');
2516
+ };
2517
+
2518
+ socket.onerror = function() {
2519
+ if (self._everConnected) {
2520
+ self._handleError([]);
2521
+ } else {
2522
+ self.setDeferredStatus('failed');
2523
+ socket.close();
2524
+ }
2525
+ };
2526
+
2527
+ socket.onmessage = function(event) {
2528
+ self._receive(JSON.parse(event.data));
2529
+ };
2530
+
2531
+ this._socket = socket;
2532
+ },
2533
+
2534
+ close: function() {
2535
+ if (!this._socket) return;
2536
+ this._socket.onopen = this._socket.onerror = this._socket.onmessage = null;
2537
+ this._socket.close();
2538
+ delete this._socket;
2539
+ },
2540
+
2541
+ isUsable: function(callback, context) {
2542
+ this.callback(function() { callback.call(context, true) });
2543
+ this.errback(function() { callback.call(context, false) });
2544
+ },
2545
+
2546
+ encode: function(messages) {
2547
+ return this._xhr.encode(messages);
2548
+ },
2549
+
2550
+ request: function(messages) {
2551
+ return this._xhr.request(messages);
2552
+ }
2553
+
2554
+ }), {
2555
+ isUsable: function(dispatcher, endpoint, callback, context) {
2556
+ var id = dispatcher.clientId;
2557
+ if (!id) return callback.call(context, false);
2558
+
2559
+ Faye.Transport.XHR.isUsable(dispatcher, endpoint, function(usable) {
2560
+ if (!usable) return callback.call(context, false);
2561
+ this.create(dispatcher, endpoint).isUsable(callback, context);
2562
+ }, this);
2563
+ },
2564
+
2565
+ create: function(dispatcher, endpoint) {
2566
+ var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {},
2567
+ id = dispatcher.clientId;
2568
+
2569
+ var url = Faye.copyObject(endpoint);
2570
+ url.pathname += '/' + (id || '');
2571
+ url = Faye.URI.stringify(url);
2572
+
2573
+ sockets[url] = sockets[url] || new this(dispatcher, endpoint);
2574
+ return sockets[url];
2575
+ }
2576
+ });
2577
+
2578
+ Faye.extend(Faye.Transport.EventSource.prototype, Faye.Deferrable);
2579
+ Faye.Transport.register('eventsource', Faye.Transport.EventSource);
2580
+
2581
+ Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, {
2582
+ encode: function(messages) {
2583
+ return Faye.toJSON(messages);
2584
+ },
2585
+
2586
+ request: function(messages) {
2587
+ var href = this.endpoint.href,
2588
+ xhr = Faye.ENV.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest(),
2589
+ self = this;
2590
+
2591
+ xhr.open('POST', href, true);
2592
+ xhr.setRequestHeader('Content-Type', 'application/json');
2593
+ xhr.setRequestHeader('Pragma', 'no-cache');
2594
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2595
+
2596
+ var headers = this._dispatcher.headers;
2597
+ for (var key in headers) {
2598
+ if (!headers.hasOwnProperty(key)) continue;
2599
+ xhr.setRequestHeader(key, headers[key]);
2600
+ }
2601
+
2602
+ var abort = function() { xhr.abort() };
2603
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.on(Faye.ENV, 'beforeunload', abort);
2604
+
2605
+ xhr.onreadystatechange = function() {
2606
+ if (!xhr || xhr.readyState !== 4) return;
2607
+
2608
+ var replies = null,
2609
+ status = xhr.status,
2610
+ text = xhr.responseText,
2611
+ successful = (status >= 200 && status < 300) || status === 304 || status === 1223;
2612
+
2613
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
2614
+ xhr.onreadystatechange = function() {};
2615
+ xhr = null;
2616
+
2617
+ if (!successful) return self._handleError(messages);
2618
+
2619
+ try {
2620
+ replies = JSON.parse(text);
2621
+ } catch (e) {}
2622
+
2623
+ if (replies)
2624
+ self._receive(replies);
2625
+ else
2626
+ self._handleError(messages);
2627
+ };
2628
+
2629
+ xhr.send(this.encode(messages));
2630
+ return xhr;
2631
+ }
2632
+ }), {
2633
+ isUsable: function(dispatcher, endpoint, callback, context) {
2634
+ callback.call(context, Faye.URI.isSameOrigin(endpoint));
2635
+ }
2636
+ });
2637
+
2638
+ Faye.Transport.register('long-polling', Faye.Transport.XHR);
2639
+
2640
+ Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
2641
+ encode: function(messages) {
2642
+ return 'message=' + encodeURIComponent(Faye.toJSON(messages));
2643
+ },
2644
+
2645
+ request: function(messages) {
2646
+ var xhrClass = Faye.ENV.XDomainRequest ? XDomainRequest : XMLHttpRequest,
2647
+ xhr = new xhrClass(),
2648
+ headers = this._dispatcher.headers,
2649
+ self = this,
2650
+ key;
2651
+
2652
+ xhr.open('POST', Faye.URI.stringify(this.endpoint), true);
2653
+
2654
+ if (xhr.setRequestHeader) {
2655
+ xhr.setRequestHeader('Pragma', 'no-cache');
2656
+ for (key in headers) {
2657
+ if (!headers.hasOwnProperty(key)) continue;
2658
+ xhr.setRequestHeader(key, headers[key]);
2659
+ }
2660
+ }
2661
+
2662
+ var cleanUp = function() {
2663
+ if (!xhr) return false;
2664
+ xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null;
2665
+ xhr = null;
2666
+ };
2667
+
2668
+ xhr.onload = function() {
2669
+ var replies = null;
2670
+ try {
2671
+ replies = JSON.parse(xhr.responseText);
2672
+ } catch (e) {}
2673
+
2674
+ cleanUp();
2675
+
2676
+ if (replies)
2677
+ self._receive(replies);
2678
+ else
2679
+ self._handleError(messages);
2680
+ };
2681
+
2682
+ xhr.onerror = xhr.ontimeout = function() {
2683
+ cleanUp();
2684
+ self._handleError(messages);
2685
+ };
2686
+
2687
+ xhr.onprogress = function() {};
2688
+ xhr.send(this.encode(messages));
2689
+ return xhr;
2690
+ }
2691
+ }), {
2692
+ isUsable: function(dispatcher, endpoint, callback, context) {
2693
+ if (Faye.URI.isSameOrigin(endpoint))
2694
+ return callback.call(context, false);
2695
+
2696
+ if (Faye.ENV.XDomainRequest)
2697
+ return callback.call(context, endpoint.protocol === Faye.ENV.location.protocol);
2698
+
2699
+ if (Faye.ENV.XMLHttpRequest) {
2700
+ var xhr = new Faye.ENV.XMLHttpRequest();
2701
+ return callback.call(context, xhr.withCredentials !== undefined);
2702
+ }
2703
+ return callback.call(context, false);
2704
+ }
2705
+ });
2706
+
2707
+ Faye.Transport.register('cross-origin-long-polling', Faye.Transport.CORS);
2708
+
2709
+ Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
2710
+ encode: function(messages) {
2711
+ var url = Faye.copyObject(this.endpoint);
2712
+ url.query.message = Faye.toJSON(messages);
2713
+ url.query.jsonp = '__jsonp' + Faye.Transport.JSONP._cbCount + '__';
2714
+ return Faye.URI.stringify(url);
2715
+ },
2716
+
2717
+ request: function(messages) {
2718
+ var head = document.getElementsByTagName('head')[0],
2719
+ script = document.createElement('script'),
2720
+ callbackName = Faye.Transport.JSONP.getCallbackName(),
2721
+ endpoint = Faye.copyObject(this.endpoint),
2722
+ self = this;
2723
+
2724
+ endpoint.query.message = Faye.toJSON(messages);
2725
+ endpoint.query.jsonp = callbackName;
2726
+
2727
+ var cleanup = function() {
2728
+ if (!Faye.ENV[callbackName]) return false;
2729
+ Faye.ENV[callbackName] = undefined;
2730
+ try { delete Faye.ENV[callbackName] } catch (e) {}
2731
+ script.parentNode.removeChild(script);
2732
+ };
2733
+
2734
+ Faye.ENV[callbackName] = function(replies) {
2735
+ cleanup();
2736
+ self._receive(replies);
2737
+ };
2738
+
2739
+ script.type = 'text/javascript';
2740
+ script.src = Faye.URI.stringify(endpoint);
2741
+ head.appendChild(script);
2742
+
2743
+ script.onerror = function() {
2744
+ cleanup();
2745
+ self._handleError(messages);
2746
+ };
2747
+
2748
+ return {abort: cleanup};
2749
+ }
2750
+ }), {
2751
+ _cbCount: 0,
2752
+
2753
+ getCallbackName: function() {
2754
+ this._cbCount += 1;
2755
+ return '__jsonp' + this._cbCount + '__';
2756
+ },
2757
+
2758
+ isUsable: function(dispatcher, endpoint, callback, context) {
2759
+ callback.call(context, true);
2760
+ }
2761
+ });
2762
+
2763
+ Faye.Transport.register('callback-polling', Faye.Transport.JSONP);
2764
+
2765
+ })();