/***********************************************************************/
/*                                                                     */
/*   UTIL FUNCTIONS                                                    */
/*                                                                     */
/***********************************************************************/
// Util functions - these are GLOBAL so they
// look like built-in functions.
// ==========================================

// Determine if an object is an array
function isArray(o) {
	return (o!=null && typeof(o)=="object" && typeof(o.length)=="number" && defined(o[0]));
}

// Determine if an object is an Object
function isObject(o) {
	return (o!=null && typeof(o)=="object" && defined(o.constructor) && o.constructor==Object);
}

// Determine if a reference is defined
function defined(o) {
	return (typeof(o)!="undefined");
}

// Iterate over an array, object, or list of items and run code against each item
// Similar functionality to Perl's map() function
function map(func) {
	var i,j,o;
	var results = [];
	if (typeof(func)=="string") {
		func = new Function('$_',func);
	}
	for (i=1; i<arguments.length; i++) {
		o = arguments[i];
		if (isArray(o)) {
			for (j=0; j<o.length; j++) {
				results[results.length] = func(o[j]);
			}
		}
		else if (isObject(o)) {
			for (j in o) {
				results[results.length] = func(o[j]);
			}
		}
		else {
			results[results.length] = func(o);
		}
	}
	return results;
}
// Sample tests
//var o = { 'a':'a1', 'b':'b1', 'c':'c1' };
//var a = [ 'a','b','c' ];
//map( 'alert($_)', o , a, "test" );
//map( function(o){ alert('Value:'+o); } , o , a, "test" );
//map( "alert($_);", map( "return $_.toUpperCase();" , o, a, "test") );


// A quick selector function based on prototype's $() function.
// This function will also expand arrays and objects
function $() {
	var results = new Array();
	var i,j,o;
	for (var i=0; i<arguments.length; i++) {
		var o = arguments[i];
		if (typeof(o)=='string') {
			if (document.getElementById) {
				o = document.getElementById(o);
			}
			else if (document.all) {
				o = document.all[o];
			}
			if (arguments.length==1) {
				return o;
			}
			results[results.length] = o;
		}
		else if (isArray(o)) {
			for (j=0; j<o.length; j++) {
				results[results.length] = o[j];
			}
		}
		else if (isObject(o)) {
			for (j in o) {
				results[results.length] = o[j];
			}
		}
		else if (arguments.length==1) {
			return o;
		}
		else {
			results[results.length] = o;
		}
  }
  return results;
}

// Set default values in an object if they are undefined
function setDefaultValues(o,values) {
	if (!defined(o) || o==null) {
		o = new Object();
	}
	if (!defined(values) || values==null) {
		return o;
	}
	for (var val in values) {
		if (!defined(o[val])) {
			o[val] = values[val];
		}
	}
	return o;
}

/***********************************************************************/
/*                                                                     */
/*   DOM FUNCTIONS                                                     */
/*                                                                     */
/***********************************************************************/
var DOM = (function() { 
	var dom = {};
	
	// Get a parent tag with a given nodename
	dom.getParentByTagName = function(o,tagName) {
		if(o==null) { return null; }
		tagName = tagName.toUpperCase();
		while (o=o.parentNode) {
			if (o.nodeName && tagName==o.nodeName) {
				return o;
			}
		}
		return null;
	}
	
	// Remove a node from its parent
	dom.removeNode = function(o) {
		if (o!=null && o.parentNode && o.parentNode.removeChild) {
			o.parentNode.removeChild(o);
			return true;
		}
		return false;
	}

	// Get the outer width in pixels of any object, including borders, padding, and margin
	dom.getOuterWidth = function(o) {
		if (typeof(o.offsetWidth)!="undefined") {
			return o.offsetWidth;
		}
		return null;
	}

	// Get the outer height in pixels of any object, including borders, padding, and margin
	dom.getOuterHeight = function(o) {
		if (typeof(o.offsetHeight)!="undefined") {
			return o.offsetHeight;
		}
		return null;
	}
	
	return dom;
})();

