/*****************************************************************************/
// utils.js - rev 22 
// Copyright (c) 2008, Mike Helgeson 
// Liscensed under the MIT License (MIT-LICENSE.txt)
// http://www.opensource.org/licenses/mit-license.php
// Created: 2006-12-13 | Updated: 2008-03-17
/*****************************************************************************/
// Evaluator functions
function isDef(o){ return typeof(o)!="undefined"; };
function isNull(o){ return o===null || !isDef(o); };
function isFalse(o){ return o===false; };
function empty(str){ return !isDef(str) || str===null || trim(str).length==0 || ( isObj(str) && !(function(o){ for (var a in o) return true; })(str) ); };	
function isStr(o){ return !isNull(o) && typeof(o)=="string";/* && String(o)===o;*/ };	
function isNum(o){ return typeof(o)=="number" && !isNaN(o);/* && (/^(?:-?\d*\.?\d+(?:e[\+\-]\d+)?)|Infinity$/i).test(o+""); */ };	
function isArr(o){ return o && (o.constructor==Array || (typeof(o)=="object" && isNum(o.length) && (o.length==0 || isDef(o[0])))); };
function isObj(o){ return o && typeof(o)=="object" && isDef(o.constructor) && o.constructor==Object && !isDef(o.nodeName) && !isArr(o); };
function isFunc(fn){ return !!fn && !isStr(fn) && !fn.nodeName && fn.constructor!=Array && (/function/i).test(fn+""); };	
function isBool(o){ return (typeof(o)=="boolean"); };
function isRegExp(o){ return (!isNull(o) && o.constructor==RegExp); };
function isDate(o){ return !isNull(o) && (o instanceof Date) && String(o)!="Invalid Date"; }; // Date Object 
function isNode(o){ return ( !empty(o) && ( isDef(o.nodeType) || isDef(o.nodeName) ) ) || window.screen==o || window.window==o; };
function isTagNode(o){ return !empty(o) && isDef(o.nodeType) && o.nodeType==1; };
function isTextNode(o){ return !empty(o) && isDef(o.nodeType) && o.nodeType==3; };
function tagIs(e){ return e && isDef(e.nodeName) && isTagNode(e) ? e.nodeName.toLowerCase() : null; }; // returns lowercase tagname
function isClass(el,c,s){ return isTagNode(el) && isDef(s=el.className) && new RegExp('(^|\\s)'+escRegExp(c)+'(\\s|$)').test(s); };
function isEmail(s){ return (/^([-\w\.]+)@([a-z0-9\-\.]+\.[a-z0-9]{2,4})$/i).test(trim(s)) ? [RegExp.$1,RegExp.$2] : false; }; // [ username, domain ] or false     
// use on "evaluators" for multiple args, first arg is fn
function All(){ 
	var k = true, a = arguments, f = a[0]; 
	if (a.length==2 && isArr(a[1])) a = a[1];
	for (var i=1; i<a.length && k!==false; i++) k = f(a[i]);
	return k; // all arguments DO match => true
	};
function Not(){ 
	var k = false, a = arguments, f = a[0]; 
	if (a.length==2 && isArr(a[1])) a = a[1];
	for (var i=1; i<a.length && k!==true; i++) k = f(a[i]);
	return !k; // all arguments DO NOT match => true
	};
/*** SLICE ARRAY-LIKE OBJECT INTO AN ARRAY (or clone arrays) ***/
function Slice( arr, start, end ){ 
	return Array.prototype.slice.call( arr, start||0, end||arr.length ); 
	};
/*** TRIM LEADING, TRAILING, AND MULTIPLE SPACES ******************/
function trim(s){ 
	return isStr(s) ? s.replace(/\s{2,}/g," ").replace(/^\s+|\s+$/g,"") : s; 
	};
/*** EXTEND FUNCTIONS AND OBJECTS << just too handy not to borrow from jquery ***/
function Extend(){
	var r=arguments, l=r.length, o=r[0]||{}, d=false, i=1, x; 
	if (isBool(o)){ d = o; o = r[i++]||{}; }  // Extend(true,...) [deep] 
	for (i;i<l;i++)	// each remaining arg
		if ((x=r[i])!=null)	// each argument!=null
			for (var a in x){  // each attribute
				if (o==x[a]) continue; // recursion catch
				else if (d && All(isObj,x[a],o[a])) o[a] = Extend(d,{},o[a],x[a]); // deep extend...
				else if (isDef(x[a])) o[a] = x[a];// normal extend
				}
	return (o);
	};
/*** EACH << iterate a function over objs|arrays, thanks again jquery ***/
function Each(o,fn) {
	if (isArr(o)) for (var i=0, l=o.length, v=o[0]; i<l && fn.call(v,i,v,o)!==false; v=o[++i] ){}
	else if (isObj(o)) for (var i in o) if (fn.call(o[i],i,o[i],o)===false) break;
	return (o);
	};

