//*** This JavaScript library is copyright 2002-2003 by Gavin Kistner and Refinery, Inc.
//*** Re-use or modification require permission
//*** http://www.refinery.com/
//*** mailto:gavin@refinery.com

function FindRule(ss,selText){
	//***Find the CSS rule by its selector text.
	//***e.g. FindRule(document.styleSheets[0],'a.selected').style.fontWeight='bold';
	if (!ss) return;
	if (ss.cssRulesCache==null) ss.cssRulesCache={};
	var cssRulesCache=ss.cssRulesCache;
	//Following line fixes IEMac at the expense of Safari
	//if (navigator && navigator.platform && navigator.platform=="MacPPC") selText = selText.replace(/(^|(\s+))([\#\.])/gi,"$2*$3");
	selText=selText.toLowerCase(); //screw case sensitivity; IE and Mozilla don't agree on case for elements
	if (cssRulesCache[selText]) return cssRulesCache[selText];
	var rules = document.all?ss.rules:ss.cssRules;
	for (var i=0;i<rules.length;i++) if (rules[i].selectorText.toLowerCase()==selText) return cssRulesCache[selText]=rules[i];
	return null;
}
function AddRule(ss,selector,styles){
	//***Cross-browser method for inserting a new rule into an existing stylesheet.
	//***e.g. AddRule(document.styleSheets[0],'a:link','color:blue; text-decoration:underline');
	if (!ss) return false;
	if (ss.insertRule) return ss.insertRule(selector+' {'+styles+'}',ss.cssRules.length);
	if (ss.addRule){
		ss.addRule(selector,styles);
		return true;
	}
	return false;
}


function RadioButtonValue(frm,radioName){
	var radios = frm.elements[radioName];
	for (var i=0,len=radios.length;i<len;i++) if (radios[i].checked) return radios[i].value;
	return null;
}


function FindXY(obj){
	//***Find the x,y location in pixels for a relatively positioned object
	//***returns an object with .x and .y properties.
	if (!obj) return { x:0,y:0 };
	var parentXY = FindXY(obj.offsetParent);
	return {x:obj.offsetLeft+parentXY.x-obj.scrollLeft, y:obj.offsetTop+parentXY.y-obj.scrollTop};
}
function FindXYWH(obj){
	//***Find the x,y location in pixels for a relatively positioned object
	//***returns an object with .x, .y, .w (width) and .h (height) properties.
	if (!obj) return { x:0, y:0, w:0, h:0 };
	var objXY = FindXY(obj);
	return { x:objXY.x, y:objXY.y, w:obj.offsetWidth, h:obj.offsetHeight };
}

function Ancestor(el,tagName,attr,value){
	//***Find an ancestor element of 'el' with the correct tagName and/or where a certain attribute equals the supplied value
	//***e.g. Ancestor(this,'table'); Ancestor(this,null,'className','selected'); FindAncestor(this,'form','autoValidate','true');
	//DebugOut("Ancestor("+(el?el.tagName?(el.tagName+"#"+el.id):el:'null')+",'"+tagName+"','"+attr+"','"+val+"')",4);
	while (el){
		if ((el.tagName||el.getAttribute) && (!tagName||!el.tagName||tagName.toLowerCase()==el.tagName.toLowerCase()) && (!attr||!el.getAttribute||el.getAttribute(attr)==value)) return el;
		el=el.parentNode;
	}
	return null;
}
function Descendant(el,tagName,attr,val){
	//***Find an descendant element of 'el' with the correct tagName and/or where a certain attribute equals the supplied value
	//DebugOut("FindDescendant("+(el?el.tagName?(el.tagName+"#"+el.id):el:'null')+",'"+tagName+"','"+attr+"','"+val+"')",4);

	if (!el) return null;
	var children = el.childNodes;
	for (var i=0,len=children.length;i<len;i++){
		var el = children[i];
		if (el.nodeType!=1) continue;
		if ((!tagName||!el.tagName||tagName.toLowerCase()==el.tagName.toLowerCase()) && (!attr||el.getAttribute(attr)==val)) return el;
	}
	for (var i=0,len=children.length;i<len;i++){
		var el = children[i];
		if (el.nodeType!=1) continue;
		var matchingChild = Descendant(el,tagName,attr,val);
		if (matchingChild) return matchingChild;
	}
	return null;
}
function FindChildWithClass(obj,className){ return Descendant(obj,null,'className',className) }

