/** * Status of page load. */ var pageLoaded = false; /** * Global reference. */ var global = 'undefined' != typeof window ? window : global; /** * Inheritance. * * @param {Function} ctor a * @param {Function} ctor b * @api private */ exports.inherits = function inherits (a, b) { function c () { } c.prototype = b.prototype; a.prototype = new c; }; /** * Object.keys */ exports.keys = Object.keys || function (obj) { var ret = []; var has = Object.prototype.hasOwnProperty; for (var i in obj) { if (has.call(obj, i)) { ret.push(i); } } return ret; }; /** * Adds an event. * * @api private */ exports.on = function (element, event, fn, capture) { if (element.attachEvent) { element.attachEvent('on' + event, fn); } else if (element.addEventListener) { element.addEventListener(event, fn, capture); } }; /** * Load utility. * * @api private */ exports.load = function (fn) { if (global.document && document.readyState === 'complete' || pageLoaded) { return fn(); } exports.on(global, 'load', fn, false); }; /** * Change the internal pageLoaded value. */ if ('undefined' != typeof window) { exports.load(function () { pageLoaded = true; }); } /** * Defers a function to ensure a spinner is not displayed by the browser. * * @param {Function} fn * @api private */ exports.defer = function (fn) { if (!exports.ua.webkit || 'undefined' != typeof importScripts) { return fn(); } exports.load(function () { setTimeout(fn, 100); }); }; /** * JSON parse. * * @see Based on jQuery#parseJSON (MIT) and JSON2 * @api private */ var rvalidchars = /^[\],:{}\s]*$/ , rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g , rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g , rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g , rtrimLeft = /^\s+/ , rtrimRight = /\s+$/ exports.parseJSON = function (data) { if ('string' != typeof data || !data) { return null; } data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); // Attempt to parse using the native JSON parser first if (global.JSON && JSON.parse) { return JSON.parse(data); } if (rvalidchars.test(data.replace(rvalidescape, '@') .replace(rvalidtokens, ']') .replace(rvalidbraces, ''))) { return (new Function('return ' + data))(); } }; /** * UA / engines detection namespace. * * @namespace */ exports.ua = {}; /** * Whether the UA supports CORS for XHR. * * @api private */ exports.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { try { var a = new XMLHttpRequest(); } catch (e) { return false; } return a.withCredentials != undefined; })(); /** * Detect webkit. * * @api private */ exports.ua.webkit = 'undefined' != typeof navigator && /webkit/i.test(navigator.userAgent); /** * Detect gecko. * * @api private */ exports.ua.gecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); /** * Detect android; */ exports.ua.android = 'undefined' != typeof navigator && /android/i.test(navigator.userAgent); /** * Detect iOS. */ exports.ua.ios = 'undefined' != typeof navigator && /^(iPad|iPhone|iPod)$/.test(navigator.platform); exports.ua.ios6 = exports.ua.ios && /OS 6_/.test(navigator.userAgent); /** * XHR request helper. * * @param {Boolean} whether we need xdomain * @api private */ exports.request = function request (xdomain) { if ('undefined' != typeof process) { var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; return new XMLHttpRequest(); } if (xdomain && 'undefined' != typeof XDomainRequest && !exports.ua.hasCORS) { return new XDomainRequest(); } // XMLHttpRequest can be disabled on IE try { if ('undefined' != typeof XMLHttpRequest && (!xdomain || exports.ua.hasCORS)) { return new XMLHttpRequest(); } } catch (e) { } if (!xdomain) { try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) { } } }; /** * Parses an URI * * @author Steven Levithan (MIT license) * @api private */ var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; var parts = [ 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host' , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' ]; exports.parseUri = function (str) { var m = re.exec(str || '') , uri = {} , i = 14; while (i--) { uri[parts[i]] = m[i] || ''; } return uri; }; /** * Compiles a querystring * * @param {Object} * @api private */ exports.qs = function (obj) { var str = ''; for (var i in obj) { if (obj.hasOwnProperty(i)) { if (str.length) str += '&'; str += i + '=' + encodeURIComponent(obj[i]); } } return str; };