Kyle-Falconer
10/25/2013 - 6:55 PM

common utility functions

common utility functions

/**
* Utility class, provided either for convenience or compatibility
*/
var Utils = {};

/**
 * the child node fix for ignoring textnodes
 * @param {Node} ele The element which is to be acted upon
 * @return {Array.<Node>} The non-text Node children of ele
 */
Utils.childNodes = function(ele){
  var children = [];
  if (Utils.isArrayLike(ele.childNodes)) {
    for (var i = 0; i < ele.childNodes.length; i++) {
      if (ele.childNodes[i].nodeType == 1){
    	  children.push(ele.childNodes[i]);
      }
    }
  }
  return children;
};

/**
 * IE7+ function for getElementsByClassName
 * @param {Node} ele The element which is to be acted upon
 * @param {string} classname The name of the class
 * @return {Array.<Node>} The elements containing the given classname
 */
Utils.getElementsByClassName = function(ele, classname) {
	if (Element.prototype.getElementsByClassName){
	  return ele.getElementsByClassName(classname);
	}
  var a = [];
  var re = new RegExp('(^| )'+classname+'( |$)');
  var els = ele.getElementsByTagName("*");
  for(var i=0,j=els.length; i<j; i++){
    if(re.test(els[i].className)){
      a.push(els[i]);
    }
  }
  return a;
};

/**
 * whether something behaves like an array
 * @param o The object to be tested
 * @return {boolean} true if o is array-like
 */
Utils.isArrayLike = function(o) {
  return (o && o.length !== undefined);
};

Utils.parseBoolean = function(val){
	if (typeof val === 'string' || val instanceof String){
		return /true/i.test(val);
	} else if (typeof val === 'boolean' || val instanceof Boolean){
		return (new Boolean(val).valueOf());
	} else if (typeof val === 'number' || val instanceof Number){
		return new Number(val).valueOf() !== 0;
	}
	return false;
};

/**
 * The complement to Node.insertBefore
 * @param {Node} newNode The node to insert.
 * @param {Node} refNode The node after which newNode is inserted.
 */
Utils.insertAfter = function (newNode, refNode) {
	refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
};

Utils.tests = {};

Utils.tests.parseBoolean = function(){
	var booleanTests = [
		['true', true],
		['false', false],
		['True', true],
		['False', false],
		[, false],
		[true, true],
		[false, false],
		['gibberish', false],
		[0, false],
		[1, true]
	];

	for (var i = 0; i < booleanTests.length; i++){
		var lhs = Utils.parseBoolean(booleanTests[i][0]);
		var rhs = booleanTests[i][1];
		var pass = lhs === rhs;

		if (!pass){
			console.error('Utils.parseBoolean('+booleanTests[i][0]+') === '+booleanTests[i][1]+'\t : \tfail');
		}
	}
};

/**
 * get parameter value by name from location string
 * @see: http://stackoverflow.com/a/901144/940217
 * @param {String} name The name/key in the location string for which the
 *     value should be retrieved
 * @return {String|null} the value corresponding to the name key
 */
function getParameterByName(name) {
    var key = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
    var regex = new RegExp('[\\?&]' + key + '=([^&#]*)'),
        results = regex.exec(location.search);
    return results === null ? '': decodeURIComponent(results[1].replace(/\+/g, ' '));
}






/**
 * @see: http://stackoverflow.com/a/6160317/940217
 * @param {Node} ele The element which is to be acted upon
 * @param {string} cls The classname to be tested
 * @return {boolean} true if ele has a classname of cls
 */
function hasClass(ele, cls) {
    return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}

/**
 * @see: http://stackoverflow.com/a/6160317/940217
 * @param {Node} ele The element which is to be acted upon
 * @param {string} cls The classname to be added
 */
function addClass(ele, cls) {
	if (ele.className.length === 0) ele.className = ""+cls;
    else if (!hasClass(ele, cls)) ele.className += " " + cls;
}

/**
 * @see: http://stackoverflow.com/a/6160317/940217
 * @param {Node} ele The element which is to be acted upon
 * @param {string} cls The classname to be removed
 */
function removeClass(ele, cls) {
    if (hasClass(ele, cls)) {
        var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
        ele.className = ele.className.replace(reg, ' ');
    }
}

/**
 * @see http://stackoverflow.com/q/7616461/940217
 * @return {number}
 */