function AddClass(obj,cName){ KillClass(obj,cName); return obj.className+=(obj.className.length>0?' ':'')+cName; }
function KillClass(obj,cName){ return obj.className=obj.className.replace(new RegExp("^"+cName+"\\b\\s*|\\s*\\b"+cName+"\\b",'g'),''); }
function HasClass(obj,cName){ return (!obj || !obj.className)?false:(new RegExp("\\b"+cName+"\\b")).test(obj.className) }
function RemoveClass(obj,cName){ KillClass(obj,cName) }


Date.prototype.customFormat = function(formatString){
	//***See http://phrogz.net/tmp/JS/FormatDateTime_JS.txt for documentation
	var YYYY,YY,MMMM,MMM,MM,M,DDDD,DDD,DD,D,hhh,hh,h,mm,m,ss,s,ampm,AMPM,dMod,th;
	var dateObject = this;
	YY = ((YYYY=dateObject.getFullYear())+"").substr(2,2);
	MM = (M=dateObject.getMonth()+1)<10?('0'+M):M;
	MMM = (MMMM=["January","February","March","April","May","June","July","August","September","October","November","December"][M-1]).substr(0,3);
	DD = (D=dateObject.getDate())<10?('0'+D):D;
	DDD = (DDDD=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][dateObject.getDay()]).substr(0,3);
	th=(D>=10&&D<=20)?'th':((dMod=D%10)==1)?'st':(dMod==2)?'nd':(dMod==3)?'rd':'th';
	formatString = formatString.replace("#YYYY#",YYYY).replace("#YY#",YY).replace("#MMMM#",MMMM).replace("#MMM#",MMM).replace("#MM#",MM).replace("#M#",M).replace("#DDDD#",DDDD).replace("#DDD#",DDD).replace("#DD#",DD).replace("#D#",D).replace("#th#",th);

	h=(hhh=dateObject.getHours());
	if (h==0) h=24;
	if (h>12) h-=12;
	hh = h<10?('0'+h):h;
	AMPM=(ampm=hhh<12?'am':'pm').toUpperCase();
	mm=(m=dateObject.getMinutes())<10?('0'+m):m;
	ss=(s=dateObject.getSeconds())<10?('0'+s):s;
	return formatString.replace("#hhh#",hhh).replace("#hh#",hh).replace("#h#",h).replace("#mm#",mm).replace("#m#",m).replace("#ss#",ss).replace("#s#",s).replace("#ampm#",ampm).replace("#AMPM#",AMPM);
}
function FormatDateTime(date,str){ return date?date.customFormat(str):'' }

Math.randomMax = function(maxVal,asFloat){
	//***Returns a random number from 0 to maxVal
	//***Pass true as second parameter for asFloat; pass false (or omit) for integer value
	var val = Math.random()*maxVal;
	return asFloat?val:Math.round(val);
}

Number.prototype.roundTo = function(decimals){
	//***Rounds a number to a specific number of decimal places
	//***Supports 'negative' decimals, e.g.
	//***   myNumber.roundToDecimals(-3)
	//***will round to the nearest thousand
	var n=this.valueOf();
	if (!decimals) decimals=0;
	if (n==0) return "0."+((Math.pow(10,decimals)+"").substr(1));
	n=Math.round(n*Math.pow(10,decimals))+"";
	var breakPoint=n.length-decimals;
	return (n.substr(0,breakPoint)+(decimals>0?"."+n.substr(breakPoint):(Math.pow(10,-decimals)+"").substr(1))).replace(/^0+/,'0');
}

String.prototype.insertAt=function(loc,strChunk){
	//***Insert a chunk of text into the middle of another string at the position indicated by 'loc'
	//***Returns the composite string without modifying the original
	return (this.valueOf().substr(0,loc))+strChunk+(this.valueOf().substr(loc))
}

