/*! * Global general-purpose JavaScript convenience * utlity helper functions (verbosity intentional) * * Some of these functions are taken straight from * other libraries and put in the global scope here. * Is that a good idea? Maybe, maybe not. */ // Avoid console errors in browsers that lack a console. (function(){ var method; var noop = function(){}; var methods = [ 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn' ]; var length = methods.length; var console = (window.console = window.console || {}); var i = 0; while ( length-- ) { method = methods[i++]; // Only stub undefined methods. if ( !console[method] ) { console[method] = noop; } } }()); function diddly(){} // utility for getting URL query string value function getQueryStringValue( param ){ var search = window.location.search; if (!param || !search) { return '' } if (search.indexOf(param) === -1) { return '' } var val = search. split(param+'=')[1]. split('&')[0]. split('#')[0]. replace(/\/*$/,''); // remove any 'bonus' trailing slashes return decodeURIComponent(val); } function getParameterByName( name ){ return getQueryStringValue(name) } // get the url hash string without the '#' function getUrlHash(){ return window.location.hash.split('#')[1] || ''; } // simplest function for getting // a value from the url hash function getUrlHashValue(start, end){ var part = '', hash = window.location.hash; if (!hash) { return '' } part = hash.split(start||'#')[1]||''; part = part.split(end||'/')[0]||''; return part; } function firstDefined() { var undefined, i = -1; while (++i < arguments.length) { if (arguments[i] !== undefined) { return arguments[i]; } } return undefined; } function isDefined( x ){ return typeof x != 'undefined' } function isUndefined( x ){ return typeof x == 'undefined' } function isString( str ){ return typeof str === 'string'; } function stringLower( str ){ return str.toString().toLowerCase(); } function stringUpper( str ){ return str.toString().toUpperCase(); } function capitalize(str){ return str.charAt(0).toUpperCase() + str.slice(1); } function sentenceCase(str) { return capitalize(str); } function titleCase(str){ return str.split(/\s+/).map(function(word){ return capitalize(word); }).join(' '); } function truncateText(text, len){ len = len || 30; // default length is 30 chars if (text.length <= len){ return text; } else { return text.substring(0, len) + '...'; } } function isTrue( val ){ return stringLower(val||'') === 'true'; } function isFalse( val ){ return stringLower(val||'') === 'false'; } function isEqual( a, b ){ // heavy-handed comparison of 2 values as strings if (arguments.length === 2 && typeof a != 'undefined'){ return (a.toString() === b.toString()); } else { return undefined; } } function isEqualLower(a, b){ return isEqual(stringLower(a||''), stringLower(b||'')); } function isObject( obj ){ // returns true for objects, arrays, and null return typeof obj == 'object'; } function isPlainObject( obj ){ return Object.prototype.toString.call(obj) === '[object Object]'; } function isEmptyObject( obj ){ var name; for ( name in obj ) { return false; } return true; } function getObject( obj ){ return isPlainObject(obj) ? obj : {}; } function isArray( arr ){ if ( Array.isArray ) { return Array.isArray(arr); } else { return Object.prototype.toString.call(arr) === '[object Array]'; } } function isEmptyArray( arr ){ return isArray(arr) && arr.length === 0; } function isEmpty( x, args ){ if (isString(x)){ return x === ''; } if (isPlainObject(x)){ return isEmptyObject(x); } if (isArray(x)){ return isEmptyArray(x); } // does a function return an 'empty' value? if (isFunction(x)){ return isEmpty(x.apply(null, [].concat(args))); } return (x === null || isUndefined(x) || !isFunction(x)); } function isFunction( func ){ return typeof func == 'function'; } function isNumber( num ){ return (typeof num == 'number' && !isNaN(num)); } // copy of jQuery's $.isNumeric() method function isNumeric( num ) { // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN // adding 1 corrects loss of precision from parseFloat (jQuery issue #15100) return !isArray( num ) && (num - parseFloat( num ) + 1) >= 0; } // copy of jQuery's $.extend() method function extend(){ var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !isFunction(target) ) { target = {}; } // Copy object if only one argument is passed. // jQuery extends its own object, but since this // isn't jQuery, 'this' is the global object (bad) if ( i === length ) { target = {}; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { // don't check for this - extend everything //if ( !options.hasOwnProperty(name) ) { // continue; //} src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( isPlainObject(copy) || (copyIsArray = isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = extend(deep, clone, copy); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; } // default deep extend function extendDeep(){ var args = toArray(arguments); return extend.apply(null, [true].concat(args)); } // clone and extend function extendCopy(){ var args = toArray(arguments); return extend.apply(null, [{}].concat(args)); } // clone and deep extend function extendCopyDeep(){ var args = toArray(arguments); return extend.apply(null, [true, {}].concat(args)); } // return a cloned copy of a single 'obj' function cloneObject(obj){ return extend(true, {}, obj); } // add child objects to 'obj' object from string // OVERWRITES PROPERTIES WITH MATCHING NAMES // setObject(foo, 'bar.baz', 123456) // -> foo: { bar: { baz: 123456 } } function setObject(obj, str, val) { var parts, part; if (typeof str != 'string' || !str.length) { return {}; } obj = getObject(obj); parts = str.split('.'); while (parts.length > 1) { part = parts.shift(); obj = getObject(obj); if (!obj[part]) { obj[part] = {}; } obj = obj[part]; } obj[parts[0]] = val || {}; return obj; } // add child objects to 'obj' object // OVERWRITES PROPERTIES WITH MATCHING NAMES function setExtendedObject(obj, str, val){ var newObj = {}; setObject(newObj, str, val); newObj = extend(true, {}, obj, newObj); return newObj; } // return the last item in an array-like object function getLast(arr){ if (!arr) { return null } if (!arr.length) { return arr } return arr[arr.length-1]; } // make sure we only run a function one time function once(func, args) { func = func || function(){}; if (func.called) { return } func.apply(null, args); func.called = true; } // execute a function on each item in an // array(-like) object with a length property // works like native Array.forEach(); function forEach( arr, fn ){ var i = -1, len; if (!arr || !arr.length) { return } len = arr.length; if (isFunction(fn)) { while (++i < len){ fn(arr[i], i); } } } // execute a function on each // of an object's own properties // works like jQuery's $.each() // but only for objects // returns array of property names function forOwn( obj, fn ){ var keys = [], key; if (!isPlainObject(obj)) { return } for (key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); if (!isFunction(fn)) continue; fn(key, obj[key]); } } return keys; } // convert array-like object or arguments to a real array // (twice as fast as Array.prototype.slice.call(arguments)) function toArray(arr) { var i = -1, len = arr.length, newArray = new Array(len); while (++i < len) { newArray[i] = arr[i]; } return newArray; } // check if 'item' is in 'arr' array function inArray(item, arr){ var i = -1, len = arr.length; if (!len) { return false; } while (++i < len){ if (arr[i] === item){ return true; } } return false; } // return new array with duplicates removed function dedupeArray(arr){ var out = [], i = -1, len = arr.length, item; while (++i < len) { item = arr[i]; if (!inArray(item, out)){ out.push(item); } } return out; } // set default values for object function setDefaults(obj, props){ //obj = getObject(obj); //forOwn(props, function(name, val){ // obj[name] = val; //}); //return obj; return extendCopyDeep(obj, props) } // converts properly formatted numeric string // to a number, or if not a proper numeric string, // just returns the value passed, unless // 'force' === true, then use parseFloat() // to try to return a number function toNumber( val, strip, force, dec ){ var deci = /\./g, thou = /,/g, num; // only do additional processing if more than one argument if (arguments.length > 1){ // do not strip non-numeric characters by default strip = (strip === 'strip') ? true : strip === true; // do not force number conversion by default // (returns 'sanitized' number) force = (force === 'force') ? true : force === true; // if comma is used for decimal separator // adjust thousands and decimal separators for JS if (dec === ','){ deci = /,/g; thou = /\./g; } // strip thousands separators and make sure // period is used for the decimal separator val = (val+'').replace(thou,'').replace(deci,'.'); // strip non-numeric characters (besides decimal) if (strip){ val = val.replace(/[^0-9\.]/g,''); } // chop off after 2nd decimal, if present val = val.split('.'); val = (val.length === 1) ? val[0] : val[0]+ '.' +val[1]; } // finally create the number num = parseFloat(val); if (num === +val){ return num; } else { return force ? num : val; } } // return a 'clean' number // remove non-numeric characters // and truncate past 2nd decimal, // if present // examples: // cleanNumber('123,456.789.001') -> 123456.789 // cleanNumber('abc123456xyz789.001a') -> 123456789.001 // cleanNumber('123.456,001abc', ',') -> 123456.001 function cleanNumber( val, dec ){ return toNumber(val, true, true, dec); } // pass an array of values to make sure ALL of them are numbers // 'numeric' argument indicates allowing numeric _string_: '1' function allNumbers( arr, numeric ){ var len = arr.length, i = -1, checkNumber = (numeric) ? isNumeric : isNumber; if ( !isArray(arr) ) { return false } while ( ++i < len ) { if ( !checkNumber(arr[i]) ) { return false; } } return true; } // pass an array of values to make sure they're ALL numeric function allNumeric( arr ){ return allNumbers( arr, true ); } // feed an array of values to check for at least one number // 'numeric' argument indicates allowing numeric string function hasNumber( arr, numeric ){ var numbers = 0, len = arr.length, i = -1, checkNumber = (numeric) ? isNumeric : isNumber; if ( !isArray(arr) ) { return false } while ( ++i < len ) { if ( checkNumber(arr[i]) ) { numbers += 1; } } return numbers > 0; } // returns number as a string with leading zeros (or other character) // thanks to - http://stackoverflow.com/a/10073788 // revised here - http://jsfiddle.net/rj0rf5hg/2/ // padNumber( 5 ) //=> '05' // padNumber( 55, 4 ) //=> '0055' // padNumber( 555, 6, 'X' ) //=> 'XXX555' function padNumber( num, size, fill ) { // only whole numbers if (parseInt(num, 10) !== +num) { return num+'' } num = num+''; // make sure 'num' is a string // make sure 'size' is a whole number // defaults to 2 digits size = (typeof size != 'undefined') ? parseInt(size, 10) : 2; fill = fill || '0'; // default fill character is '0' return (num.length >= size) ? num : new Array(size - num.length + 1).join(fill) + num; } function zeroPad( num, size, fill ){ return padNumber(num, size, fill || '0'); } // add commas to numbers function addCommas( nStr ){ nStr += ''; var x = nStr.split('.'), x1 = x[0], x2 = x.length > 1 ? '.' + x[1] : '' ; var rgx = /(\d+)(\d{3})/; while ( rgx.test(x1) ) { x1 = x1.replace(rgx, '$1' + ',' + '$2'); } return x1 + x2; } function roundNumber( num, dec ){ return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); } // convert number to file size in KB, MB, GB // rounded to 'round' decimal places function sizeFormat( size, round ){ var KB = 1024, MB = KB * KB, GB = MB * KB, TB = GB * KB; // round to 2 decimal places by default round = round || 2; if ( size >= TB ) { return roundNumber(size / TB, round) + ' TB'; } if ( size >= GB ) { return roundNumber(size / GB, round) + ' GB'; } if ( size >= MB ) { return roundNumber(size / MB, round) + ' MB'; } if ( size >= KB ) { return roundNumber(size / KB, round) + ' KB'; } return size + ' B'; } function randomID(prefix, seq) { window.autoIDcount = window.autoIDcount || 0; window.autoIDs = window.autoIDs || []; var pre = (isDefined(prefix)) ? prefix : 'i-' ; var i = (isUndefined(seq) || isTrue(seq)) ? padNumber( ++window.autoIDcount, 4 ) + '-' : ''; var newID = pre + i + (Math.random() + 1).toString(36).substr(2,8); window.autoIDs.push(newID); window.randomIDcount = window.autoIDcount; window.randomIDs = window.autoIDs; return newID; } autoID = randomID; // set 'forceLower' === true (or omit argument) // to ensure output is lowercase function toDashed(name){ return name.replace(/([A-Z])/g, function(u) { return '-' + u.toLowerCase(); }); } //hyphenate = toDashed; //dashify = toDashed; function toDashedLower(name){ return name.replace(/([A-Z])/g, function(u) { return '-' + u.toLowerCase(); }); } // set 'forceLower' === true (or omit argument) // to ensure *only* 'cameled' letters are uppercase function toCamelCase(name, forceLower) { if (isUndefined(forceLower) || isTrue(forceLower)){ name = name.toLowerCase(); } return name.replace(/\-./g, function(u){ return u.substr(1).toUpperCase(); }); } //toCamel = toCamelCase; //camelCase = toCamelCase; //camelize = toCamelCase; //camelify = toCamelCase; //camelfy = toCamelCase; function toCamelLower(name){ return toCamelCase(name, true); } // put on the String prototype just for kicks // or don't //String.prototype.toDashed = function(forceLower){ // return toDashed(this, forceLower); //}; // //String.prototype.toDashedLower = function(){ // return toDashedLower(this); //}; // //String.prototype.toCamel = function(forceLower){ // return toCamel(this, forceLower); //}; // //String.prototype.toCamelLower = function(){ // return toCamelLower(this); //}; // enhanced encodeURIComponent() that // replaces more non-word characters function encodeURIComponentAll(str) { return encodeURIComponent(str).replace(/[!'()*]/g, function(c){ return '%' + c.charCodeAt(0).toString(16); }); } function setElementData(element, name, val){ if (document.head && document.head.dataset) { name = toCamelCase(name); element.dataset[name] = val; } else { name = toDashed(name); element.setAttribute('data-' + name, val); } } function getElementData(element, name){ if (document.head && document.head.dataset) { name = toCamelCase(name); return realValue(element.dataset[name]); } else { name = toDashed(name); return realValue(element.getAttribute('data-' + name)); } } // returns real boolean for boolean string // returns real number for numeric string // returns null and undefined for those strings // (or returns original value if none of those) // useful for pulling 'real' values from // a string used in [data-] attributes function realValue(val, bool){ var undefined; // only evaluate strings if (!isString(val)) return val; if (bool){ if (val === '0'){ return false; } if (val === '1'){ return true; } } if (isNumeric(val)){ return +val; } switch(val) { case 'true': return true; case 'false': return false; case 'undefined': return undefined; case 'null': return null; default: return val; } } // return an object from a string with pipe-separated // (or custom 'delim' separated) values // 'obj_or_str' can be an existing object you want to modify function parseOptions(obj_or_str, str, delim, sep){ var obj = {}; // handle simplest case of just passing an options string if (arguments.length === 1){ str = obj_or_str; } if (isPlainObject(obj_or_str)){ obj = obj_or_str; } delim = delim || /,|;|\|/; // default delimiters ( , ; | - comma or semicolon or pipe) sep = sep || /:|=/; // default key:value separators ( : = - colon or equals) var parts = isString(str) ? str.split(delim) : []; forEach(parts, function(part){ var prop = part.split(sep)[0], val = part.split(sep)[1]; obj[prop] = realValue(val); }); return obj; } // Return the directory path for the script executing this function. // This works ONLY with scripts hard coded onto the page or loaded // with document.write() or insertScripts(). // DO NOT wait for DOM to load to run this - that will just return // the last <script> (with "src") on the page function getScriptDir() { var scripts, src, path; scripts = document.querySelectorAll('script[src]'); src = scripts[scripts.length - 1].src; if (src.indexOf('/') > -1) { path = src.split('/'); path.splice(path.length - 1, 1); return path.join('/') + '/'; } else { return ''; } } // keep track of scripts loaded with loadScript(); window.loadedScripts = []; // did we load the page in 'debug' mode? // add ?jsdebug=true or #jsdebug to url // this is used to load non-minified scripts if true function debugMode(){ var hash = window.location.hash.toLowerCase(); var debug = (getQueryStringValue('jsdebug') || getQueryStringValue('debug') || getQueryStringValue('js') || '').toLowerCase(); if (/(debug=off|debug=false)/.test(hash)) return false; if (/(debug|true|on)/.test(debug)) return true; if (/(debug)/.test(hash)) return true; return false; } window.jsdebug = window.debug = debugMode(); // return passed 'min' string if in jsdebug mode // ?jsdebug=true or #jsdebug function setMin(min){ return debugMode() ? '' : min || ''; } function scriptUrl(url, min){ var parts = url.split('|'); url = parts[0].trim(); min = (min || parts[1] || '').replace(/([!~\*])+/g,'').trim(); return serverRoot + '/scripts/' + (url.replace(/\.js$/i,'')) + setMin(min) + '.js'; } function getScriptElements(){ var scripts = document.querySelectorAll('script[src]'), scriptsArray = window.loadedScripts.slice() || [], len = scripts.length, i = -1, src; while (++i < len){ src = scripts[i].getAttribute('src'); if (scriptsArray.indexOf(src) === -1){ scriptsArray.push(src); } } if (window.jsdebug){ console.log(scriptsArray); } return window.loadedScripts = scriptsArray; } //getScriptElements(); // gather scripts thus far? function hasScript( url ){ // fastest check? if (window.loadedScripts.indexOf(url) > -1){ return true; } //// see if it's in the DOM already //else { // if (getScriptElements().indexOf(url) > -1){ // return true; // } // // if not, push it to window.loadedScripts array // else { // // and tell the closure it's not there (yet)??? // //window.loadedScripts.push(url); // return false; // } //} //var scriptLength = document.querySelectorAll('script[src="' + url + '.js"]').length; //console.log('count: ' + url + ' - ' + scriptLength); //return scriptLength; return getScriptElements().indexOf(url) > -1; } // split params passed as a pipe-separated string // and return object with property names for params function scriptParams( script ){ var obj = { url: '', min: '', name: '' }; if (isString(script)){ script = script.split('|'); obj.url = script[0] ? script[0].trim() : ''; obj.min = script[1] ? script[1].replace(/\*/,'').trim() : ''; obj.name = script[2] ? script[2].replace(/\*/,'').trim() : ''; obj.parent = script[3] ? script[3].replace(/\*/,'').trim() : ''; } else if (isPlainObject(script)) { script.url = script.src = script.url || script.src; // tolerate use of 'src' prop name extend(obj, script); } //obj.min = setMin(obj.min); obj.src = obj.url = obj.url.replace(/\.js$/i,'') + obj.min + '.js'; return obj; } // returns HTML for <script> element function scriptHTML( src, name ){ var script = ''; if (arguments.length === 0 || src === null){ return ''; } if (src && !hasScript(src)){ script += '<script type="text/javascript"'; script += ' src="' + src + '"'; script += (name) ? ' data-name="' + name + '"' : ''; script += '><\/script>'; //window.loadedScripts.push(_src); } return script; } // kludgy document.write('<script>') // ONLY WORKS INLINE ON THE PAGE // call functions that rely on these scripts // AFTERWARDS in a separate <script> element // insertScript('/scripts/app/script', '.min', 'app.script'); // DO NOT CALL insertScript() AFTER PAGE LOAD - ONLY ON INITIAL LOAD function insertScript( url, min, name ){ var script = scriptParams(url); if (hasScript(script.src)) { return } try { document.write(scriptHTML(script.src, name || script.name)); //script.element = document.querySelector('script[src="'+script.src+'"]'); // don't know if these have loaded since // they're inserted after this script runs? //script.element.onload = function(){ // if (!done) { // done = true; // window.loadedScripts.push(script.src); // //callback(this, "ok"); // } //}; //script.element.onreadystatechange = function(){ // var state; // if (!done) { // state = this.readyState; // if (state === "complete") { // this.onload(); // } // } //}; //console.log(getScriptElements()); } catch(e){ console.log(e); } } // insertScripts([{url:'/scripts/app/script',name:'app.script',min:'-min'}]); //function insertScripts( /* scripts (multiple args or array) */ ){ // var i = -1, scripts; // if (isString(arguments[0]) || arguments.length > 1){ // scripts = toArray(arguments); // } // else { // scripts = arguments[0]; // } // while (++i < scripts.length){ // if (scripts[i]){ // skip null values // insertScript(scripts[i]); // } // } //} //insertScripts.configArraySample = [ // // string with pipe separating params (spaces ok) // // (script url) | (optional min string) | (optional script name) // '/scripts/app/foo.js | .min | foo', // // or use an object with param properties // { // url: '/scripts/app/script', // REQUIRED // min: '-min', // optional // name: 'app.script' // optional // }, // { // // 'src' property name works also // src: '/scripts/app/utils', // min: '.min', // name: 'app.utils' // } //]; // returns new <script> DOM ELEMENT function scriptElement( src, title, body ){ var script = document.createElement('script'); script.type = "text/javascript"; if (title){ script.title = title; } if (src){ script.src = src; } else { script.innerHTML = body || ''; } return script; } function writeScript( src, title, body ){ document.write(scriptElement(src, title, body).outerHTML) } function writeScripts(){ var scripts = arguments[0], i = -1, len = arguments.length; if (len > 1) { scripts = arguments } len = scripts.length; while (++i < len){ writeScript.apply(null, [].concat(scripts[i])); } } // load a script, // optionally into a specific parent element, // and/or with a callback (optional) function loadScript( /* script, parent/callback, callback */ ) { var obj, parent, _parent, script, scripts, status, callback, fns = {}, noop = function(){}, args = arguments, arg2 = args[1], arg3 = args[2], len = args.length, done = false; if (len === 0){ // no args no run return; } if (len > 3){ console.log('max 3 arguments allowed'); return; } if (len >= 2){ // parent could be second argument // if there are 2 or 3 args parent = arg2; callback = arg3; } // but arg2 *could* be a callback instead of the parent if (len === 2 && (isFunction(arg2) || isPlainObject(arg2))){ parent = 'head'; callback = arg2; } // process params input in string or object format // returns params object obj = scriptParams(args[0]); obj.callback = obj.success || obj.complete || obj.callback || noop; if (isPlainObject(callback)){ fns = callback; } else { fns.callback = isFunction(callback) ? callback : noop; } fns.callback = fns.success || fns.complete || fns.callback || noop; script = scriptElement(obj.src, obj.name); script.url = obj.src; script.onload = function(complete){ status = complete || 'ok'; if (!done) { done = true; window.loadedScripts.push(obj.src); obj.callback(this, status); // callback function in config object fns.callback(this, status); // callback function argument } }; script.onreadystatechange = function(){ if (!done) { if (this.readyState === 'complete') { this.onload('complete'); } } }; script.onerror = function(){ status = 'error'; // prefer to call 'error' callback obj.callback = obj.failure || obj.error || obj.callback; fns.callback = fns.failure || fns.error || fns.callback; if (!done) { done = true; obj.callback(this, status); // callback function in config object fns.callback(this, status); } }; // 'parent' param could be a separate argument for this function // or a property property on the 'script' arg //if (parent === 'before'){ // scripts = document.querySelectorAll('script'); // scripts[scripts.length-1].insertBefore(script); // return; //} _parent = document.querySelector(parent||obj.parent||'head'); //_parent.appendChild(script); _parent.insertBefore(script, getLast(document.scripts)); } // load multiple scripts // (into the same parent, if specified) // and run optional callbacks for each script // and a final callback after all scripts are loaded function loadScripts( scripts, parent_or_callback, callback ){ var i = -1, len, _script, _parent, _callback = isFunction(callback) ? callback : function(){}; scripts = scripts.slice(); len = scripts.length; if (len === 0){ // need args return; } if (isFunction(parent_or_callback)){ _parent = 'head'; _callback = parent_or_callback; } else { _parent = parent_or_callback; } while (++i < len){ _script = scriptParams(scripts[i]); _script.min = ''; // 'min's been added already _script.parent = _script.parent || _parent; if (i === len-1){ // do callback after *last* script has loaded loadScript(_script, _script.parent, _callback); } else { loadScript(_script, _script.parent); } } } function cssUrl( url, min, query ){ var parts = url.split('|'); url = parts[0].trim(); min = (min || parts[1] || '').replace(/([!~\*])+/g,'').trim(); query = (query || parts[2] || '').trim(); if (query) { query = '?' + query.replace(/^\?/,''); } return serverRoot + '/' + (url.replace(/\.css$/i,'')) + setMin(min) + '.css' + query; } function cssElement( href ){ var el = document.createElement('link'); el.rel = 'stylesheet'; el.type = 'text/css'; el.href = cssUrl(href); return el; } // document.write(css) function writeCSS( href ){ document.write(cssElement(href).outerHTML); } function loadCSS( url, parent ){ // use CSS-style selector for 'parent' parent = parent ? document.querySelector(parent) : document.querySelector('head'); parent.appendChild(scriptElement(url, min, name)); }