Last active
February 24, 2016 21:10
-
-
Save mflorida/660fb39e9823e3bab5c2 to your computer and use it in GitHub Desktop.
globals.js file used in XNAT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* 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) | |
} | |
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); | |
} | |
// 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[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)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment