//GLOBAL FUNCTIONS
//
//DEFINE WITH ASSIGNMENT SO THAT IF THIS GETS CALLED IN AN ASP/JAVASCRIPT
//FUNCTION, IT'LL ALL WORK.

//RETURNS AN ARRAY FOR ANYTHING WITH MULTIPLE PROPERTIES AND A length
//USEFUL FOR HTML ELEMENT COLLECTIONS
makeArray = function(inList) {
	var newArray = [];
	for (var i=0; i < inList.length; i++) {
		try {
			var curThingie = inList.item(i);
		} catch(e) {
			var curThingie = inList[i];
		}
		if (!isUndefined(curThingie)) newArray.push(curThingie);
	}
	return newArray;
}

clone = function(what) {
	var newThing;

	if ([String,Number].has(what.constructor)) {
		newThing = what;
	} else {
		newThing = new what.constructor;
		for (var i in what) {
			newThing[i] = what[i];
		}
	}

	return newThing;
}

cmp = function(first, second) {
	return ((first > second) ? 1 : ((first < second) ? -1 : 0));
}

keys = function(assocArray) {
	var keys = [];
	for (var i in assocArray) keys.push(i);

	return keys;
}
values = function(assocArray) {
	var values = [];
	for (var i in assocArray) values.push(assocArray[i]);

	return values;
}

//"MAPS" A FUNCTION ONTO AN ARRAY (JUST A WRAPPER TO Array.prototype.process)
Function.prototype.map = function(theArray) {
	return theArray.process(this);
}

//COMPARABLE TO SQL SORTS: EACH ARGUMENT SPECIFIES A FUNCTION THAT GENERATES
//A COMPARISON VALUE FOR EACH MEMBER OF THE ARRAY.
//
//FOR EXAMPLE, SORT DATES BY MONTH, BACKWARDS BY YEAR, AND SOMETHING ELSE
// BY DOING THIS:
//datesArray.sortBy('getMonth','!getYear',function (d) { ... })
//
//AS AN ADDED BONUS, YOU CAN WRITE A PROPERTY OR METHOD NAME AS A STRING.
//INDICATE NEGATION WITH A '!' AS THE FIRST CHARACTER.
//
//FOR GENERIC REVERSAL OF A SORT FUNCTION, DEFINE A reverse PROPERTY ON IT AS
//true.
Array.prototype.sortBy = function() {
	var copy = this.makeEmpty();
	var sorters = makeArray(arguments).process( function(s) {
		var retVal;
		var reverse = false;

		if (s.constructor == Function) {
			retVal = s;
			reverse = s.reverse || false;
		} else {
			var reverse = s.charAt(0) == '!';
			if (reverse) s = s.substring(1);

			var referenced = copy[0][s];
			var isFunc = (referenced.constructor == Function);
			retVal = function(i) {
				return isFunc ? i[s]() : i[s];
			};
		}

		retVal.reverseVal = reverse ? -1 : 1;

		return retVal;
	});

	//FIGURE OUT VALUES TO BE COMPARED
	var compValues = copy.process(function(i) {
		var curValues = sorters.process(function(s) { return s(i); } );
		curValues.item = i;
		return curValues;
	});

	var jsSorter = function(first, second) {
		var retVal = 0;
		var index = 0;
		while (!retVal && (index < first.length)) {
			retVal = cmp(first[index], second[index])
			   * sorters[index].reverseVal;
			index++;
		}

		return retVal;
	};

	compValues.sort(jsSorter);

	//I'D LOVE TO KNOW IF I CAN process STUFF AND KEEP THE this.
	for (var cv=0; cv < compValues.length; cv++)
		this.push(compValues[cv].item);

	return this;
}

//RETURNS AN ARRAY OF INDICES OF OCCURRENCES OF val IN THE ARRAY.
Array.prototype.indicesOf = function(val, func) {
	var indices = [];
	var foundAt = false;
	do {
		foundAt = this.indexOf(val, foundAt || 0, func);
		if (foundAt !== false) {
			indices.push(foundAt);
			foundAt++;
		}
	} while (foundAt !== false);

	return indices;
}

if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(val, startFrom, func) {
		//ITERATE THROUGH AND FIND WHEN val OCCURS
		var i = startFrom || 0;
		var foundAt = false;
		while ((foundAt === false) && (i < this.length)) {
			var valToCompare = func ? func(this[i]) : this[i];
			if (valToCompare === val) foundAt = i;
			i++;
		}

		return foundAt;
	}
}

