faye-authentication 0.1.0

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