/***********************************************************************/
/*                                                                     */
/*   CSS FUNCTIONS                                                     */
/*                                                                     */
/***********************************************************************/
// Functions for interacting with CSS from JS
// ==========================================
var CSS = (function(){
	var css = {};

	// Determine if an object or class string contains a given class.
	css.hasClass = function(obj,className) {
		var re = new RegExp("(^|\\s)" + className + "(\\s|$)");
		if (typeof(obj)=="string") {
			return re.test(obj);
		}
		else if (typeof(obj)=="object" && obj.className) {
			return re.test(obj.className);
		}
		return null;
	}
	
	// Add a class to an object
	css.addClass = function(obj,className) {
		if (typeof(obj)!="object" || typeof(obj.className)=="undefined") { return false; }
		if (obj.className==null) { 
			obj.className = className; 
			return true; 
		}
		if (this.hasClass(obj,className)) { return false; }
		obj.className = obj.className + " " + className;
		return true;
	}
	
	// Remove a class from an object
	css.removeClass = function(obj,className) {
		if (typeof(obj)!="object" || !obj.className) { return false; }
		if (!this.hasClass(obj,className)) { return false; }
		var classString = obj.className;
		var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");
		classString = classString.replace(re,' ');
		obj.className = classString;
		return true;
	}
	
	// Fully replace a class with a new one
	css.replaceClass = function(obj,className,newClassName) {
		if (typeof(obj)!="object" || !obj.className) { return false; }
		css.removeClass(obj,className);
		css.addClass(obj,newClassName);
		return true;
	}
	
	// Get the computed style of an object
	css.getStyle = function(o, property) {
		if (o==null) { return null; }
		val = null;
		// Handle "float" property as a special case
		if (property=="float") {
			val = css.getStyle(o,"cssFloat");
			if (val==null) { 
				val = css.getStyle(o,"styleFloat"); 
			}
		}
		else if (o.currentStyle && typeof(o.currentStyle[property])!="undefined") {
			val = o.currentStyle[property];
		}
		else if (window.getComputedStyle) {
			val = window.getComputedStyle(o,null).getPropertyValue(property);
		}
		else if (o.style && typeof(o.style[property])!="undefined") {
			val = o.style[property];
		}
		return val;;
	}

	// Set a style on an object
	css.setStyle = function(o, property, value) {
		if (o==null || typeof(o.style)=="undefined") { return false; }
		if (property=="float") {
			o.style["cssFloat"] = value;
			o.style["styleFloat"] = value;
		}
		else if (property=="opacity") {
			o.style['-moz-opacity'] = value;
			o.style['-khtml-opacity'] = value;
			o.style.opacity = value;
			if (defined(o.style.filter)) {
				o.style.filter = "alpha(opacity=" + value*100 + ")";
			}
		}
		else {
			o.style[property] = value;
		}
		return true;
	}
	
	// Get a unique ID which doesn't already exist on the page
	css.uniqueIdNumber=1000;
	css.createId = function(o) {
		var id = null;
		while (id==null || document.getElementById(id)!=null) {
			id = "ID_"+(css.uniqueIdNumber++);
		}
		if (typeof(o)!="undefined" && o!=null && (typeof(o.id)=="undefined"||o.id=="")) {
			o.id = id;
		}
		return id;
	}
	
	return css;
})();


/***********************************************************************/
/*                                                                     */
/*   EVENT FUNCTIONS                                                   */
/*                                                                     */
/***********************************************************************/
// Functions for interacting with CSS from JS
// ==========================================
var Event = (function(){

	// Calling Event() with an event object will normalize it for IE and others
	// --------------------------------------------------------------------
	var e = function(e) {
		if (!defined(e) && defined(window.event)) {
			e = window.event;
		}
		return e;
	}
	
	// Add an event handler to a function
	// --------------------------------------------------------------------
	e.add = function( obj, type, fn, capture ) {
		if (obj.addEventListener) {
			obj.addEventListener( type, fn, capture );
		}
		else if (obj.attachEvent) {
			obj["x"+type+fn] = fn;
			obj[type+fn] = function() { obj["x"+type+fn]( window.event ); }
			obj.attachEvent( "on"+type, obj[type+fn] );
		}
	}

	// Get the mouse position of an event
	// --------------------------------------------------------------------
	e.getMouseX = function(e) {
		if (typeof(e.pageX)!="undefined") {
			return e.pageX;
		}
		if (typeof(e.clientX)!="undefined") {
			return e.clientX+Screen.getScrollLeft();
		}
		return null;
	}
	e.getMouseY = function(e) {
		if (typeof(e.pageY)!="undefined") {
			return e.pageY;
		}
		if (typeof(e.clientY)!="undefined") {
			return e.clientY+Screen.getScrollTop();
		}
		return null;
	}

	// Stop the event from bubbling up to parent elements.
	// Two method names map to the same function
	// --------------------------------------------------------------------
	e.cancelBubble = function(e) {
		if (typeof(e.stopPropagation)=="function") { e.stopPropagation(); } 
		if (defined(e.cancelBubble)) { e.cancelBubble = true; }
	}
	e.stopPropagation = e.cancelBubble;
		
	
	
	return e;
})();