if (!Array.prototype.lastIndexOf) {
	Array.prototype.lastIndexOf = function(val, startFrom, func) {
		var reverseStartFrom = startFrom && ((this.length - 1) - startFrom);
		var reversed = this;
		reversed.reverse();
		var reverseIndexOf = reversed.indexOf(val,reverseStartFrom,func);

		return (reverseIndexOf && ((this.length - 1) - reverseIndexOf));
	}
}

Array.prototype.has = function(arrayElement,func) {
	return (this.indexOf(arrayElement,func) !== false);
}

//A WRAPPER AROUND Array.prototype.remove; THIS DOESN'T AFFECT THE ORIGINAL
//ARRAY.
Array.prototype.omit = function() {
	var newArray = clone(this);

	return newArray.removeByValue.apply(newArray,arguments);
}
Array.prototype.except = Array.prototype.omit;

//EMPTIES AN ARRAY, RETURNING ALL THE OLD VALUES (BASICALLY SWAPPING OUT
//"THIS" ARRAY FOR AN EMPTY ONE)
Array.prototype.makeEmpty = function() {
	var values = [];
	while (this.length > 0) values.push(this.shift());

	return values;
}

//REMOVES ONE OR MORE VALUES, GIVEN BY INDEX, FROM AN ARRAY AND
//RETURNS THE SHORTENED ARRAY.
//TO COMBINE WITH ANOTHER FUNCTION, LOOK AT THE apply METHOD OF Function;
//FOR EXAMPLE, someArray.remove.apply(someArray,someArray.indicesOf('foo'))
Array.prototype.removeByIndex = function() {
	var argsArray = makeArray(arguments);
	var copyOfThis = this.makeEmpty();

	for (var i=0; i < copyOfThis.length; i++)
		if (!argsArray.has(i))
			this.push(copyOfThis[i]);

	return this;
}
Array.prototype.remove = Array.prototype.removeByIndex;

Array.prototype.removeByValue = function(val, all) {
	if (all) {
		this.removeByIndex.apply(this, this.indicesOf(val));
	} else {
		this.removeByIndex(this.indexOf(val));
	}

	return this;
}

Array.prototype.last = function() {
	return this[this.length - 1];
}

//CURRENT ARRAY BECOMES KEYS AND otherArray BECOMES VALUES OF A NEW HASH
Array.prototype.combine = function(otherArray) {
	var newObject = false;

	if (this.length == otherArray.length) {
		newObject = {};
		for (var i=0; i < this.length; i++)
			newObject[this[i]] = otherArray[i];
	}

	return newObject;
}

//SET THEORY STUFF
Array.prototype.union = function(otherArray) {
	var thisDiffOther = this.process(function(i) {
		return otherArray.has(i);
	});

	return this.concat(thisDiffOther);
}
Array.prototype.intersection = function(otherArray) {
	var newArray = [];

	this.process( function(i) { if (otherArray.has(i)) newArray.push(i); } );

	return newArray;
}

//SHOULD TAKE IN AN ARRAY ELEMENT AND RETURN A BOOLEAN.
//THIS WORKS FOR OR BECAUSE (a || b) <=> !(!a && !b).
Array.__utils_logical = function(theArray, toAnd, func) {
	var i=0;
	var retVal = true;
	while (retVal && (i < theArray.length)) {
		var newAnd = func ? func(theArray[i]) : theArray[i];
		retVal = retVal && (toAnd ? newAnd : !newAnd);
		i++;
	}

	return (toAnd ? retVal : !retVal);
}
//THESE CAN BE DONE WITH JUST THE select METHOD, BUT IT'S FASTER THIS WAY.
//THEY ARE MOST USEFUL IN CONJUCTION WITH process.
Array.prototype.logicalAnd = function(func) {
	return Array.__utils_logical(this, true, func);
}
Array.prototype.logicalOr = function(func) {
	return Array.__utils_logical(this, false, func);
}
if (!Array.prototype.every) {
	Array.prototype.every = Array.prototype.logicalAnd;
}
if (!Array.prototype.some) {
	Array.prototype.some = Array.prototype.logicalOr;
}

//RETURNS ALL VALUES THAT, RUN THROUGH THE OPTIONAL func,
//EVALUATE TO BOOLEAN TRUE
if (!Array.prototype.filter) {
	Array.prototype.filter = function(func) {
		func = func || function(val) { return val; };

		var returnArray = [];
		var processed = this.process(func);
		for (var i=0; i < this.length; i++)
			if (processed[i]) returnArray.push(this[i]);

		return returnArray;
	}
}

//RETURNS AN ARRAY OF OUTPUT VALUES FROM THE SUPPLIED FUNCTION, WHICH CAN HAVE
//2 ARGUMENTS: A "CURRENT VALUE", AND A "CURRENT INDEX".
//DUPLICATES THE ARRAY IF NO FUNCTION SUPPLIED
Array.prototype.process = function(func) {
	func = func || function(val) {return val};

	var returnedArray = [];
	for (var i=0; i < this.length; i++)
		returnedArray.push(func(this[i],i));

	return returnedArray;
}
if (!Array.prototype.map) {
	Array.prototype.map = Array.prototype.process;
}
if (!Array.prototype.forEach) {
	Array.prototype.forEach = function(func, thisObject) {
		var copy = this;
		copy.process(func, thisObject);
	}
}

//FOR COMPATIBILITY WITH IE5/MAC, AMONG OTHERS
if (!Array.prototype.push)
	Array.prototype.push = function() {
		for (var a=0; a < arguments.length; a++)
			this[this.length] = arguments[a];
		return this.length;
	}
if (!Array.prototype.pop)
	Array.prototype.pop = function() {
		delete this[--this.length];
	}

String.prototype.toArray = function() {
	return this.match(/./g);
}
String.prototype.inetAtoN = function() {
	var ipSplit = this.match(/^(\d+)\D(\d+)\D(\d+)\D(\d+)$/);
	var ipNumber = 0;

	if (ipSplit.length > 1) {
		ipSplit.shift();

		//CAN'T USE BIT-SHIFT HERE BECAUSE OF INTEGER SIZE PROBLEMS....
		for (var i=0; i < ipSplit.length; i++)
			ipNumber += ipSplit[3-i]*Math.pow(2,8*i);
	} else ipNumber = false;

	return ipNumber;
}

String.prototype.enetAtoN = function() {
	var inputRadix = (arguments.length > 0) ? arguments[0] : 16;
	var enetStripped = this.replace(/[^\da-fA-F]/g,'');
	var enetNumber = 0;
	var enetSplit = new Array;

	//ALL FORMATS EXCEPT UNSTRIPPED HAVE SEPARATORS BETWEEN OCTETS
	if ((inputRadix == 16) &&(enetStripped.length == 12)) {
		for (var i=0; i < enetStripped.length; i += 2)
			enetSplit.push(enetStripped.substr(i,2));
	} else enetSplit = this.split(/[^\da-fA-F]/);

	for (var e=0; e < 6; e++)
		enetNumber += parseInt(enetSplit[5-e],inputRadix)*Math.pow(2,8*e);

	return parseInt(enetNumber);
} 

String.prototype.lpad = function(totalWidth) {
	var padding = ((arguments.length > 1) ? arguments[1].toString() : ' ');
	var thisCopy = this;

	while (thisCopy.length < totalWidth) thisCopy = padding + thisCopy;

	return thisCopy;
}

Number.prototype.inetNtoA = function() {
	var ipAsciiSplit = new Array;
	var remaining = this;
	var current;
	for (var i=3; i >= 0; i--) {
		current = Math.floor(remaining / Math.pow(2,8*i));
		ipAsciiSplit.push(current);
		remaining -= current * Math.pow(2,8*i);
	}

	return ipAsciiSplit.join('.');
}

Number.prototype.ordinalSuffix = function() {
	var suffix = false;

	if (parseInt(this) == this) {
		var thisStr = this.toString();
		var tens = thisStr.charAt(thisStr.length - 2);

		if (tens != '1') {
			var lastDigit = thisStr.charAt(thisStr.length - 1);
			if ((lastDigit >= 1) && (lastDigit <= 3))
				suffix = ['st','nd','rd'][parseInt(lastDigit)-1];
		}

		if (!suffix) suffix = 'th';
	}

	return suffix;
}
Number.prototype.toOrdinalString = function() {
	var ordinalSuffix = this.ordinalSuffix();

	return ordinalSuffix && (this.toString()+ordinalSuffix);
}
Number.prototype.between = function(first,second) {
	return (Math.min(first,second) <= this)
	   && (this <= Math.max(first,second));
}