String.prototype.makeASCII = function(){
	//***Attempt to clean up a few common errors intelligently
	var badChars=String.fromCharCode(8211,8212,8216,8217,8220,8221);
	var newChars="--''\"\"";
	var str = this.valueOf();
	for (var i=0,len=badChars.length;i<len;i++) str = str.replace(new RegExp(badChars.charAt(i),"g"),newChars.charAt(i));
	return str.replace(/[^\t-~]/g,"");
}

Array.prototype.randomItem=function(){
	//***Returns a randomly-chosen item from an array
	return this[Math.randomMax(this.length-1)];
}
Array.prototype.applyToEach = function(f){
	//***Runs a function on each member of an array, in order, passing in the value, the index, and whether it is the fist and/or last item
	for (var i=0,len=this.length;i<len;i++) f(this[i],i,i==0,i==len-1)
}
Array.prototype.removeItem=function(el){
	for (var i=0;i<this.length;i++) if (this[i]==el) this.splice(i,1);
}

if (typeof(Array.prototype.push)!='function') Array.prototype.push=function(){
	for (var i=0,n=this.length,len=arguments.length;i<len;i++) this[n++]=arguments[i];
	return n;
}
if (typeof(Array.prototype.splice)=='function' && typeof([0].splice(0))=="number") Array.prototype.splice = null;
if (typeof(Array.prototype.splice)!='function') Array.prototype.splice=function(n,c){
	var l=this.length,g=arguments.length,a,d,t,o,w,z;
	n=(n=(n=isNaN(n*=1)?0:n)<0?l+n:n)<0?0:n;
	c=(c=(c=isNaN(c*=1)?0:c)<0?0:c)>l-n?l-n:c;
	t=n+c;
	a=this.slice(n,t);
	if ((d=g-2-c)>0) for (w=l+d-1,o=l-1;o>=t;) this[w--]=this[o--];
	else for (o=t,w=t+d;o<l;) this[w++]=this[o++];
	for (o=2,w=n;o<g;) this[w++]=arguments[o++];
	this.length=l+d;
	return a;
}
if (typeof(Array.prototype.slice)!='function') Array.prototype.slice=function(s,e){
	var l=this.length;
	s=(s=(s=isNaN(s*=1)?0:s)<0?s+l:s)<0?0:s;
	e=(e=(e=isNaN(e*=1)?0:e)<0?e+l:e)>l?l:e;
	for (var a=[],ct=0,i=s;i<e;i++) a[ct++]=this[i];
	return a;
}

//***Commented out since this is rarely used
/*
Boolean.prototype.XOR=function(bool2){
	var bool1=this.valueOf();
	return (bool1==true && bool2==false) || (bool2==true && bool1==false);
	//return (bool1 && !bool2) || (bool2 && !bool1);
}
*/

function AttachEvent(obj,evt,fnc,useCapture){
	if (!useCapture) useCapture=false;
	//***Cross browser attach event function. For 'evt' pass a string value with the leading "on" omitted
	//***e.g. AttachEvent(window,'load',MyFunction,true);
	//DebugOut('AttachEvent('+obj+',"'+evt+'",'+(fnc==null?null:'[function]')+','+useCapture+')',3);
	if (obj.addEventListener){
		obj.addEventListener(evt,fnc,useCapture);
		return true;
	} else if (obj.attachEvent) return obj.attachEvent("on"+evt,fnc);
	else{
		MyAttachEvent(obj,evt,fnc);
		obj['on'+evt]=function(){ MyFireEvent(obj,evt) };
	}
}
function MyAttachEvent(obj,evt,fnc){
	if (!obj.myEvents) obj.myEvents={};
	if (!obj.myEvents[evt]) obj.myEvents[evt]=[];
	var evts = obj.myEvents[evt];
	evts[evts.length]=fnc;
}
function MyFireEvent(obj,evt){
	if (!obj || !obj.myEvents || !obj.myEvents[evt]) return;
	var evts = obj.myEvents[evt];
	for (var i=0,len=evts.length;i<len;i++) evts[i]();
}


