3 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
7 (function (exports, global) {
10 * Utilities namespace.
15 var util = exports.util = {};
20 * @author Steven Levithan <stevenlevithan.com> (MIT license)
24 var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
26 var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
27 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
30 util.parseUri = function (str) {
31 var m = re.exec(str || '')
36 uri[parts[i]] = m[i] || '';
43 * Produces a unique url that identifies a Socket.IO connection.
49 util.uniqueUri = function (uri) {
50 var protocol = uri.protocol
54 if ('document' in global) {
55 host = host || document.domain;
56 port = port || (protocol == 'https'
57 && document.location.protocol !== 'https:' ? 443 : document.location.port);
59 host = host || 'localhost';
61 if (!port && protocol == 'https') {
66 return (protocol || 'http') + '://' + host + ':' + (port || 80);
70 * Mergest 2 query strings in to once unique query string
72 * @param {String} base
73 * @param {String} addition
77 util.query = function (base, addition) {
78 var query = util.chunkQuery(base || '')
81 util.merge(query, util.chunkQuery(addition || ''));
82 for (var part in query) {
83 if (query.hasOwnProperty(part)) {
84 components.push(part + '=' + query[part]);
88 return components.length ? '?' + components.join('&') : '';
92 * Transforms a querystring in to an object
98 util.chunkQuery = function (qs) {
100 , params = qs.split('&')
106 kv = params[i].split('=');
108 query[kv[0]] = kv[1];
116 * Executes the given function when the page is loaded.
118 * io.util.load(function () { console.log('page loaded'); });
120 * @param {Function} fn
124 var pageLoaded = false;
126 util.load = function (fn) {
127 if ('document' in global && document.readyState === 'complete' || pageLoaded) {
131 util.on(global, 'load', fn, false);
140 util.on = function (element, event, fn, capture) {
141 if (element.attachEvent) {
142 element.attachEvent('on' + event, fn);
143 } else if (element.addEventListener) {
144 element.addEventListener(event, fn, capture);
149 * Generates the correct `XMLHttpRequest` for regular and cross domain requests.
151 * @param {Boolean} [xdomain] Create a request that can be used cross domain.
152 * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest.
156 util.request = function (xdomain) {
158 var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
159 return new XMLHttpRequest();
162 if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) {
163 return new XDomainRequest();
166 if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
167 return new XMLHttpRequest();
172 return new ActiveXObject('Microsoft.XMLHTTP');
180 * XHR based transport constructor.
187 * Change the internal pageLoaded value.
190 if ('undefined' != typeof window) {
191 util.load(function () {
197 * Defers a function to ensure a spinner is not displayed by the browser
199 * @param {Function} fn
203 util.defer = function (fn) {
204 if (!util.ua.webkit || 'undefined' != typeof importScripts) {
208 util.load(function () {
214 * Merges two objects.
219 util.merge = function merge (target, additional, deep, lastseen) {
220 var seen = lastseen || []
221 , depth = typeof deep == 'undefined' ? 2 : deep
224 for (prop in additional) {
225 if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
226 if (typeof target[prop] !== 'object' || !depth) {
227 target[prop] = additional[prop];
228 seen.push(additional[prop]);
230 util.merge(target[prop], additional[prop], depth - 1, seen);
239 * Merges prototypes from objects
244 util.mixin = function (ctor, ctor2) {
245 util.merge(ctor.prototype, ctor2.prototype);
249 * Shortcut for prototypical and static inheritance.
254 util.inherit = function (ctor, ctor2) {
256 f.prototype = ctor2.prototype;
257 ctor.prototype = new f;
261 * Checks if the given object is an Array.
263 * io.util.isArray([]); // true
264 * io.util.isArray({}); // false
270 util.isArray = Array.isArray || function (obj) {
271 return Object.prototype.toString.call(obj) === '[object Array]';
275 * Intersects values of two arrays into a third
280 util.intersect = function (arr, arr2) {
282 , longest = arr.length > arr2.length ? arr : arr2
283 , shortest = arr.length > arr2.length ? arr2 : arr;
285 for (var i = 0, l = shortest.length; i < l; i++) {
286 if (~util.indexOf(longest, shortest[i]))
287 ret.push(shortest[i]);
294 * Array indexOf compatibility.
300 util.indexOf = function (arr, o, i) {
302 for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0;
303 i < j && arr[i] !== o; i++) {}
305 return j <= i ? -1 : i;
309 * Converts enumerables to array.
314 util.toArray = function (enu) {
317 for (var i = 0, l = enu.length; i < l; i++)
324 * UA / engines detection namespace.
332 * Whether the UA supports CORS for XHR.
337 util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
339 var a = new XMLHttpRequest();
344 return a.withCredentials != undefined;
353 util.ua.webkit = 'undefined' != typeof navigator
354 && /webkit/i.test(navigator.userAgent);
357 * Detect iPad/iPhone/iPod.
362 util.ua.iDevice = 'undefined' != typeof navigator
363 && /iPad|iPhone|iPod/i.test(navigator.userAgent);
365 })('undefined' != typeof io ? io : module.exports, this);