Date.monthNames = [
   'January','February','March','April','May','June',
   'July','August','September','October','November','December'
];
Date.dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday',
   'Friday','Saturday'
];
Date.prototype.getMonthName = function() {
	return Date.monthNames[this.getMonth()];
}
Date.prototype.getDayName = function() {
	return Date.dayNames[this.getDay()];
}

Date.prototype.format = function(inString) {
	var formatted = '';
	var pad;

	for (var c=0; c < inString.length; c++) {
		var curChar = inString.charAt(c);
		if (curChar == '%') {
			var nextChar = inString.charAt(c+1);
			var ctrlChar;

			if (nextChar == '-') {
				pad = false;
				ctrlChar = inString.charAt(c+2);
				c++;
			} else if (nextChar == '_') {
				pad = ' ';
				ctrlChar = inString.charAt(c+2);
				c++;
			} else {
				pad = '0';
				ctrlChar = nextChar;
			}

			function numFormat(number,digits) {
				var negative = (number < 0);
				return pad
				   ? (negative ? '-' : '')
				      + Math.abs(number).toString().lpad(digits,pad)
				   : number;
			}

			c++;

			switch (ctrlChar) {
			case 'a':
				formatted += this.getDayName().slice(0,3);
				break;
			case 'A':
				formatted += this.getDayName();
				break;
			case 'b':
			case 'h':
				formatted += this.getMonthName().slice(0,3);
				break;
			case 'B':
				formatted += this.getMonthName();
				break;
			case 'c':
				formatted += this.toString();
				break;
			case 'C':
				formatted += numFormat(parseInt(this.getFullYear()/100),2);
				break;
			case 'd':
				formatted += numFormat(this.getDate(),2);
				break;
			case 'D':
			case 'x':
				formatted += this.format("%m/%d/%y");
				break;
			case 'e':
				formatted += this.format('%_d');
				break;
			case 'F':
				formatted += this.format('%Y-%m-%d');
				break;
//			case 'g':
//			case 'G':
			case 'H':
				formatted += numFormat(this.getHour(),2);
				break;
			case 'I':
				var simpleHour = this.getHour();
				var modHour;
				if (simpleHour == 0) {
					modHour = 12;
				} else if (simpleHour > 12) {
					modHour -= 12;
				} else {
					modHour = simpleHour;
				}

				formatted += numFormat(modHour,2);
				break;
			case 'j':
				var yearDay = this.getDate();
				var monthNum = this.getMonth();

				//ADD IN DAYS FROM MONTHS PAST
				for (var m=0; m < monthNum; m++) {
					yearDay += [1,3,5,8,10].has(monthNum)
					   ? (monthNum == 1)
					      ? ((this.getFullYear % 2) ? 28 : 29)
					      : 30
					   : 31
					;
				}

				formatted += numFormat(yearDay,3);
				break;
			case 'k':
				formatted += this.format("%_H");
				break;
			case 'l':
				formatted += this.format("%_I");
				break;
			case 'm':
				formatted += numFormat(this.getMonth() + 1,2);
				break;
			case 'M':
				formatted += numFormat(this.getMinutes(),2);
				break;
			case 'n':
				formatted += "\n";
				break;
			case 'N':
				formatted += numFormat(this.getMilliseconds(),3)
				   + '000000';
				break;
			case 'p':
				formatted += (this.getHours() > 11) ? 'PM' : 'AM';
				break;
			case 'P':
				formatted += this.format("%p").toLowerCase();
				break;
			case 'r':
				formatted += this.format("%I:%M:%S %p");
				break;
			case 'R':
				formatted += this.format("%H:%M");
				break;
			case 's':
				formatted += parseInt(this.getTime() / 1000);
				break;
			case 'S':
				formatted += numFormat(this.getSeconds(),2);
				break;
			case 't':
				formatted += "\t";
				break;
			case 'T':
			case 'X':
				formatted += this.format("%H:%M:%S");
				break;
			case 'u':
				var plainDay = this.getDay();
				formatted += (plainDay == 0) ? 7 : plainDay;
				break;
//			case 'U':
//			case 'V':
			case 'w':
				formatted += this.getDay();
				break;
//			case 'W':
			case 'y':
				formatted += this.getFullYear().toString().substr(-2);
				break;
			case 'Y':
				formatted += this.getFullYear();
				break;
			case 'z':
//				formatted += this.toString().match(/GMT(.*)\s/)[1];
				var minutesOff = this.getTimezoneOffset();
				formatted += numFormat(-1*minutesOff/60, 2)+'00';
				break;
			case 'Z':
				break;
			default:
				formatted += "%"+ctrlChar;
			}
		} else {
			formatted += curChar;
		}
	}

	return formatted;
}

