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;
}