/***********************************************************************/
/*                                                                     */
/*   SCREEN FUNCTIONS                                                  */
/*                                                                     */
/***********************************************************************/
// Functions for find details about the user's screen
// ==================================================
var Screen = (function() {

	var screen = {};

	// Get a reference to the body
	// --------------------------------------------------------------------
	screen.getBody = function() {
		if (document.body) {
			return document.body;
		}
		if (document.getElementsByTagName) {
			var bodies = document.getElementsByTagName("BODY");
			if (bodies!=null && bodies.length>0) {
				return bodies[0];
			}
		}
		return null;
	}

	// Get the amount that the main document has scrolled from top
	// --------------------------------------------------------------------
	screen.getScrollTop = function() {
		if (document.documentElement && defined(document.documentElement.scrollTop) && document.documentElement.scrollTop>0) {
			return document.documentElement.scrollTop;
		}
		if (document.body && defined(document.body.scrollTop)) {
			return document.body.scrollTop;
		}
		return null;
	}
	
	// Get the amount that the main document has scrolled from left
	// --------------------------------------------------------------------
	screen.getScrollLeft = function() {
		if (document.documentElement && defined(document.documentElement.scrollLeft) && document.documentElement.scrollLeft>0) {
			return document.documentElement.scrollLeft;
		}
		if (document.body && defined(document.body.scrollLeft)) {
			return document.body.scrollLeft;
		}
		return null;
	}
	
	// Util function to default a bad number to 0
	// --------------------------------------------------------------------
	function zero(n) {
		return (typeof(n)=="undefined" || isNaN(n))?0:n;
	}

	// Get the width of the entire document
	// --------------------------------------------------------------------
	screen.getDocumentWidth = function() {
		var width = 0;
		if (!document.compatMode || document.compatMode=="CSS1Compat") {
		    var rightMargin = parseInt(CSS.getStyle(document.body,'marginRight'),10) || 0;
		    var leftMargin = parseInt(CSS.getStyle(document.body,'marginLeft'), 10) || 0;
			width = Math.max(document.body.offsetWidth + leftMargin + rightMargin, document.documentElement.clientWidth);
		}
		else {
			width =  Math.max(document.body.clientWidth, document.body.scrollWidth);
		}
		if (isNaN(width) || width==0) {
			width = zero(self.innerWidth);
		}
		return width;
	}
	
	// Get the height of the entire document
	// --------------------------------------------------------------------
	screen.getDocumentHeight = function() {
		var innerHeight = (typeof(self.innerHeight)!="undefined"&&!isNaN(self.innerHeight))?self.innerHeight:0;
		if (!document.compatMode || document.compatMode=="CSS1Compat") {
		    var topMargin = parseInt(CSS.getStyle(document.body,'marginTop'),10) || 0;
		    var bottomMargin = parseInt(CSS.getStyle(document.body,'marginBottom'), 10) || 0;
			return Math.max(document.body.offsetHeight + topMargin + bottomMargin, document.documentElement.clientHeight, document.documentElement.scrollHeight, zero(self.innerHeight));
		}
		return Math.max(document.body.scrollHeight, document.body.clientHeight, zero(self.innerHeight));
	}
	
	// Get the width of the viewport (viewable area) in the browser window
	// --------------------------------------------------------------------
	screen.getViewportWidth = function() {
		if (!document.compatMode || document.compatMode=="CSS1Compat") {
			return document.documentElement.clientWidth;
		}
		else if (document.compatMode) {
			return document.body.clientWidth;
		}
		return zero(self.innerWidth);
	}
	
	// Get the height of the viewport (viewable area) in the browser window
	// --------------------------------------------------------------------
	screen.getViewportHeight = function() {
		if (!window.opera && (!document.compatMode || document.compatMode=="CSS1Compat")) {
			return document.documentElement.clientHeight;
		}
		else if (document.compatMode && !window.opera) {
			return document.body.clientHeight;
		}
		return zero(self.innerHeight);
	}	

	return screen;
})();


/***********************************************************************/
/*                                                                     */
/*   SORT FUNCTIONS                                                    */
/*                                                                     */
/***********************************************************************/
// Generic sorting functions
// =========================
var Sort = (function(){
	var sort = {};

	sort.AlphaNumeric = function(a,b) {
		if (a==b) { return 0; }
		if (a<b) { return -1; }
		return 1;
	}
	sort.Default = sort.AlphaNumeric;
	
	sort.Numeric = function(a,b) {
		if (typeof(a)!="number") {
			a = parseFloat(a.replace(/,/g,''));
			if (isNaN(a) || a==null) { a=0; }
		}
		if (typeof(b)!="number") {
			b = parseFloat(b.replace(/,/g,''));
			if (isNaN(b) || b==null) { b=0; }
		}
		return a-b;
	}

	sort.IgnoreCase = function(a,b) {
		return sort.AlphaNumeric((""+a).toLowerCase(),(""+b).toLowerCase());
	}

	sort.Currency = function(a,b) {
		a = a.replace(/^[^\d\.]/,'');
		b = b.replace(/^[^\d\.]/,'');
		return sort.Numeric(a,b);
	}
	
	sort.Date = function(a,b) {
		// inner util function to parse date formats
		function getdate(str) {
			// inner util function to convert 2-digit years to 4
			function fixYear(yr) {
				yr = +yr;
				if (yr<50) { yr += 2000; }
				else if (yr<100) { yr += 1900; }
				return yr;
			}
			var ret;
			// YYYY-MM-DD
			if (ret=str.match(/(\d{2,4})-(\d{1,2})-(\d{1,2})/)) {
				return (fixYear(ret[1])*10000) + (ret[2]*100) + (+ret[3]);
			}
			// MM/DD/YY[YY] or MM-DD-YY[YY]
			if (ret=str.match(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})/)) {
				return (fixYear(ret[3])*10000) + (ret[1]*100) + (+ret[2]);
			}
			return 99999999; // So non-parsed dates will be last, not first
		}
		return sort.Numeric(getdate(a),getdate(b));
	}

	return sort;
})();