String.prototype.hashCode = function(){
	if (Array.prototype.reduce){
		return this.split('').reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a;},0);              
	} 
	var hash = 0;
	if (this.length === 0){ return hash; }
	for (var i = 0; i < this.length; i++) {
		var character = this.charCodeAt(i);
		hash = ((hash<<5)-hash)+character;
		hash = hash & hash; // Convert to 32bit integer
	}
	return hash;
};

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
};

/**
 * @param {string} src the URL to use for the script tag.
 * @param {string=} scriptTagId the optional id to apply to the script tag so that the tag can be later removed
 */
function AddScript(src, scriptTagId) {
    var HeadTag = document.getElementsByTagName('head')[0];
    var HeadScripts = HeadTag.getElementsByTagName('script');
    var ScriptNode = document.createElement('script');
    ScriptNode.src = src;
    ScriptNode.async = true;
	if (typeof scriptTagId != 'undefined') {
		ScriptNode.id = scriptTagId;
	}
    if (HeadScripts.length === 0) {
        HeadTag.appendChild(ScriptNode);
    } else {
        HeadTag.insertBefore(ScriptNode, HeadScripts[0]);
    }
}

/**
 * Determines whether or not a given attribute exists and is not "false"
 * Meant to provide backwords compatibility at least through IE 8.
 * @param {Element} elem The element which contains the attribute
 * @param {string} attribute The attribute to test
 * @return {boolean} True if the attribute exists and is not "false"
 */
function falsyAttribute(elem, attribute){
  return elem.getAttribute(attribute) === null ? false : elem.getAttribute(attribute).toLowerCase() !== 'false';
}



/**
 * getComputedStyle function for IE8
 * borrowed from:
 * http://missouristate.info/scripts/2013/common.js
 */
"getComputedStyle" in window || function() {
  function c(a, b, g, e) {
    var h = b[g];
    b = parseFloat(h);
    h = h.split(/\d/)[0];
    e = null !== e ? e : /%|em/.test(h) && a.parentElement ? c(a.parentElement, a.parentElement.currentStyle, "fontSize", null) : 16;
    a = "fontSize" == g ? e : /width/i.test(g) ? a.clientWidth : a.clientHeight;
    return "em" == h ? b * e : "in" == h ? 96 * b : "pt" == h ? 96 * b / 72 : "%" == h ? b / 100 * a : b;
  }
  function a(a, c) {
    var b = "border" == c ? "Width" : "", e = c + "Top" + b, h = c + "Right" + b, l = c + "Bottom" + b, b = c + "Left" + b;
    a[c] = (a[e] == a[h] == a[l] == a[b] ? [a[e]] : a[e] == a[l] && a[b] == a[h] ? [a[e], a[h]] : a[b] == a[h] ? [a[e], a[h], a[l]] : [a[e], a[h], a[l], a[b]]).join(" ");
  }
  function b(b) {
    var d, g = b.currentStyle, e = c(b, g, "fontSize", null);
    for (d in g) {
      /width|height|margin.|padding.|border.+W/.test(d) && "auto" !== this[d] ? this[d] = c(b, g, d, e) + "px" : "styleFloat" === d ? this["float"] = g[d] : this[d] = g[d];
    }
    a(this, "margin");
    a(this, "padding");
    a(this, "border");
    this.fontSize = e + "px";
    return this;
  }
  b.prototype = {};
  window.getComputedStyle = function(a) {
    return new b(a);
  };
}();

/**
 * IE8 compatible method for getting the inside width of an element
 * @param {Element} elem
 * @return {number}
 */
function computedWidth(elem) {
    var currElemStyles = window.getComputedStyle ? window.getComputedStyle(elem) : {};
    var isNotIE8 = elem.addEventListener;
    var elemWidth;

    if (isNotIE8) {
        elemWidth = parseFloat(currElemStyles.width);
    } else {
        var boundingRect = elem.getBoundingClientRect();
        elemWidth = boundingRect.right - boundingRect.left;
    }
    if (currElemStyles.boxSizing === 'border-box' || 
        currElemStyles.MozBoxSizing === 'border-box' || 
        currElemStyles.webkitBoxSizing === 'border-box') {
        elemWidth -= parseFloat(currElemStyles.paddingLeft) + 
                     parseFloat(currElemStyles.paddingRight) + 
                     parseFloat(currElemStyles.borderLeftWidth) + 
                     parseFloat(currElemStyles.borderRightWidth);
    } else if (!isNotIE8 && currElemStyles.width !== 'NaNpx') {
        elemWidth = currElemStyles.width;
    }
    return elemWidth;
}