Date.prototype.addInterval = function(multiplier, intervalType) {
	var copy = new Date(this.toString());
	copy.setMilliseconds(this.getMilliseconds());

	var funcs = {
		year: 'FullYear',
		month: 'Month',
		day: 'Date',
		hour: 'Hours',
		minute: 'Minutes',
		second: 'Seconds',
		millisecond: 'Milliseconds'
	};

	var funcSuffix = funcs[intervalType];
	copy['set'+funcSuffix](copy['get'+funcSuffix]() + parseInt(multiplier));

	return copy;
}

//THANKS TO CAIO CHASSOT FOR THIS WONDERFUL TRICK
fauxEvents = {
	IE: function (element) {
		this.type = window.event.type;
		this.bubbles = true;

		this.currentTarget = element;
		this.target = window.event.srcElement;
		this.relatedTarget = window.event.fromElement;
		this.relatedTarget = window.event.toElement;
		this.preventDefault = function() {window.event.returnValue = false}
		this.stopPropagation = function() {window.event.cancelBubble = true}
		this.altKey = event.altKey;
		this.ctrlKey = event.ctrlKey;
		this.shiftKey = event.shiftKey;
		this.clientX = event.clientX;
		this.clientY = event.clientY;

		return this;
	},
	legacy: function (element) {
		this.currentTarget = element;
	}
};

//AN ARRAY OF {elem: ..., listeners: []}
try {
	window.XBlisteners = [];
} catch(e) {}

function XBaddEventListener(elem, eType, func, capture, force) {
	if (elem.addEventListener) {
		elem.addEventListener(eType, func, capture);
	} else {
		var XBdomType = elem.attachEvent ? 'IE' : 'legacy';

		//W3C DOM SAYS NO DUPLICATE EVENTS. (IF YOU REALLY MEAN IT, WRAP
		//DUPLICATES LIKE SO: function(e) { ... })
		var alreadyDone = elem.XBlisteners && elem.XBlisteners[eType]
		   && elem.XBlisteners[eType].has(func);

		if (!alreadyDone) {
			var Event = fauxEvents[XBdomType];

			if (!elem.XBlisteners) {
				elem.XBlisteners = {};
			}

			if (!elem.XBlisteners[eType]) {
				elem.XBlisteners[eType] = [];

				var wrapperFunc = function() {
					elem.XBlisteners[eType].process(
						function(lstr) { lstr( new Event(elem) ); }
					);
				};

				var legacyEventType = 'on'+eType;
				if (elem.attachEvent) {
					elem.attachEvent(legacyEventType, wrapperFunc);
				} else {
					elem[legacyEventType] = wrapperFunc;
				}
			}

			elem.XBlisteners[eType].push(func);
		}
	}
}
function XBremoveEventListener (elem, eType, func, capture) {
	if (elem.addEventListener) {
		elem.removeEventListener(eType, func, capture);
	} else {
		elem.XBlisteners[eType].removeByValue(func);
	}
}

