1 /*! Socket.IO.js build:0.9.16, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
3 var io = ('undefined' === typeof module ? {} : module.exports);
8 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
12 (function (exports, global) {
28 io.version = '0.9.16';
31 * Protocol implemented.
39 * Available transports, these will be populated with the available transports
47 * Keep track of jsonp callbacks.
55 * Keep track of our io.Sockets
63 * Manages connections to hosts.
66 * @Param {Boolean} force creation of new socket (defaults to false)
70 io.connect = function (host, details) {
71 var uri = io.util.parseUri(host)
75 if (global && global.location) {
76 uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
77 uri.host = uri.host || (global.document
78 ? global.document.domain : global.location.hostname);
79 uri.port = uri.port || global.location.port;
82 uuri = io.util.uniqueUri(uri);
86 , secure: 'https' == uri.protocol
87 , port: uri.port || ('https' == uri.protocol ? 443 : 80)
88 , query: uri.query || ''
91 io.util.merge(options, details);
93 if (options['force new connection'] || !io.sockets[uuri]) {
94 socket = new io.Socket(options);
97 if (!options['force new connection'] && socket) {
98 io.sockets[uuri] = socket;
101 socket = socket || io.sockets[uuri];
103 // if path is different from '' or /
104 return socket.of(uri.path.length > 1 ? uri.path : '');
107 })('object' === typeof module ? module.exports : (this.io = {}), this);
110 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
114 (function (exports, global) {
117 * Utilities namespace.
122 var util = exports.util = {};
127 * @author Steven Levithan <stevenlevithan.com> (MIT license)
131 var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
133 var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
134 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
137 util.parseUri = function (str) {
138 var m = re.exec(str || '')
143 uri[parts[i]] = m[i] || '';
150 * Produces a unique url that identifies a Socket.IO connection.
152 * @param {Object} uri
156 util.uniqueUri = function (uri) {
157 var protocol = uri.protocol
161 if ('document' in global) {
162 host = host || document.domain;
163 port = port || (protocol == 'https'
164 && document.location.protocol !== 'https:' ? 443 : document.location.port);
166 host = host || 'localhost';
168 if (!port && protocol == 'https') {
173 return (protocol || 'http') + '://' + host + ':' + (port || 80);
177 * Mergest 2 query strings in to once unique query string
179 * @param {String} base
180 * @param {String} addition
184 util.query = function (base, addition) {
185 var query = util.chunkQuery(base || '')
188 util.merge(query, util.chunkQuery(addition || ''));
189 for (var part in query) {
190 if (query.hasOwnProperty(part)) {
191 components.push(part + '=' + query[part]);
195 return components.length ? '?' + components.join('&') : '';
199 * Transforms a querystring in to an object
205 util.chunkQuery = function (qs) {
207 , params = qs.split('&')
213 kv = params[i].split('=');
215 query[kv[0]] = kv[1];
223 * Executes the given function when the page is loaded.
225 * io.util.load(function () { console.log('page loaded'); });
227 * @param {Function} fn
231 var pageLoaded = false;
233 util.load = function (fn) {
234 if ('document' in global && document.readyState === 'complete' || pageLoaded) {
238 util.on(global, 'load', fn, false);
247 util.on = function (element, event, fn, capture) {
248 if (element.attachEvent) {
249 element.attachEvent('on' + event, fn);
250 } else if (element.addEventListener) {
251 element.addEventListener(event, fn, capture);
256 * Generates the correct `XMLHttpRequest` for regular and cross domain requests.
258 * @param {Boolean} [xdomain] Create a request that can be used cross domain.
259 * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest.
263 util.request = function (xdomain) {
265 if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) {
266 return new XDomainRequest();
269 if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
270 return new XMLHttpRequest();
275 return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP');
283 * XHR based transport constructor.
290 * Change the internal pageLoaded value.
293 if ('undefined' != typeof window) {
294 util.load(function () {
300 * Defers a function to ensure a spinner is not displayed by the browser
302 * @param {Function} fn
306 util.defer = function (fn) {
307 if (!util.ua.webkit || 'undefined' != typeof importScripts) {
311 util.load(function () {
317 * Merges two objects.
322 util.merge = function merge (target, additional, deep, lastseen) {
323 var seen = lastseen || []
324 , depth = typeof deep == 'undefined' ? 2 : deep
327 for (prop in additional) {
328 if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
329 if (typeof target[prop] !== 'object' || !depth) {
330 target[prop] = additional[prop];
331 seen.push(additional[prop]);
333 util.merge(target[prop], additional[prop], depth - 1, seen);
342 * Merges prototypes from objects
347 util.mixin = function (ctor, ctor2) {
348 util.merge(ctor.prototype, ctor2.prototype);
352 * Shortcut for prototypical and static inheritance.
357 util.inherit = function (ctor, ctor2) {
359 f.prototype = ctor2.prototype;
360 ctor.prototype = new f;
364 * Checks if the given object is an Array.
366 * io.util.isArray([]); // true
367 * io.util.isArray({}); // false
373 util.isArray = Array.isArray || function (obj) {
374 return Object.prototype.toString.call(obj) === '[object Array]';
378 * Intersects values of two arrays into a third
383 util.intersect = function (arr, arr2) {
385 , longest = arr.length > arr2.length ? arr : arr2
386 , shortest = arr.length > arr2.length ? arr2 : arr;
388 for (var i = 0, l = shortest.length; i < l; i++) {
389 if (~util.indexOf(longest, shortest[i]))
390 ret.push(shortest[i]);
397 * Array indexOf compatibility.
403 util.indexOf = function (arr, o, i) {
405 for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0;
406 i < j && arr[i] !== o; i++) {}
408 return j <= i ? -1 : i;
412 * Converts enumerables to array.
417 util.toArray = function (enu) {
420 for (var i = 0, l = enu.length; i < l; i++)
427 * UA / engines detection namespace.
435 * Whether the UA supports CORS for XHR.
440 util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
442 var a = new XMLHttpRequest();
447 return a.withCredentials != undefined;
456 util.ua.webkit = 'undefined' != typeof navigator
457 && /webkit/i.test(navigator.userAgent);
460 * Detect iPad/iPhone/iPod.
465 util.ua.iDevice = 'undefined' != typeof navigator
466 && /iPad|iPhone|iPod/i.test(navigator.userAgent);
468 })('undefined' != typeof io ? io : module.exports, this);
471 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
475 (function (exports, io) {
478 * Expose constructor.
481 exports.EventEmitter = EventEmitter;
484 * Event emitter constructor.
489 function EventEmitter () {};
497 EventEmitter.prototype.on = function (name, fn) {
502 if (!this.$events[name]) {
503 this.$events[name] = fn;
504 } else if (io.util.isArray(this.$events[name])) {
505 this.$events[name].push(fn);
507 this.$events[name] = [this.$events[name], fn];
513 EventEmitter.prototype.addListener = EventEmitter.prototype.on;
516 * Adds a volatile listener.
521 EventEmitter.prototype.once = function (name, fn) {
525 self.removeListener(name, on);
526 fn.apply(this, arguments);
536 * Removes a listener.
541 EventEmitter.prototype.removeListener = function (name, fn) {
542 if (this.$events && this.$events[name]) {
543 var list = this.$events[name];
545 if (io.util.isArray(list)) {
548 for (var i = 0, l = list.length; i < l; i++) {
549 if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
562 delete this.$events[name];
564 } else if (list === fn || (list.listener && list.listener === fn)) {
565 delete this.$events[name];
573 * Removes all listeners for an event.
578 EventEmitter.prototype.removeAllListeners = function (name) {
579 if (name === undefined) {
584 if (this.$events && this.$events[name]) {
585 this.$events[name] = null;
592 * Gets all listeners for a certain event.
597 EventEmitter.prototype.listeners = function (name) {
602 if (!this.$events[name]) {
603 this.$events[name] = [];
606 if (!io.util.isArray(this.$events[name])) {
607 this.$events[name] = [this.$events[name]];
610 return this.$events[name];
619 EventEmitter.prototype.emit = function (name) {
624 var handler = this.$events[name];
630 var args = Array.prototype.slice.call(arguments, 1);
632 if ('function' == typeof handler) {
633 handler.apply(this, args);
634 } else if (io.util.isArray(handler)) {
635 var listeners = handler.slice();
637 for (var i = 0, l = listeners.length; i < l; i++) {
638 listeners[i].apply(this, args);
648 'undefined' != typeof io ? io : module.exports
649 , 'undefined' != typeof io ? io : module.parent.exports
654 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
659 * Based on JSON2 (http://www.JSON.org/js.html).
662 (function (exports, nativeJSON) {
665 // use native JSON if it's available
666 if (nativeJSON && nativeJSON.parse){
667 return exports.JSON = {
668 parse: nativeJSON.parse
669 , stringify: nativeJSON.stringify
673 var JSON = exports.JSON = {};
676 // Format integers to have at least two digits.
677 return n < 10 ? '0' + n : n;
680 function date(d, key) {
681 return isFinite(d.valueOf()) ?
682 d.getUTCFullYear() + '-' +
683 f(d.getUTCMonth() + 1) + '-' +
684 f(d.getUTCDate()) + 'T' +
685 f(d.getUTCHours()) + ':' +
686 f(d.getUTCMinutes()) + ':' +
687 f(d.getUTCSeconds()) + 'Z' : null;
690 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
691 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
694 meta = { // table of character substitutions
706 function quote(string) {
708 // If the string contains no control characters, no quote characters, and no
709 // backslash characters, then we can safely slap some quotes around it.
710 // Otherwise we must also replace the offending characters with safe escape
713 escapable.lastIndex = 0;
714 return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
716 return typeof c === 'string' ? c :
717 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
718 }) + '"' : '"' + string + '"';
722 function str(key, holder) {
724 // Produce a string from holder[key].
726 var i, // The loop counter.
727 k, // The member key.
728 v, // The member value.
734 // If the value has a toJSON method, call it to obtain a replacement value.
736 if (value instanceof Date) {
740 // If we were called with a replacer function, then call the replacer to
741 // obtain a replacement value.
743 if (typeof rep === 'function') {
744 value = rep.call(holder, key, value);
747 // What happens next depends on the value's type.
749 switch (typeof value) {
755 // JSON numbers must be finite. Encode non-finite numbers as null.
757 return isFinite(value) ? String(value) : 'null';
762 // If the value is a boolean or null, convert it to a string. Note:
763 // typeof null does not produce 'null'. The case is included here in
764 // the remote chance that this gets fixed someday.
766 return String(value);
768 // If the type is 'object', we might be dealing with an object or an array or
773 // Due to a specification blunder in ECMAScript, typeof null is 'object',
774 // so watch out for that case.
780 // Make an array to hold the partial results of stringifying this object value.
785 // Is the value an array?
787 if (Object.prototype.toString.apply(value) === '[object Array]') {
789 // The value is an array. Stringify every element. Use null as a placeholder
790 // for non-JSON values.
792 length = value.length;
793 for (i = 0; i < length; i += 1) {
794 partial[i] = str(i, value) || 'null';
797 // Join all of the elements together, separated with commas, and wrap them in
800 v = partial.length === 0 ? '[]' : gap ?
801 '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
802 '[' + partial.join(',') + ']';
807 // If the replacer is an array, use it to select the members to be stringified.
809 if (rep && typeof rep === 'object') {
811 for (i = 0; i < length; i += 1) {
812 if (typeof rep[i] === 'string') {
816 partial.push(quote(k) + (gap ? ': ' : ':') + v);
822 // Otherwise, iterate through all of the keys in the object.
825 if (Object.prototype.hasOwnProperty.call(value, k)) {
828 partial.push(quote(k) + (gap ? ': ' : ':') + v);
834 // Join all of the member texts together, separated with commas,
835 // and wrap them in braces.
837 v = partial.length === 0 ? '{}' : gap ?
838 '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
839 '{' + partial.join(',') + '}';
845 // If the JSON object does not yet have a stringify method, give it one.
847 JSON.stringify = function (value, replacer, space) {
849 // The stringify method takes a value and an optional replacer, and an optional
850 // space parameter, and returns a JSON text. The replacer can be a function
851 // that can replace values, or an array of strings that will select the keys.
852 // A default replacer method can be provided. Use of the space parameter can
853 // produce text that is more easily readable.
859 // If the space parameter is a number, make an indent string containing that
862 if (typeof space === 'number') {
863 for (i = 0; i < space; i += 1) {
867 // If the space parameter is a string, it will be used as the indent string.
869 } else if (typeof space === 'string') {
873 // If there is a replacer, it must be a function or an array.
874 // Otherwise, throw an error.
877 if (replacer && typeof replacer !== 'function' &&
878 (typeof replacer !== 'object' ||
879 typeof replacer.length !== 'number')) {
880 throw new Error('JSON.stringify');
883 // Make a fake root object containing our value under the key of ''.
884 // Return the result of stringifying the value.
886 return str('', {'': value});
889 // If the JSON object does not yet have a parse method, give it one.
891 JSON.parse = function (text, reviver) {
892 // The parse method takes a text and an optional reviver function, and returns
893 // a JavaScript value if the text is a valid JSON text.
897 function walk(holder, key) {
899 // The walk method is used to recursively walk the resulting structure so
900 // that modifications can be made.
902 var k, v, value = holder[key];
903 if (value && typeof value === 'object') {
905 if (Object.prototype.hasOwnProperty.call(value, k)) {
907 if (v !== undefined) {
915 return reviver.call(holder, key, value);
919 // Parsing happens in four stages. In the first stage, we replace certain
920 // Unicode characters with escape sequences. JavaScript handles many characters
921 // incorrectly, either silently deleting them, or treating them as line endings.
926 text = text.replace(cx, function (a) {
928 ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
932 // In the second stage, we run the text against regular expressions that look
933 // for non-JSON patterns. We are especially concerned with '()' and 'new'
934 // because they can cause invocation, and '=' because it can cause mutation.
935 // But just to be safe, we want to reject all unexpected forms.
937 // We split the second stage into 4 regexp operations in order to work around
938 // crippling inefficiencies in IE's and Safari's regexp engines. First we
939 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
940 // replace all simple value tokens with ']' characters. Third, we delete all
941 // open brackets that follow a colon or comma or that begin the text. Finally,
942 // we look to see that the remaining characters are only whitespace or ']' or
943 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
946 .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
947 .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
948 .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
950 // In the third stage we use the eval function to compile the text into a
951 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
952 // in JavaScript: it can begin a block or an object literal. We wrap the text
953 // in parens to eliminate the ambiguity.
955 j = eval('(' + text + ')');
957 // In the optional fourth stage, we recursively walk the new structure, passing
958 // each name/value pair to a reviver function for possible transformation.
960 return typeof reviver === 'function' ?
961 walk({'': j}, '') : j;
964 // If the text is not JSON parseable, then a SyntaxError is thrown.
966 throw new SyntaxError('JSON.parse');
970 'undefined' != typeof io ? io : module.exports
971 , typeof JSON !== 'undefined' ? JSON : undefined
976 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
980 (function (exports, io) {
988 var parser = exports.parser = {};
994 var packets = parser.packets = [
1010 var reasons = parser.reasons = [
1011 'transport not supported'
1012 , 'client not handshaken'
1020 var advice = parser.advice = [
1029 , indexOf = io.util.indexOf;
1037 parser.encodePacket = function (packet) {
1038 var type = indexOf(packets, packet.type)
1039 , id = packet.id || ''
1040 , endpoint = packet.endpoint || ''
1044 switch (packet.type) {
1046 var reason = packet.reason ? indexOf(reasons, packet.reason) : ''
1047 , adv = packet.advice ? indexOf(advice, packet.advice) : '';
1049 if (reason !== '' || adv !== '')
1050 data = reason + (adv !== '' ? ('+' + adv) : '');
1055 if (packet.data !== '')
1060 var ev = { name: packet.name };
1062 if (packet.args && packet.args.length) {
1063 ev.args = packet.args;
1066 data = JSON.stringify(ev);
1070 data = JSON.stringify(packet.data);
1080 + (packet.args && packet.args.length
1081 ? '+' + JSON.stringify(packet.args) : '');
1085 // construct packet with required fragments
1088 , id + (ack == 'data' ? '+' : '')
1092 // data fragment is optional
1093 if (data !== null && data !== undefined)
1096 return encoded.join(':');
1100 * Encodes multiple messages (payload).
1102 * @param {Array} messages
1106 parser.encodePayload = function (packets) {
1109 if (packets.length == 1)
1112 for (var i = 0, l = packets.length; i < l; i++) {
1113 var packet = packets[i];
1114 decoded += '\ufffd' + packet.length + '\ufffd' + packets[i];
1126 var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
1128 parser.decodePacket = function (data) {
1129 var pieces = data.match(regexp);
1131 if (!pieces) return {};
1133 var id = pieces[2] || ''
1134 , data = pieces[5] || ''
1136 type: packets[pieces[1]]
1137 , endpoint: pieces[4] || ''
1140 // whether we need to acknowledge the packet
1144 packet.ack = 'data';
1149 // handle different packet types
1150 switch (packet.type) {
1152 var pieces = data.split('+');
1153 packet.reason = reasons[pieces[0]] || '';
1154 packet.advice = advice[pieces[1]] || '';
1158 packet.data = data || '';
1163 var opts = JSON.parse(data);
1164 packet.name = opts.name;
1165 packet.args = opts.args;
1168 packet.args = packet.args || [];
1173 packet.data = JSON.parse(data);
1178 packet.qs = data || '';
1182 var pieces = data.match(/^([0-9]+)(\+)?(.*)/);
1184 packet.ackId = pieces[1];
1189 packet.args = pieces[3] ? JSON.parse(pieces[3]) : [];
1204 * Decodes data payload. Detects multiple messages
1206 * @return {Array} messages
1210 parser.decodePayload = function (data) {
1211 // IE doesn't like data[i] for unicode chars, charAt works fine
1212 if (data.charAt(0) == '\ufffd') {
1215 for (var i = 1, length = ''; i < data.length; i++) {
1216 if (data.charAt(i) == '\ufffd') {
1217 ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length)));
1218 i += Number(length) + 1;
1221 length += data.charAt(i);
1227 return [parser.decodePacket(data)];
1232 'undefined' != typeof io ? io : module.exports
1233 , 'undefined' != typeof io ? io : module.parent.exports
1237 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
1241 (function (exports, io) {
1244 * Expose constructor.
1247 exports.Transport = Transport;
1250 * This is the transport template for all supported transport methods.
1256 function Transport (socket, sessid) {
1257 this.socket = socket;
1258 this.sessid = sessid;
1262 * Apply EventEmitter mixin.
1265 io.util.mixin(Transport, io.EventEmitter);
1269 * Indicates whether heartbeats is enabled for this transport
1274 Transport.prototype.heartbeats = function () {
1279 * Handles the response from the server. When a new response is received
1280 * it will automatically update the timeout, decode the message and
1281 * forwards the response to the onMessage function for further processing.
1283 * @param {String} data Response from the server.
1287 Transport.prototype.onData = function (data) {
1288 this.clearCloseTimeout();
1290 // If the connection in currently open (or in a reopening state) reset the close
1291 // timeout since we have just received data. This check is necessary so
1292 // that we don't reset the timeout on an explicitly disconnected connection.
1293 if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) {
1294 this.setCloseTimeout();
1298 // todo: we should only do decodePayload for xhr transports
1299 var msgs = io.parser.decodePayload(data);
1301 if (msgs && msgs.length) {
1302 for (var i = 0, l = msgs.length; i < l; i++) {
1303 this.onPacket(msgs[i]);
1317 Transport.prototype.onPacket = function (packet) {
1318 this.socket.setHeartbeatTimeout();
1320 if (packet.type == 'heartbeat') {
1321 return this.onHeartbeat();
1324 if (packet.type == 'connect' && packet.endpoint == '') {
1328 if (packet.type == 'error' && packet.advice == 'reconnect') {
1329 this.isOpen = false;
1332 this.socket.onPacket(packet);
1338 * Sets close timeout
1343 Transport.prototype.setCloseTimeout = function () {
1344 if (!this.closeTimeout) {
1347 this.closeTimeout = setTimeout(function () {
1348 self.onDisconnect();
1349 }, this.socket.closeTimeout);
1354 * Called when transport disconnects.
1359 Transport.prototype.onDisconnect = function () {
1360 if (this.isOpen) this.close();
1361 this.clearTimeouts();
1362 this.socket.onDisconnect();
1367 * Called when transport connects
1372 Transport.prototype.onConnect = function () {
1373 this.socket.onConnect();
1378 * Clears close timeout
1383 Transport.prototype.clearCloseTimeout = function () {
1384 if (this.closeTimeout) {
1385 clearTimeout(this.closeTimeout);
1386 this.closeTimeout = null;
1396 Transport.prototype.clearTimeouts = function () {
1397 this.clearCloseTimeout();
1399 if (this.reopenTimeout) {
1400 clearTimeout(this.reopenTimeout);
1407 * @param {Object} packet object.
1411 Transport.prototype.packet = function (packet) {
1412 this.send(io.parser.encodePacket(packet));
1416 * Send the received heartbeat message back to server. So the server
1417 * knows we are still connected.
1419 * @param {String} heartbeat Heartbeat response from the server.
1423 Transport.prototype.onHeartbeat = function (heartbeat) {
1424 this.packet({ type: 'heartbeat' });
1428 * Called when the transport opens.
1433 Transport.prototype.onOpen = function () {
1435 this.clearCloseTimeout();
1436 this.socket.onOpen();
1440 * Notifies the base when the connection with the Socket.IO server
1441 * has been disconnected.
1446 Transport.prototype.onClose = function () {
1449 /* FIXME: reopen delay causing a infinit loop
1450 this.reopenTimeout = setTimeout(function () {
1452 }, this.socket.options['reopen delay']);*/
1454 this.isOpen = false;
1455 this.socket.onClose();
1456 this.onDisconnect();
1460 * Generates a connection url based on the Socket.IO URL Protocol.
1461 * See <https://github.com/learnboost/socket.io-node/> for more details.
1463 * @returns {String} Connection url
1467 Transport.prototype.prepareUrl = function () {
1468 var options = this.socket.options;
1470 return this.scheme() + '://'
1471 + options.host + ':' + options.port + '/'
1472 + options.resource + '/' + io.protocol
1473 + '/' + this.name + '/' + this.sessid;
1477 * Checks if the transport is ready to start a connection.
1479 * @param {Socket} socket The socket instance that needs a transport
1480 * @param {Function} fn The callback
1484 Transport.prototype.ready = function (socket, fn) {
1488 'undefined' != typeof io ? io : module.exports
1489 , 'undefined' != typeof io ? io : module.parent.exports
1493 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
1497 (function (exports, io, global) {
1500 * Expose constructor.
1503 exports.Socket = Socket;
1506 * Create a new `Socket.IO client` which can establish a persistent
1507 * connection with a Socket.IO enabled server.
1512 function Socket (options) {
1516 , document: 'document' in global ? document : false
1517 , resource: 'socket.io'
1518 , transports: io.transports
1519 , 'connect timeout': 10000
1520 , 'try multiple transports': true
1522 , 'reconnection delay': 500
1523 , 'reconnection limit': Infinity
1524 , 'reopen delay': 3000
1525 , 'max reconnection attempts': 10
1526 , 'sync disconnect on unload': false
1527 , 'auto connect': true
1528 , 'flash policy port': 10843
1529 , 'manualFlush': false
1532 io.util.merge(this.options, options);
1534 this.connected = false;
1536 this.connecting = false;
1537 this.reconnecting = false;
1538 this.namespaces = {};
1540 this.doBuffer = false;
1542 if (this.options['sync disconnect on unload'] &&
1543 (!this.isXDomain() || io.util.ua.hasCORS)) {
1545 io.util.on(global, 'beforeunload', function () {
1546 self.disconnectSync();
1550 if (this.options['auto connect']) {
1556 * Apply EventEmitter mixin.
1559 io.util.mixin(Socket, io.EventEmitter);
1562 * Returns a namespace listener/emitter for this socket
1567 Socket.prototype.of = function (name) {
1568 if (!this.namespaces[name]) {
1569 this.namespaces[name] = new io.SocketNamespace(this, name);
1572 this.namespaces[name].packet({ type: 'connect' });
1576 return this.namespaces[name];
1580 * Emits the given event to the Socket and all namespaces
1585 Socket.prototype.publish = function () {
1586 this.emit.apply(this, arguments);
1590 for (var i in this.namespaces) {
1591 if (this.namespaces.hasOwnProperty(i)) {
1593 nsp.$emit.apply(nsp, arguments);
1599 * Performs the handshake
1604 function empty () { };
1606 Socket.prototype.handshake = function (fn) {
1608 , options = this.options;
1610 function complete (data) {
1611 if (data instanceof Error) {
1612 self.connecting = false;
1613 self.onError(data.message);
1615 fn.apply(null, data.split(':'));
1620 'http' + (options.secure ? 's' : '') + ':/'
1621 , options.host + ':' + options.port
1624 , io.util.query(this.options.query, 't=' + +new Date)
1627 if (this.isXDomain() && !io.util.ua.hasCORS) {
1628 var insertAt = document.getElementsByTagName('script')[0]
1629 , script = document.createElement('script');
1631 script.src = url + '&jsonp=' + io.j.length;
1632 insertAt.parentNode.insertBefore(script, insertAt);
1634 io.j.push(function (data) {
1636 script.parentNode.removeChild(script);
1639 var xhr = io.util.request();
1641 xhr.open('GET', url, true);
1642 if (this.isXDomain()) {
1643 xhr.withCredentials = true;
1645 xhr.onreadystatechange = function () {
1646 if (xhr.readyState == 4) {
1647 xhr.onreadystatechange = empty;
1649 if (xhr.status == 200) {
1650 complete(xhr.responseText);
1651 } else if (xhr.status == 403) {
1652 self.onError(xhr.responseText);
1654 self.connecting = false;
1655 !self.reconnecting && self.onError(xhr.responseText);
1664 * Find an available transport based on the options supplied in the constructor.
1669 Socket.prototype.getTransport = function (override) {
1670 var transports = override || this.transports, match;
1672 for (var i = 0, transport; transport = transports[i]; i++) {
1673 if (io.Transport[transport]
1674 && io.Transport[transport].check(this)
1675 && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) {
1676 return new io.Transport[transport](this, this.sessionid);
1684 * Connects to the server.
1686 * @param {Function} [fn] Callback.
1687 * @returns {io.Socket}
1691 Socket.prototype.connect = function (fn) {
1692 if (this.connecting) {
1697 self.connecting = true;
1699 this.handshake(function (sid, heartbeat, close, transports) {
1700 self.sessionid = sid;
1701 self.closeTimeout = close * 1000;
1702 self.heartbeatTimeout = heartbeat * 1000;
1703 if(!self.transports)
1704 self.transports = self.origTransports = (transports ? io.util.intersect(
1705 transports.split(',')
1706 , self.options.transports
1707 ) : self.options.transports);
1709 self.setHeartbeatTimeout();
1711 function connect (transports){
1712 if (self.transport) self.transport.clearTimeouts();
1714 self.transport = self.getTransport(transports);
1715 if (!self.transport) return self.publish('connect_failed');
1717 // once the transport is ready
1718 self.transport.ready(self, function () {
1719 self.connecting = true;
1720 self.publish('connecting', self.transport.name);
1721 self.transport.open();
1723 if (self.options['connect timeout']) {
1724 self.connectTimeoutTimer = setTimeout(function () {
1725 if (!self.connected) {
1726 self.connecting = false;
1728 if (self.options['try multiple transports']) {
1729 var remaining = self.transports;
1731 while (remaining.length > 0 && remaining.splice(0,1)[0] !=
1732 self.transport.name) {}
1734 if (remaining.length){
1737 self.publish('connect_failed');
1741 }, self.options['connect timeout']);
1746 connect(self.transports);
1748 self.once('connect', function (){
1749 clearTimeout(self.connectTimeoutTimer);
1751 fn && typeof fn == 'function' && fn();
1759 * Clears and sets a new heartbeat timeout using the value given by the
1760 * server during the handshake.
1765 Socket.prototype.setHeartbeatTimeout = function () {
1766 clearTimeout(this.heartbeatTimeoutTimer);
1767 if(this.transport && !this.transport.heartbeats()) return;
1770 this.heartbeatTimeoutTimer = setTimeout(function () {
1771 self.transport.onClose();
1772 }, this.heartbeatTimeout);
1778 * @param {Object} data packet.
1779 * @returns {io.Socket}
1783 Socket.prototype.packet = function (data) {
1784 if (this.connected && !this.doBuffer) {
1785 this.transport.packet(data);
1787 this.buffer.push(data);
1799 Socket.prototype.setBuffer = function (v) {
1802 if (!v && this.connected && this.buffer.length) {
1803 if (!this.options['manualFlush']) {
1810 * Flushes the buffer data over the wire.
1811 * To be invoked manually when 'manualFlush' is set to true.
1816 Socket.prototype.flushBuffer = function() {
1817 this.transport.payload(this.buffer);
1823 * Disconnect the established connect.
1825 * @returns {io.Socket}
1829 Socket.prototype.disconnect = function () {
1830 if (this.connected || this.connecting) {
1832 this.of('').packet({ type: 'disconnect' });
1835 // handle disconnection immediately
1836 this.onDisconnect('booted');
1843 * Disconnects the socket with a sync XHR.
1848 Socket.prototype.disconnectSync = function () {
1849 // ensure disconnection
1850 var xhr = io.util.request();
1852 'http' + (this.options.secure ? 's' : '') + ':/'
1853 , this.options.host + ':' + this.options.port
1854 , this.options.resource
1858 ].join('/') + '/?disconnect=1';
1860 xhr.open('GET', uri, false);
1863 // handle disconnection immediately
1864 this.onDisconnect('booted');
1868 * Check if we need to use cross domain enabled transports. Cross domain would
1869 * be a different port or different domain name.
1871 * @returns {Boolean}
1875 Socket.prototype.isXDomain = function () {
1877 var port = global.location.port ||
1878 ('https:' == global.location.protocol ? 443 : 80);
1880 return this.options.host !== global.location.hostname
1881 || this.options.port != port;
1885 * Called upon handshake.
1890 Socket.prototype.onConnect = function () {
1891 if (!this.connected) {
1892 this.connected = true;
1893 this.connecting = false;
1894 if (!this.doBuffer) {
1895 // make sure to flush the buffer
1896 this.setBuffer(false);
1898 this.emit('connect');
1903 * Called when the transport opens
1908 Socket.prototype.onOpen = function () {
1913 * Called when the transport closes.
1918 Socket.prototype.onClose = function () {
1920 clearTimeout(this.heartbeatTimeoutTimer);
1924 * Called when the transport first opens a connection
1929 Socket.prototype.onPacket = function (packet) {
1930 this.of(packet.endpoint).onPacket(packet);
1939 Socket.prototype.onError = function (err) {
1940 if (err && err.advice) {
1941 if (err.advice === 'reconnect' && (this.connected || this.connecting)) {
1943 if (this.options.reconnect) {
1949 this.publish('error', err && err.reason ? err.reason : err);
1953 * Called when the transport disconnects.
1958 Socket.prototype.onDisconnect = function (reason) {
1959 var wasConnected = this.connected
1960 , wasConnecting = this.connecting;
1962 this.connected = false;
1963 this.connecting = false;
1966 if (wasConnected || wasConnecting) {
1967 this.transport.close();
1968 this.transport.clearTimeouts();
1970 this.publish('disconnect', reason);
1972 if ('booted' != reason && this.options.reconnect && !this.reconnecting) {
1980 * Called upon reconnection.
1985 Socket.prototype.reconnect = function () {
1986 this.reconnecting = true;
1987 this.reconnectionAttempts = 0;
1988 this.reconnectionDelay = this.options['reconnection delay'];
1991 , maxAttempts = this.options['max reconnection attempts']
1992 , tryMultiple = this.options['try multiple transports']
1993 , limit = this.options['reconnection limit'];
1996 if (self.connected) {
1997 for (var i in self.namespaces) {
1998 if (self.namespaces.hasOwnProperty(i) && '' !== i) {
1999 self.namespaces[i].packet({ type: 'connect' });
2002 self.publish('reconnect', self.transport.name, self.reconnectionAttempts);
2005 clearTimeout(self.reconnectionTimer);
2007 self.removeListener('connect_failed', maybeReconnect);
2008 self.removeListener('connect', maybeReconnect);
2010 self.reconnecting = false;
2012 delete self.reconnectionAttempts;
2013 delete self.reconnectionDelay;
2014 delete self.reconnectionTimer;
2015 delete self.redoTransports;
2017 self.options['try multiple transports'] = tryMultiple;
2020 function maybeReconnect () {
2021 if (!self.reconnecting) {
2025 if (self.connected) {
2029 if (self.connecting && self.reconnecting) {
2030 return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
2033 if (self.reconnectionAttempts++ >= maxAttempts) {
2034 if (!self.redoTransports) {
2035 self.on('connect_failed', maybeReconnect);
2036 self.options['try multiple transports'] = true;
2037 self.transports = self.origTransports;
2038 self.transport = self.getTransport();
2039 self.redoTransports = true;
2042 self.publish('reconnect_failed');
2046 if (self.reconnectionDelay < limit) {
2047 self.reconnectionDelay *= 2; // exponential back off
2051 self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts);
2052 self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
2056 this.options['try multiple transports'] = false;
2057 this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
2059 this.on('connect', maybeReconnect);
2063 'undefined' != typeof io ? io : module.exports
2064 , 'undefined' != typeof io ? io : module.parent.exports
2069 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2073 (function (exports, io) {
2076 * Expose constructor.
2079 exports.SocketNamespace = SocketNamespace;
2082 * Socket namespace constructor.
2088 function SocketNamespace (socket, name) {
2089 this.socket = socket;
2090 this.name = name || '';
2092 this.json = new Flag(this, 'json');
2093 this.ackPackets = 0;
2098 * Apply EventEmitter mixin.
2101 io.util.mixin(SocketNamespace, io.EventEmitter);
2104 * Copies emit since we override it
2109 SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit;
2112 * Creates a new namespace, by proxying the request to the socket. This
2113 * allows us to use the synax as we do on the server.
2118 SocketNamespace.prototype.of = function () {
2119 return this.socket.of.apply(this.socket, arguments);
2128 SocketNamespace.prototype.packet = function (packet) {
2129 packet.endpoint = this.name;
2130 this.socket.packet(packet);
2141 SocketNamespace.prototype.send = function (data, fn) {
2143 type: this.flags.json ? 'json' : 'message'
2147 if ('function' == typeof fn) {
2148 packet.id = ++this.ackPackets;
2150 this.acks[packet.id] = fn;
2153 return this.packet(packet);
2162 SocketNamespace.prototype.emit = function (name) {
2163 var args = Array.prototype.slice.call(arguments, 1)
2164 , lastArg = args[args.length - 1]
2170 if ('function' == typeof lastArg) {
2171 packet.id = ++this.ackPackets;
2172 packet.ack = 'data';
2173 this.acks[packet.id] = lastArg;
2174 args = args.slice(0, args.length - 1);
2179 return this.packet(packet);
2183 * Disconnects the namespace
2188 SocketNamespace.prototype.disconnect = function () {
2189 if (this.name === '') {
2190 this.socket.disconnect();
2192 this.packet({ type: 'disconnect' });
2193 this.$emit('disconnect');
2205 SocketNamespace.prototype.onPacket = function (packet) {
2211 , args: io.util.toArray(arguments)
2216 switch (packet.type) {
2218 this.$emit('connect');
2222 if (this.name === '') {
2223 this.socket.onDisconnect(packet.reason || 'booted');
2225 this.$emit('disconnect', packet.reason);
2231 var params = ['message', packet.data];
2233 if (packet.ack == 'data') {
2235 } else if (packet.ack) {
2236 this.packet({ type: 'ack', ackId: packet.id });
2239 this.$emit.apply(this, params);
2243 var params = [packet.name].concat(packet.args);
2245 if (packet.ack == 'data')
2248 this.$emit.apply(this, params);
2252 if (this.acks[packet.ackId]) {
2253 this.acks[packet.ackId].apply(this, packet.args);
2254 delete this.acks[packet.ackId];
2260 this.socket.onError(packet);
2262 if (packet.reason == 'unauthorized') {
2263 this.$emit('connect_failed', packet.reason);
2265 this.$emit('error', packet.reason);
2278 function Flag (nsp, name) {
2279 this.namespace = nsp;
2289 Flag.prototype.send = function () {
2290 this.namespace.flags[this.name] = true;
2291 this.namespace.send.apply(this.namespace, arguments);
2300 Flag.prototype.emit = function () {
2301 this.namespace.flags[this.name] = true;
2302 this.namespace.emit.apply(this.namespace, arguments);
2306 'undefined' != typeof io ? io : module.exports
2307 , 'undefined' != typeof io ? io : module.parent.exports
2312 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2316 (function (exports, io, global) {
2319 * Expose constructor.
2322 exports.websocket = WS;
2325 * The WebSocket transport uses the HTML5 WebSocket API to establish an
2326 * persistent connection with the Socket.IO server. This transport will also
2327 * be inherited by the FlashSocket fallback as it provides a API compatible
2328 * polyfill for the WebSockets.
2331 * @extends {io.Transport}
2335 function WS (socket) {
2336 io.Transport.apply(this, arguments);
2340 * Inherits from Transport.
2343 io.util.inherit(WS, io.Transport);
2351 WS.prototype.name = 'websocket';
2354 * Initializes a new `WebSocket` connection with the Socket.IO server. We attach
2355 * all the appropriate listeners to handle the responses from the server.
2357 * @returns {Transport}
2361 WS.prototype.open = function () {
2362 var query = io.util.query(this.socket.options.query)
2368 Socket = global.MozWebSocket || global.WebSocket;
2371 this.websocket = new Socket(this.prepareUrl() + query);
2373 this.websocket.onopen = function () {
2375 self.socket.setBuffer(false);
2377 this.websocket.onmessage = function (ev) {
2378 self.onData(ev.data);
2380 this.websocket.onclose = function () {
2382 self.socket.setBuffer(true);
2384 this.websocket.onerror = function (e) {
2392 * Send a message to the Socket.IO server. The message will automatically be
2393 * encoded in the correct message format.
2395 * @returns {Transport}
2399 // Do to a bug in the current IDevices browser, we need to wrap the send in a
2400 // setTimeout, when they resume from sleeping the browser will crash if
2401 // we don't allow the browser time to detect the socket has been closed
2402 if (io.util.ua.iDevice) {
2403 WS.prototype.send = function (data) {
2405 setTimeout(function() {
2406 self.websocket.send(data);
2411 WS.prototype.send = function (data) {
2412 this.websocket.send(data);
2423 WS.prototype.payload = function (arr) {
2424 for (var i = 0, l = arr.length; i < l; i++) {
2425 this.packet(arr[i]);
2431 * Disconnect the established `WebSocket` connection.
2433 * @returns {Transport}
2437 WS.prototype.close = function () {
2438 this.websocket.close();
2443 * Handle the errors that `WebSocket` might be giving when we
2444 * are attempting to connect or send messages.
2446 * @param {Error} e The error.
2450 WS.prototype.onError = function (e) {
2451 this.socket.onError(e);
2455 * Returns the appropriate scheme for the URI generation.
2459 WS.prototype.scheme = function () {
2460 return this.socket.options.secure ? 'wss' : 'ws';
2464 * Checks if the browser has support for native `WebSockets` and that
2465 * it's not the polyfill created for the FlashSocket transport.
2471 WS.check = function () {
2472 return ('WebSocket' in global && !('__addTask' in WebSocket))
2473 || 'MozWebSocket' in global;
2477 * Check if the `WebSocket` transport support cross domain communications.
2479 * @returns {Boolean}
2483 WS.xdomainCheck = function () {
2488 * Add the transport to your public io.transports array.
2493 io.transports.push('websocket');
2496 'undefined' != typeof io ? io.Transport : module.exports
2497 , 'undefined' != typeof io ? io : module.parent.exports
2503 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
2507 (function (exports, io) {
2510 * Expose constructor.
2513 exports.flashsocket = Flashsocket;
2516 * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket
2517 * specification. It uses a .swf file to communicate with the server. If you want
2518 * to serve the .swf file from a other server than where the Socket.IO script is
2519 * coming from you need to use the insecure version of the .swf. More information
2520 * about this can be found on the github page.
2523 * @extends {io.Transport.websocket}
2527 function Flashsocket () {
2528 io.Transport.websocket.apply(this, arguments);
2532 * Inherits from Transport.
2535 io.util.inherit(Flashsocket, io.Transport.websocket);
2543 Flashsocket.prototype.name = 'flashsocket';
2546 * Disconnect the established `FlashSocket` connection. This is done by adding a
2547 * new task to the FlashSocket. The rest will be handled off by the `WebSocket`
2550 * @returns {Transport}
2554 Flashsocket.prototype.open = function () {
2558 WebSocket.__addTask(function () {
2559 io.Transport.websocket.prototype.open.apply(self, args);
2565 * Sends a message to the Socket.IO server. This is done by adding a new
2566 * task to the FlashSocket. The rest will be handled off by the `WebSocket`
2569 * @returns {Transport}
2573 Flashsocket.prototype.send = function () {
2574 var self = this, args = arguments;
2575 WebSocket.__addTask(function () {
2576 io.Transport.websocket.prototype.send.apply(self, args);
2582 * Disconnects the established `FlashSocket` connection.
2584 * @returns {Transport}
2588 Flashsocket.prototype.close = function () {
2589 WebSocket.__tasks.length = 0;
2590 io.Transport.websocket.prototype.close.call(this);
2595 * The WebSocket fall back needs to append the flash container to the body
2596 * element, so we need to make sure we have access to it. Or defer the call
2597 * until we are sure there is a body element.
2599 * @param {Socket} socket The socket instance that needs a transport
2600 * @param {Function} fn The callback
2604 Flashsocket.prototype.ready = function (socket, fn) {
2606 var options = socket.options
2607 , port = options['flash policy port']
2609 'http' + (options.secure ? 's' : '') + ':/'
2610 , options.host + ':' + options.port
2612 , 'static/flashsocket'
2613 , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf'
2616 // Only start downloading the swf file when the checked that this browser
2617 // actually supports it
2618 if (!Flashsocket.loaded) {
2619 if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') {
2620 // Set the correct file based on the XDomain settings
2621 WEB_SOCKET_SWF_LOCATION = path.join('/');
2625 WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port);
2628 WebSocket.__initialize();
2629 Flashsocket.loaded = true;
2636 if (document.body) return init();
2642 * Check if the FlashSocket transport is supported as it requires that the Adobe
2643 * Flash Player plug-in version `10.0.0` or greater is installed. And also check if
2644 * the polyfill is correctly loaded.
2646 * @returns {Boolean}
2650 Flashsocket.check = function () {
2652 typeof WebSocket == 'undefined'
2653 || !('__initialize' in WebSocket) || !swfobject
2656 return swfobject.getFlashPlayerVersion().major >= 10;
2660 * Check if the FlashSocket transport can be used as cross domain / cross origin
2661 * transport. Because we can't see which type (secure or insecure) of .swf is used
2662 * we will just return true.
2664 * @returns {Boolean}
2668 Flashsocket.xdomainCheck = function () {
2673 * Disable AUTO_INITIALIZATION
2676 if (typeof window != 'undefined') {
2677 WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
2681 * Add the transport to your public io.transports array.
2686 io.transports.push('flashsocket');
2688 'undefined' != typeof io ? io.Transport : module.exports
2689 , 'undefined' != typeof io ? io : module.parent.exports
2691 /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
2692 is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
2694 if ('undefined' != typeof window) {
2695 var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?(['Active'].concat('').join('X')):"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
2697 // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2698 // License: New BSD License
2699 // Reference: http://dev.w3.org/html5/websockets/
2700 // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
2704 if ('undefined' == typeof window || window.WebSocket) return;
2706 var console = window.console;
2707 if (!console || !console.log || !console.error) {
2708 console = {log: function(){ }, error: function(){ }};
2711 if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
2712 console.error("Flash Player >= 10.0.0 is required.");
2715 if (location.protocol == "file:") {
2717 "WARNING: web-socket-js doesn't work in file:///... URL " +
2718 "unless you set Flash Security Settings properly. " +
2719 "Open the page via Web server i.e. http://...");
2723 * This class represents a faux web socket.
2724 * @param {string} url
2725 * @param {array or string} protocols
2726 * @param {string} proxyHost
2727 * @param {int} proxyPort
2728 * @param {string} headers
2730 WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
2732 self.__id = WebSocket.__nextId++;
2733 WebSocket.__instances[self.__id] = self;
2734 self.readyState = WebSocket.CONNECTING;
2735 self.bufferedAmount = 0;
2739 } else if (typeof protocols == "string") {
2740 protocols = [protocols];
2742 // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
2743 // Otherwise, when onopen fires immediately, onopen is called before it is set.
2744 setTimeout(function() {
2745 WebSocket.__addTask(function() {
2746 WebSocket.__flash.create(
2747 self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
2753 * Send data to the web socket.
2754 * @param {string} data The data to send to the socket.
2755 * @return {boolean} True for success, false for failure.
2757 WebSocket.prototype.send = function(data) {
2758 if (this.readyState == WebSocket.CONNECTING) {
2759 throw "INVALID_STATE_ERR: Web Socket connection has not been established";
2761 // We use encodeURIComponent() here, because FABridge doesn't work if
2762 // the argument includes some characters. We don't use escape() here
2764 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
2765 // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
2766 // preserve all Unicode characters either e.g. "\uffff" in Firefox.
2767 // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
2768 // additional testing.
2769 var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
2770 if (result < 0) { // success
2773 this.bufferedAmount += result;
2779 * Close this web socket gracefully.
2781 WebSocket.prototype.close = function() {
2782 if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
2785 this.readyState = WebSocket.CLOSING;
2786 WebSocket.__flash.close(this.__id);
2790 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2792 * @param {string} type
2793 * @param {function} listener
2794 * @param {boolean} useCapture
2797 WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
2798 if (!(type in this.__events)) {
2799 this.__events[type] = [];
2801 this.__events[type].push(listener);
2805 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2807 * @param {string} type
2808 * @param {function} listener
2809 * @param {boolean} useCapture
2812 WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
2813 if (!(type in this.__events)) return;
2814 var events = this.__events[type];
2815 for (var i = events.length - 1; i >= 0; --i) {
2816 if (events[i] === listener) {
2817 events.splice(i, 1);
2824 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
2826 * @param {Event} event
2829 WebSocket.prototype.dispatchEvent = function(event) {
2830 var events = this.__events[event.type] || [];
2831 for (var i = 0; i < events.length; ++i) {
2834 var handler = this["on" + event.type];
2835 if (handler) handler(event);
2839 * Handles an event from Flash.
2840 * @param {Object} flashEvent
2842 WebSocket.prototype.__handleEvent = function(flashEvent) {
2843 if ("readyState" in flashEvent) {
2844 this.readyState = flashEvent.readyState;
2846 if ("protocol" in flashEvent) {
2847 this.protocol = flashEvent.protocol;
2851 if (flashEvent.type == "open" || flashEvent.type == "error") {
2852 jsEvent = this.__createSimpleEvent(flashEvent.type);
2853 } else if (flashEvent.type == "close") {
2854 // TODO implement jsEvent.wasClean
2855 jsEvent = this.__createSimpleEvent("close");
2856 } else if (flashEvent.type == "message") {
2857 var data = decodeURIComponent(flashEvent.message);
2858 jsEvent = this.__createMessageEvent("message", data);
2860 throw "unknown event type: " + flashEvent.type;
2863 this.dispatchEvent(jsEvent);
2866 WebSocket.prototype.__createSimpleEvent = function(type) {
2867 if (document.createEvent && window.Event) {
2868 var event = document.createEvent("Event");
2869 event.initEvent(type, false, false);
2872 return {type: type, bubbles: false, cancelable: false};
2876 WebSocket.prototype.__createMessageEvent = function(type, data) {
2877 if (document.createEvent && window.MessageEvent && !window.opera) {
2878 var event = document.createEvent("MessageEvent");
2879 event.initMessageEvent("message", false, false, data, null, null, window, null);
2882 // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
2883 return {type: type, data: data, bubbles: false, cancelable: false};
2888 * Define the WebSocket readyState enumeration.
2890 WebSocket.CONNECTING = 0;
2892 WebSocket.CLOSING = 2;
2893 WebSocket.CLOSED = 3;
2895 WebSocket.__flash = null;
2896 WebSocket.__instances = {};
2897 WebSocket.__tasks = [];
2898 WebSocket.__nextId = 0;
2901 * Load a new flash security policy file.
2902 * @param {string} url
2904 WebSocket.loadFlashPolicyFile = function(url){
2905 WebSocket.__addTask(function() {
2906 WebSocket.__flash.loadManualPolicyFile(url);
2911 * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
2913 WebSocket.__initialize = function() {
2914 if (WebSocket.__flash) return;
2916 if (WebSocket.__swfLocation) {
2917 // For backword compatibility.
2918 window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
2920 if (!window.WEB_SOCKET_SWF_LOCATION) {
2921 console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
2924 var container = document.createElement("div");
2925 container.id = "webSocketContainer";
2926 // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
2927 // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
2928 // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
2929 // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
2930 // the best we can do as far as we know now.
2931 container.style.position = "absolute";
2932 if (WebSocket.__isFlashLite()) {
2933 container.style.left = "0px";
2934 container.style.top = "0px";
2936 container.style.left = "-100px";
2937 container.style.top = "-100px";
2939 var holder = document.createElement("div");
2940 holder.id = "webSocketFlash";
2941 container.appendChild(holder);
2942 document.body.appendChild(container);
2943 // See this article for hasPriority:
2944 // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
2946 WEB_SOCKET_SWF_LOCATION,
2950 "10.0.0" /* SWF version */,
2953 {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
2957 console.error("[WebSocket] swfobject.embedSWF failed");
2963 * Called by Flash to notify JS that it's fully loaded and ready
2964 * for communication.
2966 WebSocket.__onFlashInitialized = function() {
2967 // We need to set a timeout here to avoid round-trip calls
2968 // to flash during the initialization process.
2969 setTimeout(function() {
2970 WebSocket.__flash = document.getElementById("webSocketFlash");
2971 WebSocket.__flash.setCallerUrl(location.href);
2972 WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
2973 for (var i = 0; i < WebSocket.__tasks.length; ++i) {
2974 WebSocket.__tasks[i]();
2976 WebSocket.__tasks = [];
2981 * Called by Flash to notify WebSockets events are fired.
2983 WebSocket.__onFlashEvent = function() {
2984 setTimeout(function() {
2986 // Gets events using receiveEvents() instead of getting it from event object
2987 // of Flash event. This is to make sure to keep message order.
2988 // It seems sometimes Flash events don't arrive in the same order as they are sent.
2989 var events = WebSocket.__flash.receiveEvents();
2990 for (var i = 0; i < events.length; ++i) {
2991 WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
3001 WebSocket.__log = function(message) {
3002 console.log(decodeURIComponent(message));
3006 WebSocket.__error = function(message) {
3007 console.error(decodeURIComponent(message));
3010 WebSocket.__addTask = function(task) {
3011 if (WebSocket.__flash) {
3014 WebSocket.__tasks.push(task);
3019 * Test if the browser is running flash lite.
3020 * @return {boolean} True if flash lite is running, false otherwise.
3022 WebSocket.__isFlashLite = function() {
3023 if (!window.navigator || !window.navigator.mimeTypes) {
3026 var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
3027 if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
3030 return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
3033 if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
3034 if (window.addEventListener) {
3035 window.addEventListener("load", function(){
3036 WebSocket.__initialize();
3039 window.attachEvent("onload", function(){
3040 WebSocket.__initialize();
3049 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3053 (function (exports, io, global) {
3056 * Expose constructor.
3070 function XHR (socket) {
3071 if (!socket) return;
3073 io.Transport.apply(this, arguments);
3074 this.sendBuffer = [];
3078 * Inherits from Transport.
3081 io.util.inherit(XHR, io.Transport);
3084 * Establish a connection
3086 * @returns {Transport}
3090 XHR.prototype.open = function () {
3091 this.socket.setBuffer(false);
3095 // we need to make sure the request succeeds since we have no indication
3096 // whether the request opened or not until it succeeded.
3097 this.setCloseTimeout();
3103 * Check if we need to send data to the Socket.IO server, if we have data in our
3104 * buffer we encode it and forward it to the `post` method.
3109 XHR.prototype.payload = function (payload) {
3112 for (var i = 0, l = payload.length; i < l; i++) {
3113 msgs.push(io.parser.encodePacket(payload[i]));
3116 this.send(io.parser.encodePayload(msgs));
3120 * Send data to the Socket.IO server.
3122 * @param data The message
3123 * @returns {Transport}
3127 XHR.prototype.send = function (data) {
3133 * Posts a encoded message to the Socket.IO server.
3135 * @param {String} data A encoded message.
3139 function empty () { };
3141 XHR.prototype.post = function (data) {
3143 this.socket.setBuffer(true);
3145 function stateChange () {
3146 if (this.readyState == 4) {
3147 this.onreadystatechange = empty;
3148 self.posting = false;
3150 if (this.status == 200){
3151 self.socket.setBuffer(false);
3158 function onload () {
3159 this.onload = empty;
3160 self.socket.setBuffer(false);
3163 this.sendXHR = this.request('POST');
3165 if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
3166 this.sendXHR.onload = this.sendXHR.onerror = onload;
3168 this.sendXHR.onreadystatechange = stateChange;
3171 this.sendXHR.send(data);
3175 * Disconnects the established `XHR` connection.
3177 * @returns {Transport}
3181 XHR.prototype.close = function () {
3187 * Generates a configured XHR request
3189 * @param {String} url The url that needs to be requested.
3190 * @param {String} method The method the request should use.
3191 * @returns {XMLHttpRequest}
3195 XHR.prototype.request = function (method) {
3196 var req = io.util.request(this.socket.isXDomain())
3197 , query = io.util.query(this.socket.options.query, 't=' + +new Date);
3199 req.open(method || 'GET', this.prepareUrl() + query, true);
3201 if (method == 'POST') {
3203 if (req.setRequestHeader) {
3204 req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
3207 req.contentType = 'text/plain';
3216 * Returns the scheme to use for the transport URLs.
3221 XHR.prototype.scheme = function () {
3222 return this.socket.options.secure ? 'https' : 'http';
3226 * Check if the XHR transports are supported
3228 * @param {Boolean} xdomain Check if we support cross domain requests.
3229 * @returns {Boolean}
3233 XHR.check = function (socket, xdomain) {
3235 var request = io.util.request(xdomain),
3236 usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest),
3237 socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'),
3238 isXProtocol = (global.location && socketProtocol != global.location.protocol);
3239 if (request && !(usesXDomReq && isXProtocol)) {
3248 * Check if the XHR transport supports cross domain requests.
3250 * @returns {Boolean}
3254 XHR.xdomainCheck = function (socket) {
3255 return XHR.check(socket, true);
3259 'undefined' != typeof io ? io.Transport : module.exports
3260 , 'undefined' != typeof io ? io : module.parent.exports
3265 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3269 (function (exports, io) {
3272 * Expose constructor.
3275 exports.htmlfile = HTMLFile;
3278 * The HTMLFile transport creates a `forever iframe` based transport
3279 * for Internet Explorer. Regular forever iframe implementations will
3280 * continuously trigger the browsers buzy indicators. If the forever iframe
3281 * is created inside a `htmlfile` these indicators will not be trigged.
3284 * @extends {io.Transport.XHR}
3288 function HTMLFile (socket) {
3289 io.Transport.XHR.apply(this, arguments);
3293 * Inherits from XHR transport.
3296 io.util.inherit(HTMLFile, io.Transport.XHR);
3304 HTMLFile.prototype.name = 'htmlfile';
3307 * Creates a new Ac...eX `htmlfile` with a forever loading iframe
3308 * that can be used to listen to messages. Inside the generated
3309 * `htmlfile` a reference will be made to the HTMLFile transport.
3314 HTMLFile.prototype.get = function () {
3315 this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
3317 this.doc.write('<html></html>');
3319 this.doc.parentWindow.s = this;
3321 var iframeC = this.doc.createElement('div');
3322 iframeC.className = 'socketio';
3324 this.doc.body.appendChild(iframeC);
3325 this.iframe = this.doc.createElement('iframe');
3327 iframeC.appendChild(this.iframe);
3330 , query = io.util.query(this.socket.options.query, 't='+ +new Date);
3332 this.iframe.src = this.prepareUrl() + query;
3334 io.util.on(window, 'unload', function () {
3340 * The Socket.IO server will write script tags inside the forever
3341 * iframe, this function will be used as callback for the incoming
3344 * @param {String} data The message
3345 * @param {document} doc Reference to the context
3349 HTMLFile.prototype._ = function (data, doc) {
3350 // unescape all forward slashes. see GH-1251
3351 data = data.replace(/\\\//g, '/');
3354 var script = doc.getElementsByTagName('script')[0];
3355 script.parentNode.removeChild(script);
3360 * Destroy the established connection, iframe and `htmlfile`.
3361 * And calls the `CollectGarbage` function of Internet Explorer
3362 * to release the memory.
3367 HTMLFile.prototype.destroy = function () {
3370 this.iframe.src = 'about:blank';
3374 this.iframe.parentNode.removeChild(this.iframe);
3382 * Disconnects the established connection.
3384 * @returns {Transport} Chaining.
3388 HTMLFile.prototype.close = function () {
3390 return io.Transport.XHR.prototype.close.call(this);
3394 * Checks if the browser supports this transport. The browser
3395 * must have an `Ac...eXObject` implementation.
3401 HTMLFile.check = function (socket) {
3402 if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){
3404 var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile');
3405 return a && io.Transport.XHR.check(socket);
3412 * Check if cross domain requests are supported.
3414 * @returns {Boolean}
3418 HTMLFile.xdomainCheck = function () {
3419 // we can probably do handling for sub-domains, we should
3420 // test that it's cross domain but a subdomain here
3425 * Add the transport to your public io.transports array.
3430 io.transports.push('htmlfile');
3433 'undefined' != typeof io ? io.Transport : module.exports
3434 , 'undefined' != typeof io ? io : module.parent.exports
3439 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3443 (function (exports, io, global) {
3446 * Expose constructor.
3449 exports['xhr-polling'] = XHRPolling;
3452 * The XHR-polling transport uses long polling XHR requests to create a
3453 * "persistent" connection with the server.
3459 function XHRPolling () {
3460 io.Transport.XHR.apply(this, arguments);
3464 * Inherits from XHR transport.
3467 io.util.inherit(XHRPolling, io.Transport.XHR);
3470 * Merge the properties from XHR transport
3473 io.util.merge(XHRPolling, io.Transport.XHR);
3481 XHRPolling.prototype.name = 'xhr-polling';
3484 * Indicates whether heartbeats is enabled for this transport
3489 XHRPolling.prototype.heartbeats = function () {
3494 * Establish a connection, for iPhone and Android this will be done once the page
3497 * @returns {Transport} Chaining.
3501 XHRPolling.prototype.open = function () {
3504 io.Transport.XHR.prototype.open.call(self);
3509 * Starts a XHR request to wait for incoming messages.
3514 function empty () {};
3516 XHRPolling.prototype.get = function () {
3517 if (!this.isOpen) return;
3521 function stateChange () {
3522 if (this.readyState == 4) {
3523 this.onreadystatechange = empty;
3525 if (this.status == 200) {
3526 self.onData(this.responseText);
3534 function onload () {
3535 this.onload = empty;
3536 this.onerror = empty;
3537 self.retryCounter = 1;
3538 self.onData(this.responseText);
3542 function onerror () {
3543 self.retryCounter ++;
3544 if(!self.retryCounter || self.retryCounter > 3) {
3551 this.xhr = this.request();
3553 if (global.XDomainRequest && this.xhr instanceof XDomainRequest) {
3554 this.xhr.onload = onload;
3555 this.xhr.onerror = onerror;
3557 this.xhr.onreadystatechange = stateChange;
3560 this.xhr.send(null);
3564 * Handle the unclean close behavior.
3569 XHRPolling.prototype.onClose = function () {
3570 io.Transport.XHR.prototype.onClose.call(this);
3573 this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty;
3582 * Webkit based browsers show a infinit spinner when you start a XHR request
3583 * before the browsers onload event is called so we need to defer opening of
3584 * the transport until the onload event is called. Wrapping the cb in our
3585 * defer method solve this.
3587 * @param {Socket} socket The socket instance that needs a transport
3588 * @param {Function} fn The callback
3592 XHRPolling.prototype.ready = function (socket, fn) {
3595 io.util.defer(function () {
3601 * Add the transport to your public io.transports array.
3606 io.transports.push('xhr-polling');
3609 'undefined' != typeof io ? io.Transport : module.exports
3610 , 'undefined' != typeof io ? io : module.parent.exports
3616 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
3620 (function (exports, io, global) {
3622 * There is a way to hide the loading indicator in Firefox. If you create and
3623 * remove a iframe it will stop showing the current loading indicator.
3624 * Unfortunately we can't feature detect that and UA sniffing is evil.
3629 var indicator = global.document && "MozAppearance" in
3630 global.document.documentElement.style;
3633 * Expose constructor.
3636 exports['jsonp-polling'] = JSONPPolling;
3639 * The JSONP transport creates an persistent connection by dynamically
3640 * inserting a script tag in the page. This script tag will receive the
3641 * information of the Socket.IO server. When new information is received
3642 * it creates a new script tag for the new data stream.
3645 * @extends {io.Transport.xhr-polling}
3649 function JSONPPolling (socket) {
3650 io.Transport['xhr-polling'].apply(this, arguments);
3652 this.index = io.j.length;
3656 io.j.push(function (msg) {
3662 * Inherits from XHR polling transport.
3665 io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
3673 JSONPPolling.prototype.name = 'jsonp-polling';
3676 * Posts a encoded message to the Socket.IO server using an iframe.
3677 * The iframe is used because script tags can create POST based requests.
3678 * The iframe is positioned outside of the view so the user does not
3679 * notice it's existence.
3681 * @param {String} data A encoded message.
3685 JSONPPolling.prototype.post = function (data) {
3687 , query = io.util.query(
3688 this.socket.options.query
3689 , 't='+ (+new Date) + '&i=' + this.index
3693 var form = document.createElement('form')
3694 , area = document.createElement('textarea')
3695 , id = this.iframeId = 'socketio_iframe_' + this.index
3698 form.className = 'socketio';
3699 form.style.position = 'absolute';
3700 form.style.top = '0px';
3701 form.style.left = '0px';
3702 form.style.display = 'none';
3704 form.method = 'POST';
3705 form.setAttribute('accept-charset', 'utf-8');
3707 form.appendChild(area);
3708 document.body.appendChild(form);
3714 this.form.action = this.prepareUrl() + query;
3716 function complete () {
3718 self.socket.setBuffer(false);
3721 function initIframe () {
3723 self.form.removeChild(self.iframe);
3727 // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
3728 iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
3730 iframe = document.createElement('iframe');
3731 iframe.name = self.iframeId;
3734 iframe.id = self.iframeId;
3736 self.form.appendChild(iframe);
3737 self.iframe = iframe;
3742 // we temporarily stringify until we figure out how to prevent
3743 // browsers from turning `\n` into `\r\n` in form inputs
3744 this.area.value = io.JSON.stringify(data);
3750 if (this.iframe.attachEvent) {
3751 iframe.onreadystatechange = function () {
3752 if (self.iframe.readyState == 'complete') {
3757 this.iframe.onload = complete;
3760 this.socket.setBuffer(true);
3764 * Creates a new JSONP poll that can be used to listen
3765 * for messages from the Socket.IO server.
3770 JSONPPolling.prototype.get = function () {
3772 , script = document.createElement('script')
3773 , query = io.util.query(
3774 this.socket.options.query
3775 , 't='+ (+new Date) + '&i=' + this.index
3779 this.script.parentNode.removeChild(this.script);
3783 script.async = true;
3784 script.src = this.prepareUrl() + query;
3785 script.onerror = function () {
3789 var insertAt = document.getElementsByTagName('script')[0];
3790 insertAt.parentNode.insertBefore(script, insertAt);
3791 this.script = script;
3794 setTimeout(function () {
3795 var iframe = document.createElement('iframe');
3796 document.body.appendChild(iframe);
3797 document.body.removeChild(iframe);
3803 * Callback function for the incoming message stream from the Socket.IO server.
3805 * @param {String} data The message
3809 JSONPPolling.prototype._ = function (msg) {
3818 * The indicator hack only works after onload
3820 * @param {Socket} socket The socket instance that needs a transport
3821 * @param {Function} fn The callback
3825 JSONPPolling.prototype.ready = function (socket, fn) {
3827 if (!indicator) return fn.call(this);
3829 io.util.load(function () {
3835 * Checks if browser supports this transport.
3841 JSONPPolling.check = function () {
3842 return 'document' in global;
3846 * Check if cross domain requests are supported
3848 * @returns {Boolean}
3852 JSONPPolling.xdomainCheck = function () {
3857 * Add the transport to your public io.transports array.
3862 io.transports.push('jsonp-polling');
3865 'undefined' != typeof io ? io.Transport : module.exports
3866 , 'undefined' != typeof io ? io : module.parent.exports
3870 if (typeof define === "function" && define.amd) {
3871 define([], function () { return io; });