function PreventDefault(evt){
	//***Cross browser function to prevent the default action from occuring.
	if (!evt && window.event) evt=window.event;
	if (evt!=null){
		if (typeof(evt.preventDefault)=='function') evt.preventDefault();
		else evt.returnValue=false;
	}
	return false;
}
function CancelBubble(evt){
	//***Cross browser function to prevent the event from bubbling.
	if (!evt && window.event) evt=window.event;
	if (evt!=null){
		if (typeof evt.stopPropagation=='function') evt.stopPropagation();
		else evt.cancelBubble=true;
	}
	return false;
}


function HandleEventForObject(evt){
	if (!evt && window.event) evt=window.evt;
	if (!evt) return alert("Couldn't find an event object for HandleEventForObject");
	var el = evt.target || evt.srcElement;
	while (el && (!el.jsObj || typeof el.jsObj.handleEvent!='function')) el=el.parentNode;
	if (!el) return alert("Couldn't find a handling object for HandleEventForObject");
	el.jsObj.handleEvent(evt);
}


// *** Commented out because this adds a custom property to every object which shows up when using   for(x in obj)
// *** See http://phrogz.net/JS/Object.prototype.toSourceCode.js for documentation
/*
Object.prototype.toSourceCode=function(hier,lv,forObj){
	var out,tabs='',idre=/^[a-z_][a-z0-9_]*$/i;
	if (!lv) lv=0;
	if (hier) tabs=Math.pow(10,lv).toString().substr(1).replace(/0/g,"\t");
	if (this.constructor==Array){
		out=(forObj?'':tabs)+'['+(hier?'\n':'');
		for (var i=0,len=this.length;i<len;i++) out+=((this[i]!=null)?this[i].toSourceCode(hier,lv+1):'null')+(i<(len-1)?',':'')+(hier?'\n':'');
		return out+tabs+']';
	}else if(this.constructor==Object){
		out=(forObj?'':tabs)+'{'+(hier?'\n':'');
		for (var key in this) if (key!='toSourceCode') out+=tabs+(hier?'\t':'')+(idre.test(key)?key:("'"+key+"'"))+":"+(this[key]==null?'null':this[key].toSourceCode(hier,lv+1,true))+","+(hier?'\n':'');
		out=out.replace(/,(\n?)$/,'$1');
		return out+tabs+'}';
	}else if(this.constructor==String) return (forObj?'':tabs)+"'"+this.toString().replace(/\\/g,"\\\\").replace(/'/g,"\\'")+"'";
	else return (forObj?'':tabs)+this.toString();
}
*/

var debugOutInfo = {	debugLine:0,lastDebugMsg:null,debugMsgCount:0 }
function DebugOut(msg,verbosity,preventRepeats){
	//*** Set global variable 'debugLevel' to an integer. Higher numbers corresponds to more verbose.
	if (this.debugLevel==null || debugLevel<verbosity) return;
	if (preventRepeats){
		if (msg==debugOutInfo.lastDebugMsg){
			debugOutInfo.debugMsgCount++;
			return;
		}
		debugOutInfo.lastDebugMsg = msg;
	}

	var out = document.getElementById('debugoutput');
	if (debugOutInfo.debugMsgCount>0){
		var dupsMessge = "previous output repeated "+debugOutInfo.debugMsgCount+" times...";
		if (out)	out.innerHTML="#"+(debugLine)+": "+dupsMessge+"<br>"+out.innerHTML;
		else alert(dupsMessge);
		debugOutInfo.debugMsgCount=0;
	}
	if (out)	out.innerHTML="#"+(debugOutInfo.debugLine++)+": "+msg.replace(/\n/g,"<br>").replace(/</g,"&lt;").replace(/>/g,"&gt;")+"<br>"+out.innerHTML;
	else alert(msg);
}


function IsHidden(el){
	//***returns true if this element, or any ancestor, is visibility:hidden or display:none (uses currentStyle, IE only)
	while (el!=null){
		if ((el.style && (el.style.display=='none' || el.style.visibility=='hidden')) ||	(el.currentStyle && (el.currentStyle.display=='none' || el.currentStyle.visibility=='hidden')))	return true;
		el=el.parentNode;
	}
	return false;
}