/*
XBaddEventListener = function(elem, eType, func, capture, force) {
	var onEType = 'on'+eType;

	//I DON'T LIKE undefined AS AN ASSIGNED VALUE
	var toAdd = force || isUndefined(force);

	if (elem.addEventListener) {  //W3C DOM
		if (isUndefined(capture)) capture = false;
		if (toAdd) elem.addEventListener(eType, func, capture);
		else elem.removeEventListener(eType, func, capture);
	} else if (elem.attachEvent) {   //M$ DOM
		if (toAdd) {
			//WE NEED TO ATTACH TWO ARRAYS TO THE ELEMENT TO MAKE EVENTS
			//REMOVABLE: ONE CONTAINING SUPPLIED FUNCTIONS, AND ANOTHER
			//CONTAINING WRAPPER FUNCTIONS WITH THE FAUX EVENT OBJECTS
			if (isUndefined(elem[onEType+'WrapperFuncs']))
				elem[onEType+'WrapperFuncs'] = new Array;
			if (isUndefined(elem[onEType+'SuppliedFuncs']))
				elem[onEType+'SuppliedFuncs'] = new Array;
			elem[onEType+'SuppliedFuncs'].push(func);

			var wrapper = function() { func( new ieEvent(elem)); };
			elem[onEType+'WrapperFuncs'].push(wrapper);

			elem.attachEvent(onEType,elem[onEType+'WrapperFuncs'].last());
		} else {
			var removeIndex =
			   elem[onEType+'SuppliedFuncs'].indicesOf(func).last();
			var removeWrapper = elem[onEType+'WrapperFuncs'][removeIndex];
			elem.detachEvent(onEType, removeWrapper);
			elem[onEType+'SuppliedFuncs'].replace(func);
			elem[onEType+'WrapperFuncs'].replace(removeWrapper);
		}
	} else elem[onEType] = (toAdd ? func : null);   //"TRADITIONAL"
}

XBremoveEventListener = function(elem,eType,func,capture) {
	if (isUndefined(capture)) capture = false;
	XBaddEventListener(elem,eType,func,capture,false);
}
*/

//BROWSER-NEUTRAL FUNCTION TO GET TEXT INSIDE AN ELEMENT
XBinnerText = function(elem) {
	var returnVal = '';

	if (typeof elem == 'string') {
		returnVal = elem;
	} else if (typeof elem == 'undefined') {
		returnVal = elem;
	} else if (elem.innerText) {
		returnVal = elem.innerText;
	} else {
		var cs = elem.childNodes;
		for (var i=0; i < cs.length; i++)
			switch (cs[i].nodeType) {
				case 1: //ELEMENT NODE
					returnVal += XBinnerText(cs[i]);
					break;
				case 3: //TEXT NODE
					returnVal += cs[i].nodeValue;
					break;
			}
	}

	return returnVal;
}

XBgetPropertyValue = function(elem,property) {
//	return (elem.style && elem.style.getPropertyValue)
//	   ? document.defaultView.getComputedStyle(elem,'')
//	      .getPropertyValue(property)
//	   : (elem[XBgetPropertyValue.ie[property]] || false)
	   return (elem[XBgetPropertyValue.ie[property]] || false)
	;
}
XBgetPropertyValue.ie = {
	top: 'offsetTop',
	left: 'offsetLeft',
	right: 'offsetRight',
	bottom: 'offsetBottom'
};

//SINCE IE5 DOESN'T SUPPORT THE undefined KEYWORD
isUndefined = function(v) {
	var undef;
	return (v === undef);
}

//METHODS TO ABSTRACT AWAY HTML CLASS HANDLING
//splitClasses FIRST TESTS TO SEE IF classSplit EXISTS, SO IT DOUBLES AS A
//CHECKER TO SEE IF CLASSES ARE ALREADY SPLIT. BY THIS METHOD ALL ELEMENTS
//CAN USE THESE METHODS, EVEN DYNAMICALLY GENERATED ONES.
splitClasses = function(elem) {
	var hasClassSplit = !!elem.classSplit;
	if (!hasClassSplit) {
		var hasClass = !isUndefined(elem.className);
		if (hasClass)
			elem.classSplit = (elem.className === '')
			   ? []
			   : elem.className.split(/\s+/);
	}

	return elem.classSplit || false;
};
classHas = function(elem,theClass) {
	return splitClasses(elem) && elem.classSplit.has(theClass);
}
//ADDS ANY NUMBER OF CLASSES TO AN ELEMENT (GIVEN AFTER THE FIRST ARGUMENT)
classAdd = function(elem) {
	if (splitClasses(elem)) {
		for (var a=1; a < arguments.length; a++)
			if (!classHas(elem,arguments[a]))
				elem.classSplit.push(arguments[a]);
		elem.className = elem.classSplit.join(' ');
	}

	return elem.classSplit || false;
}
//REMOVES ANY NUMBER OF CLASSES FROM AN ELEMENT, LIKE classAdd
classRemove = function(elem) {
	if (splitClasses(elem)) {
		for (var a = 1; a < arguments.length; a++)
			elem.classSplit.remove.apply(
			   elem.classSplit,
			   elem.classSplit.indicesOf(arguments[a])
			);
		elem.className = elem.classSplit.join(' ');
	}

	return elem.classSplit || false;
}