/** READ URL PARAMETERS ***********************************************************/
function getParams(uri){ // CONSTRUCTOR
	return this instanceof getParams ? (function( t, u ){
		if ( isObj(u) || isArr(u) ) return Extend( t, u );
		var re = (/([^;?&=#]+)=([^;?&=#]+)/), d = decodeURIComponent, i, m,
		u = String( isDef(u) ? u : window.location ), a = u.match(/[;?&][^;?&=#]+=[^;?&=#]+/g);
		for ( i=0; i<( a==null ? 0 : a.length ); i++ ) // each parameter
			if ( m = re.exec(a[i]) ) t[ d( m[1] ) ] = d( m[2]||"" ); // {key:value}
		if ( m = (/#(.*)$/).exec(u) ) t["#"] = d( m[1]||"" ); // {#:fragment}
		return t; // = getParams instance
		})( this, uri ) : new getParams( uri );
	};
getParams.prototype.toString = function(){ // METHOD
	var t = this, e = encodeURIComponent, s = "?", f = "#", a;
	for (a in t) if (a!="toString" && a!=f) s += e(a)+"="+e( t[a] )+"&"; // ?key=value&
	return s.replace((/[&?]$/), ( isDef(t[f]) ? f+e( t[f] ) : "" ) ); // #fragment
	};
/*** USE INSTEAD OF document.getElementById() *********************************/
function getElm(id){ 
	return document.layers ? document[id] :
		document.getElementById ? document.getElementById(id) :
		document.all ? document.all[id] : null; 
	};
/*** DO ROUND/FLOOR/CEIL MATH ROUNDING FUNCTION ***/
(function(w){
	var r = function(n,d,f){ 
		n = parseFloat(n); d = parseFloat(d);
		d = Math.pow(10, isNum(d) ? d : 0 ); 
		return isNum(n) ? Math[f](d*n)/d : NaN;
		};	
	w.Round = function(n,d){ return r(n,d,'round'); };
	w.Floor = function(n,d){ return r(n,d,'floor'); };
	w.Ceil  = function(n,d){ return r(n,d,'ceil');  };	
	})(window)
/*** RANDOM NUMBER between (x...y) ***********************************/
function rand(x,y){ 
	if (isArr(x)) return x[randInt(x.length-1)];
	x = isNum(x=parseFloat(x))?(x):(1); 
	y = isNum(y=parseFloat(y))?(y):(0);
	var a = Math.min(x,y), b = Math.max(x,y)-a;
	return Math.random()*b+a; // [0-1]*(max-min)+min
	};
function randInt(x,y){ return Math.round(rand(!isNum(x)?100:x,y)); };
/*** TEST EXISTENCE OF A CLASSNAME ****************/
function hasClass(c,e) { var s = e.className;
	return isTagNode(e) && isDef(s) ? new RegExp('(^|\\s)'+escRegExp(c)+'(\\s|$)').test(s) : false;
	};
/*** ESCAPE SPECIAL REG EXP CHARACTERS ****************/
function escRegExp(str) {
	var c = "/ . * + ? $ ^ | ( ) [ ] { } \\".replace(/\s/g,"|\\");
	return String(str||"").replace(new RegExp("(\\"+c+")","g"),"\\$1");
	};
/*** AP/PRE-PEND A NUMBER WITH CHARACTERS (default to "0", -2 digits(left)) ***************/
function Pad(s,d,c){
	s = String(s||""); c = String(c||0); 
	d = isNum(d=parseFloat(d)) ? d : -2;
	var t = "", x = Math.abs(d)-s.length; 
	while (t.length<x) t += c; t = t.substr(0,x);
	return d>0 ? s+t : d<0 ? t+s : s;
	};
Pad.dec = function(s,d,c){ 
	s = String(s).split("."); 
	d = isNum(d=parseFloat(d)) ? d : 2; 
	if (d!=0) s[1] = Pad( s[1], d, c );
	return s.join(".");
	};
/*** NUMBER FORMATTER... DEFINE A CURRENCY FORMAT ***/
function NumberFormat(str,opts){ str = String(str); 
	opts = Extend( {}, NumberFormat.Defaults, opts ); // load the options
	var arr, A, Z, re, orig = Extend( {}, opts ), k, d, cnv = opts.convert;  // vars
	return Each( ( arr = str.split(" ") ), function(i,val){ // each word								
		opts = Extend( {}, orig ); val = unNumberFormat( val, opts ); A = ""; Z = ""; // restore options, unFormat val, reset vars
		if ((/^([^\d\-]+)?(-?\d*\.?\d+(?:e[\+\-]\d+)?)([^\d]+)?$/).test(val) ){ A=RegExp.$1; val=RegExp.$2; Z=RegExp.$3; } // save non-numeric chars
		if ((/\w/).test(A+""+Z)||!(/^-?\d*\.?\d+(?:e[\+\-]\d+)?$/).test(val)||!isNum(val=parseFloat(val))) return true; // not a number > continue
		if ( isFunc(cnv) && (val=cnv.call(opts,val))===false ) return true; // if convert fn is false > continue
		if (isNum(opts.rounding) && isNum(val)) val = Round(val,opts.rounding); // rounding fn
		val = Pad.dec( val, isNum(opts.padding)?(opts.padding):(0) ); // padding fn
		k = opts.thousand; d = opts.decimal; re = new RegExp("^((?:.*\\s)*[^\\.]*\\d)(\\d{3})((?:"+escRegExp(k)+"|\\.|\\s).*)?$"); // match numbers > 1000
		if (isStr(k) && (/[^\d\.]+/).test(k)) while (re.test(val)) val = val.replace(re,"$1"+k+"$2$3"); // insert thousand seperators
		if (isStr(d) && (/[^\d\.]+/).test(d)) val = val.replace(/(\d)\.(\d)/g,"$1"+d+"$2"); // replace decimal seperators
		if (opts.moveSign){ opts.prefix = ((/^\-/).test(val)?"-":"")+(opts.prefix||""); val = String(val).replace(/^\-/,""); } // move negative sign
		arr[i] = A + (opts.prefix||"") + val + (opts.suffix||"") + Z; // update formatted value, include stored extra chars, prefix/suffix
		}).join(" ");
	};
NumberFormat.Defaults = {
	rounding: 	null, // number of decimal places to round
	padding:	null, // number of decimal places to pad
	decimal: 	'.', // decimal point seperator
	thousand: 	'', // thousand, million, etc. seperator
	prefix:		'', // number prefix
	suffix: 	'', // number suffix
	moveSign:	false, // true: the negative sign (-) should precede the optional prefix
	convert: 	function(n){ return n; } // fn to convert the output number, return false to skip (scope: options)
	};
function unNumberFormat(str,opts){ str = String(str); 
	opts = Extend( {}, NumberFormat.Defaults, opts ); // load the options
	var dg = "(\\d)", k = opts.thousand, d = opts.decimal, p = opts.prefix, s = opts.suffix; // ref to options
	var remove = function(re,x){ while (re.test(str)) str = str.replace(re,"$1"+(x||"")+"$2"); }; // util
	remove((/(\d)\.0+([^\d]|$)/)); remove((/(\d\.\d*[1-9]+)0+([^\d]|$)/)); // remove trailing zeros (padding)
	if ( isStr(k) && (/[^\d\.]+/).test(k) ) remove(new RegExp(dg+escRegExp(k)+dg)); // remove thousand sep
	if ( isStr(d) && (/[^\d\.]+/).test(d) ) remove(new RegExp(dg+escRegExp(d)+dg),"."); // restore decimal point
	if ( !empty(p) ) remove(new RegExp("([^\\d]|^|-)"+escRegExp(p)+dg)); // remove prefix
	if ( !empty(s) ) remove(new RegExp(dg+escRegExp(s)+"([^\\d]|$)")); // remove suffix
	return str; 
	};
function commas(str){ return NumberFormat(str,{thousand:","}); };
function $cash(str){ return NumberFormat(str,{thousand:",",rounding:2,padding:2,prefix:"$",moveSign:true}); };
/*** ANYTHING TO A STRING ********************************************************/
function toStr(o){ var str;
	if (!isDef(o)) return 'undefined';
	if (isNull(o)) return 'null';
	if (o===Math) return 'Math';
	if (o===window) return 'window';
	if (o===screen) return 'screen';
	if (o===document) return 'document';
	if (o===document.body) return 'document.body';
	if (isNode(o)) return "(NODE) ";
	if (isNum(o)||isBool(o)) return String(o);
	if (isStr(o)) return "'"+o.replace(/\'/g,"\\'")+"'";
	if (isDate(o)) return "(new Date("+o.getTime()+"))";
	if (isArr(o)){ str = "[";
		for (var i=0; i<o.length; i++) str += toStr(o[i])+',';
		return str=="[" ? "([])" : str.replace(/,$/,']');
		}
	if (isObj(o)){ str = "{"; 
		for (var a in o) str += toStr(a)+':'+toStr(o[a])+',';
		return str=="{" ? "({})" : str.replace(/,$/,'}');
		}
	if (isDef(o.toSource)) return o.toSource(); // RegExp, Function
	return o; 
	};
function unStr(s){ 
	if (!isStr(s)||empty(s)) return s;
	try { eval('s=('+s+');'); }
	catch(ex){ s = null; };
	return s;
	};
