ttfm-socket 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/dist-client/actions/addDj.d.ts +26 -0
- package/dist-client/actions/joinRoom.d.ts +27 -0
- package/dist-client/actions/playOneTimeAnimation.d.ts +31 -0
- package/dist-client/actions/removeDj.d.ts +26 -0
- package/dist-client/actions/skipSong.d.ts +22 -0
- package/dist-client/actions/updateNextSong.d.ts +22 -0
- package/dist-client/actions/voteOnSong.d.ts +27 -0
- package/dist-client/client/ActionheroWebsocketClient.js +3638 -0
- package/dist-client/client/ActionheroWebsocketClient.js.map +1 -0
- package/dist-client/client/SocketClient.d.ts +51 -0
- package/dist-client/client/SocketClient.js +152 -0
- package/dist-client/client/SocketClient.js.map +1 -0
- package/dist-client/client/index.d.ts +14 -0
- package/dist-client/client/index.js +27 -0
- package/dist-client/client/index.js.map +1 -0
- package/dist-client/client/types.d.ts +23 -0
- package/dist-client/client/types.js +4 -0
- package/dist-client/client/types.js.map +1 -0
- package/dist-client/types/messages.d.ts +104 -0
- package/dist-client/types/messages.js +30 -0
- package/dist-client/types/messages.js.map +1 -0
- package/dist-client/types/names.d.ts +46 -0
- package/dist-client/types/names.js +55 -0
- package/dist-client/types/names.js.map +1 -0
- package/dist-client/types/services.d.ts +196 -0
- package/dist-client/types/services.js +18 -0
- package/dist-client/types/services.js.map +1 -0
- package/dist-client/types/state.d.ts +65 -0
- package/dist-client/types/state.js +3 -0
- package/dist-client/types/state.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,3638 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(function UMDish(name, context, definition, plugins) {
|
|
3
|
+
context[name] = definition.call(context);
|
|
4
|
+
for (var i = 0; i < plugins.length; i++) {
|
|
5
|
+
plugins[i](context[name]);
|
|
6
|
+
}
|
|
7
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
8
|
+
module.exports = context[name];
|
|
9
|
+
}
|
|
10
|
+
else if (typeof define === "function" && define.amd) {
|
|
11
|
+
define(function reference() { return context[name]; });
|
|
12
|
+
}
|
|
13
|
+
})("Primus", this || {}, function wrapper() {
|
|
14
|
+
var define, module, exports, Primus = (function () { function r(e, n, t) { function o(i, f) { if (!n[i]) {
|
|
15
|
+
if (!e[i]) {
|
|
16
|
+
var c = "function" == typeof require && require;
|
|
17
|
+
if (!f && c)
|
|
18
|
+
return c(i, !0);
|
|
19
|
+
if (u)
|
|
20
|
+
return u(i, !0);
|
|
21
|
+
var a = new Error("Cannot find module '" + i + "'");
|
|
22
|
+
throw a.code = "MODULE_NOT_FOUND", a;
|
|
23
|
+
}
|
|
24
|
+
var p = n[i] = { exports: {} };
|
|
25
|
+
e[i][0].call(p.exports, function (r) { var n = e[i][1][r]; return o(n || r); }, p, p.exports, r, e, n, t);
|
|
26
|
+
} return n[i].exports; } for (var u = "function" == typeof require && require, i = 0; i < t.length; i++)
|
|
27
|
+
o(t[i]); return o; } return r; })()({ 1: [function (_dereq_, module, exports) {
|
|
28
|
+
'use strict';
|
|
29
|
+
/**
|
|
30
|
+
* Create a function that will cleanup the instance.
|
|
31
|
+
*
|
|
32
|
+
* @param {Array|String} keys Properties on the instance that needs to be cleared.
|
|
33
|
+
* @param {Object} options Additional configuration.
|
|
34
|
+
* @returns {Function} Destroy function
|
|
35
|
+
* @api public
|
|
36
|
+
*/
|
|
37
|
+
module.exports = function demolish(keys, options) {
|
|
38
|
+
var split = /[, ]+/;
|
|
39
|
+
options = options || {};
|
|
40
|
+
keys = keys || [];
|
|
41
|
+
if ('string' === typeof keys)
|
|
42
|
+
keys = keys.split(split);
|
|
43
|
+
/**
|
|
44
|
+
* Run addition cleanup hooks.
|
|
45
|
+
*
|
|
46
|
+
* @param {String} key Name of the clean up hook to run.
|
|
47
|
+
* @param {Mixed} selfie Reference to the instance we're cleaning up.
|
|
48
|
+
* @api private
|
|
49
|
+
*/
|
|
50
|
+
function run(key, selfie) {
|
|
51
|
+
if (!options[key])
|
|
52
|
+
return;
|
|
53
|
+
if ('string' === typeof options[key])
|
|
54
|
+
options[key] = options[key].split(split);
|
|
55
|
+
if ('function' === typeof options[key])
|
|
56
|
+
return options[key].call(selfie);
|
|
57
|
+
for (var i = 0, type, what; i < options[key].length; i++) {
|
|
58
|
+
what = options[key][i];
|
|
59
|
+
type = typeof what;
|
|
60
|
+
if ('function' === type) {
|
|
61
|
+
what.call(selfie);
|
|
62
|
+
}
|
|
63
|
+
else if ('string' === type && 'function' === typeof selfie[what]) {
|
|
64
|
+
selfie[what]();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Destroy the instance completely and clean up all the existing references.
|
|
70
|
+
*
|
|
71
|
+
* @returns {Boolean}
|
|
72
|
+
* @api public
|
|
73
|
+
*/
|
|
74
|
+
return function destroy() {
|
|
75
|
+
var selfie = this, i = 0, prop;
|
|
76
|
+
if (selfie[keys[0]] === null)
|
|
77
|
+
return false;
|
|
78
|
+
run('before', selfie);
|
|
79
|
+
for (; i < keys.length; i++) {
|
|
80
|
+
prop = keys[i];
|
|
81
|
+
if (selfie[prop]) {
|
|
82
|
+
if ('function' === typeof selfie[prop].destroy)
|
|
83
|
+
selfie[prop].destroy();
|
|
84
|
+
selfie[prop] = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (selfie.emit)
|
|
88
|
+
selfie.emit('destroy');
|
|
89
|
+
run('after', selfie);
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}, {}], 2: [function (_dereq_, module, exports) {
|
|
94
|
+
'use strict';
|
|
95
|
+
/**
|
|
96
|
+
* Returns a function that when invoked executes all the listeners of the
|
|
97
|
+
* given event with the given arguments.
|
|
98
|
+
*
|
|
99
|
+
* @returns {Function} The function that emits all the things.
|
|
100
|
+
* @api public
|
|
101
|
+
*/
|
|
102
|
+
module.exports = function emits() {
|
|
103
|
+
var self = this, parser;
|
|
104
|
+
for (var i = 0, l = arguments.length, args = new Array(l); i < l; i++) {
|
|
105
|
+
args[i] = arguments[i];
|
|
106
|
+
}
|
|
107
|
+
//
|
|
108
|
+
// If the last argument is a function, assume that it's a parser.
|
|
109
|
+
//
|
|
110
|
+
if ('function' !== typeof args[args.length - 1])
|
|
111
|
+
return function emitter() {
|
|
112
|
+
for (var i = 0, l = arguments.length, arg = new Array(l); i < l; i++) {
|
|
113
|
+
arg[i] = arguments[i];
|
|
114
|
+
}
|
|
115
|
+
return self.emit.apply(self, args.concat(arg));
|
|
116
|
+
};
|
|
117
|
+
parser = args.pop();
|
|
118
|
+
/**
|
|
119
|
+
* The actual function that emits the given event. It returns a boolean
|
|
120
|
+
* indicating if the event was emitted.
|
|
121
|
+
*
|
|
122
|
+
* @returns {Boolean}
|
|
123
|
+
* @api public
|
|
124
|
+
*/
|
|
125
|
+
return function emitter() {
|
|
126
|
+
for (var i = 0, l = arguments.length, arg = new Array(l + 1); i < l; i++) {
|
|
127
|
+
arg[i + 1] = arguments[i];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Async completion method for the parser.
|
|
131
|
+
*
|
|
132
|
+
* @param {Error} err Optional error when parsing failed.
|
|
133
|
+
* @param {Mixed} returned Emit instructions.
|
|
134
|
+
* @api private
|
|
135
|
+
*/
|
|
136
|
+
arg[0] = function next(err, returned) {
|
|
137
|
+
if (err)
|
|
138
|
+
return self.emit('error', err);
|
|
139
|
+
arg = returned === undefined
|
|
140
|
+
? arg.slice(1) : returned === null
|
|
141
|
+
? [] : returned;
|
|
142
|
+
self.emit.apply(self, args.concat(arg));
|
|
143
|
+
};
|
|
144
|
+
parser.apply(self, arg);
|
|
145
|
+
return true;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}, {}], 3: [function (_dereq_, module, exports) {
|
|
149
|
+
'use strict';
|
|
150
|
+
var has = Object.prototype.hasOwnProperty, prefix = '~';
|
|
151
|
+
/**
|
|
152
|
+
* Constructor to create a storage for our `EE` objects.
|
|
153
|
+
* An `Events` instance is a plain object whose properties are event names.
|
|
154
|
+
*
|
|
155
|
+
* @constructor
|
|
156
|
+
* @private
|
|
157
|
+
*/
|
|
158
|
+
function Events() { }
|
|
159
|
+
//
|
|
160
|
+
// We try to not inherit from `Object.prototype`. In some engines creating an
|
|
161
|
+
// instance in this way is faster than calling `Object.create(null)` directly.
|
|
162
|
+
// If `Object.create(null)` is not supported we prefix the event names with a
|
|
163
|
+
// character to make sure that the built-in object properties are not
|
|
164
|
+
// overridden or used as an attack vector.
|
|
165
|
+
//
|
|
166
|
+
if (Object.create) {
|
|
167
|
+
Events.prototype = Object.create(null);
|
|
168
|
+
//
|
|
169
|
+
// This hack is needed because the `__proto__` property is still inherited in
|
|
170
|
+
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
|
|
171
|
+
//
|
|
172
|
+
if (!new Events().__proto__)
|
|
173
|
+
prefix = false;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Representation of a single event listener.
|
|
177
|
+
*
|
|
178
|
+
* @param {Function} fn The listener function.
|
|
179
|
+
* @param {*} context The context to invoke the listener with.
|
|
180
|
+
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
|
|
181
|
+
* @constructor
|
|
182
|
+
* @private
|
|
183
|
+
*/
|
|
184
|
+
function EE(fn, context, once) {
|
|
185
|
+
this.fn = fn;
|
|
186
|
+
this.context = context;
|
|
187
|
+
this.once = once || false;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Add a listener for a given event.
|
|
191
|
+
*
|
|
192
|
+
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
|
|
193
|
+
* @param {(String|Symbol)} event The event name.
|
|
194
|
+
* @param {Function} fn The listener function.
|
|
195
|
+
* @param {*} context The context to invoke the listener with.
|
|
196
|
+
* @param {Boolean} once Specify if the listener is a one-time listener.
|
|
197
|
+
* @returns {EventEmitter}
|
|
198
|
+
* @private
|
|
199
|
+
*/
|
|
200
|
+
function addListener(emitter, event, fn, context, once) {
|
|
201
|
+
if (typeof fn !== 'function') {
|
|
202
|
+
throw new TypeError('The listener must be a function');
|
|
203
|
+
}
|
|
204
|
+
var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
|
|
205
|
+
if (!emitter._events[evt])
|
|
206
|
+
emitter._events[evt] = listener, emitter._eventsCount++;
|
|
207
|
+
else if (!emitter._events[evt].fn)
|
|
208
|
+
emitter._events[evt].push(listener);
|
|
209
|
+
else
|
|
210
|
+
emitter._events[evt] = [emitter._events[evt], listener];
|
|
211
|
+
return emitter;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Clear event by name.
|
|
215
|
+
*
|
|
216
|
+
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
|
|
217
|
+
* @param {(String|Symbol)} evt The Event name.
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
function clearEvent(emitter, evt) {
|
|
221
|
+
if (--emitter._eventsCount === 0)
|
|
222
|
+
emitter._events = new Events();
|
|
223
|
+
else
|
|
224
|
+
delete emitter._events[evt];
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Minimal `EventEmitter` interface that is molded against the Node.js
|
|
228
|
+
* `EventEmitter` interface.
|
|
229
|
+
*
|
|
230
|
+
* @constructor
|
|
231
|
+
* @public
|
|
232
|
+
*/
|
|
233
|
+
function EventEmitter() {
|
|
234
|
+
this._events = new Events();
|
|
235
|
+
this._eventsCount = 0;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Return an array listing the events for which the emitter has registered
|
|
239
|
+
* listeners.
|
|
240
|
+
*
|
|
241
|
+
* @returns {Array}
|
|
242
|
+
* @public
|
|
243
|
+
*/
|
|
244
|
+
EventEmitter.prototype.eventNames = function eventNames() {
|
|
245
|
+
var names = [], events, name;
|
|
246
|
+
if (this._eventsCount === 0)
|
|
247
|
+
return names;
|
|
248
|
+
for (name in (events = this._events)) {
|
|
249
|
+
if (has.call(events, name))
|
|
250
|
+
names.push(prefix ? name.slice(1) : name);
|
|
251
|
+
}
|
|
252
|
+
if (Object.getOwnPropertySymbols) {
|
|
253
|
+
return names.concat(Object.getOwnPropertySymbols(events));
|
|
254
|
+
}
|
|
255
|
+
return names;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Return the listeners registered for a given event.
|
|
259
|
+
*
|
|
260
|
+
* @param {(String|Symbol)} event The event name.
|
|
261
|
+
* @returns {Array} The registered listeners.
|
|
262
|
+
* @public
|
|
263
|
+
*/
|
|
264
|
+
EventEmitter.prototype.listeners = function listeners(event) {
|
|
265
|
+
var evt = prefix ? prefix + event : event, handlers = this._events[evt];
|
|
266
|
+
if (!handlers)
|
|
267
|
+
return [];
|
|
268
|
+
if (handlers.fn)
|
|
269
|
+
return [handlers.fn];
|
|
270
|
+
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
|
|
271
|
+
ee[i] = handlers[i].fn;
|
|
272
|
+
}
|
|
273
|
+
return ee;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Return the number of listeners listening to a given event.
|
|
277
|
+
*
|
|
278
|
+
* @param {(String|Symbol)} event The event name.
|
|
279
|
+
* @returns {Number} The number of listeners.
|
|
280
|
+
* @public
|
|
281
|
+
*/
|
|
282
|
+
EventEmitter.prototype.listenerCount = function listenerCount(event) {
|
|
283
|
+
var evt = prefix ? prefix + event : event, listeners = this._events[evt];
|
|
284
|
+
if (!listeners)
|
|
285
|
+
return 0;
|
|
286
|
+
if (listeners.fn)
|
|
287
|
+
return 1;
|
|
288
|
+
return listeners.length;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Calls each of the listeners registered for a given event.
|
|
292
|
+
*
|
|
293
|
+
* @param {(String|Symbol)} event The event name.
|
|
294
|
+
* @returns {Boolean} `true` if the event had listeners, else `false`.
|
|
295
|
+
* @public
|
|
296
|
+
*/
|
|
297
|
+
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
|
|
298
|
+
var evt = prefix ? prefix + event : event;
|
|
299
|
+
if (!this._events[evt])
|
|
300
|
+
return false;
|
|
301
|
+
var listeners = this._events[evt], len = arguments.length, args, i;
|
|
302
|
+
if (listeners.fn) {
|
|
303
|
+
if (listeners.once)
|
|
304
|
+
this.removeListener(event, listeners.fn, undefined, true);
|
|
305
|
+
switch (len) {
|
|
306
|
+
case 1: return listeners.fn.call(listeners.context), true;
|
|
307
|
+
case 2: return listeners.fn.call(listeners.context, a1), true;
|
|
308
|
+
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
|
|
309
|
+
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
|
|
310
|
+
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
|
|
311
|
+
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
|
|
312
|
+
}
|
|
313
|
+
for (i = 1, args = new Array(len - 1); i < len; i++) {
|
|
314
|
+
args[i - 1] = arguments[i];
|
|
315
|
+
}
|
|
316
|
+
listeners.fn.apply(listeners.context, args);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
var length = listeners.length, j;
|
|
320
|
+
for (i = 0; i < length; i++) {
|
|
321
|
+
if (listeners[i].once)
|
|
322
|
+
this.removeListener(event, listeners[i].fn, undefined, true);
|
|
323
|
+
switch (len) {
|
|
324
|
+
case 1:
|
|
325
|
+
listeners[i].fn.call(listeners[i].context);
|
|
326
|
+
break;
|
|
327
|
+
case 2:
|
|
328
|
+
listeners[i].fn.call(listeners[i].context, a1);
|
|
329
|
+
break;
|
|
330
|
+
case 3:
|
|
331
|
+
listeners[i].fn.call(listeners[i].context, a1, a2);
|
|
332
|
+
break;
|
|
333
|
+
case 4:
|
|
334
|
+
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
if (!args)
|
|
338
|
+
for (j = 1, args = new Array(len - 1); j < len; j++) {
|
|
339
|
+
args[j - 1] = arguments[j];
|
|
340
|
+
}
|
|
341
|
+
listeners[i].fn.apply(listeners[i].context, args);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return true;
|
|
346
|
+
};
|
|
347
|
+
/**
|
|
348
|
+
* Add a listener for a given event.
|
|
349
|
+
*
|
|
350
|
+
* @param {(String|Symbol)} event The event name.
|
|
351
|
+
* @param {Function} fn The listener function.
|
|
352
|
+
* @param {*} [context=this] The context to invoke the listener with.
|
|
353
|
+
* @returns {EventEmitter} `this`.
|
|
354
|
+
* @public
|
|
355
|
+
*/
|
|
356
|
+
EventEmitter.prototype.on = function on(event, fn, context) {
|
|
357
|
+
return addListener(this, event, fn, context, false);
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* Add a one-time listener for a given event.
|
|
361
|
+
*
|
|
362
|
+
* @param {(String|Symbol)} event The event name.
|
|
363
|
+
* @param {Function} fn The listener function.
|
|
364
|
+
* @param {*} [context=this] The context to invoke the listener with.
|
|
365
|
+
* @returns {EventEmitter} `this`.
|
|
366
|
+
* @public
|
|
367
|
+
*/
|
|
368
|
+
EventEmitter.prototype.once = function once(event, fn, context) {
|
|
369
|
+
return addListener(this, event, fn, context, true);
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* Remove the listeners of a given event.
|
|
373
|
+
*
|
|
374
|
+
* @param {(String|Symbol)} event The event name.
|
|
375
|
+
* @param {Function} fn Only remove the listeners that match this function.
|
|
376
|
+
* @param {*} context Only remove the listeners that have this context.
|
|
377
|
+
* @param {Boolean} once Only remove one-time listeners.
|
|
378
|
+
* @returns {EventEmitter} `this`.
|
|
379
|
+
* @public
|
|
380
|
+
*/
|
|
381
|
+
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
|
|
382
|
+
var evt = prefix ? prefix + event : event;
|
|
383
|
+
if (!this._events[evt])
|
|
384
|
+
return this;
|
|
385
|
+
if (!fn) {
|
|
386
|
+
clearEvent(this, evt);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
var listeners = this._events[evt];
|
|
390
|
+
if (listeners.fn) {
|
|
391
|
+
if (listeners.fn === fn &&
|
|
392
|
+
(!once || listeners.once) &&
|
|
393
|
+
(!context || listeners.context === context)) {
|
|
394
|
+
clearEvent(this, evt);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
|
|
399
|
+
if (listeners[i].fn !== fn ||
|
|
400
|
+
(once && !listeners[i].once) ||
|
|
401
|
+
(context && listeners[i].context !== context)) {
|
|
402
|
+
events.push(listeners[i]);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
//
|
|
406
|
+
// Reset the array, or remove it completely if we have no more listeners.
|
|
407
|
+
//
|
|
408
|
+
if (events.length)
|
|
409
|
+
this._events[evt] = events.length === 1 ? events[0] : events;
|
|
410
|
+
else
|
|
411
|
+
clearEvent(this, evt);
|
|
412
|
+
}
|
|
413
|
+
return this;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Remove all listeners, or those of the specified event.
|
|
417
|
+
*
|
|
418
|
+
* @param {(String|Symbol)} [event] The event name.
|
|
419
|
+
* @returns {EventEmitter} `this`.
|
|
420
|
+
* @public
|
|
421
|
+
*/
|
|
422
|
+
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
|
|
423
|
+
var evt;
|
|
424
|
+
if (event) {
|
|
425
|
+
evt = prefix ? prefix + event : event;
|
|
426
|
+
if (this._events[evt])
|
|
427
|
+
clearEvent(this, evt);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
this._events = new Events();
|
|
431
|
+
this._eventsCount = 0;
|
|
432
|
+
}
|
|
433
|
+
return this;
|
|
434
|
+
};
|
|
435
|
+
//
|
|
436
|
+
// Alias methods names because people roll like that.
|
|
437
|
+
//
|
|
438
|
+
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
439
|
+
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
|
|
440
|
+
//
|
|
441
|
+
// Expose the prefix.
|
|
442
|
+
//
|
|
443
|
+
EventEmitter.prefixed = prefix;
|
|
444
|
+
//
|
|
445
|
+
// Allow `EventEmitter` to be imported as module namespace.
|
|
446
|
+
//
|
|
447
|
+
EventEmitter.EventEmitter = EventEmitter;
|
|
448
|
+
//
|
|
449
|
+
// Expose the module.
|
|
450
|
+
//
|
|
451
|
+
if ('undefined' !== typeof module) {
|
|
452
|
+
module.exports = EventEmitter;
|
|
453
|
+
}
|
|
454
|
+
}, {}], 4: [function (_dereq_, module, exports) {
|
|
455
|
+
if (typeof Object.create === 'function') {
|
|
456
|
+
// implementation from standard node.js 'util' module
|
|
457
|
+
module.exports = function inherits(ctor, superCtor) {
|
|
458
|
+
if (superCtor) {
|
|
459
|
+
ctor.super_ = superCtor;
|
|
460
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
|
461
|
+
constructor: {
|
|
462
|
+
value: ctor,
|
|
463
|
+
enumerable: false,
|
|
464
|
+
writable: true,
|
|
465
|
+
configurable: true
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
// old school shim for old browsers
|
|
473
|
+
module.exports = function inherits(ctor, superCtor) {
|
|
474
|
+
if (superCtor) {
|
|
475
|
+
ctor.super_ = superCtor;
|
|
476
|
+
var TempCtor = function () { };
|
|
477
|
+
TempCtor.prototype = superCtor.prototype;
|
|
478
|
+
ctor.prototype = new TempCtor();
|
|
479
|
+
ctor.prototype.constructor = ctor;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}, {}], 5: [function (_dereq_, module, exports) {
|
|
484
|
+
'use strict';
|
|
485
|
+
var regex = new RegExp('^((?:\\d+)?\\.?\\d+) *(' + [
|
|
486
|
+
'milliseconds?',
|
|
487
|
+
'msecs?',
|
|
488
|
+
'ms',
|
|
489
|
+
'seconds?',
|
|
490
|
+
'secs?',
|
|
491
|
+
's',
|
|
492
|
+
'minutes?',
|
|
493
|
+
'mins?',
|
|
494
|
+
'm',
|
|
495
|
+
'hours?',
|
|
496
|
+
'hrs?',
|
|
497
|
+
'h',
|
|
498
|
+
'days?',
|
|
499
|
+
'd',
|
|
500
|
+
'weeks?',
|
|
501
|
+
'wks?',
|
|
502
|
+
'w',
|
|
503
|
+
'years?',
|
|
504
|
+
'yrs?',
|
|
505
|
+
'y'
|
|
506
|
+
].join('|') + ')?$', 'i');
|
|
507
|
+
var second = 1000, minute = second * 60, hour = minute * 60, day = hour * 24, week = day * 7, year = day * 365;
|
|
508
|
+
/**
|
|
509
|
+
* Parse a time string and return the number value of it.
|
|
510
|
+
*
|
|
511
|
+
* @param {String} ms Time string.
|
|
512
|
+
* @returns {Number}
|
|
513
|
+
* @api private
|
|
514
|
+
*/
|
|
515
|
+
module.exports = function millisecond(ms) {
|
|
516
|
+
var type = typeof ms, amount, match;
|
|
517
|
+
if ('number' === type)
|
|
518
|
+
return ms;
|
|
519
|
+
else if ('string' !== type || '0' === ms || !ms)
|
|
520
|
+
return 0;
|
|
521
|
+
else if (+ms)
|
|
522
|
+
return +ms;
|
|
523
|
+
//
|
|
524
|
+
// We are vulnerable to the regular expression denial of service (ReDoS).
|
|
525
|
+
// In order to mitigate this we don't parse the input string if it is too long.
|
|
526
|
+
// See https://nodesecurity.io/advisories/46.
|
|
527
|
+
//
|
|
528
|
+
if (ms.length > 10000 || !(match = regex.exec(ms)))
|
|
529
|
+
return 0;
|
|
530
|
+
amount = parseFloat(match[1]);
|
|
531
|
+
switch (match[2].toLowerCase()) {
|
|
532
|
+
case 'years':
|
|
533
|
+
case 'year':
|
|
534
|
+
case 'yrs':
|
|
535
|
+
case 'yr':
|
|
536
|
+
case 'y':
|
|
537
|
+
return amount * year;
|
|
538
|
+
case 'weeks':
|
|
539
|
+
case 'week':
|
|
540
|
+
case 'wks':
|
|
541
|
+
case 'wk':
|
|
542
|
+
case 'w':
|
|
543
|
+
return amount * week;
|
|
544
|
+
case 'days':
|
|
545
|
+
case 'day':
|
|
546
|
+
case 'd':
|
|
547
|
+
return amount * day;
|
|
548
|
+
case 'hours':
|
|
549
|
+
case 'hour':
|
|
550
|
+
case 'hrs':
|
|
551
|
+
case 'hr':
|
|
552
|
+
case 'h':
|
|
553
|
+
return amount * hour;
|
|
554
|
+
case 'minutes':
|
|
555
|
+
case 'minute':
|
|
556
|
+
case 'mins':
|
|
557
|
+
case 'min':
|
|
558
|
+
case 'm':
|
|
559
|
+
return amount * minute;
|
|
560
|
+
case 'seconds':
|
|
561
|
+
case 'second':
|
|
562
|
+
case 'secs':
|
|
563
|
+
case 'sec':
|
|
564
|
+
case 's':
|
|
565
|
+
return amount * second;
|
|
566
|
+
default:
|
|
567
|
+
return amount;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}, {}], 6: [function (_dereq_, module, exports) {
|
|
571
|
+
'use strict';
|
|
572
|
+
/**
|
|
573
|
+
* Wrap callbacks to prevent double execution.
|
|
574
|
+
*
|
|
575
|
+
* @param {Function} fn Function that should only be called once.
|
|
576
|
+
* @returns {Function} A wrapped callback which prevents execution.
|
|
577
|
+
* @api public
|
|
578
|
+
*/
|
|
579
|
+
module.exports = function one(fn) {
|
|
580
|
+
var called = 0, value;
|
|
581
|
+
/**
|
|
582
|
+
* The function that prevents double execution.
|
|
583
|
+
*
|
|
584
|
+
* @api private
|
|
585
|
+
*/
|
|
586
|
+
function onetime() {
|
|
587
|
+
if (called)
|
|
588
|
+
return value;
|
|
589
|
+
called = 1;
|
|
590
|
+
value = fn.apply(this, arguments);
|
|
591
|
+
fn = null;
|
|
592
|
+
return value;
|
|
593
|
+
}
|
|
594
|
+
//
|
|
595
|
+
// To make debugging more easy we want to use the name of the supplied
|
|
596
|
+
// function. So when you look at the functions that are assigned to event
|
|
597
|
+
// listeners you don't see a load of `onetime` functions but actually the
|
|
598
|
+
// names of the functions that this module will call.
|
|
599
|
+
//
|
|
600
|
+
onetime.displayName = fn.displayName || fn.name || onetime.displayName || onetime.name;
|
|
601
|
+
return onetime;
|
|
602
|
+
};
|
|
603
|
+
}, {}], 7: [function (_dereq_, module, exports) {
|
|
604
|
+
// shim for using process in browser
|
|
605
|
+
var process = module.exports = {};
|
|
606
|
+
// cached from whatever global is present so that test runners that stub it
|
|
607
|
+
// don't break things. But we need to wrap it in a try catch in case it is
|
|
608
|
+
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
|
609
|
+
// function because try/catches deoptimize in certain engines.
|
|
610
|
+
var cachedSetTimeout;
|
|
611
|
+
var cachedClearTimeout;
|
|
612
|
+
function defaultSetTimout() {
|
|
613
|
+
throw new Error('setTimeout has not been defined');
|
|
614
|
+
}
|
|
615
|
+
function defaultClearTimeout() {
|
|
616
|
+
throw new Error('clearTimeout has not been defined');
|
|
617
|
+
}
|
|
618
|
+
(function () {
|
|
619
|
+
try {
|
|
620
|
+
if (typeof setTimeout === 'function') {
|
|
621
|
+
cachedSetTimeout = setTimeout;
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
cachedSetTimeout = defaultSetTimout;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch (e) {
|
|
628
|
+
cachedSetTimeout = defaultSetTimout;
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
if (typeof clearTimeout === 'function') {
|
|
632
|
+
cachedClearTimeout = clearTimeout;
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
cachedClearTimeout = defaultClearTimeout;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch (e) {
|
|
639
|
+
cachedClearTimeout = defaultClearTimeout;
|
|
640
|
+
}
|
|
641
|
+
}());
|
|
642
|
+
function runTimeout(fun) {
|
|
643
|
+
if (cachedSetTimeout === setTimeout) {
|
|
644
|
+
//normal enviroments in sane situations
|
|
645
|
+
return setTimeout(fun, 0);
|
|
646
|
+
}
|
|
647
|
+
// if setTimeout wasn't available but was latter defined
|
|
648
|
+
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
649
|
+
cachedSetTimeout = setTimeout;
|
|
650
|
+
return setTimeout(fun, 0);
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
654
|
+
return cachedSetTimeout(fun, 0);
|
|
655
|
+
}
|
|
656
|
+
catch (e) {
|
|
657
|
+
try {
|
|
658
|
+
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
659
|
+
return cachedSetTimeout.call(null, fun, 0);
|
|
660
|
+
}
|
|
661
|
+
catch (e) {
|
|
662
|
+
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
|
663
|
+
return cachedSetTimeout.call(this, fun, 0);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
function runClearTimeout(marker) {
|
|
668
|
+
if (cachedClearTimeout === clearTimeout) {
|
|
669
|
+
//normal enviroments in sane situations
|
|
670
|
+
return clearTimeout(marker);
|
|
671
|
+
}
|
|
672
|
+
// if clearTimeout wasn't available but was latter defined
|
|
673
|
+
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
674
|
+
cachedClearTimeout = clearTimeout;
|
|
675
|
+
return clearTimeout(marker);
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
679
|
+
return cachedClearTimeout(marker);
|
|
680
|
+
}
|
|
681
|
+
catch (e) {
|
|
682
|
+
try {
|
|
683
|
+
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
684
|
+
return cachedClearTimeout.call(null, marker);
|
|
685
|
+
}
|
|
686
|
+
catch (e) {
|
|
687
|
+
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
|
688
|
+
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
|
689
|
+
return cachedClearTimeout.call(this, marker);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
var queue = [];
|
|
694
|
+
var draining = false;
|
|
695
|
+
var currentQueue;
|
|
696
|
+
var queueIndex = -1;
|
|
697
|
+
function cleanUpNextTick() {
|
|
698
|
+
if (!draining || !currentQueue) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
draining = false;
|
|
702
|
+
if (currentQueue.length) {
|
|
703
|
+
queue = currentQueue.concat(queue);
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
queueIndex = -1;
|
|
707
|
+
}
|
|
708
|
+
if (queue.length) {
|
|
709
|
+
drainQueue();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function drainQueue() {
|
|
713
|
+
if (draining) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
var timeout = runTimeout(cleanUpNextTick);
|
|
717
|
+
draining = true;
|
|
718
|
+
var len = queue.length;
|
|
719
|
+
while (len) {
|
|
720
|
+
currentQueue = queue;
|
|
721
|
+
queue = [];
|
|
722
|
+
while (++queueIndex < len) {
|
|
723
|
+
if (currentQueue) {
|
|
724
|
+
currentQueue[queueIndex].run();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
queueIndex = -1;
|
|
728
|
+
len = queue.length;
|
|
729
|
+
}
|
|
730
|
+
currentQueue = null;
|
|
731
|
+
draining = false;
|
|
732
|
+
runClearTimeout(timeout);
|
|
733
|
+
}
|
|
734
|
+
process.nextTick = function (fun) {
|
|
735
|
+
var args = new Array(arguments.length - 1);
|
|
736
|
+
if (arguments.length > 1) {
|
|
737
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
738
|
+
args[i - 1] = arguments[i];
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
queue.push(new Item(fun, args));
|
|
742
|
+
if (queue.length === 1 && !draining) {
|
|
743
|
+
runTimeout(drainQueue);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
// v8 likes predictible objects
|
|
747
|
+
function Item(fun, array) {
|
|
748
|
+
this.fun = fun;
|
|
749
|
+
this.array = array;
|
|
750
|
+
}
|
|
751
|
+
Item.prototype.run = function () {
|
|
752
|
+
this.fun.apply(null, this.array);
|
|
753
|
+
};
|
|
754
|
+
process.title = 'browser';
|
|
755
|
+
process.browser = true;
|
|
756
|
+
process.env = {};
|
|
757
|
+
process.argv = [];
|
|
758
|
+
process.version = ''; // empty string to avoid regexp issues
|
|
759
|
+
process.versions = {};
|
|
760
|
+
function noop() { }
|
|
761
|
+
process.on = noop;
|
|
762
|
+
process.addListener = noop;
|
|
763
|
+
process.once = noop;
|
|
764
|
+
process.off = noop;
|
|
765
|
+
process.removeListener = noop;
|
|
766
|
+
process.removeAllListeners = noop;
|
|
767
|
+
process.emit = noop;
|
|
768
|
+
process.prependListener = noop;
|
|
769
|
+
process.prependOnceListener = noop;
|
|
770
|
+
process.listeners = function (name) { return []; };
|
|
771
|
+
process.binding = function (name) {
|
|
772
|
+
throw new Error('process.binding is not supported');
|
|
773
|
+
};
|
|
774
|
+
process.cwd = function () { return '/'; };
|
|
775
|
+
process.chdir = function (dir) {
|
|
776
|
+
throw new Error('process.chdir is not supported');
|
|
777
|
+
};
|
|
778
|
+
process.umask = function () { return 0; };
|
|
779
|
+
}, {}], 8: [function (_dereq_, module, exports) {
|
|
780
|
+
'use strict';
|
|
781
|
+
var has = Object.prototype.hasOwnProperty, undef;
|
|
782
|
+
/**
|
|
783
|
+
* Decode a URI encoded string.
|
|
784
|
+
*
|
|
785
|
+
* @param {String} input The URI encoded string.
|
|
786
|
+
* @returns {String|Null} The decoded string.
|
|
787
|
+
* @api private
|
|
788
|
+
*/
|
|
789
|
+
function decode(input) {
|
|
790
|
+
try {
|
|
791
|
+
return decodeURIComponent(input.replace(/\+/g, ' '));
|
|
792
|
+
}
|
|
793
|
+
catch (e) {
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Attempts to encode a given input.
|
|
799
|
+
*
|
|
800
|
+
* @param {String} input The string that needs to be encoded.
|
|
801
|
+
* @returns {String|Null} The encoded string.
|
|
802
|
+
* @api private
|
|
803
|
+
*/
|
|
804
|
+
function encode(input) {
|
|
805
|
+
try {
|
|
806
|
+
return encodeURIComponent(input);
|
|
807
|
+
}
|
|
808
|
+
catch (e) {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Simple query string parser.
|
|
814
|
+
*
|
|
815
|
+
* @param {String} query The query string that needs to be parsed.
|
|
816
|
+
* @returns {Object}
|
|
817
|
+
* @api public
|
|
818
|
+
*/
|
|
819
|
+
function querystring(query) {
|
|
820
|
+
var parser = /([^=?#&]+)=?([^&]*)/g, result = {}, part;
|
|
821
|
+
while (part = parser.exec(query)) {
|
|
822
|
+
var key = decode(part[1]), value = decode(part[2]);
|
|
823
|
+
//
|
|
824
|
+
// Prevent overriding of existing properties. This ensures that build-in
|
|
825
|
+
// methods like `toString` or __proto__ are not overriden by malicious
|
|
826
|
+
// querystrings.
|
|
827
|
+
//
|
|
828
|
+
// In the case if failed decoding, we want to omit the key/value pairs
|
|
829
|
+
// from the result.
|
|
830
|
+
//
|
|
831
|
+
if (key === null || value === null || key in result)
|
|
832
|
+
continue;
|
|
833
|
+
result[key] = value;
|
|
834
|
+
}
|
|
835
|
+
return result;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Transform a query string to an object.
|
|
839
|
+
*
|
|
840
|
+
* @param {Object} obj Object that should be transformed.
|
|
841
|
+
* @param {String} prefix Optional prefix.
|
|
842
|
+
* @returns {String}
|
|
843
|
+
* @api public
|
|
844
|
+
*/
|
|
845
|
+
function querystringify(obj, prefix) {
|
|
846
|
+
prefix = prefix || '';
|
|
847
|
+
var pairs = [], value, key;
|
|
848
|
+
//
|
|
849
|
+
// Optionally prefix with a '?' if needed
|
|
850
|
+
//
|
|
851
|
+
if ('string' !== typeof prefix)
|
|
852
|
+
prefix = '?';
|
|
853
|
+
for (key in obj) {
|
|
854
|
+
if (has.call(obj, key)) {
|
|
855
|
+
value = obj[key];
|
|
856
|
+
//
|
|
857
|
+
// Edge cases where we actually want to encode the value to an empty
|
|
858
|
+
// string instead of the stringified value.
|
|
859
|
+
//
|
|
860
|
+
if (!value && (value === null || value === undef || isNaN(value))) {
|
|
861
|
+
value = '';
|
|
862
|
+
}
|
|
863
|
+
key = encode(key);
|
|
864
|
+
value = encode(value);
|
|
865
|
+
//
|
|
866
|
+
// If we failed to encode the strings, we should bail out as we don't
|
|
867
|
+
// want to add invalid strings to the query.
|
|
868
|
+
//
|
|
869
|
+
if (key === null || value === null)
|
|
870
|
+
continue;
|
|
871
|
+
pairs.push(key + '=' + value);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return pairs.length ? prefix + pairs.join('&') : '';
|
|
875
|
+
}
|
|
876
|
+
//
|
|
877
|
+
// Expose the module.
|
|
878
|
+
//
|
|
879
|
+
exports.stringify = querystringify;
|
|
880
|
+
exports.parse = querystring;
|
|
881
|
+
}, {}], 9: [function (_dereq_, module, exports) {
|
|
882
|
+
'use strict';
|
|
883
|
+
var EventEmitter = _dereq_('eventemitter3'), millisecond = _dereq_('millisecond'), destroy = _dereq_('demolish'), Tick = _dereq_('tick-tock'), one = _dereq_('one-time');
|
|
884
|
+
/**
|
|
885
|
+
* Returns sane defaults about a given value.
|
|
886
|
+
*
|
|
887
|
+
* @param {String} name Name of property we want.
|
|
888
|
+
* @param {Recovery} selfie Recovery instance that got created.
|
|
889
|
+
* @param {Object} opts User supplied options we want to check.
|
|
890
|
+
* @returns {Number} Some default value.
|
|
891
|
+
* @api private
|
|
892
|
+
*/
|
|
893
|
+
function defaults(name, selfie, opts) {
|
|
894
|
+
return millisecond(name in opts ? opts[name] : (name in selfie ? selfie[name] : Recovery[name]));
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Attempt to recover your connection with reconnection attempt.
|
|
898
|
+
*
|
|
899
|
+
* @constructor
|
|
900
|
+
* @param {Object} options Configuration
|
|
901
|
+
* @api public
|
|
902
|
+
*/
|
|
903
|
+
function Recovery(options) {
|
|
904
|
+
var recovery = this;
|
|
905
|
+
if (!(recovery instanceof Recovery))
|
|
906
|
+
return new Recovery(options);
|
|
907
|
+
options = options || {};
|
|
908
|
+
recovery.attempt = null; // Stores the current reconnect attempt.
|
|
909
|
+
recovery._fn = null; // Stores the callback.
|
|
910
|
+
recovery['reconnect timeout'] = defaults('reconnect timeout', recovery, options);
|
|
911
|
+
recovery.retries = defaults('retries', recovery, options);
|
|
912
|
+
recovery.factor = defaults('factor', recovery, options);
|
|
913
|
+
recovery.max = defaults('max', recovery, options);
|
|
914
|
+
recovery.min = defaults('min', recovery, options);
|
|
915
|
+
recovery.timers = new Tick(recovery);
|
|
916
|
+
}
|
|
917
|
+
Recovery.prototype = new EventEmitter();
|
|
918
|
+
Recovery.prototype.constructor = Recovery;
|
|
919
|
+
Recovery['reconnect timeout'] = '30 seconds'; // Maximum time to wait for an answer.
|
|
920
|
+
Recovery.max = Infinity; // Maximum delay.
|
|
921
|
+
Recovery.min = '500 ms'; // Minimum delay.
|
|
922
|
+
Recovery.retries = 10; // Maximum amount of retries.
|
|
923
|
+
Recovery.factor = 2; // Exponential back off factor.
|
|
924
|
+
/**
|
|
925
|
+
* Start a new reconnect procedure.
|
|
926
|
+
*
|
|
927
|
+
* @returns {Recovery}
|
|
928
|
+
* @api public
|
|
929
|
+
*/
|
|
930
|
+
Recovery.prototype.reconnect = function reconnect() {
|
|
931
|
+
var recovery = this;
|
|
932
|
+
return recovery.backoff(function backedoff(err, opts) {
|
|
933
|
+
opts.duration = (+new Date()) - opts.start;
|
|
934
|
+
if (err)
|
|
935
|
+
return recovery.emit('reconnect failed', err, opts);
|
|
936
|
+
recovery.emit('reconnected', opts);
|
|
937
|
+
}, recovery.attempt);
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* Exponential back off algorithm for retry operations. It uses a randomized
|
|
941
|
+
* retry so we don't DDOS our server when it goes down under pressure.
|
|
942
|
+
*
|
|
943
|
+
* @param {Function} fn Callback to be called after the timeout.
|
|
944
|
+
* @param {Object} opts Options for configuring the timeout.
|
|
945
|
+
* @returns {Recovery}
|
|
946
|
+
* @api private
|
|
947
|
+
*/
|
|
948
|
+
Recovery.prototype.backoff = function backoff(fn, opts) {
|
|
949
|
+
var recovery = this;
|
|
950
|
+
opts = opts || recovery.attempt || {};
|
|
951
|
+
//
|
|
952
|
+
// Bailout when we already have a back off process running. We shouldn't call
|
|
953
|
+
// the callback then.
|
|
954
|
+
//
|
|
955
|
+
if (opts.backoff)
|
|
956
|
+
return recovery;
|
|
957
|
+
opts['reconnect timeout'] = defaults('reconnect timeout', recovery, opts);
|
|
958
|
+
opts.retries = defaults('retries', recovery, opts);
|
|
959
|
+
opts.factor = defaults('factor', recovery, opts);
|
|
960
|
+
opts.max = defaults('max', recovery, opts);
|
|
961
|
+
opts.min = defaults('min', recovery, opts);
|
|
962
|
+
opts.start = +opts.start || +new Date();
|
|
963
|
+
opts.duration = +opts.duration || 0;
|
|
964
|
+
opts.attempt = +opts.attempt || 0;
|
|
965
|
+
//
|
|
966
|
+
// Bailout if we are about to make too much attempts.
|
|
967
|
+
//
|
|
968
|
+
if (opts.attempt === opts.retries) {
|
|
969
|
+
fn.call(recovery, new Error('Unable to recover'), opts);
|
|
970
|
+
return recovery;
|
|
971
|
+
}
|
|
972
|
+
//
|
|
973
|
+
// Prevent duplicate back off attempts using the same options object and
|
|
974
|
+
// increment our attempt as we're about to have another go at this thing.
|
|
975
|
+
//
|
|
976
|
+
opts.backoff = true;
|
|
977
|
+
opts.attempt++;
|
|
978
|
+
recovery.attempt = opts;
|
|
979
|
+
//
|
|
980
|
+
// Calculate the timeout, but make it randomly so we don't retry connections
|
|
981
|
+
// at the same interval and defeat the purpose. This exponential back off is
|
|
982
|
+
// based on the work of:
|
|
983
|
+
//
|
|
984
|
+
// http://dthain.blogspot.nl/2009/02/exponential-backoff-in-distributed.html
|
|
985
|
+
//
|
|
986
|
+
opts.scheduled = opts.attempt !== 1
|
|
987
|
+
? Math.min(Math.round((Math.random() + 1) * opts.min * Math.pow(opts.factor, opts.attempt - 1)), opts.max)
|
|
988
|
+
: opts.min;
|
|
989
|
+
recovery.timers.setTimeout('reconnect', function delay() {
|
|
990
|
+
opts.duration = (+new Date()) - opts.start;
|
|
991
|
+
opts.backoff = false;
|
|
992
|
+
recovery.timers.clear('reconnect, timeout');
|
|
993
|
+
//
|
|
994
|
+
// Create a `one` function which can only be called once. So we can use the
|
|
995
|
+
// same function for different types of invocations to create a much better
|
|
996
|
+
// and usable API.
|
|
997
|
+
//
|
|
998
|
+
var connect = recovery._fn = one(function connect(err) {
|
|
999
|
+
recovery.reset();
|
|
1000
|
+
if (err)
|
|
1001
|
+
return recovery.backoff(fn, opts);
|
|
1002
|
+
fn.call(recovery, undefined, opts);
|
|
1003
|
+
});
|
|
1004
|
+
recovery.emit('reconnect', opts, connect);
|
|
1005
|
+
recovery.timers.setTimeout('timeout', function timeout() {
|
|
1006
|
+
var err = new Error('Failed to reconnect in a timely manner');
|
|
1007
|
+
opts.duration = (+new Date()) - opts.start;
|
|
1008
|
+
recovery.emit('reconnect timeout', err, opts);
|
|
1009
|
+
connect(err);
|
|
1010
|
+
}, opts['reconnect timeout']);
|
|
1011
|
+
}, opts.scheduled);
|
|
1012
|
+
//
|
|
1013
|
+
// Emit a `reconnecting` event with current reconnect options. This allows
|
|
1014
|
+
// them to update the UI and provide their users with feedback.
|
|
1015
|
+
//
|
|
1016
|
+
recovery.emit('reconnect scheduled', opts);
|
|
1017
|
+
return recovery;
|
|
1018
|
+
};
|
|
1019
|
+
/**
|
|
1020
|
+
* Check if the reconnection process is currently reconnecting.
|
|
1021
|
+
*
|
|
1022
|
+
* @returns {Boolean}
|
|
1023
|
+
* @api public
|
|
1024
|
+
*/
|
|
1025
|
+
Recovery.prototype.reconnecting = function reconnecting() {
|
|
1026
|
+
return !!this.attempt;
|
|
1027
|
+
};
|
|
1028
|
+
/**
|
|
1029
|
+
* Tell our reconnection procedure that we're passed.
|
|
1030
|
+
*
|
|
1031
|
+
* @param {Error} err Reconnection failed.
|
|
1032
|
+
* @returns {Recovery}
|
|
1033
|
+
* @api public
|
|
1034
|
+
*/
|
|
1035
|
+
Recovery.prototype.reconnected = function reconnected(err) {
|
|
1036
|
+
if (this._fn)
|
|
1037
|
+
this._fn(err);
|
|
1038
|
+
return this;
|
|
1039
|
+
};
|
|
1040
|
+
/**
|
|
1041
|
+
* Reset the reconnection attempt so it can be re-used again.
|
|
1042
|
+
*
|
|
1043
|
+
* @returns {Recovery}
|
|
1044
|
+
* @api public
|
|
1045
|
+
*/
|
|
1046
|
+
Recovery.prototype.reset = function reset() {
|
|
1047
|
+
this._fn = this.attempt = null;
|
|
1048
|
+
this.timers.clear('reconnect, timeout');
|
|
1049
|
+
return this;
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* Clean up the instance.
|
|
1053
|
+
*
|
|
1054
|
+
* @type {Function}
|
|
1055
|
+
* @returns {Boolean}
|
|
1056
|
+
* @api public
|
|
1057
|
+
*/
|
|
1058
|
+
Recovery.prototype.destroy = destroy('timers attempt _fn');
|
|
1059
|
+
//
|
|
1060
|
+
// Expose the module.
|
|
1061
|
+
//
|
|
1062
|
+
module.exports = Recovery;
|
|
1063
|
+
}, { "demolish": 1, "eventemitter3": 10, "millisecond": 5, "one-time": 6, "tick-tock": 12 }], 10: [function (_dereq_, module, exports) {
|
|
1064
|
+
'use strict';
|
|
1065
|
+
//
|
|
1066
|
+
// We store our EE objects in a plain object whose properties are event names.
|
|
1067
|
+
// If `Object.create(null)` is not supported we prefix the event names with a
|
|
1068
|
+
// `~` to make sure that the built-in object properties are not overridden or
|
|
1069
|
+
// used as an attack vector.
|
|
1070
|
+
// We also assume that `Object.create(null)` is available when the event name
|
|
1071
|
+
// is an ES6 Symbol.
|
|
1072
|
+
//
|
|
1073
|
+
var prefix = typeof Object.create !== 'function' ? '~' : false;
|
|
1074
|
+
/**
|
|
1075
|
+
* Representation of a single EventEmitter function.
|
|
1076
|
+
*
|
|
1077
|
+
* @param {Function} fn Event handler to be called.
|
|
1078
|
+
* @param {Mixed} context Context for function execution.
|
|
1079
|
+
* @param {Boolean} once Only emit once
|
|
1080
|
+
* @api private
|
|
1081
|
+
*/
|
|
1082
|
+
function EE(fn, context, once) {
|
|
1083
|
+
this.fn = fn;
|
|
1084
|
+
this.context = context;
|
|
1085
|
+
this.once = once || false;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Minimal EventEmitter interface that is molded against the Node.js
|
|
1089
|
+
* EventEmitter interface.
|
|
1090
|
+
*
|
|
1091
|
+
* @constructor
|
|
1092
|
+
* @api public
|
|
1093
|
+
*/
|
|
1094
|
+
function EventEmitter() { }
|
|
1095
|
+
/**
|
|
1096
|
+
* Holds the assigned EventEmitters by name.
|
|
1097
|
+
*
|
|
1098
|
+
* @type {Object}
|
|
1099
|
+
* @private
|
|
1100
|
+
*/
|
|
1101
|
+
EventEmitter.prototype._events = undefined;
|
|
1102
|
+
/**
|
|
1103
|
+
* Return a list of assigned event listeners.
|
|
1104
|
+
*
|
|
1105
|
+
* @param {String} event The events that should be listed.
|
|
1106
|
+
* @param {Boolean} exists We only need to know if there are listeners.
|
|
1107
|
+
* @returns {Array|Boolean}
|
|
1108
|
+
* @api public
|
|
1109
|
+
*/
|
|
1110
|
+
EventEmitter.prototype.listeners = function listeners(event, exists) {
|
|
1111
|
+
var evt = prefix ? prefix + event : event, available = this._events && this._events[evt];
|
|
1112
|
+
if (exists)
|
|
1113
|
+
return !!available;
|
|
1114
|
+
if (!available)
|
|
1115
|
+
return [];
|
|
1116
|
+
if (available.fn)
|
|
1117
|
+
return [available.fn];
|
|
1118
|
+
for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {
|
|
1119
|
+
ee[i] = available[i].fn;
|
|
1120
|
+
}
|
|
1121
|
+
return ee;
|
|
1122
|
+
};
|
|
1123
|
+
/**
|
|
1124
|
+
* Emit an event to all registered event listeners.
|
|
1125
|
+
*
|
|
1126
|
+
* @param {String} event The name of the event.
|
|
1127
|
+
* @returns {Boolean} Indication if we've emitted an event.
|
|
1128
|
+
* @api public
|
|
1129
|
+
*/
|
|
1130
|
+
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
|
|
1131
|
+
var evt = prefix ? prefix + event : event;
|
|
1132
|
+
if (!this._events || !this._events[evt])
|
|
1133
|
+
return false;
|
|
1134
|
+
var listeners = this._events[evt], len = arguments.length, args, i;
|
|
1135
|
+
if ('function' === typeof listeners.fn) {
|
|
1136
|
+
if (listeners.once)
|
|
1137
|
+
this.removeListener(event, listeners.fn, undefined, true);
|
|
1138
|
+
switch (len) {
|
|
1139
|
+
case 1: return listeners.fn.call(listeners.context), true;
|
|
1140
|
+
case 2: return listeners.fn.call(listeners.context, a1), true;
|
|
1141
|
+
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
|
|
1142
|
+
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
|
|
1143
|
+
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
|
|
1144
|
+
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
|
|
1145
|
+
}
|
|
1146
|
+
for (i = 1, args = new Array(len - 1); i < len; i++) {
|
|
1147
|
+
args[i - 1] = arguments[i];
|
|
1148
|
+
}
|
|
1149
|
+
listeners.fn.apply(listeners.context, args);
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
var length = listeners.length, j;
|
|
1153
|
+
for (i = 0; i < length; i++) {
|
|
1154
|
+
if (listeners[i].once)
|
|
1155
|
+
this.removeListener(event, listeners[i].fn, undefined, true);
|
|
1156
|
+
switch (len) {
|
|
1157
|
+
case 1:
|
|
1158
|
+
listeners[i].fn.call(listeners[i].context);
|
|
1159
|
+
break;
|
|
1160
|
+
case 2:
|
|
1161
|
+
listeners[i].fn.call(listeners[i].context, a1);
|
|
1162
|
+
break;
|
|
1163
|
+
case 3:
|
|
1164
|
+
listeners[i].fn.call(listeners[i].context, a1, a2);
|
|
1165
|
+
break;
|
|
1166
|
+
default:
|
|
1167
|
+
if (!args)
|
|
1168
|
+
for (j = 1, args = new Array(len - 1); j < len; j++) {
|
|
1169
|
+
args[j - 1] = arguments[j];
|
|
1170
|
+
}
|
|
1171
|
+
listeners[i].fn.apply(listeners[i].context, args);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
return true;
|
|
1176
|
+
};
|
|
1177
|
+
/**
|
|
1178
|
+
* Register a new EventListener for the given event.
|
|
1179
|
+
*
|
|
1180
|
+
* @param {String} event Name of the event.
|
|
1181
|
+
* @param {Functon} fn Callback function.
|
|
1182
|
+
* @param {Mixed} context The context of the function.
|
|
1183
|
+
* @api public
|
|
1184
|
+
*/
|
|
1185
|
+
EventEmitter.prototype.on = function on(event, fn, context) {
|
|
1186
|
+
var listener = new EE(fn, context || this), evt = prefix ? prefix + event : event;
|
|
1187
|
+
if (!this._events)
|
|
1188
|
+
this._events = prefix ? {} : Object.create(null);
|
|
1189
|
+
if (!this._events[evt])
|
|
1190
|
+
this._events[evt] = listener;
|
|
1191
|
+
else {
|
|
1192
|
+
if (!this._events[evt].fn)
|
|
1193
|
+
this._events[evt].push(listener);
|
|
1194
|
+
else
|
|
1195
|
+
this._events[evt] = [
|
|
1196
|
+
this._events[evt], listener
|
|
1197
|
+
];
|
|
1198
|
+
}
|
|
1199
|
+
return this;
|
|
1200
|
+
};
|
|
1201
|
+
/**
|
|
1202
|
+
* Add an EventListener that's only called once.
|
|
1203
|
+
*
|
|
1204
|
+
* @param {String} event Name of the event.
|
|
1205
|
+
* @param {Function} fn Callback function.
|
|
1206
|
+
* @param {Mixed} context The context of the function.
|
|
1207
|
+
* @api public
|
|
1208
|
+
*/
|
|
1209
|
+
EventEmitter.prototype.once = function once(event, fn, context) {
|
|
1210
|
+
var listener = new EE(fn, context || this, true), evt = prefix ? prefix + event : event;
|
|
1211
|
+
if (!this._events)
|
|
1212
|
+
this._events = prefix ? {} : Object.create(null);
|
|
1213
|
+
if (!this._events[evt])
|
|
1214
|
+
this._events[evt] = listener;
|
|
1215
|
+
else {
|
|
1216
|
+
if (!this._events[evt].fn)
|
|
1217
|
+
this._events[evt].push(listener);
|
|
1218
|
+
else
|
|
1219
|
+
this._events[evt] = [
|
|
1220
|
+
this._events[evt], listener
|
|
1221
|
+
];
|
|
1222
|
+
}
|
|
1223
|
+
return this;
|
|
1224
|
+
};
|
|
1225
|
+
/**
|
|
1226
|
+
* Remove event listeners.
|
|
1227
|
+
*
|
|
1228
|
+
* @param {String} event The event we want to remove.
|
|
1229
|
+
* @param {Function} fn The listener that we need to find.
|
|
1230
|
+
* @param {Mixed} context Only remove listeners matching this context.
|
|
1231
|
+
* @param {Boolean} once Only remove once listeners.
|
|
1232
|
+
* @api public
|
|
1233
|
+
*/
|
|
1234
|
+
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
|
|
1235
|
+
var evt = prefix ? prefix + event : event;
|
|
1236
|
+
if (!this._events || !this._events[evt])
|
|
1237
|
+
return this;
|
|
1238
|
+
var listeners = this._events[evt], events = [];
|
|
1239
|
+
if (fn) {
|
|
1240
|
+
if (listeners.fn) {
|
|
1241
|
+
if (listeners.fn !== fn
|
|
1242
|
+
|| (once && !listeners.once)
|
|
1243
|
+
|| (context && listeners.context !== context)) {
|
|
1244
|
+
events.push(listeners);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
for (var i = 0, length = listeners.length; i < length; i++) {
|
|
1249
|
+
if (listeners[i].fn !== fn
|
|
1250
|
+
|| (once && !listeners[i].once)
|
|
1251
|
+
|| (context && listeners[i].context !== context)) {
|
|
1252
|
+
events.push(listeners[i]);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
//
|
|
1258
|
+
// Reset the array, or remove it completely if we have no more listeners.
|
|
1259
|
+
//
|
|
1260
|
+
if (events.length) {
|
|
1261
|
+
this._events[evt] = events.length === 1 ? events[0] : events;
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
delete this._events[evt];
|
|
1265
|
+
}
|
|
1266
|
+
return this;
|
|
1267
|
+
};
|
|
1268
|
+
/**
|
|
1269
|
+
* Remove all listeners or only the listeners for the specified event.
|
|
1270
|
+
*
|
|
1271
|
+
* @param {String} event The event want to remove all listeners for.
|
|
1272
|
+
* @api public
|
|
1273
|
+
*/
|
|
1274
|
+
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
|
|
1275
|
+
if (!this._events)
|
|
1276
|
+
return this;
|
|
1277
|
+
if (event)
|
|
1278
|
+
delete this._events[prefix ? prefix + event : event];
|
|
1279
|
+
else
|
|
1280
|
+
this._events = prefix ? {} : Object.create(null);
|
|
1281
|
+
return this;
|
|
1282
|
+
};
|
|
1283
|
+
//
|
|
1284
|
+
// Alias methods names because people roll like that.
|
|
1285
|
+
//
|
|
1286
|
+
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
1287
|
+
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
|
|
1288
|
+
//
|
|
1289
|
+
// This function doesn't apply anymore.
|
|
1290
|
+
//
|
|
1291
|
+
EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
|
|
1292
|
+
return this;
|
|
1293
|
+
};
|
|
1294
|
+
//
|
|
1295
|
+
// Expose the prefix.
|
|
1296
|
+
//
|
|
1297
|
+
EventEmitter.prefixed = prefix;
|
|
1298
|
+
//
|
|
1299
|
+
// Expose the module.
|
|
1300
|
+
//
|
|
1301
|
+
if ('undefined' !== typeof module) {
|
|
1302
|
+
module.exports = EventEmitter;
|
|
1303
|
+
}
|
|
1304
|
+
}, {}], 11: [function (_dereq_, module, exports) {
|
|
1305
|
+
'use strict';
|
|
1306
|
+
/**
|
|
1307
|
+
* Check if we're required to add a port number.
|
|
1308
|
+
*
|
|
1309
|
+
* @see https://url.spec.whatwg.org/#default-port
|
|
1310
|
+
* @param {Number|String} port Port number we need to check
|
|
1311
|
+
* @param {String} protocol Protocol we need to check against.
|
|
1312
|
+
* @returns {Boolean} Is it a default port for the given protocol
|
|
1313
|
+
* @api private
|
|
1314
|
+
*/
|
|
1315
|
+
module.exports = function required(port, protocol) {
|
|
1316
|
+
protocol = protocol.split(':')[0];
|
|
1317
|
+
port = +port;
|
|
1318
|
+
if (!port)
|
|
1319
|
+
return false;
|
|
1320
|
+
switch (protocol) {
|
|
1321
|
+
case 'http':
|
|
1322
|
+
case 'ws':
|
|
1323
|
+
return port !== 80;
|
|
1324
|
+
case 'https':
|
|
1325
|
+
case 'wss':
|
|
1326
|
+
return port !== 443;
|
|
1327
|
+
case 'ftp':
|
|
1328
|
+
return port !== 21;
|
|
1329
|
+
case 'gopher':
|
|
1330
|
+
return port !== 70;
|
|
1331
|
+
case 'file':
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
return port !== 0;
|
|
1335
|
+
};
|
|
1336
|
+
}, {}], 12: [function (_dereq_, module, exports) {
|
|
1337
|
+
(function (setImmediate, clearImmediate) {
|
|
1338
|
+
(function () {
|
|
1339
|
+
'use strict';
|
|
1340
|
+
var has = Object.prototype.hasOwnProperty, ms = _dereq_('millisecond');
|
|
1341
|
+
/**
|
|
1342
|
+
* Timer instance.
|
|
1343
|
+
*
|
|
1344
|
+
* @constructor
|
|
1345
|
+
* @param {Object} timer New timer instance.
|
|
1346
|
+
* @param {Function} clear Clears the timer instance.
|
|
1347
|
+
* @param {Function} duration Duration of the timer.
|
|
1348
|
+
* @param {Function} fn The functions that need to be executed.
|
|
1349
|
+
* @api private
|
|
1350
|
+
*/
|
|
1351
|
+
function Timer(timer, clear, duration, fn) {
|
|
1352
|
+
this.start = +(new Date());
|
|
1353
|
+
this.duration = duration;
|
|
1354
|
+
this.clear = clear;
|
|
1355
|
+
this.timer = timer;
|
|
1356
|
+
this.fns = [fn];
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Calculate the time left for a given timer.
|
|
1360
|
+
*
|
|
1361
|
+
* @returns {Number} Time in milliseconds.
|
|
1362
|
+
* @api public
|
|
1363
|
+
*/
|
|
1364
|
+
Timer.prototype.remaining = function remaining() {
|
|
1365
|
+
return this.duration - this.taken();
|
|
1366
|
+
};
|
|
1367
|
+
/**
|
|
1368
|
+
* Calculate the amount of time it has taken since we've set the timer.
|
|
1369
|
+
*
|
|
1370
|
+
* @returns {Number}
|
|
1371
|
+
* @api public
|
|
1372
|
+
*/
|
|
1373
|
+
Timer.prototype.taken = function taken() {
|
|
1374
|
+
return +(new Date()) - this.start;
|
|
1375
|
+
};
|
|
1376
|
+
/**
|
|
1377
|
+
* Custom wrappers for the various of clear{whatever} functions. We cannot
|
|
1378
|
+
* invoke them directly as this will cause thrown errors in Google Chrome with
|
|
1379
|
+
* an Illegal Invocation Error
|
|
1380
|
+
*
|
|
1381
|
+
* @see #2
|
|
1382
|
+
* @type {Function}
|
|
1383
|
+
* @api private
|
|
1384
|
+
*/
|
|
1385
|
+
function unsetTimeout(id) { clearTimeout(id); }
|
|
1386
|
+
function unsetInterval(id) { clearInterval(id); }
|
|
1387
|
+
function unsetImmediate(id) { clearImmediate(id); }
|
|
1388
|
+
/**
|
|
1389
|
+
* Simple timer management.
|
|
1390
|
+
*
|
|
1391
|
+
* @constructor
|
|
1392
|
+
* @param {Mixed} context Context of the callbacks that we execute.
|
|
1393
|
+
* @api public
|
|
1394
|
+
*/
|
|
1395
|
+
function Tick(context) {
|
|
1396
|
+
if (!(this instanceof Tick))
|
|
1397
|
+
return new Tick(context);
|
|
1398
|
+
this.timers = {};
|
|
1399
|
+
this.context = context || this;
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Return a function which will just iterate over all assigned callbacks and
|
|
1403
|
+
* optionally clear the timers from memory if needed.
|
|
1404
|
+
*
|
|
1405
|
+
* @param {String} name Name of the timer we need to execute.
|
|
1406
|
+
* @param {Boolean} clear Also clear from memory.
|
|
1407
|
+
* @returns {Function}
|
|
1408
|
+
* @api private
|
|
1409
|
+
*/
|
|
1410
|
+
Tick.prototype.tock = function ticktock(name, clear) {
|
|
1411
|
+
var tock = this;
|
|
1412
|
+
return function tickedtock() {
|
|
1413
|
+
if (!(name in tock.timers))
|
|
1414
|
+
return;
|
|
1415
|
+
var timer = tock.timers[name], fns = timer.fns.slice(), l = fns.length, i = 0;
|
|
1416
|
+
if (clear)
|
|
1417
|
+
tock.clear(name);
|
|
1418
|
+
else
|
|
1419
|
+
tock.start = +new Date();
|
|
1420
|
+
for (; i < l; i++) {
|
|
1421
|
+
fns[i].call(tock.context);
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
};
|
|
1425
|
+
/**
|
|
1426
|
+
* Add a new timeout.
|
|
1427
|
+
*
|
|
1428
|
+
* @param {String} name Name of the timer.
|
|
1429
|
+
* @param {Function} fn Completion callback.
|
|
1430
|
+
* @param {Mixed} time Duration of the timer.
|
|
1431
|
+
* @returns {Tick}
|
|
1432
|
+
* @api public
|
|
1433
|
+
*/
|
|
1434
|
+
Tick.prototype.setTimeout = function timeout(name, fn, time) {
|
|
1435
|
+
var tick = this, tock;
|
|
1436
|
+
if (tick.timers[name]) {
|
|
1437
|
+
tick.timers[name].fns.push(fn);
|
|
1438
|
+
return tick;
|
|
1439
|
+
}
|
|
1440
|
+
tock = ms(time);
|
|
1441
|
+
tick.timers[name] = new Timer(setTimeout(tick.tock(name, true), ms(time)), unsetTimeout, tock, fn);
|
|
1442
|
+
return tick;
|
|
1443
|
+
};
|
|
1444
|
+
/**
|
|
1445
|
+
* Add a new interval.
|
|
1446
|
+
*
|
|
1447
|
+
* @param {String} name Name of the timer.
|
|
1448
|
+
* @param {Function} fn Completion callback.
|
|
1449
|
+
* @param {Mixed} time Interval of the timer.
|
|
1450
|
+
* @returns {Tick}
|
|
1451
|
+
* @api public
|
|
1452
|
+
*/
|
|
1453
|
+
Tick.prototype.setInterval = function interval(name, fn, time) {
|
|
1454
|
+
var tick = this, tock;
|
|
1455
|
+
if (tick.timers[name]) {
|
|
1456
|
+
tick.timers[name].fns.push(fn);
|
|
1457
|
+
return tick;
|
|
1458
|
+
}
|
|
1459
|
+
tock = ms(time);
|
|
1460
|
+
tick.timers[name] = new Timer(setInterval(tick.tock(name), ms(time)), unsetInterval, tock, fn);
|
|
1461
|
+
return tick;
|
|
1462
|
+
};
|
|
1463
|
+
/**
|
|
1464
|
+
* Add a new setImmediate.
|
|
1465
|
+
*
|
|
1466
|
+
* @param {String} name Name of the timer.
|
|
1467
|
+
* @param {Function} fn Completion callback.
|
|
1468
|
+
* @returns {Tick}
|
|
1469
|
+
* @api public
|
|
1470
|
+
*/
|
|
1471
|
+
Tick.prototype.setImmediate = function immediate(name, fn) {
|
|
1472
|
+
var tick = this;
|
|
1473
|
+
if ('function' !== typeof setImmediate)
|
|
1474
|
+
return tick.setTimeout(name, fn, 0);
|
|
1475
|
+
if (tick.timers[name]) {
|
|
1476
|
+
tick.timers[name].fns.push(fn);
|
|
1477
|
+
return tick;
|
|
1478
|
+
}
|
|
1479
|
+
tick.timers[name] = new Timer(setImmediate(tick.tock(name, true)), unsetImmediate, 0, fn);
|
|
1480
|
+
return tick;
|
|
1481
|
+
};
|
|
1482
|
+
/**
|
|
1483
|
+
* Check if we have a timer set.
|
|
1484
|
+
*
|
|
1485
|
+
* @param {String} name
|
|
1486
|
+
* @returns {Boolean}
|
|
1487
|
+
* @api public
|
|
1488
|
+
*/
|
|
1489
|
+
Tick.prototype.active = function active(name) {
|
|
1490
|
+
return name in this.timers;
|
|
1491
|
+
};
|
|
1492
|
+
/**
|
|
1493
|
+
* Properly clean up all timeout references. If no arguments are supplied we
|
|
1494
|
+
* will attempt to clear every single timer that is present.
|
|
1495
|
+
*
|
|
1496
|
+
* @param {Arguments} ..args.. The names of the timeouts we need to clear
|
|
1497
|
+
* @returns {Tick}
|
|
1498
|
+
* @api public
|
|
1499
|
+
*/
|
|
1500
|
+
Tick.prototype.clear = function clear() {
|
|
1501
|
+
var args = arguments.length ? arguments : [], tick = this, timer, i, l;
|
|
1502
|
+
if (args.length === 1 && 'string' === typeof args[0]) {
|
|
1503
|
+
args = args[0].split(/[, ]+/);
|
|
1504
|
+
}
|
|
1505
|
+
if (!args.length) {
|
|
1506
|
+
for (timer in tick.timers) {
|
|
1507
|
+
if (has.call(tick.timers, timer))
|
|
1508
|
+
args.push(timer);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
for (i = 0, l = args.length; i < l; i++) {
|
|
1512
|
+
timer = tick.timers[args[i]];
|
|
1513
|
+
if (!timer)
|
|
1514
|
+
continue;
|
|
1515
|
+
timer.clear(timer.timer);
|
|
1516
|
+
timer.fns = timer.timer = timer.clear = null;
|
|
1517
|
+
delete tick.timers[args[i]];
|
|
1518
|
+
}
|
|
1519
|
+
return tick;
|
|
1520
|
+
};
|
|
1521
|
+
/**
|
|
1522
|
+
* Adjust a timeout or interval to a new duration.
|
|
1523
|
+
*
|
|
1524
|
+
* @returns {Tick}
|
|
1525
|
+
* @api public
|
|
1526
|
+
*/
|
|
1527
|
+
Tick.prototype.adjust = function adjust(name, time) {
|
|
1528
|
+
var interval, tick = this, tock = ms(time), timer = tick.timers[name];
|
|
1529
|
+
if (!timer)
|
|
1530
|
+
return tick;
|
|
1531
|
+
interval = timer.clear === unsetInterval;
|
|
1532
|
+
timer.clear(timer.timer);
|
|
1533
|
+
timer.start = +(new Date());
|
|
1534
|
+
timer.duration = tock;
|
|
1535
|
+
timer.timer = (interval ? setInterval : setTimeout)(tick.tock(name, !interval), tock);
|
|
1536
|
+
return tick;
|
|
1537
|
+
};
|
|
1538
|
+
/**
|
|
1539
|
+
* We will no longer use this module, prepare your self for global cleanups.
|
|
1540
|
+
*
|
|
1541
|
+
* @returns {Boolean}
|
|
1542
|
+
* @api public
|
|
1543
|
+
*/
|
|
1544
|
+
Tick.prototype.end = Tick.prototype.destroy = function end() {
|
|
1545
|
+
if (!this.context)
|
|
1546
|
+
return false;
|
|
1547
|
+
this.clear();
|
|
1548
|
+
this.context = this.timers = null;
|
|
1549
|
+
return true;
|
|
1550
|
+
};
|
|
1551
|
+
//
|
|
1552
|
+
// Expose the timer factory.
|
|
1553
|
+
//
|
|
1554
|
+
Tick.Timer = Timer;
|
|
1555
|
+
module.exports = Tick;
|
|
1556
|
+
}).call(this);
|
|
1557
|
+
}).call(this, _dereq_("timers").setImmediate, _dereq_("timers").clearImmediate);
|
|
1558
|
+
}, { "millisecond": 5, "timers": 13 }], 13: [function (_dereq_, module, exports) {
|
|
1559
|
+
(function (setImmediate, clearImmediate) {
|
|
1560
|
+
(function () {
|
|
1561
|
+
var nextTick = _dereq_('process/browser.js').nextTick;
|
|
1562
|
+
var apply = Function.prototype.apply;
|
|
1563
|
+
var slice = Array.prototype.slice;
|
|
1564
|
+
var immediateIds = {};
|
|
1565
|
+
var nextImmediateId = 0;
|
|
1566
|
+
// DOM APIs, for completeness
|
|
1567
|
+
exports.setTimeout = function () {
|
|
1568
|
+
return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
|
|
1569
|
+
};
|
|
1570
|
+
exports.setInterval = function () {
|
|
1571
|
+
return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
|
|
1572
|
+
};
|
|
1573
|
+
exports.clearTimeout =
|
|
1574
|
+
exports.clearInterval = function (timeout) { timeout.close(); };
|
|
1575
|
+
function Timeout(id, clearFn) {
|
|
1576
|
+
this._id = id;
|
|
1577
|
+
this._clearFn = clearFn;
|
|
1578
|
+
}
|
|
1579
|
+
Timeout.prototype.unref = Timeout.prototype.ref = function () { };
|
|
1580
|
+
Timeout.prototype.close = function () {
|
|
1581
|
+
this._clearFn.call(window, this._id);
|
|
1582
|
+
};
|
|
1583
|
+
// Does not start the time, just sets up the members needed.
|
|
1584
|
+
exports.enroll = function (item, msecs) {
|
|
1585
|
+
clearTimeout(item._idleTimeoutId);
|
|
1586
|
+
item._idleTimeout = msecs;
|
|
1587
|
+
};
|
|
1588
|
+
exports.unenroll = function (item) {
|
|
1589
|
+
clearTimeout(item._idleTimeoutId);
|
|
1590
|
+
item._idleTimeout = -1;
|
|
1591
|
+
};
|
|
1592
|
+
exports._unrefActive = exports.active = function (item) {
|
|
1593
|
+
clearTimeout(item._idleTimeoutId);
|
|
1594
|
+
var msecs = item._idleTimeout;
|
|
1595
|
+
if (msecs >= 0) {
|
|
1596
|
+
item._idleTimeoutId = setTimeout(function onTimeout() {
|
|
1597
|
+
if (item._onTimeout)
|
|
1598
|
+
item._onTimeout();
|
|
1599
|
+
}, msecs);
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
// That's not how node.js implements it but the exposed api is the same.
|
|
1603
|
+
exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function (fn) {
|
|
1604
|
+
var id = nextImmediateId++;
|
|
1605
|
+
var args = arguments.length < 2 ? false : slice.call(arguments, 1);
|
|
1606
|
+
immediateIds[id] = true;
|
|
1607
|
+
nextTick(function onNextTick() {
|
|
1608
|
+
if (immediateIds[id]) {
|
|
1609
|
+
// fn.call() is faster so we optimize for the common use-case
|
|
1610
|
+
// @see http://jsperf.com/call-apply-segu
|
|
1611
|
+
if (args) {
|
|
1612
|
+
fn.apply(null, args);
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
fn.call(null);
|
|
1616
|
+
}
|
|
1617
|
+
// Prevent ids from leaking
|
|
1618
|
+
exports.clearImmediate(id);
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
return id;
|
|
1622
|
+
};
|
|
1623
|
+
exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function (id) {
|
|
1624
|
+
delete immediateIds[id];
|
|
1625
|
+
};
|
|
1626
|
+
}).call(this);
|
|
1627
|
+
}).call(this, _dereq_("timers").setImmediate, _dereq_("timers").clearImmediate);
|
|
1628
|
+
}, { "process/browser.js": 7, "timers": 13 }], 14: [function (_dereq_, module, exports) {
|
|
1629
|
+
(function (global) {
|
|
1630
|
+
(function () {
|
|
1631
|
+
'use strict';
|
|
1632
|
+
var required = _dereq_('requires-port'), qs = _dereq_('querystringify'), controlOrWhitespace = /^[\x00-\x20\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/, CRHTLF = /[\n\r\t]/g, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//, port = /:\d+$/, protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i, windowsDriveLetter = /^[a-zA-Z]:/;
|
|
1633
|
+
/**
|
|
1634
|
+
* Remove control characters and whitespace from the beginning of a string.
|
|
1635
|
+
*
|
|
1636
|
+
* @param {Object|String} str String to trim.
|
|
1637
|
+
* @returns {String} A new string representing `str` stripped of control
|
|
1638
|
+
* characters and whitespace from its beginning.
|
|
1639
|
+
* @public
|
|
1640
|
+
*/
|
|
1641
|
+
function trimLeft(str) {
|
|
1642
|
+
return (str ? str : '').toString().replace(controlOrWhitespace, '');
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* These are the parse rules for the URL parser, it informs the parser
|
|
1646
|
+
* about:
|
|
1647
|
+
*
|
|
1648
|
+
* 0. The char it Needs to parse, if it's a string it should be done using
|
|
1649
|
+
* indexOf, RegExp using exec and NaN means set as current value.
|
|
1650
|
+
* 1. The property we should set when parsing this value.
|
|
1651
|
+
* 2. Indication if it's backwards or forward parsing, when set as number it's
|
|
1652
|
+
* the value of extra chars that should be split off.
|
|
1653
|
+
* 3. Inherit from location if non existing in the parser.
|
|
1654
|
+
* 4. `toLowerCase` the resulting value.
|
|
1655
|
+
*/
|
|
1656
|
+
var rules = [
|
|
1657
|
+
['#', 'hash'],
|
|
1658
|
+
['?', 'query'],
|
|
1659
|
+
function sanitize(address, url) {
|
|
1660
|
+
return isSpecial(url.protocol) ? address.replace(/\\/g, '/') : address;
|
|
1661
|
+
},
|
|
1662
|
+
['/', 'pathname'],
|
|
1663
|
+
['@', 'auth', 1],
|
|
1664
|
+
[NaN, 'host', undefined, 1, 1],
|
|
1665
|
+
[/:(\d*)$/, 'port', undefined, 1],
|
|
1666
|
+
[NaN, 'hostname', undefined, 1, 1] // Set left over.
|
|
1667
|
+
];
|
|
1668
|
+
/**
|
|
1669
|
+
* These properties should not be copied or inherited from. This is only needed
|
|
1670
|
+
* for all non blob URL's as a blob URL does not include a hash, only the
|
|
1671
|
+
* origin.
|
|
1672
|
+
*
|
|
1673
|
+
* @type {Object}
|
|
1674
|
+
* @private
|
|
1675
|
+
*/
|
|
1676
|
+
var ignore = { hash: 1, query: 1 };
|
|
1677
|
+
/**
|
|
1678
|
+
* The location object differs when your code is loaded through a normal page,
|
|
1679
|
+
* Worker or through a worker using a blob. And with the blobble begins the
|
|
1680
|
+
* trouble as the location object will contain the URL of the blob, not the
|
|
1681
|
+
* location of the page where our code is loaded in. The actual origin is
|
|
1682
|
+
* encoded in the `pathname` so we can thankfully generate a good "default"
|
|
1683
|
+
* location from it so we can generate proper relative URL's again.
|
|
1684
|
+
*
|
|
1685
|
+
* @param {Object|String} loc Optional default location object.
|
|
1686
|
+
* @returns {Object} lolcation object.
|
|
1687
|
+
* @public
|
|
1688
|
+
*/
|
|
1689
|
+
function lolcation(loc) {
|
|
1690
|
+
var globalVar;
|
|
1691
|
+
if (typeof window !== 'undefined')
|
|
1692
|
+
globalVar = window;
|
|
1693
|
+
else if (typeof global !== 'undefined')
|
|
1694
|
+
globalVar = global;
|
|
1695
|
+
else if (typeof self !== 'undefined')
|
|
1696
|
+
globalVar = self;
|
|
1697
|
+
else
|
|
1698
|
+
globalVar = {};
|
|
1699
|
+
var location = globalVar.location || {};
|
|
1700
|
+
loc = loc || location;
|
|
1701
|
+
var finaldestination = {}, type = typeof loc, key;
|
|
1702
|
+
if ('blob:' === loc.protocol) {
|
|
1703
|
+
finaldestination = new Url(unescape(loc.pathname), {});
|
|
1704
|
+
}
|
|
1705
|
+
else if ('string' === type) {
|
|
1706
|
+
finaldestination = new Url(loc, {});
|
|
1707
|
+
for (key in ignore)
|
|
1708
|
+
delete finaldestination[key];
|
|
1709
|
+
}
|
|
1710
|
+
else if ('object' === type) {
|
|
1711
|
+
for (key in loc) {
|
|
1712
|
+
if (key in ignore)
|
|
1713
|
+
continue;
|
|
1714
|
+
finaldestination[key] = loc[key];
|
|
1715
|
+
}
|
|
1716
|
+
if (finaldestination.slashes === undefined) {
|
|
1717
|
+
finaldestination.slashes = slashes.test(loc.href);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
return finaldestination;
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Check whether a protocol scheme is special.
|
|
1724
|
+
*
|
|
1725
|
+
* @param {String} The protocol scheme of the URL
|
|
1726
|
+
* @return {Boolean} `true` if the protocol scheme is special, else `false`
|
|
1727
|
+
* @private
|
|
1728
|
+
*/
|
|
1729
|
+
function isSpecial(scheme) {
|
|
1730
|
+
return (scheme === 'file:' ||
|
|
1731
|
+
scheme === 'ftp:' ||
|
|
1732
|
+
scheme === 'http:' ||
|
|
1733
|
+
scheme === 'https:' ||
|
|
1734
|
+
scheme === 'ws:' ||
|
|
1735
|
+
scheme === 'wss:');
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* @typedef ProtocolExtract
|
|
1739
|
+
* @type Object
|
|
1740
|
+
* @property {String} protocol Protocol matched in the URL, in lowercase.
|
|
1741
|
+
* @property {Boolean} slashes `true` if protocol is followed by "//", else `false`.
|
|
1742
|
+
* @property {String} rest Rest of the URL that is not part of the protocol.
|
|
1743
|
+
*/
|
|
1744
|
+
/**
|
|
1745
|
+
* Extract protocol information from a URL with/without double slash ("//").
|
|
1746
|
+
*
|
|
1747
|
+
* @param {String} address URL we want to extract from.
|
|
1748
|
+
* @param {Object} location
|
|
1749
|
+
* @return {ProtocolExtract} Extracted information.
|
|
1750
|
+
* @private
|
|
1751
|
+
*/
|
|
1752
|
+
function extractProtocol(address, location) {
|
|
1753
|
+
address = trimLeft(address);
|
|
1754
|
+
address = address.replace(CRHTLF, '');
|
|
1755
|
+
location = location || {};
|
|
1756
|
+
var match = protocolre.exec(address);
|
|
1757
|
+
var protocol = match[1] ? match[1].toLowerCase() : '';
|
|
1758
|
+
var forwardSlashes = !!match[2];
|
|
1759
|
+
var otherSlashes = !!match[3];
|
|
1760
|
+
var slashesCount = 0;
|
|
1761
|
+
var rest;
|
|
1762
|
+
if (forwardSlashes) {
|
|
1763
|
+
if (otherSlashes) {
|
|
1764
|
+
rest = match[2] + match[3] + match[4];
|
|
1765
|
+
slashesCount = match[2].length + match[3].length;
|
|
1766
|
+
}
|
|
1767
|
+
else {
|
|
1768
|
+
rest = match[2] + match[4];
|
|
1769
|
+
slashesCount = match[2].length;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
else {
|
|
1773
|
+
if (otherSlashes) {
|
|
1774
|
+
rest = match[3] + match[4];
|
|
1775
|
+
slashesCount = match[3].length;
|
|
1776
|
+
}
|
|
1777
|
+
else {
|
|
1778
|
+
rest = match[4];
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (protocol === 'file:') {
|
|
1782
|
+
if (slashesCount >= 2) {
|
|
1783
|
+
rest = rest.slice(2);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
else if (isSpecial(protocol)) {
|
|
1787
|
+
rest = match[4];
|
|
1788
|
+
}
|
|
1789
|
+
else if (protocol) {
|
|
1790
|
+
if (forwardSlashes) {
|
|
1791
|
+
rest = rest.slice(2);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
else if (slashesCount >= 2 && isSpecial(location.protocol)) {
|
|
1795
|
+
rest = match[4];
|
|
1796
|
+
}
|
|
1797
|
+
return {
|
|
1798
|
+
protocol: protocol,
|
|
1799
|
+
slashes: forwardSlashes || isSpecial(protocol),
|
|
1800
|
+
slashesCount: slashesCount,
|
|
1801
|
+
rest: rest
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Resolve a relative URL pathname against a base URL pathname.
|
|
1806
|
+
*
|
|
1807
|
+
* @param {String} relative Pathname of the relative URL.
|
|
1808
|
+
* @param {String} base Pathname of the base URL.
|
|
1809
|
+
* @return {String} Resolved pathname.
|
|
1810
|
+
* @private
|
|
1811
|
+
*/
|
|
1812
|
+
function resolve(relative, base) {
|
|
1813
|
+
if (relative === '')
|
|
1814
|
+
return base;
|
|
1815
|
+
var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')), i = path.length, last = path[i - 1], unshift = false, up = 0;
|
|
1816
|
+
while (i--) {
|
|
1817
|
+
if (path[i] === '.') {
|
|
1818
|
+
path.splice(i, 1);
|
|
1819
|
+
}
|
|
1820
|
+
else if (path[i] === '..') {
|
|
1821
|
+
path.splice(i, 1);
|
|
1822
|
+
up++;
|
|
1823
|
+
}
|
|
1824
|
+
else if (up) {
|
|
1825
|
+
if (i === 0)
|
|
1826
|
+
unshift = true;
|
|
1827
|
+
path.splice(i, 1);
|
|
1828
|
+
up--;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
if (unshift)
|
|
1832
|
+
path.unshift('');
|
|
1833
|
+
if (last === '.' || last === '..')
|
|
1834
|
+
path.push('');
|
|
1835
|
+
return path.join('/');
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* The actual URL instance. Instead of returning an object we've opted-in to
|
|
1839
|
+
* create an actual constructor as it's much more memory efficient and
|
|
1840
|
+
* faster and it pleases my OCD.
|
|
1841
|
+
*
|
|
1842
|
+
* It is worth noting that we should not use `URL` as class name to prevent
|
|
1843
|
+
* clashes with the global URL instance that got introduced in browsers.
|
|
1844
|
+
*
|
|
1845
|
+
* @constructor
|
|
1846
|
+
* @param {String} address URL we want to parse.
|
|
1847
|
+
* @param {Object|String} [location] Location defaults for relative paths.
|
|
1848
|
+
* @param {Boolean|Function} [parser] Parser for the query string.
|
|
1849
|
+
* @private
|
|
1850
|
+
*/
|
|
1851
|
+
function Url(address, location, parser) {
|
|
1852
|
+
address = trimLeft(address);
|
|
1853
|
+
address = address.replace(CRHTLF, '');
|
|
1854
|
+
if (!(this instanceof Url)) {
|
|
1855
|
+
return new Url(address, location, parser);
|
|
1856
|
+
}
|
|
1857
|
+
var relative, extracted, parse, instruction, index, key, instructions = rules.slice(), type = typeof location, url = this, i = 0;
|
|
1858
|
+
//
|
|
1859
|
+
// The following if statements allows this module two have compatibility with
|
|
1860
|
+
// 2 different API:
|
|
1861
|
+
//
|
|
1862
|
+
// 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments
|
|
1863
|
+
// where the boolean indicates that the query string should also be parsed.
|
|
1864
|
+
//
|
|
1865
|
+
// 2. The `URL` interface of the browser which accepts a URL, object as
|
|
1866
|
+
// arguments. The supplied object will be used as default values / fall-back
|
|
1867
|
+
// for relative paths.
|
|
1868
|
+
//
|
|
1869
|
+
if ('object' !== type && 'string' !== type) {
|
|
1870
|
+
parser = location;
|
|
1871
|
+
location = null;
|
|
1872
|
+
}
|
|
1873
|
+
if (parser && 'function' !== typeof parser)
|
|
1874
|
+
parser = qs.parse;
|
|
1875
|
+
location = lolcation(location);
|
|
1876
|
+
//
|
|
1877
|
+
// Extract protocol information before running the instructions.
|
|
1878
|
+
//
|
|
1879
|
+
extracted = extractProtocol(address || '', location);
|
|
1880
|
+
relative = !extracted.protocol && !extracted.slashes;
|
|
1881
|
+
url.slashes = extracted.slashes || relative && location.slashes;
|
|
1882
|
+
url.protocol = extracted.protocol || location.protocol || '';
|
|
1883
|
+
address = extracted.rest;
|
|
1884
|
+
//
|
|
1885
|
+
// When the authority component is absent the URL starts with a path
|
|
1886
|
+
// component.
|
|
1887
|
+
//
|
|
1888
|
+
if (extracted.protocol === 'file:' && (extracted.slashesCount !== 2 || windowsDriveLetter.test(address)) ||
|
|
1889
|
+
(!extracted.slashes &&
|
|
1890
|
+
(extracted.protocol ||
|
|
1891
|
+
extracted.slashesCount < 2 ||
|
|
1892
|
+
!isSpecial(url.protocol)))) {
|
|
1893
|
+
instructions[3] = [/(.*)/, 'pathname'];
|
|
1894
|
+
}
|
|
1895
|
+
for (; i < instructions.length; i++) {
|
|
1896
|
+
instruction = instructions[i];
|
|
1897
|
+
if (typeof instruction === 'function') {
|
|
1898
|
+
address = instruction(address, url);
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
parse = instruction[0];
|
|
1902
|
+
key = instruction[1];
|
|
1903
|
+
if (parse !== parse) {
|
|
1904
|
+
url[key] = address;
|
|
1905
|
+
}
|
|
1906
|
+
else if ('string' === typeof parse) {
|
|
1907
|
+
index = parse === '@'
|
|
1908
|
+
? address.lastIndexOf(parse)
|
|
1909
|
+
: address.indexOf(parse);
|
|
1910
|
+
if (~index) {
|
|
1911
|
+
if ('number' === typeof instruction[2]) {
|
|
1912
|
+
url[key] = address.slice(0, index);
|
|
1913
|
+
address = address.slice(index + instruction[2]);
|
|
1914
|
+
}
|
|
1915
|
+
else {
|
|
1916
|
+
url[key] = address.slice(index);
|
|
1917
|
+
address = address.slice(0, index);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
else if ((index = parse.exec(address))) {
|
|
1922
|
+
url[key] = index[1];
|
|
1923
|
+
address = address.slice(0, index.index);
|
|
1924
|
+
}
|
|
1925
|
+
url[key] = url[key] || (relative && instruction[3] ? location[key] || '' : '');
|
|
1926
|
+
//
|
|
1927
|
+
// Hostname, host and protocol should be lowercased so they can be used to
|
|
1928
|
+
// create a proper `origin`.
|
|
1929
|
+
//
|
|
1930
|
+
if (instruction[4])
|
|
1931
|
+
url[key] = url[key].toLowerCase();
|
|
1932
|
+
}
|
|
1933
|
+
//
|
|
1934
|
+
// Also parse the supplied query string in to an object. If we're supplied
|
|
1935
|
+
// with a custom parser as function use that instead of the default build-in
|
|
1936
|
+
// parser.
|
|
1937
|
+
//
|
|
1938
|
+
if (parser)
|
|
1939
|
+
url.query = parser(url.query);
|
|
1940
|
+
//
|
|
1941
|
+
// If the URL is relative, resolve the pathname against the base URL.
|
|
1942
|
+
//
|
|
1943
|
+
if (relative
|
|
1944
|
+
&& location.slashes
|
|
1945
|
+
&& url.pathname.charAt(0) !== '/'
|
|
1946
|
+
&& (url.pathname !== '' || location.pathname !== '')) {
|
|
1947
|
+
url.pathname = resolve(url.pathname, location.pathname);
|
|
1948
|
+
}
|
|
1949
|
+
//
|
|
1950
|
+
// Default to a / for pathname if none exists. This normalizes the URL
|
|
1951
|
+
// to always have a /
|
|
1952
|
+
//
|
|
1953
|
+
if (url.pathname.charAt(0) !== '/' && isSpecial(url.protocol)) {
|
|
1954
|
+
url.pathname = '/' + url.pathname;
|
|
1955
|
+
}
|
|
1956
|
+
//
|
|
1957
|
+
// We should not add port numbers if they are already the default port number
|
|
1958
|
+
// for a given protocol. As the host also contains the port number we're going
|
|
1959
|
+
// override it with the hostname which contains no port number.
|
|
1960
|
+
//
|
|
1961
|
+
if (!required(url.port, url.protocol)) {
|
|
1962
|
+
url.host = url.hostname;
|
|
1963
|
+
url.port = '';
|
|
1964
|
+
}
|
|
1965
|
+
//
|
|
1966
|
+
// Parse down the `auth` for the username and password.
|
|
1967
|
+
//
|
|
1968
|
+
url.username = url.password = '';
|
|
1969
|
+
if (url.auth) {
|
|
1970
|
+
index = url.auth.indexOf(':');
|
|
1971
|
+
if (~index) {
|
|
1972
|
+
url.username = url.auth.slice(0, index);
|
|
1973
|
+
url.username = encodeURIComponent(decodeURIComponent(url.username));
|
|
1974
|
+
url.password = url.auth.slice(index + 1);
|
|
1975
|
+
url.password = encodeURIComponent(decodeURIComponent(url.password));
|
|
1976
|
+
}
|
|
1977
|
+
else {
|
|
1978
|
+
url.username = encodeURIComponent(decodeURIComponent(url.auth));
|
|
1979
|
+
}
|
|
1980
|
+
url.auth = url.password ? url.username + ':' + url.password : url.username;
|
|
1981
|
+
}
|
|
1982
|
+
url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
|
|
1983
|
+
? url.protocol + '//' + url.host
|
|
1984
|
+
: 'null';
|
|
1985
|
+
//
|
|
1986
|
+
// The href is just the compiled result.
|
|
1987
|
+
//
|
|
1988
|
+
url.href = url.toString();
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* This is convenience method for changing properties in the URL instance to
|
|
1992
|
+
* insure that they all propagate correctly.
|
|
1993
|
+
*
|
|
1994
|
+
* @param {String} part Property we need to adjust.
|
|
1995
|
+
* @param {Mixed} value The newly assigned value.
|
|
1996
|
+
* @param {Boolean|Function} fn When setting the query, it will be the function
|
|
1997
|
+
* used to parse the query.
|
|
1998
|
+
* When setting the protocol, double slash will be
|
|
1999
|
+
* removed from the final url if it is true.
|
|
2000
|
+
* @returns {URL} URL instance for chaining.
|
|
2001
|
+
* @public
|
|
2002
|
+
*/
|
|
2003
|
+
function set(part, value, fn) {
|
|
2004
|
+
var url = this;
|
|
2005
|
+
switch (part) {
|
|
2006
|
+
case 'query':
|
|
2007
|
+
if ('string' === typeof value && value.length) {
|
|
2008
|
+
value = (fn || qs.parse)(value);
|
|
2009
|
+
}
|
|
2010
|
+
url[part] = value;
|
|
2011
|
+
break;
|
|
2012
|
+
case 'port':
|
|
2013
|
+
url[part] = value;
|
|
2014
|
+
if (!required(value, url.protocol)) {
|
|
2015
|
+
url.host = url.hostname;
|
|
2016
|
+
url[part] = '';
|
|
2017
|
+
}
|
|
2018
|
+
else if (value) {
|
|
2019
|
+
url.host = url.hostname + ':' + value;
|
|
2020
|
+
}
|
|
2021
|
+
break;
|
|
2022
|
+
case 'hostname':
|
|
2023
|
+
url[part] = value;
|
|
2024
|
+
if (url.port)
|
|
2025
|
+
value += ':' + url.port;
|
|
2026
|
+
url.host = value;
|
|
2027
|
+
break;
|
|
2028
|
+
case 'host':
|
|
2029
|
+
url[part] = value;
|
|
2030
|
+
if (port.test(value)) {
|
|
2031
|
+
value = value.split(':');
|
|
2032
|
+
url.port = value.pop();
|
|
2033
|
+
url.hostname = value.join(':');
|
|
2034
|
+
}
|
|
2035
|
+
else {
|
|
2036
|
+
url.hostname = value;
|
|
2037
|
+
url.port = '';
|
|
2038
|
+
}
|
|
2039
|
+
break;
|
|
2040
|
+
case 'protocol':
|
|
2041
|
+
url.protocol = value.toLowerCase();
|
|
2042
|
+
url.slashes = !fn;
|
|
2043
|
+
break;
|
|
2044
|
+
case 'pathname':
|
|
2045
|
+
case 'hash':
|
|
2046
|
+
if (value) {
|
|
2047
|
+
var char = part === 'pathname' ? '/' : '#';
|
|
2048
|
+
url[part] = value.charAt(0) !== char ? char + value : value;
|
|
2049
|
+
}
|
|
2050
|
+
else {
|
|
2051
|
+
url[part] = value;
|
|
2052
|
+
}
|
|
2053
|
+
break;
|
|
2054
|
+
case 'username':
|
|
2055
|
+
case 'password':
|
|
2056
|
+
url[part] = encodeURIComponent(value);
|
|
2057
|
+
break;
|
|
2058
|
+
case 'auth':
|
|
2059
|
+
var index = value.indexOf(':');
|
|
2060
|
+
if (~index) {
|
|
2061
|
+
url.username = value.slice(0, index);
|
|
2062
|
+
url.username = encodeURIComponent(decodeURIComponent(url.username));
|
|
2063
|
+
url.password = value.slice(index + 1);
|
|
2064
|
+
url.password = encodeURIComponent(decodeURIComponent(url.password));
|
|
2065
|
+
}
|
|
2066
|
+
else {
|
|
2067
|
+
url.username = encodeURIComponent(decodeURIComponent(value));
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
for (var i = 0; i < rules.length; i++) {
|
|
2071
|
+
var ins = rules[i];
|
|
2072
|
+
if (ins[4])
|
|
2073
|
+
url[ins[1]] = url[ins[1]].toLowerCase();
|
|
2074
|
+
}
|
|
2075
|
+
url.auth = url.password ? url.username + ':' + url.password : url.username;
|
|
2076
|
+
url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host
|
|
2077
|
+
? url.protocol + '//' + url.host
|
|
2078
|
+
: 'null';
|
|
2079
|
+
url.href = url.toString();
|
|
2080
|
+
return url;
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* Transform the properties back in to a valid and full URL string.
|
|
2084
|
+
*
|
|
2085
|
+
* @param {Function} stringify Optional query stringify function.
|
|
2086
|
+
* @returns {String} Compiled version of the URL.
|
|
2087
|
+
* @public
|
|
2088
|
+
*/
|
|
2089
|
+
function toString(stringify) {
|
|
2090
|
+
if (!stringify || 'function' !== typeof stringify)
|
|
2091
|
+
stringify = qs.stringify;
|
|
2092
|
+
var query, url = this, host = url.host, protocol = url.protocol;
|
|
2093
|
+
if (protocol && protocol.charAt(protocol.length - 1) !== ':')
|
|
2094
|
+
protocol += ':';
|
|
2095
|
+
var result = protocol +
|
|
2096
|
+
((url.protocol && url.slashes) || isSpecial(url.protocol) ? '//' : '');
|
|
2097
|
+
if (url.username) {
|
|
2098
|
+
result += url.username;
|
|
2099
|
+
if (url.password)
|
|
2100
|
+
result += ':' + url.password;
|
|
2101
|
+
result += '@';
|
|
2102
|
+
}
|
|
2103
|
+
else if (url.password) {
|
|
2104
|
+
result += ':' + url.password;
|
|
2105
|
+
result += '@';
|
|
2106
|
+
}
|
|
2107
|
+
else if (url.protocol !== 'file:' &&
|
|
2108
|
+
isSpecial(url.protocol) &&
|
|
2109
|
+
!host &&
|
|
2110
|
+
url.pathname !== '/') {
|
|
2111
|
+
//
|
|
2112
|
+
// Add back the empty userinfo, otherwise the original invalid URL
|
|
2113
|
+
// might be transformed into a valid one with `url.pathname` as host.
|
|
2114
|
+
//
|
|
2115
|
+
result += '@';
|
|
2116
|
+
}
|
|
2117
|
+
//
|
|
2118
|
+
// Trailing colon is removed from `url.host` when it is parsed. If it still
|
|
2119
|
+
// ends with a colon, then add back the trailing colon that was removed. This
|
|
2120
|
+
// prevents an invalid URL from being transformed into a valid one.
|
|
2121
|
+
//
|
|
2122
|
+
if (host[host.length - 1] === ':' || (port.test(url.hostname) && !url.port)) {
|
|
2123
|
+
host += ':';
|
|
2124
|
+
}
|
|
2125
|
+
result += host + url.pathname;
|
|
2126
|
+
query = 'object' === typeof url.query ? stringify(url.query) : url.query;
|
|
2127
|
+
if (query)
|
|
2128
|
+
result += '?' !== query.charAt(0) ? '?' + query : query;
|
|
2129
|
+
if (url.hash)
|
|
2130
|
+
result += url.hash;
|
|
2131
|
+
return result;
|
|
2132
|
+
}
|
|
2133
|
+
Url.prototype = { set: set, toString: toString };
|
|
2134
|
+
//
|
|
2135
|
+
// Expose the URL parser and some additional properties that might be useful for
|
|
2136
|
+
// others or testing.
|
|
2137
|
+
//
|
|
2138
|
+
Url.extractProtocol = extractProtocol;
|
|
2139
|
+
Url.location = lolcation;
|
|
2140
|
+
Url.trimLeft = trimLeft;
|
|
2141
|
+
Url.qs = qs;
|
|
2142
|
+
module.exports = Url;
|
|
2143
|
+
}).call(this);
|
|
2144
|
+
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
|
|
2145
|
+
}, { "querystringify": 8, "requires-port": 11 }], 15: [function (_dereq_, module, exports) {
|
|
2146
|
+
'use strict';
|
|
2147
|
+
var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64, map = {}, seed = 0, i = 0, prev;
|
|
2148
|
+
/**
|
|
2149
|
+
* Return a string representing the specified number.
|
|
2150
|
+
*
|
|
2151
|
+
* @param {Number} num The number to convert.
|
|
2152
|
+
* @returns {String} The string representation of the number.
|
|
2153
|
+
* @api public
|
|
2154
|
+
*/
|
|
2155
|
+
function encode(num) {
|
|
2156
|
+
var encoded = '';
|
|
2157
|
+
do {
|
|
2158
|
+
encoded = alphabet[num % length] + encoded;
|
|
2159
|
+
num = Math.floor(num / length);
|
|
2160
|
+
} while (num > 0);
|
|
2161
|
+
return encoded;
|
|
2162
|
+
}
|
|
2163
|
+
/**
|
|
2164
|
+
* Return the integer value specified by the given string.
|
|
2165
|
+
*
|
|
2166
|
+
* @param {String} str The string to convert.
|
|
2167
|
+
* @returns {Number} The integer value represented by the string.
|
|
2168
|
+
* @api public
|
|
2169
|
+
*/
|
|
2170
|
+
function decode(str) {
|
|
2171
|
+
var decoded = 0;
|
|
2172
|
+
for (i = 0; i < str.length; i++) {
|
|
2173
|
+
decoded = decoded * length + map[str.charAt(i)];
|
|
2174
|
+
}
|
|
2175
|
+
return decoded;
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Yeast: A tiny growing id generator.
|
|
2179
|
+
*
|
|
2180
|
+
* @returns {String} A unique id.
|
|
2181
|
+
* @api public
|
|
2182
|
+
*/
|
|
2183
|
+
function yeast() {
|
|
2184
|
+
var now = encode(+new Date());
|
|
2185
|
+
if (now !== prev)
|
|
2186
|
+
return seed = 0, prev = now;
|
|
2187
|
+
return now + '.' + encode(seed++);
|
|
2188
|
+
}
|
|
2189
|
+
//
|
|
2190
|
+
// Map each character to its index.
|
|
2191
|
+
//
|
|
2192
|
+
for (; i < length; i++)
|
|
2193
|
+
map[alphabet[i]] = i;
|
|
2194
|
+
//
|
|
2195
|
+
// Expose the `yeast`, `encode` and `decode` functions.
|
|
2196
|
+
//
|
|
2197
|
+
yeast.encode = encode;
|
|
2198
|
+
yeast.decode = decode;
|
|
2199
|
+
module.exports = yeast;
|
|
2200
|
+
}, {}], 16: [function (_dereq_, module, exports) {
|
|
2201
|
+
/*globals require, define */
|
|
2202
|
+
'use strict';
|
|
2203
|
+
var EventEmitter = _dereq_('eventemitter3'), TickTock = _dereq_('tick-tock'), Recovery = _dereq_('recovery'), qs = _dereq_('querystringify'), inherits = _dereq_('inherits'), destroy = _dereq_('demolish'), yeast = _dereq_('yeast'), u2028 = /\u2028/g, u2029 = /\u2029/g;
|
|
2204
|
+
/**
|
|
2205
|
+
* Context assertion, ensure that some of our public Primus methods are called
|
|
2206
|
+
* with the correct context to ensure that
|
|
2207
|
+
*
|
|
2208
|
+
* @param {Primus} self The context of the function.
|
|
2209
|
+
* @param {String} method The method name.
|
|
2210
|
+
* @api private
|
|
2211
|
+
*/
|
|
2212
|
+
function context(self, method) {
|
|
2213
|
+
if (self instanceof Primus)
|
|
2214
|
+
return;
|
|
2215
|
+
var failure = new Error('Primus#' + method + '\'s context should called with a Primus instance');
|
|
2216
|
+
if ('function' !== typeof self.listeners || !self.listeners('error').length) {
|
|
2217
|
+
throw failure;
|
|
2218
|
+
}
|
|
2219
|
+
self.emit('error', failure);
|
|
2220
|
+
}
|
|
2221
|
+
//
|
|
2222
|
+
// Sets the default connection URL, it uses the default origin of the browser
|
|
2223
|
+
// when supported but degrades for older browsers. In Node.js, we cannot guess
|
|
2224
|
+
// where the user wants to connect to, so we just default to localhost.
|
|
2225
|
+
//
|
|
2226
|
+
var defaultUrl;
|
|
2227
|
+
try {
|
|
2228
|
+
if (location.origin) {
|
|
2229
|
+
defaultUrl = location.origin;
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
defaultUrl = location.protocol + '//' + location.host;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
catch (e) {
|
|
2236
|
+
defaultUrl = 'http://127.0.0.1';
|
|
2237
|
+
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Primus is a real-time library agnostic framework for establishing real-time
|
|
2240
|
+
* connections with servers.
|
|
2241
|
+
*
|
|
2242
|
+
* Options:
|
|
2243
|
+
* - reconnect, configuration for the reconnect process.
|
|
2244
|
+
* - manual, don't automatically call `.open` to start the connection.
|
|
2245
|
+
* - websockets, force the use of WebSockets, even when you should avoid them.
|
|
2246
|
+
* - timeout, connect timeout, server didn't respond in a timely manner.
|
|
2247
|
+
* - pingTimeout, The maximum amount of time to wait for the server to send a ping.
|
|
2248
|
+
* - network, Use network events as leading method for network connection drops.
|
|
2249
|
+
* - strategy, Reconnection strategies.
|
|
2250
|
+
* - transport, Transport options.
|
|
2251
|
+
* - url, uri, The URL to use connect with the server.
|
|
2252
|
+
*
|
|
2253
|
+
* @constructor
|
|
2254
|
+
* @param {String} url The URL of your server.
|
|
2255
|
+
* @param {Object} options The configuration.
|
|
2256
|
+
* @api public
|
|
2257
|
+
*/
|
|
2258
|
+
function Primus(url, options) {
|
|
2259
|
+
if (!(this instanceof Primus))
|
|
2260
|
+
return new Primus(url, options);
|
|
2261
|
+
Primus.Stream.call(this);
|
|
2262
|
+
if ('function' !== typeof this.client) {
|
|
2263
|
+
return this.critical(new Error('The client library has not been compiled correctly, see ' +
|
|
2264
|
+
'https://github.com/primus/primus#client-library for more details'));
|
|
2265
|
+
}
|
|
2266
|
+
if ('object' === typeof url) {
|
|
2267
|
+
options = url;
|
|
2268
|
+
url = options.url || options.uri || defaultUrl;
|
|
2269
|
+
}
|
|
2270
|
+
else {
|
|
2271
|
+
options = options || {};
|
|
2272
|
+
}
|
|
2273
|
+
if ('ping' in options || 'pong' in options) {
|
|
2274
|
+
return this.critical(new Error('The `ping` and `pong` options have been removed'));
|
|
2275
|
+
}
|
|
2276
|
+
var primus = this;
|
|
2277
|
+
// The maximum number of messages that can be placed in queue.
|
|
2278
|
+
options.queueSize = 'queueSize' in options ? options.queueSize : Infinity;
|
|
2279
|
+
// Connection timeout duration.
|
|
2280
|
+
options.timeout = 'timeout' in options ? options.timeout : 10e3;
|
|
2281
|
+
// Stores the back off configuration.
|
|
2282
|
+
options.reconnect = 'reconnect' in options ? options.reconnect : {};
|
|
2283
|
+
// Heartbeat ping interval.
|
|
2284
|
+
options.pingTimeout = 'pingTimeout' in options ? options.pingTimeout : 45000;
|
|
2285
|
+
// Reconnect strategies.
|
|
2286
|
+
options.strategy = 'strategy' in options ? options.strategy : [];
|
|
2287
|
+
// Custom transport options.
|
|
2288
|
+
options.transport = 'transport' in options ? options.transport : {};
|
|
2289
|
+
primus.buffer = []; // Stores premature send data.
|
|
2290
|
+
primus.writable = true; // Silly stream compatibility.
|
|
2291
|
+
primus.readable = true; // Silly stream compatibility.
|
|
2292
|
+
primus.url = primus.parse(url || defaultUrl); // Parse the URL to a readable format.
|
|
2293
|
+
primus.readyState = Primus.CLOSED; // The readyState of the connection.
|
|
2294
|
+
primus.options = options; // Reference to the supplied options.
|
|
2295
|
+
primus.timers = new TickTock(this); // Contains all our timers.
|
|
2296
|
+
primus.socket = null; // Reference to the internal connection.
|
|
2297
|
+
primus.disconnect = false; // Did we receive a disconnect packet?
|
|
2298
|
+
primus.transport = options.transport; // Transport options.
|
|
2299
|
+
primus.transformers = {
|
|
2300
|
+
outgoing: [],
|
|
2301
|
+
incoming: []
|
|
2302
|
+
};
|
|
2303
|
+
//
|
|
2304
|
+
// Create our reconnection instance.
|
|
2305
|
+
//
|
|
2306
|
+
primus.recovery = new Recovery(options.reconnect);
|
|
2307
|
+
//
|
|
2308
|
+
// Parse the reconnection strategy. It can have the following strategies:
|
|
2309
|
+
//
|
|
2310
|
+
// - timeout: Reconnect when we have a network timeout.
|
|
2311
|
+
// - disconnect: Reconnect when we have an unexpected disconnect.
|
|
2312
|
+
// - online: Reconnect when we're back online.
|
|
2313
|
+
//
|
|
2314
|
+
if ('string' === typeof options.strategy) {
|
|
2315
|
+
options.strategy = options.strategy.split(/\s?,\s?/g);
|
|
2316
|
+
}
|
|
2317
|
+
if (false === options.strategy) {
|
|
2318
|
+
//
|
|
2319
|
+
// Strategies are disabled, but we still need an empty array to join it in
|
|
2320
|
+
// to nothing.
|
|
2321
|
+
//
|
|
2322
|
+
options.strategy = [];
|
|
2323
|
+
}
|
|
2324
|
+
else if (!options.strategy.length) {
|
|
2325
|
+
options.strategy.push('disconnect', 'online');
|
|
2326
|
+
//
|
|
2327
|
+
// Timeout based reconnection should only be enabled conditionally. When
|
|
2328
|
+
// authorization is enabled it could trigger.
|
|
2329
|
+
//
|
|
2330
|
+
if (!this.authorization)
|
|
2331
|
+
options.strategy.push('timeout');
|
|
2332
|
+
}
|
|
2333
|
+
options.strategy = options.strategy.join(',').toLowerCase();
|
|
2334
|
+
//
|
|
2335
|
+
// Force the use of WebSockets, even when we've detected some potential
|
|
2336
|
+
// broken WebSocket implementation.
|
|
2337
|
+
//
|
|
2338
|
+
if ('websockets' in options) {
|
|
2339
|
+
primus.AVOID_WEBSOCKETS = !options.websockets;
|
|
2340
|
+
}
|
|
2341
|
+
//
|
|
2342
|
+
// Force or disable the use of NETWORK events as leading client side
|
|
2343
|
+
// disconnection detection.
|
|
2344
|
+
//
|
|
2345
|
+
if ('network' in options) {
|
|
2346
|
+
primus.NETWORK_EVENTS = options.network;
|
|
2347
|
+
}
|
|
2348
|
+
//
|
|
2349
|
+
// Check if the user wants to manually initialise a connection. If they don't,
|
|
2350
|
+
// we want to do it after a really small timeout so we give the users enough
|
|
2351
|
+
// time to listen for `error` events etc.
|
|
2352
|
+
//
|
|
2353
|
+
if (!options.manual)
|
|
2354
|
+
primus.timers.setTimeout('open', function open() {
|
|
2355
|
+
primus.timers.clear('open');
|
|
2356
|
+
primus.open();
|
|
2357
|
+
}, 0);
|
|
2358
|
+
primus.initialise(options);
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Simple require wrapper to make browserify, node and require.js play nice.
|
|
2362
|
+
*
|
|
2363
|
+
* @param {String} name The module to require.
|
|
2364
|
+
* @returns {Object|Undefined} The module that we required.
|
|
2365
|
+
* @api private
|
|
2366
|
+
*/
|
|
2367
|
+
Primus.requires = Primus.require = function requires(name) {
|
|
2368
|
+
if ('function' !== typeof _dereq_)
|
|
2369
|
+
return undefined;
|
|
2370
|
+
return !('function' === typeof define && define.amd)
|
|
2371
|
+
? _dereq_(name)
|
|
2372
|
+
: undefined;
|
|
2373
|
+
};
|
|
2374
|
+
//
|
|
2375
|
+
// It's possible that we're running in Node.js or in a Node.js compatible
|
|
2376
|
+
// environment. In this cases we try to inherit from the Stream base class.
|
|
2377
|
+
//
|
|
2378
|
+
try {
|
|
2379
|
+
Primus.Stream = Primus.requires('stream');
|
|
2380
|
+
}
|
|
2381
|
+
catch (e) { }
|
|
2382
|
+
if (!Primus.Stream)
|
|
2383
|
+
Primus.Stream = EventEmitter;
|
|
2384
|
+
inherits(Primus, Primus.Stream);
|
|
2385
|
+
/**
|
|
2386
|
+
* Primus readyStates, used internally to set the correct ready state.
|
|
2387
|
+
*
|
|
2388
|
+
* @type {Number}
|
|
2389
|
+
* @private
|
|
2390
|
+
*/
|
|
2391
|
+
Primus.OPENING = 1; // We're opening the connection.
|
|
2392
|
+
Primus.CLOSED = 2; // No active connection.
|
|
2393
|
+
Primus.OPEN = 3; // The connection is open.
|
|
2394
|
+
/**
|
|
2395
|
+
* Are we working with a potentially broken WebSockets implementation? This
|
|
2396
|
+
* boolean can be used by transformers to remove `WebSockets` from their
|
|
2397
|
+
* supported transports.
|
|
2398
|
+
*
|
|
2399
|
+
* @type {Boolean}
|
|
2400
|
+
* @private
|
|
2401
|
+
*/
|
|
2402
|
+
Primus.prototype.AVOID_WEBSOCKETS = false;
|
|
2403
|
+
/**
|
|
2404
|
+
* Some browsers support registering emitting `online` and `offline` events when
|
|
2405
|
+
* the connection has been dropped on the client. We're going to detect it in
|
|
2406
|
+
* a simple `try {} catch (e) {}` statement so we don't have to do complicated
|
|
2407
|
+
* feature detection.
|
|
2408
|
+
*
|
|
2409
|
+
* @type {Boolean}
|
|
2410
|
+
* @private
|
|
2411
|
+
*/
|
|
2412
|
+
Primus.prototype.NETWORK_EVENTS = false;
|
|
2413
|
+
Primus.prototype.online = true;
|
|
2414
|
+
try {
|
|
2415
|
+
if (Primus.prototype.NETWORK_EVENTS = 'onLine' in navigator
|
|
2416
|
+
&& (window.addEventListener || document.body.attachEvent)) {
|
|
2417
|
+
if (!navigator.onLine) {
|
|
2418
|
+
Primus.prototype.online = false;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
catch (e) { }
|
|
2423
|
+
/**
|
|
2424
|
+
* The Ark contains all our plugins definitions. It's namespaced by
|
|
2425
|
+
* name => plugin.
|
|
2426
|
+
*
|
|
2427
|
+
* @type {Object}
|
|
2428
|
+
* @private
|
|
2429
|
+
*/
|
|
2430
|
+
Primus.prototype.ark = {};
|
|
2431
|
+
/**
|
|
2432
|
+
* Simple emit wrapper that returns a function that emits an event once it's
|
|
2433
|
+
* called. This makes it easier for transports to emit specific events.
|
|
2434
|
+
*
|
|
2435
|
+
* @returns {Function} A function that will emit the event when called.
|
|
2436
|
+
* @api public
|
|
2437
|
+
*/
|
|
2438
|
+
Primus.prototype.emits = _dereq_('emits');
|
|
2439
|
+
/**
|
|
2440
|
+
* Return the given plugin.
|
|
2441
|
+
*
|
|
2442
|
+
* @param {String} name The name of the plugin.
|
|
2443
|
+
* @returns {Object|undefined} The plugin or undefined.
|
|
2444
|
+
* @api public
|
|
2445
|
+
*/
|
|
2446
|
+
Primus.prototype.plugin = function plugin(name) {
|
|
2447
|
+
context(this, 'plugin');
|
|
2448
|
+
if (name)
|
|
2449
|
+
return this.ark[name];
|
|
2450
|
+
var plugins = {};
|
|
2451
|
+
for (name in this.ark) {
|
|
2452
|
+
plugins[name] = this.ark[name];
|
|
2453
|
+
}
|
|
2454
|
+
return plugins;
|
|
2455
|
+
};
|
|
2456
|
+
/**
|
|
2457
|
+
* Checks if the given event is an emitted event by Primus.
|
|
2458
|
+
*
|
|
2459
|
+
* @param {String} evt The event name.
|
|
2460
|
+
* @returns {Boolean} Indication of the event is reserved for internal use.
|
|
2461
|
+
* @api public
|
|
2462
|
+
*/
|
|
2463
|
+
Primus.prototype.reserved = function reserved(evt) {
|
|
2464
|
+
return (/^(incoming|outgoing)::/).test(evt)
|
|
2465
|
+
|| evt in this.reserved.events;
|
|
2466
|
+
};
|
|
2467
|
+
/**
|
|
2468
|
+
* The actual events that are used by the client.
|
|
2469
|
+
*
|
|
2470
|
+
* @type {Object}
|
|
2471
|
+
* @public
|
|
2472
|
+
*/
|
|
2473
|
+
Primus.prototype.reserved.events = {
|
|
2474
|
+
'reconnect scheduled': 1,
|
|
2475
|
+
'reconnect timeout': 1,
|
|
2476
|
+
'readyStateChange': 1,
|
|
2477
|
+
'reconnect failed': 1,
|
|
2478
|
+
'reconnected': 1,
|
|
2479
|
+
'reconnect': 1,
|
|
2480
|
+
'offline': 1,
|
|
2481
|
+
'timeout': 1,
|
|
2482
|
+
'destroy': 1,
|
|
2483
|
+
'online': 1,
|
|
2484
|
+
'error': 1,
|
|
2485
|
+
'close': 1,
|
|
2486
|
+
'open': 1,
|
|
2487
|
+
'data': 1,
|
|
2488
|
+
'end': 1
|
|
2489
|
+
};
|
|
2490
|
+
/**
|
|
2491
|
+
* Initialise the Primus and setup all parsers and internal listeners.
|
|
2492
|
+
*
|
|
2493
|
+
* @param {Object} options The original options object.
|
|
2494
|
+
* @returns {Primus}
|
|
2495
|
+
* @api private
|
|
2496
|
+
*/
|
|
2497
|
+
Primus.prototype.initialise = function initialise(options) {
|
|
2498
|
+
var primus = this;
|
|
2499
|
+
primus.recovery
|
|
2500
|
+
.on('reconnected', primus.emits('reconnected'))
|
|
2501
|
+
.on('reconnect failed', primus.emits('reconnect failed', function failed(next) {
|
|
2502
|
+
primus.emit('end');
|
|
2503
|
+
next();
|
|
2504
|
+
}))
|
|
2505
|
+
.on('reconnect timeout', primus.emits('reconnect timeout'))
|
|
2506
|
+
.on('reconnect scheduled', primus.emits('reconnect scheduled'))
|
|
2507
|
+
.on('reconnect', primus.emits('reconnect', function reconnect(next) {
|
|
2508
|
+
primus.emit('outgoing::reconnect');
|
|
2509
|
+
next();
|
|
2510
|
+
}));
|
|
2511
|
+
primus.on('outgoing::open', function opening() {
|
|
2512
|
+
var readyState = primus.readyState;
|
|
2513
|
+
primus.readyState = Primus.OPENING;
|
|
2514
|
+
if (readyState !== primus.readyState) {
|
|
2515
|
+
primus.emit('readyStateChange', 'opening');
|
|
2516
|
+
}
|
|
2517
|
+
});
|
|
2518
|
+
primus.on('incoming::open', function opened() {
|
|
2519
|
+
var readyState = primus.readyState;
|
|
2520
|
+
if (primus.recovery.reconnecting()) {
|
|
2521
|
+
primus.recovery.reconnected();
|
|
2522
|
+
}
|
|
2523
|
+
//
|
|
2524
|
+
// The connection has been opened so we should set our state to
|
|
2525
|
+
// (writ|read)able so our stream compatibility works as intended.
|
|
2526
|
+
//
|
|
2527
|
+
primus.writable = true;
|
|
2528
|
+
primus.readable = true;
|
|
2529
|
+
//
|
|
2530
|
+
// Make sure we are flagged as `online` as we've successfully opened the
|
|
2531
|
+
// connection.
|
|
2532
|
+
//
|
|
2533
|
+
if (!primus.online) {
|
|
2534
|
+
primus.online = true;
|
|
2535
|
+
primus.emit('online');
|
|
2536
|
+
}
|
|
2537
|
+
primus.readyState = Primus.OPEN;
|
|
2538
|
+
if (readyState !== primus.readyState) {
|
|
2539
|
+
primus.emit('readyStateChange', 'open');
|
|
2540
|
+
}
|
|
2541
|
+
primus.heartbeat();
|
|
2542
|
+
if (primus.buffer.length) {
|
|
2543
|
+
var data = primus.buffer.slice(), length = data.length, i = 0;
|
|
2544
|
+
primus.buffer.length = 0;
|
|
2545
|
+
for (; i < length; i++) {
|
|
2546
|
+
primus._write(data[i]);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
primus.emit('open');
|
|
2550
|
+
});
|
|
2551
|
+
primus.on('incoming::ping', function ping(time) {
|
|
2552
|
+
primus.online = true;
|
|
2553
|
+
primus.heartbeat();
|
|
2554
|
+
primus.emit('outgoing::pong', time);
|
|
2555
|
+
primus._write('primus::pong::' + time);
|
|
2556
|
+
});
|
|
2557
|
+
primus.on('incoming::error', function error(e) {
|
|
2558
|
+
var connect = primus.timers.active('connect'), err = e;
|
|
2559
|
+
//
|
|
2560
|
+
// When the error is not an Error instance we try to normalize it.
|
|
2561
|
+
//
|
|
2562
|
+
if ('string' === typeof e) {
|
|
2563
|
+
err = new Error(e);
|
|
2564
|
+
}
|
|
2565
|
+
else if (!(e instanceof Error) && 'object' === typeof e) {
|
|
2566
|
+
//
|
|
2567
|
+
// BrowserChannel and SockJS returns an object which contains some
|
|
2568
|
+
// details of the error. In order to have a proper error we "copy" the
|
|
2569
|
+
// details in an Error instance.
|
|
2570
|
+
//
|
|
2571
|
+
err = new Error(e.message || e.reason);
|
|
2572
|
+
for (var key in e) {
|
|
2573
|
+
if (Object.prototype.hasOwnProperty.call(e, key))
|
|
2574
|
+
err[key] = e[key];
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
//
|
|
2578
|
+
// We're still doing a reconnect attempt, it could be that we failed to
|
|
2579
|
+
// connect because the server was down. Failing connect attempts should
|
|
2580
|
+
// always emit an `error` event instead of a `open` event.
|
|
2581
|
+
//
|
|
2582
|
+
//
|
|
2583
|
+
if (primus.recovery.reconnecting())
|
|
2584
|
+
return primus.recovery.reconnected(err);
|
|
2585
|
+
if (primus.listeners('error').length)
|
|
2586
|
+
primus.emit('error', err);
|
|
2587
|
+
//
|
|
2588
|
+
// We received an error while connecting, this most likely the result of an
|
|
2589
|
+
// unauthorized access to the server.
|
|
2590
|
+
//
|
|
2591
|
+
if (connect) {
|
|
2592
|
+
if (~primus.options.strategy.indexOf('timeout')) {
|
|
2593
|
+
primus.recovery.reconnect();
|
|
2594
|
+
}
|
|
2595
|
+
else {
|
|
2596
|
+
primus.end();
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
primus.on('incoming::data', function message(raw) {
|
|
2601
|
+
primus.decoder(raw, function decoding(err, data) {
|
|
2602
|
+
//
|
|
2603
|
+
// Do a "safe" emit('error') when we fail to parse a message. We don't
|
|
2604
|
+
// want to throw here as listening to errors should be optional.
|
|
2605
|
+
//
|
|
2606
|
+
if (err)
|
|
2607
|
+
return primus.listeners('error').length && primus.emit('error', err);
|
|
2608
|
+
//
|
|
2609
|
+
// Handle all "primus::" prefixed protocol messages.
|
|
2610
|
+
//
|
|
2611
|
+
if (primus.protocol(data))
|
|
2612
|
+
return;
|
|
2613
|
+
primus.transforms(primus, primus, 'incoming', data, raw);
|
|
2614
|
+
});
|
|
2615
|
+
});
|
|
2616
|
+
primus.on('incoming::end', function end() {
|
|
2617
|
+
var readyState = primus.readyState;
|
|
2618
|
+
//
|
|
2619
|
+
// This `end` started with the receiving of a primus::server::close packet
|
|
2620
|
+
// which indicated that the user/developer on the server closed the
|
|
2621
|
+
// connection and it was not a result of a network disruption. So we should
|
|
2622
|
+
// kill the connection without doing a reconnect.
|
|
2623
|
+
//
|
|
2624
|
+
if (primus.disconnect) {
|
|
2625
|
+
primus.disconnect = false;
|
|
2626
|
+
return primus.end();
|
|
2627
|
+
}
|
|
2628
|
+
//
|
|
2629
|
+
// Always set the readyState to closed, and if we're still connecting, close
|
|
2630
|
+
// the connection so we're sure that everything after this if statement block
|
|
2631
|
+
// is only executed because our readyState is set to `open`.
|
|
2632
|
+
//
|
|
2633
|
+
primus.readyState = Primus.CLOSED;
|
|
2634
|
+
if (readyState !== primus.readyState) {
|
|
2635
|
+
primus.emit('readyStateChange', 'end');
|
|
2636
|
+
}
|
|
2637
|
+
if (primus.timers.active('connect'))
|
|
2638
|
+
primus.end();
|
|
2639
|
+
if (readyState !== Primus.OPEN) {
|
|
2640
|
+
return primus.recovery.reconnecting()
|
|
2641
|
+
? primus.recovery.reconnect()
|
|
2642
|
+
: false;
|
|
2643
|
+
}
|
|
2644
|
+
this.writable = false;
|
|
2645
|
+
this.readable = false;
|
|
2646
|
+
//
|
|
2647
|
+
// Clear all timers in case we're not going to reconnect.
|
|
2648
|
+
//
|
|
2649
|
+
this.timers.clear();
|
|
2650
|
+
//
|
|
2651
|
+
// Fire the `close` event as an indication of connection disruption.
|
|
2652
|
+
// This is also fired by `primus#end` so it is emitted in all cases.
|
|
2653
|
+
//
|
|
2654
|
+
primus.emit('close');
|
|
2655
|
+
//
|
|
2656
|
+
// The disconnect was unintentional, probably because the server has
|
|
2657
|
+
// shutdown, so if the reconnection is enabled start a reconnect procedure.
|
|
2658
|
+
//
|
|
2659
|
+
if (~primus.options.strategy.indexOf('disconnect')) {
|
|
2660
|
+
return primus.recovery.reconnect();
|
|
2661
|
+
}
|
|
2662
|
+
primus.emit('outgoing::end');
|
|
2663
|
+
primus.emit('end');
|
|
2664
|
+
});
|
|
2665
|
+
//
|
|
2666
|
+
// Setup the real-time client.
|
|
2667
|
+
//
|
|
2668
|
+
primus.client();
|
|
2669
|
+
//
|
|
2670
|
+
// Process the potential plugins.
|
|
2671
|
+
//
|
|
2672
|
+
for (var plugin in primus.ark) {
|
|
2673
|
+
primus.ark[plugin].call(primus, primus, options);
|
|
2674
|
+
}
|
|
2675
|
+
//
|
|
2676
|
+
// NOTE: The following code is only required if we're supporting network
|
|
2677
|
+
// events as it requires access to browser globals.
|
|
2678
|
+
//
|
|
2679
|
+
if (!primus.NETWORK_EVENTS)
|
|
2680
|
+
return primus;
|
|
2681
|
+
/**
|
|
2682
|
+
* Handler for offline notifications.
|
|
2683
|
+
*
|
|
2684
|
+
* @api private
|
|
2685
|
+
*/
|
|
2686
|
+
primus.offlineHandler = function offline() {
|
|
2687
|
+
if (!primus.online)
|
|
2688
|
+
return; // Already or still offline, bailout.
|
|
2689
|
+
primus.online = false;
|
|
2690
|
+
primus.emit('offline');
|
|
2691
|
+
primus.end();
|
|
2692
|
+
//
|
|
2693
|
+
// It is certainly possible that we're in a reconnection loop and that the
|
|
2694
|
+
// user goes offline. In this case we want to kill the existing attempt so
|
|
2695
|
+
// when the user goes online, it will attempt to reconnect freshly again.
|
|
2696
|
+
//
|
|
2697
|
+
primus.recovery.reset();
|
|
2698
|
+
};
|
|
2699
|
+
/**
|
|
2700
|
+
* Handler for online notifications.
|
|
2701
|
+
*
|
|
2702
|
+
* @api private
|
|
2703
|
+
*/
|
|
2704
|
+
primus.onlineHandler = function online() {
|
|
2705
|
+
if (primus.online)
|
|
2706
|
+
return; // Already or still online, bailout.
|
|
2707
|
+
primus.online = true;
|
|
2708
|
+
primus.emit('online');
|
|
2709
|
+
if (~primus.options.strategy.indexOf('online')) {
|
|
2710
|
+
primus.recovery.reconnect();
|
|
2711
|
+
}
|
|
2712
|
+
};
|
|
2713
|
+
if (window.addEventListener) {
|
|
2714
|
+
window.addEventListener('offline', primus.offlineHandler, false);
|
|
2715
|
+
window.addEventListener('online', primus.onlineHandler, false);
|
|
2716
|
+
}
|
|
2717
|
+
else if (document.body.attachEvent) {
|
|
2718
|
+
document.body.attachEvent('onoffline', primus.offlineHandler);
|
|
2719
|
+
document.body.attachEvent('ononline', primus.onlineHandler);
|
|
2720
|
+
}
|
|
2721
|
+
return primus;
|
|
2722
|
+
};
|
|
2723
|
+
/**
|
|
2724
|
+
* Really dead simple protocol parser. We simply assume that every message that
|
|
2725
|
+
* is prefixed with `primus::` could be used as some sort of protocol definition
|
|
2726
|
+
* for Primus.
|
|
2727
|
+
*
|
|
2728
|
+
* @param {String} msg The data.
|
|
2729
|
+
* @returns {Boolean} Is a protocol message.
|
|
2730
|
+
* @api private
|
|
2731
|
+
*/
|
|
2732
|
+
Primus.prototype.protocol = function protocol(msg) {
|
|
2733
|
+
if ('string' !== typeof msg
|
|
2734
|
+
|| msg.indexOf('primus::') !== 0)
|
|
2735
|
+
return false;
|
|
2736
|
+
var last = msg.indexOf(':', 8), value = msg.slice(last + 2);
|
|
2737
|
+
switch (msg.slice(8, last)) {
|
|
2738
|
+
case 'ping':
|
|
2739
|
+
this.emit('incoming::ping', +value);
|
|
2740
|
+
break;
|
|
2741
|
+
case 'server':
|
|
2742
|
+
//
|
|
2743
|
+
// The server is closing the connection, forcefully disconnect so we don't
|
|
2744
|
+
// reconnect again.
|
|
2745
|
+
//
|
|
2746
|
+
if ('close' === value) {
|
|
2747
|
+
this.disconnect = true;
|
|
2748
|
+
}
|
|
2749
|
+
break;
|
|
2750
|
+
case 'id':
|
|
2751
|
+
this.emit('incoming::id', value);
|
|
2752
|
+
break;
|
|
2753
|
+
//
|
|
2754
|
+
// Unknown protocol, somebody is probably sending `primus::` prefixed
|
|
2755
|
+
// messages.
|
|
2756
|
+
//
|
|
2757
|
+
default:
|
|
2758
|
+
return false;
|
|
2759
|
+
}
|
|
2760
|
+
return true;
|
|
2761
|
+
};
|
|
2762
|
+
/**
|
|
2763
|
+
* Execute the set of message transformers from Primus on the incoming or
|
|
2764
|
+
* outgoing message.
|
|
2765
|
+
* This function and it's content should be in sync with Spark#transforms in
|
|
2766
|
+
* spark.js.
|
|
2767
|
+
*
|
|
2768
|
+
* @param {Primus} primus Reference to the Primus instance with message transformers.
|
|
2769
|
+
* @param {Spark|Primus} connection Connection that receives or sends data.
|
|
2770
|
+
* @param {String} type The type of message, 'incoming' or 'outgoing'.
|
|
2771
|
+
* @param {Mixed} data The data to send or that has been received.
|
|
2772
|
+
* @param {String} raw The raw encoded data.
|
|
2773
|
+
* @returns {Primus}
|
|
2774
|
+
* @api public
|
|
2775
|
+
*/
|
|
2776
|
+
Primus.prototype.transforms = function transforms(primus, connection, type, data, raw) {
|
|
2777
|
+
var packet = { data: data }, fns = primus.transformers[type];
|
|
2778
|
+
//
|
|
2779
|
+
// Iterate in series over the message transformers so we can allow optional
|
|
2780
|
+
// asynchronous execution of message transformers which could for example
|
|
2781
|
+
// retrieve additional data from the server, do extra decoding or even
|
|
2782
|
+
// message validation.
|
|
2783
|
+
//
|
|
2784
|
+
(function transform(index, done) {
|
|
2785
|
+
var transformer = fns[index++];
|
|
2786
|
+
if (!transformer)
|
|
2787
|
+
return done();
|
|
2788
|
+
if (1 === transformer.length) {
|
|
2789
|
+
if (false === transformer.call(connection, packet)) {
|
|
2790
|
+
//
|
|
2791
|
+
// When false is returned by an incoming transformer it means that's
|
|
2792
|
+
// being handled by the transformer and we should not emit the `data`
|
|
2793
|
+
// event.
|
|
2794
|
+
//
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
return transform(index, done);
|
|
2798
|
+
}
|
|
2799
|
+
transformer.call(connection, packet, function finished(err, arg) {
|
|
2800
|
+
if (err)
|
|
2801
|
+
return connection.emit('error', err);
|
|
2802
|
+
if (false === arg)
|
|
2803
|
+
return;
|
|
2804
|
+
transform(index, done);
|
|
2805
|
+
});
|
|
2806
|
+
}(0, function done() {
|
|
2807
|
+
//
|
|
2808
|
+
// We always emit 2 arguments for the data event, the first argument is the
|
|
2809
|
+
// parsed data and the second argument is the raw string that we received.
|
|
2810
|
+
// This allows you, for example, to do some validation on the parsed data
|
|
2811
|
+
// and then save the raw string in your database without the stringify
|
|
2812
|
+
// overhead.
|
|
2813
|
+
//
|
|
2814
|
+
if ('incoming' === type)
|
|
2815
|
+
return connection.emit('data', packet.data, raw);
|
|
2816
|
+
connection._write(packet.data);
|
|
2817
|
+
}));
|
|
2818
|
+
return this;
|
|
2819
|
+
};
|
|
2820
|
+
/**
|
|
2821
|
+
* Retrieve the current id from the server.
|
|
2822
|
+
*
|
|
2823
|
+
* @param {Function} fn Callback function.
|
|
2824
|
+
* @returns {Primus}
|
|
2825
|
+
* @api public
|
|
2826
|
+
*/
|
|
2827
|
+
Primus.prototype.id = function id(fn) {
|
|
2828
|
+
if (this.socket && this.socket.id)
|
|
2829
|
+
return fn(this.socket.id);
|
|
2830
|
+
this._write('primus::id::');
|
|
2831
|
+
return this.once('incoming::id', fn);
|
|
2832
|
+
};
|
|
2833
|
+
/**
|
|
2834
|
+
* Establish a connection with the server. When this function is called we
|
|
2835
|
+
* assume that we don't have any open connections. If you do call it when you
|
|
2836
|
+
* have a connection open, it could cause duplicate connections.
|
|
2837
|
+
*
|
|
2838
|
+
* @returns {Primus}
|
|
2839
|
+
* @api public
|
|
2840
|
+
*/
|
|
2841
|
+
Primus.prototype.open = function open() {
|
|
2842
|
+
context(this, 'open');
|
|
2843
|
+
//
|
|
2844
|
+
// Only start a `connection timeout` procedure if we're not reconnecting as
|
|
2845
|
+
// that shouldn't count as an initial connection. This should be started
|
|
2846
|
+
// before the connection is opened to capture failing connections and kill the
|
|
2847
|
+
// timeout.
|
|
2848
|
+
//
|
|
2849
|
+
if (!this.recovery.reconnecting() && this.options.timeout)
|
|
2850
|
+
this.timeout();
|
|
2851
|
+
this.emit('outgoing::open');
|
|
2852
|
+
return this;
|
|
2853
|
+
};
|
|
2854
|
+
/**
|
|
2855
|
+
* Send a new message.
|
|
2856
|
+
*
|
|
2857
|
+
* @param {Mixed} data The data that needs to be written.
|
|
2858
|
+
* @returns {Boolean} Always returns true as we don't support back pressure.
|
|
2859
|
+
* @api public
|
|
2860
|
+
*/
|
|
2861
|
+
Primus.prototype.write = function write(data) {
|
|
2862
|
+
context(this, 'write');
|
|
2863
|
+
this.transforms(this, this, 'outgoing', data);
|
|
2864
|
+
return true;
|
|
2865
|
+
};
|
|
2866
|
+
/**
|
|
2867
|
+
* The actual message writer.
|
|
2868
|
+
*
|
|
2869
|
+
* @param {Mixed} data The message that needs to be written.
|
|
2870
|
+
* @returns {Boolean} Successful write to the underlaying transport.
|
|
2871
|
+
* @api private
|
|
2872
|
+
*/
|
|
2873
|
+
Primus.prototype._write = function write(data) {
|
|
2874
|
+
var primus = this;
|
|
2875
|
+
//
|
|
2876
|
+
// The connection is closed, normally this would already be done in the
|
|
2877
|
+
// `spark.write` method, but as `_write` is used internally, we should also
|
|
2878
|
+
// add the same check here to prevent potential crashes by writing to a dead
|
|
2879
|
+
// socket.
|
|
2880
|
+
//
|
|
2881
|
+
if (Primus.OPEN !== primus.readyState) {
|
|
2882
|
+
//
|
|
2883
|
+
// If the buffer is at capacity, remove the first item.
|
|
2884
|
+
//
|
|
2885
|
+
if (this.buffer.length === this.options.queueSize) {
|
|
2886
|
+
this.buffer.splice(0, 1);
|
|
2887
|
+
}
|
|
2888
|
+
this.buffer.push(data);
|
|
2889
|
+
return false;
|
|
2890
|
+
}
|
|
2891
|
+
primus.encoder(data, function encoded(err, packet) {
|
|
2892
|
+
//
|
|
2893
|
+
// Do a "safe" emit('error') when we fail to parse a message. We don't
|
|
2894
|
+
// want to throw here as listening to errors should be optional.
|
|
2895
|
+
//
|
|
2896
|
+
if (err)
|
|
2897
|
+
return primus.listeners('error').length && primus.emit('error', err);
|
|
2898
|
+
//
|
|
2899
|
+
// Hack 1: \u2028 and \u2029 are allowed inside a JSON string, but JavaScript
|
|
2900
|
+
// defines them as newline separators. Unescaped control characters are not
|
|
2901
|
+
// allowed inside JSON strings, so this causes an error at parse time. We
|
|
2902
|
+
// work around this issue by escaping these characters. This can cause
|
|
2903
|
+
// errors with JSONP requests or if the string is just evaluated.
|
|
2904
|
+
//
|
|
2905
|
+
if ('string' === typeof packet) {
|
|
2906
|
+
if (~packet.indexOf('\u2028'))
|
|
2907
|
+
packet = packet.replace(u2028, '\\u2028');
|
|
2908
|
+
if (~packet.indexOf('\u2029'))
|
|
2909
|
+
packet = packet.replace(u2029, '\\u2029');
|
|
2910
|
+
}
|
|
2911
|
+
primus.emit('outgoing::data', packet);
|
|
2912
|
+
});
|
|
2913
|
+
return true;
|
|
2914
|
+
};
|
|
2915
|
+
/**
|
|
2916
|
+
* Set a timer that, upon expiration, closes the client.
|
|
2917
|
+
*
|
|
2918
|
+
* @returns {Primus}
|
|
2919
|
+
* @api private
|
|
2920
|
+
*/
|
|
2921
|
+
Primus.prototype.heartbeat = function heartbeat() {
|
|
2922
|
+
if (!this.options.pingTimeout)
|
|
2923
|
+
return this;
|
|
2924
|
+
this.timers.clear('heartbeat');
|
|
2925
|
+
this.timers.setTimeout('heartbeat', function expired() {
|
|
2926
|
+
//
|
|
2927
|
+
// The network events already captured the offline event.
|
|
2928
|
+
//
|
|
2929
|
+
if (!this.online)
|
|
2930
|
+
return;
|
|
2931
|
+
this.online = false;
|
|
2932
|
+
this.emit('offline');
|
|
2933
|
+
this.emit('incoming::end');
|
|
2934
|
+
}, this.options.pingTimeout);
|
|
2935
|
+
return this;
|
|
2936
|
+
};
|
|
2937
|
+
/**
|
|
2938
|
+
* Start a connection timeout.
|
|
2939
|
+
*
|
|
2940
|
+
* @returns {Primus}
|
|
2941
|
+
* @api private
|
|
2942
|
+
*/
|
|
2943
|
+
Primus.prototype.timeout = function timeout() {
|
|
2944
|
+
var primus = this;
|
|
2945
|
+
/**
|
|
2946
|
+
* Remove all references to the timeout listener as we've received an event
|
|
2947
|
+
* that can be used to determine state.
|
|
2948
|
+
*
|
|
2949
|
+
* @api private
|
|
2950
|
+
*/
|
|
2951
|
+
function remove() {
|
|
2952
|
+
primus.removeListener('error', remove)
|
|
2953
|
+
.removeListener('open', remove)
|
|
2954
|
+
.removeListener('end', remove)
|
|
2955
|
+
.timers.clear('connect');
|
|
2956
|
+
}
|
|
2957
|
+
primus.timers.setTimeout('connect', function expired() {
|
|
2958
|
+
remove(); // Clean up old references.
|
|
2959
|
+
if (primus.readyState === Primus.OPEN || primus.recovery.reconnecting()) {
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
primus.emit('timeout');
|
|
2963
|
+
//
|
|
2964
|
+
// We failed to connect to the server.
|
|
2965
|
+
//
|
|
2966
|
+
if (~primus.options.strategy.indexOf('timeout')) {
|
|
2967
|
+
primus.recovery.reconnect();
|
|
2968
|
+
}
|
|
2969
|
+
else {
|
|
2970
|
+
primus.end();
|
|
2971
|
+
}
|
|
2972
|
+
}, primus.options.timeout);
|
|
2973
|
+
return primus.on('error', remove)
|
|
2974
|
+
.on('open', remove)
|
|
2975
|
+
.on('end', remove);
|
|
2976
|
+
};
|
|
2977
|
+
/**
|
|
2978
|
+
* Close the connection completely.
|
|
2979
|
+
*
|
|
2980
|
+
* @param {Mixed} data last packet of data.
|
|
2981
|
+
* @returns {Primus}
|
|
2982
|
+
* @api public
|
|
2983
|
+
*/
|
|
2984
|
+
Primus.prototype.end = function end(data) {
|
|
2985
|
+
context(this, 'end');
|
|
2986
|
+
if (this.readyState === Primus.CLOSED
|
|
2987
|
+
&& !this.timers.active('connect')
|
|
2988
|
+
&& !this.timers.active('open')) {
|
|
2989
|
+
//
|
|
2990
|
+
// If we are reconnecting stop the reconnection procedure.
|
|
2991
|
+
//
|
|
2992
|
+
if (this.recovery.reconnecting()) {
|
|
2993
|
+
this.recovery.reset();
|
|
2994
|
+
this.emit('end');
|
|
2995
|
+
}
|
|
2996
|
+
return this;
|
|
2997
|
+
}
|
|
2998
|
+
if (data !== undefined)
|
|
2999
|
+
this.write(data);
|
|
3000
|
+
this.writable = false;
|
|
3001
|
+
this.readable = false;
|
|
3002
|
+
var readyState = this.readyState;
|
|
3003
|
+
this.readyState = Primus.CLOSED;
|
|
3004
|
+
if (readyState !== this.readyState) {
|
|
3005
|
+
this.emit('readyStateChange', 'end');
|
|
3006
|
+
}
|
|
3007
|
+
this.timers.clear();
|
|
3008
|
+
this.emit('outgoing::end');
|
|
3009
|
+
this.emit('close');
|
|
3010
|
+
this.emit('end');
|
|
3011
|
+
return this;
|
|
3012
|
+
};
|
|
3013
|
+
/**
|
|
3014
|
+
* Completely demolish the Primus instance and forcefully nuke all references.
|
|
3015
|
+
*
|
|
3016
|
+
* @returns {Boolean}
|
|
3017
|
+
* @api public
|
|
3018
|
+
*/
|
|
3019
|
+
Primus.prototype.destroy = destroy('url timers options recovery socket transport transformers', {
|
|
3020
|
+
before: 'end',
|
|
3021
|
+
after: ['removeAllListeners', function detach() {
|
|
3022
|
+
if (!this.NETWORK_EVENTS)
|
|
3023
|
+
return;
|
|
3024
|
+
if (window.addEventListener) {
|
|
3025
|
+
window.removeEventListener('offline', this.offlineHandler);
|
|
3026
|
+
window.removeEventListener('online', this.onlineHandler);
|
|
3027
|
+
}
|
|
3028
|
+
else if (document.body.attachEvent) {
|
|
3029
|
+
document.body.detachEvent('onoffline', this.offlineHandler);
|
|
3030
|
+
document.body.detachEvent('ononline', this.onlineHandler);
|
|
3031
|
+
}
|
|
3032
|
+
}]
|
|
3033
|
+
});
|
|
3034
|
+
/**
|
|
3035
|
+
* Create a shallow clone of a given object.
|
|
3036
|
+
*
|
|
3037
|
+
* @param {Object} obj The object that needs to be cloned.
|
|
3038
|
+
* @returns {Object} Copy.
|
|
3039
|
+
* @api private
|
|
3040
|
+
*/
|
|
3041
|
+
Primus.prototype.clone = function clone(obj) {
|
|
3042
|
+
return this.merge({}, obj);
|
|
3043
|
+
};
|
|
3044
|
+
/**
|
|
3045
|
+
* Merge different objects in to one target object.
|
|
3046
|
+
*
|
|
3047
|
+
* @param {Object} target The object where everything should be merged in.
|
|
3048
|
+
* @returns {Object} Original target with all merged objects.
|
|
3049
|
+
* @api private
|
|
3050
|
+
*/
|
|
3051
|
+
Primus.prototype.merge = function merge(target) {
|
|
3052
|
+
for (var i = 1, key, obj; i < arguments.length; i++) {
|
|
3053
|
+
obj = arguments[i];
|
|
3054
|
+
for (key in obj) {
|
|
3055
|
+
if (Object.prototype.hasOwnProperty.call(obj, key))
|
|
3056
|
+
target[key] = obj[key];
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
return target;
|
|
3060
|
+
};
|
|
3061
|
+
/**
|
|
3062
|
+
* Parse the connection string.
|
|
3063
|
+
*
|
|
3064
|
+
* @type {Function}
|
|
3065
|
+
* @param {String} url Connection URL.
|
|
3066
|
+
* @returns {Object} Parsed connection.
|
|
3067
|
+
* @api private
|
|
3068
|
+
*/
|
|
3069
|
+
Primus.prototype.parse = _dereq_('url-parse');
|
|
3070
|
+
/**
|
|
3071
|
+
* Parse a query string.
|
|
3072
|
+
*
|
|
3073
|
+
* @param {String} query The query string that needs to be parsed.
|
|
3074
|
+
* @returns {Object} Parsed query string.
|
|
3075
|
+
* @api private
|
|
3076
|
+
*/
|
|
3077
|
+
Primus.prototype.querystring = qs.parse;
|
|
3078
|
+
/**
|
|
3079
|
+
* Transform a query string object back into string equiv.
|
|
3080
|
+
*
|
|
3081
|
+
* @param {Object} obj The query string object.
|
|
3082
|
+
* @returns {String}
|
|
3083
|
+
* @api private
|
|
3084
|
+
*/
|
|
3085
|
+
Primus.prototype.querystringify = qs.stringify;
|
|
3086
|
+
/**
|
|
3087
|
+
* Generates a connection URI.
|
|
3088
|
+
*
|
|
3089
|
+
* @param {String} protocol The protocol that should used to crate the URI.
|
|
3090
|
+
* @returns {String|options} The URL.
|
|
3091
|
+
* @api private
|
|
3092
|
+
*/
|
|
3093
|
+
Primus.prototype.uri = function uri(options) {
|
|
3094
|
+
var url = this.url, server = [], qsa = false;
|
|
3095
|
+
//
|
|
3096
|
+
// Query strings are only allowed when we've received clearance for it.
|
|
3097
|
+
//
|
|
3098
|
+
if (options.query)
|
|
3099
|
+
qsa = true;
|
|
3100
|
+
options = options || {};
|
|
3101
|
+
options.protocol = 'protocol' in options
|
|
3102
|
+
? options.protocol
|
|
3103
|
+
: 'http:';
|
|
3104
|
+
options.query = url.query && qsa
|
|
3105
|
+
? url.query.slice(1)
|
|
3106
|
+
: false;
|
|
3107
|
+
options.secure = 'secure' in options
|
|
3108
|
+
? options.secure
|
|
3109
|
+
: url.protocol === 'https:' || url.protocol === 'wss:';
|
|
3110
|
+
options.auth = 'auth' in options
|
|
3111
|
+
? options.auth
|
|
3112
|
+
: url.auth;
|
|
3113
|
+
options.pathname = 'pathname' in options
|
|
3114
|
+
? options.pathname
|
|
3115
|
+
: this.pathname;
|
|
3116
|
+
options.port = 'port' in options
|
|
3117
|
+
? +options.port
|
|
3118
|
+
: +url.port || (options.secure ? 443 : 80);
|
|
3119
|
+
//
|
|
3120
|
+
// We need to make sure that we create a unique connection URL every time to
|
|
3121
|
+
// prevent back forward cache from becoming an issue. We're doing this by
|
|
3122
|
+
// forcing an cache busting query string in to the URL.
|
|
3123
|
+
//
|
|
3124
|
+
var querystring = this.querystring(options.query || '');
|
|
3125
|
+
querystring._primuscb = yeast();
|
|
3126
|
+
options.query = this.querystringify(querystring);
|
|
3127
|
+
//
|
|
3128
|
+
// Allow transformation of the options before we construct a full URL from it.
|
|
3129
|
+
//
|
|
3130
|
+
this.emit('outgoing::url', options);
|
|
3131
|
+
//
|
|
3132
|
+
// Automatically suffix the protocol so we can supply `ws:` and `http:` and
|
|
3133
|
+
// it gets transformed correctly.
|
|
3134
|
+
//
|
|
3135
|
+
server.push(options.secure ? options.protocol.replace(':', 's:') : options.protocol, '');
|
|
3136
|
+
server.push(options.auth ? options.auth + '@' + url.host : url.host);
|
|
3137
|
+
//
|
|
3138
|
+
// Pathnames are optional as some Transformers would just use the pathname
|
|
3139
|
+
// directly.
|
|
3140
|
+
//
|
|
3141
|
+
if (options.pathname)
|
|
3142
|
+
server.push(options.pathname.slice(1));
|
|
3143
|
+
//
|
|
3144
|
+
// Optionally add a search query.
|
|
3145
|
+
//
|
|
3146
|
+
if (qsa)
|
|
3147
|
+
server[server.length - 1] += '?' + options.query;
|
|
3148
|
+
else
|
|
3149
|
+
delete options.query;
|
|
3150
|
+
if (options.object)
|
|
3151
|
+
return options;
|
|
3152
|
+
return server.join('/');
|
|
3153
|
+
};
|
|
3154
|
+
/**
|
|
3155
|
+
* Register a new message transformer. This allows you to easily manipulate incoming
|
|
3156
|
+
* and outgoing data which is particularity handy for plugins that want to send
|
|
3157
|
+
* meta data together with the messages.
|
|
3158
|
+
*
|
|
3159
|
+
* @param {String} type Incoming or outgoing
|
|
3160
|
+
* @param {Function} fn A new message transformer.
|
|
3161
|
+
* @returns {Primus}
|
|
3162
|
+
* @api public
|
|
3163
|
+
*/
|
|
3164
|
+
Primus.prototype.transform = function transform(type, fn) {
|
|
3165
|
+
context(this, 'transform');
|
|
3166
|
+
if (!(type in this.transformers)) {
|
|
3167
|
+
return this.critical(new Error('Invalid transformer type'));
|
|
3168
|
+
}
|
|
3169
|
+
this.transformers[type].push(fn);
|
|
3170
|
+
return this;
|
|
3171
|
+
};
|
|
3172
|
+
/**
|
|
3173
|
+
* A critical error has occurred, if we have an `error` listener, emit it there.
|
|
3174
|
+
* If not, throw it, so we get a stack trace + proper error message.
|
|
3175
|
+
*
|
|
3176
|
+
* @param {Error} err The critical error.
|
|
3177
|
+
* @returns {Primus}
|
|
3178
|
+
* @api private
|
|
3179
|
+
*/
|
|
3180
|
+
Primus.prototype.critical = function critical(err) {
|
|
3181
|
+
if (this.emit('error', err))
|
|
3182
|
+
return this;
|
|
3183
|
+
throw err;
|
|
3184
|
+
};
|
|
3185
|
+
/**
|
|
3186
|
+
* Syntax sugar, adopt a Socket.IO like API.
|
|
3187
|
+
*
|
|
3188
|
+
* @param {String} url The URL we want to connect to.
|
|
3189
|
+
* @param {Object} options Connection options.
|
|
3190
|
+
* @returns {Primus}
|
|
3191
|
+
* @api public
|
|
3192
|
+
*/
|
|
3193
|
+
Primus.connect = function connect(url, options) {
|
|
3194
|
+
return new Primus(url, options);
|
|
3195
|
+
};
|
|
3196
|
+
//
|
|
3197
|
+
// Expose the EventEmitter so it can be re-used by wrapping libraries we're also
|
|
3198
|
+
// exposing the Stream interface.
|
|
3199
|
+
//
|
|
3200
|
+
Primus.EventEmitter = EventEmitter;
|
|
3201
|
+
//
|
|
3202
|
+
// These libraries are automatically inserted at the server-side using the
|
|
3203
|
+
// Primus#library method.
|
|
3204
|
+
//
|
|
3205
|
+
Primus.prototype.client = function client() {
|
|
3206
|
+
var primus = this, socket;
|
|
3207
|
+
//
|
|
3208
|
+
// Select an available WebSocket factory.
|
|
3209
|
+
//
|
|
3210
|
+
var Factory = (function factory() {
|
|
3211
|
+
if ('undefined' !== typeof WebSocket)
|
|
3212
|
+
return WebSocket;
|
|
3213
|
+
if ('undefined' !== typeof MozWebSocket)
|
|
3214
|
+
return MozWebSocket;
|
|
3215
|
+
try {
|
|
3216
|
+
return Primus.requires('ws');
|
|
3217
|
+
}
|
|
3218
|
+
catch (e) { }
|
|
3219
|
+
return undefined;
|
|
3220
|
+
})();
|
|
3221
|
+
if (!Factory)
|
|
3222
|
+
return primus.critical(new Error('Missing required `ws` module. Please run `npm install --save ws`'));
|
|
3223
|
+
//
|
|
3224
|
+
// Connect to the given URL.
|
|
3225
|
+
//
|
|
3226
|
+
primus.on('outgoing::open', function opening() {
|
|
3227
|
+
primus.emit('outgoing::end');
|
|
3228
|
+
//
|
|
3229
|
+
// FireFox will throw an error when we try to establish a connection from
|
|
3230
|
+
// a secure page to an unsecured WebSocket connection. This is inconsistent
|
|
3231
|
+
// behaviour between different browsers. This should ideally be solved in
|
|
3232
|
+
// Primus when we connect.
|
|
3233
|
+
//
|
|
3234
|
+
try {
|
|
3235
|
+
var options = {
|
|
3236
|
+
protocol: primus.url.protocol === 'ws+unix:' ? 'ws+unix:' : 'ws:',
|
|
3237
|
+
query: true
|
|
3238
|
+
};
|
|
3239
|
+
//
|
|
3240
|
+
// Only allow primus.transport object in Node.js, it will throw in
|
|
3241
|
+
// browsers with a TypeError if we supply to much arguments.
|
|
3242
|
+
//
|
|
3243
|
+
if (Factory.length === 3) {
|
|
3244
|
+
if ('ws+unix:' === options.protocol) {
|
|
3245
|
+
options.pathname =
|
|
3246
|
+
'/' +
|
|
3247
|
+
primus.url.hostname +
|
|
3248
|
+
primus.url.pathname +
|
|
3249
|
+
':' +
|
|
3250
|
+
primus.pathname;
|
|
3251
|
+
}
|
|
3252
|
+
primus.socket = socket = new Factory(primus.uri(options), // URL
|
|
3253
|
+
[], // Sub protocols
|
|
3254
|
+
primus.transport // options.
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
else {
|
|
3258
|
+
primus.socket = socket = new Factory(primus.uri(options));
|
|
3259
|
+
socket.binaryType = 'arraybuffer';
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
catch (e) {
|
|
3263
|
+
return primus.emit('error', e);
|
|
3264
|
+
}
|
|
3265
|
+
//
|
|
3266
|
+
// Setup the Event handlers.
|
|
3267
|
+
//
|
|
3268
|
+
socket.onopen = primus.emits('incoming::open');
|
|
3269
|
+
socket.onerror = primus.emits('incoming::error');
|
|
3270
|
+
socket.onclose = primus.emits('incoming::end');
|
|
3271
|
+
socket.onmessage = primus.emits('incoming::data', function parse(next, evt) {
|
|
3272
|
+
next(undefined, evt.data);
|
|
3273
|
+
});
|
|
3274
|
+
});
|
|
3275
|
+
//
|
|
3276
|
+
// We need to write a new message to the socket.
|
|
3277
|
+
//
|
|
3278
|
+
primus.on('outgoing::data', function write(message) {
|
|
3279
|
+
if (!socket || socket.readyState !== Factory.OPEN)
|
|
3280
|
+
return;
|
|
3281
|
+
try {
|
|
3282
|
+
socket.send(message);
|
|
3283
|
+
}
|
|
3284
|
+
catch (e) {
|
|
3285
|
+
primus.emit('incoming::error', e);
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
//
|
|
3289
|
+
// Attempt to reconnect the socket.
|
|
3290
|
+
//
|
|
3291
|
+
primus.on('outgoing::reconnect', function reconnect() {
|
|
3292
|
+
primus.emit('outgoing::open');
|
|
3293
|
+
});
|
|
3294
|
+
//
|
|
3295
|
+
// We need to close the socket.
|
|
3296
|
+
//
|
|
3297
|
+
primus.on('outgoing::end', function close() {
|
|
3298
|
+
if (!socket)
|
|
3299
|
+
return;
|
|
3300
|
+
socket.onerror = socket.onopen = socket.onclose = socket.onmessage = function () { };
|
|
3301
|
+
socket.close();
|
|
3302
|
+
socket = null;
|
|
3303
|
+
});
|
|
3304
|
+
};
|
|
3305
|
+
Primus.prototype.authorization = false;
|
|
3306
|
+
Primus.prototype.pathname = "/primus";
|
|
3307
|
+
Primus.prototype.encoder = function encoder(data, fn) {
|
|
3308
|
+
var err;
|
|
3309
|
+
try {
|
|
3310
|
+
data = JSON.stringify(data);
|
|
3311
|
+
}
|
|
3312
|
+
catch (e) {
|
|
3313
|
+
err = e;
|
|
3314
|
+
}
|
|
3315
|
+
fn(err, data);
|
|
3316
|
+
};
|
|
3317
|
+
Primus.prototype.decoder = function decoder(data, fn) {
|
|
3318
|
+
var err;
|
|
3319
|
+
if ('string' !== typeof data)
|
|
3320
|
+
return fn(err, data);
|
|
3321
|
+
try {
|
|
3322
|
+
data = JSON.parse(data);
|
|
3323
|
+
}
|
|
3324
|
+
catch (e) {
|
|
3325
|
+
err = e;
|
|
3326
|
+
}
|
|
3327
|
+
fn(err, data);
|
|
3328
|
+
};
|
|
3329
|
+
Primus.prototype.version = "8.0.7";
|
|
3330
|
+
if ('undefined' !== typeof document
|
|
3331
|
+
&& 'undefined' !== typeof navigator) {
|
|
3332
|
+
//
|
|
3333
|
+
// Hack 2: If you press ESC in FireFox it will close all active connections.
|
|
3334
|
+
// Normally this makes sense, when your page is still loading. But versions
|
|
3335
|
+
// before FireFox 22 will close all connections including WebSocket connections
|
|
3336
|
+
// after page load. One way to prevent this is to do a `preventDefault()` and
|
|
3337
|
+
// cancel the operation before it bubbles up to the browsers default handler.
|
|
3338
|
+
// It needs to be added as `keydown` event, if it's added keyup it will not be
|
|
3339
|
+
// able to prevent the connection from being closed.
|
|
3340
|
+
//
|
|
3341
|
+
if (document.addEventListener) {
|
|
3342
|
+
document.addEventListener('keydown', function keydown(e) {
|
|
3343
|
+
if (e.keyCode !== 27 || !e.preventDefault)
|
|
3344
|
+
return;
|
|
3345
|
+
e.preventDefault();
|
|
3346
|
+
}, false);
|
|
3347
|
+
}
|
|
3348
|
+
//
|
|
3349
|
+
// Hack 3: This is a Mac/Apple bug only, when you're behind a reverse proxy or
|
|
3350
|
+
// have you network settings set to `automatic proxy discovery` the safari
|
|
3351
|
+
// browser will crash when the WebSocket constructor is initialised. There is
|
|
3352
|
+
// no way to detect the usage of these proxies available in JavaScript so we
|
|
3353
|
+
// need to do some nasty browser sniffing. This only affects Safari versions
|
|
3354
|
+
// lower then 5.1.4
|
|
3355
|
+
//
|
|
3356
|
+
var ua = (navigator.userAgent || '').toLowerCase(), parsed = ua.match(/.+(?:rv|it|ra|ie)[/: ](\d+)\.(\d+)(?:\.(\d+))?/) || [], version = +[parsed[1], parsed[2]].join('.');
|
|
3357
|
+
if (!~ua.indexOf('chrome')
|
|
3358
|
+
&& ~ua.indexOf('safari')
|
|
3359
|
+
&& version < 534.54) {
|
|
3360
|
+
Primus.prototype.AVOID_WEBSOCKETS = true;
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
//
|
|
3364
|
+
// Expose the library.
|
|
3365
|
+
//
|
|
3366
|
+
module.exports = Primus;
|
|
3367
|
+
}, { "demolish": 1, "emits": 2, "eventemitter3": 3, "inherits": 4, "querystringify": 8, "recovery": 9, "tick-tock": 12, "url-parse": 14, "yeast": 15 }] }, {}, [16])(16);
|
|
3368
|
+
return Primus;
|
|
3369
|
+
}, []);
|
|
3370
|
+
;
|
|
3371
|
+
;
|
|
3372
|
+
;
|
|
3373
|
+
(function (exports) {
|
|
3374
|
+
var ActionheroWebsocketClient = function (options, client) {
|
|
3375
|
+
var self = this;
|
|
3376
|
+
self.callbacks = {};
|
|
3377
|
+
self.id = null;
|
|
3378
|
+
self.events = {};
|
|
3379
|
+
self.rooms = [];
|
|
3380
|
+
self.state = 'disconnected';
|
|
3381
|
+
self.options = self.defaults();
|
|
3382
|
+
for (var i in options) {
|
|
3383
|
+
self.options[i] = options[i];
|
|
3384
|
+
}
|
|
3385
|
+
if (client) {
|
|
3386
|
+
self.externalClient = true;
|
|
3387
|
+
self.client = client;
|
|
3388
|
+
}
|
|
3389
|
+
};
|
|
3390
|
+
if (typeof Primus === 'undefined') {
|
|
3391
|
+
var util = require('util');
|
|
3392
|
+
var EventEmitter = require('events').EventEmitter;
|
|
3393
|
+
util.inherits(ActionheroWebsocketClient, EventEmitter);
|
|
3394
|
+
}
|
|
3395
|
+
else {
|
|
3396
|
+
ActionheroWebsocketClient.prototype = new Primus.EventEmitter();
|
|
3397
|
+
}
|
|
3398
|
+
ActionheroWebsocketClient.prototype.defaults = function () {
|
|
3399
|
+
return { apiPath: '/api', cookieKey: 'token', url: window.location.origin };
|
|
3400
|
+
};
|
|
3401
|
+
// //////////////
|
|
3402
|
+
// CONNECTION //
|
|
3403
|
+
// //////////////
|
|
3404
|
+
ActionheroWebsocketClient.prototype.connect = function (callback) {
|
|
3405
|
+
var self = this;
|
|
3406
|
+
self.messageId = self.messageId || 0;
|
|
3407
|
+
if (self.client && self.externalClient !== true) {
|
|
3408
|
+
self.client.end();
|
|
3409
|
+
self.client.removeAllListeners();
|
|
3410
|
+
delete self.client;
|
|
3411
|
+
self.client = Primus.connect(self.urlWithSession(), self.options);
|
|
3412
|
+
}
|
|
3413
|
+
else if (self.client && self.externalClient === true) {
|
|
3414
|
+
self.client.end();
|
|
3415
|
+
self.client.open();
|
|
3416
|
+
}
|
|
3417
|
+
else {
|
|
3418
|
+
self.client = Primus.connect(self.urlWithSession(), self.options);
|
|
3419
|
+
}
|
|
3420
|
+
self.client.once('open', function () {
|
|
3421
|
+
self.configure(function (details) {
|
|
3422
|
+
self.state = 'connected';
|
|
3423
|
+
self.emit('connected');
|
|
3424
|
+
if (typeof callback === 'function') {
|
|
3425
|
+
callback(null, details);
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
});
|
|
3429
|
+
self.client.on('error', function (error) {
|
|
3430
|
+
self.emit('error', error);
|
|
3431
|
+
});
|
|
3432
|
+
self.client.on('reconnect', function () {
|
|
3433
|
+
self.state = 'connected';
|
|
3434
|
+
self.emit('reconnect');
|
|
3435
|
+
});
|
|
3436
|
+
self.client.on('reconnecting', function () {
|
|
3437
|
+
self.emit('reconnecting');
|
|
3438
|
+
self.state = 'reconnecting';
|
|
3439
|
+
self.emit('disconnected');
|
|
3440
|
+
});
|
|
3441
|
+
self.client.on('timeout', function () {
|
|
3442
|
+
self.state = 'timeout';
|
|
3443
|
+
self.emit('timeout');
|
|
3444
|
+
});
|
|
3445
|
+
self.client.on('close', function () {
|
|
3446
|
+
if (self.state !== 'disconnected') {
|
|
3447
|
+
self.state = 'disconnected';
|
|
3448
|
+
self.emit('disconnected');
|
|
3449
|
+
}
|
|
3450
|
+
});
|
|
3451
|
+
self.client.on('end', function () {
|
|
3452
|
+
if (self.state !== 'disconnected') {
|
|
3453
|
+
self.state = 'disconnected';
|
|
3454
|
+
self.emit('disconnected');
|
|
3455
|
+
}
|
|
3456
|
+
});
|
|
3457
|
+
self.client.on('data', function (data) {
|
|
3458
|
+
self.handleMessage(data);
|
|
3459
|
+
});
|
|
3460
|
+
};
|
|
3461
|
+
ActionheroWebsocketClient.prototype.urlWithSession = function () {
|
|
3462
|
+
var self = this;
|
|
3463
|
+
var url = self.options.url;
|
|
3464
|
+
if (self.options.cookieKey && self.options.cookieKey.length > 0) {
|
|
3465
|
+
var cookieValue = self.getCookie(self.options.cookieKey);
|
|
3466
|
+
if (cookieValue && cookieValue.length > 0) {
|
|
3467
|
+
url += '?' + self.options.cookieKey + '=' + cookieValue;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
return url;
|
|
3471
|
+
};
|
|
3472
|
+
ActionheroWebsocketClient.prototype.getCookie = function (name) {
|
|
3473
|
+
if (typeof document === 'undefined' || !document.cookie) {
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
var match = document.cookie.match(new RegExp(name + '=([^;]+)'));
|
|
3477
|
+
if (match)
|
|
3478
|
+
return match[1];
|
|
3479
|
+
};
|
|
3480
|
+
ActionheroWebsocketClient.prototype.configure = function (callback) {
|
|
3481
|
+
var self = this;
|
|
3482
|
+
self.rooms.forEach(function (room) {
|
|
3483
|
+
self.send({ event: 'roomAdd', room: room });
|
|
3484
|
+
});
|
|
3485
|
+
self.detailsView(function (details) {
|
|
3486
|
+
self.id = details.data.id;
|
|
3487
|
+
self.fingerprint = details.data.fingerprint;
|
|
3488
|
+
self.rooms = details.data.rooms;
|
|
3489
|
+
return callback(details);
|
|
3490
|
+
});
|
|
3491
|
+
};
|
|
3492
|
+
// /////////////
|
|
3493
|
+
// MESSAGING //
|
|
3494
|
+
// /////////////
|
|
3495
|
+
ActionheroWebsocketClient.prototype.send = function (args, callback) {
|
|
3496
|
+
// primus will buffer messages when not connected
|
|
3497
|
+
var self = this;
|
|
3498
|
+
self.messageId++;
|
|
3499
|
+
args.messageId = args.params
|
|
3500
|
+
? (args.params.messageId || args.messageId || self.messageId)
|
|
3501
|
+
: (args.messageId || self.messageId);
|
|
3502
|
+
if (typeof callback === 'function') {
|
|
3503
|
+
self.callbacks[args.messageId] = callback;
|
|
3504
|
+
}
|
|
3505
|
+
self.client.write(args);
|
|
3506
|
+
};
|
|
3507
|
+
ActionheroWebsocketClient.prototype.handleMessage = function (message) {
|
|
3508
|
+
var self = this;
|
|
3509
|
+
self.emit('message', message);
|
|
3510
|
+
var messageId = message.messageId;
|
|
3511
|
+
if (message.context === 'response') {
|
|
3512
|
+
if (typeof self.callbacks[messageId] === 'function') {
|
|
3513
|
+
self.callbacks[messageId](message);
|
|
3514
|
+
}
|
|
3515
|
+
delete self.callbacks[messageId];
|
|
3516
|
+
}
|
|
3517
|
+
else if (message.context === 'user') {
|
|
3518
|
+
self.emit('say', message);
|
|
3519
|
+
}
|
|
3520
|
+
else if (message.context === 'alert') {
|
|
3521
|
+
self.emit('alert', message);
|
|
3522
|
+
}
|
|
3523
|
+
else if (message.welcome && message.context === 'api') {
|
|
3524
|
+
self.welcomeMessage = message.welcome;
|
|
3525
|
+
self.emit('welcome', message);
|
|
3526
|
+
}
|
|
3527
|
+
else if (message.context === 'api') {
|
|
3528
|
+
self.emit('api', message);
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
// ///////////
|
|
3532
|
+
// ACTIONS //
|
|
3533
|
+
// ///////////
|
|
3534
|
+
ActionheroWebsocketClient.prototype.action = function (action, params, callback) {
|
|
3535
|
+
if (!callback && typeof params === 'function') {
|
|
3536
|
+
callback = params;
|
|
3537
|
+
params = null;
|
|
3538
|
+
}
|
|
3539
|
+
if (!params) {
|
|
3540
|
+
params = {};
|
|
3541
|
+
}
|
|
3542
|
+
params.action = action;
|
|
3543
|
+
if (this.state !== 'connected') {
|
|
3544
|
+
this.actionWeb(params, callback);
|
|
3545
|
+
}
|
|
3546
|
+
else {
|
|
3547
|
+
this.actionWebSocket(params, callback);
|
|
3548
|
+
}
|
|
3549
|
+
};
|
|
3550
|
+
ActionheroWebsocketClient.prototype.actionWeb = function (params, callback) {
|
|
3551
|
+
var xmlhttp = new XMLHttpRequest();
|
|
3552
|
+
xmlhttp.onreadystatechange = function () {
|
|
3553
|
+
var response;
|
|
3554
|
+
if (xmlhttp.readyState === 4) {
|
|
3555
|
+
if (xmlhttp.status === 200) {
|
|
3556
|
+
response = JSON.parse(xmlhttp.responseText);
|
|
3557
|
+
}
|
|
3558
|
+
else {
|
|
3559
|
+
try {
|
|
3560
|
+
response = JSON.parse(xmlhttp.responseText);
|
|
3561
|
+
}
|
|
3562
|
+
catch (e) {
|
|
3563
|
+
response = { error: { statusText: xmlhttp.statusText, responseText: xmlhttp.responseText } };
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
callback(response);
|
|
3567
|
+
}
|
|
3568
|
+
};
|
|
3569
|
+
var method = (params.httpMethod || 'POST').toUpperCase();
|
|
3570
|
+
var url = this.options.url + this.options.apiPath + '?action=' + params.action;
|
|
3571
|
+
if (method === 'GET') {
|
|
3572
|
+
for (var param in params) {
|
|
3573
|
+
if (~['action', 'httpMethod'].indexOf(param))
|
|
3574
|
+
continue;
|
|
3575
|
+
url += '&' + param + '=' + params[param];
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
xmlhttp.open(method, url, true);
|
|
3579
|
+
xmlhttp.setRequestHeader('Content-Type', 'application/json');
|
|
3580
|
+
xmlhttp.send(JSON.stringify(params));
|
|
3581
|
+
};
|
|
3582
|
+
ActionheroWebsocketClient.prototype.actionWebSocket = function (params, callback) {
|
|
3583
|
+
this.send({ event: 'action', params: params }, callback);
|
|
3584
|
+
};
|
|
3585
|
+
// ////////////
|
|
3586
|
+
// COMMANDS //
|
|
3587
|
+
// ////////////
|
|
3588
|
+
ActionheroWebsocketClient.prototype.say = function (room, message, callback) {
|
|
3589
|
+
this.send({ event: 'say', room: room, message: message }, callback);
|
|
3590
|
+
};
|
|
3591
|
+
ActionheroWebsocketClient.prototype.file = function (file, callback) {
|
|
3592
|
+
this.send({ event: 'file', file: file }, callback);
|
|
3593
|
+
};
|
|
3594
|
+
ActionheroWebsocketClient.prototype.detailsView = function (callback) {
|
|
3595
|
+
this.send({ event: 'detailsView' }, callback);
|
|
3596
|
+
};
|
|
3597
|
+
ActionheroWebsocketClient.prototype.roomView = function (room, callback) {
|
|
3598
|
+
this.send({ event: 'roomView', room: room }, callback);
|
|
3599
|
+
};
|
|
3600
|
+
ActionheroWebsocketClient.prototype.roomAdd = function (room, callback) {
|
|
3601
|
+
var self = this;
|
|
3602
|
+
self.send({ event: 'roomAdd', room: room }, function (data) {
|
|
3603
|
+
self.configure(function () {
|
|
3604
|
+
if (typeof callback === 'function') {
|
|
3605
|
+
callback(data);
|
|
3606
|
+
}
|
|
3607
|
+
});
|
|
3608
|
+
});
|
|
3609
|
+
};
|
|
3610
|
+
ActionheroWebsocketClient.prototype.roomLeave = function (room, callback) {
|
|
3611
|
+
var self = this;
|
|
3612
|
+
var index = self.rooms.indexOf(room);
|
|
3613
|
+
if (index > -1) {
|
|
3614
|
+
self.rooms.splice(index, 1);
|
|
3615
|
+
}
|
|
3616
|
+
this.send({ event: 'roomLeave', room: room }, function (data) {
|
|
3617
|
+
self.configure(function () {
|
|
3618
|
+
if (typeof callback === 'function') {
|
|
3619
|
+
callback(data);
|
|
3620
|
+
}
|
|
3621
|
+
});
|
|
3622
|
+
});
|
|
3623
|
+
};
|
|
3624
|
+
ActionheroWebsocketClient.prototype.documentation = function (callback) {
|
|
3625
|
+
this.send({ event: 'documentation' }, callback);
|
|
3626
|
+
};
|
|
3627
|
+
ActionheroWebsocketClient.prototype.disconnect = function () {
|
|
3628
|
+
this.state = 'disconnected';
|
|
3629
|
+
this.client.end();
|
|
3630
|
+
this.emit('disconnected');
|
|
3631
|
+
};
|
|
3632
|
+
// depreciated lowercase name
|
|
3633
|
+
var ActionheroWebsocketClient = ActionheroWebsocketClient;
|
|
3634
|
+
ActionheroWebsocketClient;
|
|
3635
|
+
exports.ActionheroWebsocketClient = ActionheroWebsocketClient;
|
|
3636
|
+
exports.ActionheroWebsocketClient = ActionheroWebsocketClient;
|
|
3637
|
+
})(typeof exports === 'undefined' ? window : exports);
|
|
3638
|
+
//# sourceMappingURL=ActionheroWebsocketClient.js.map
|