//<script language="JavaScript">
//
//--------------------------------------//
//
// Contents:
//	App class
//	misc. functions
//	object extensions
//
//-------------------------------------//


// Convert JavaScript Date object to ISO8601-compliant string.
// Ex:	20030401						date (no time), no separators
//		20030401T083000					date and time, no separators
//		20030401T083000.006				date, time, milliseconds, no seps
//		20030401T083000.006-0500		date, time, milli, timezone, no seps
//		2003-04-01T08:30:00.006-05:00	date, time, milli, timezone, with separators
//
// All parameters default to false (will return 2nd example above).
// Params 'inclMillisecs' and 'inclTimezone' have no effect if 'noTime' = true.
//
Date.prototype.toIsoString = function ( noTime, inclMillisecs, inclSeparators, inclTimezone )
{
	var d = this;
	if (noTime==null)
		noTime = false;
		
	if (inclMillisecs==null)
		inclMillisecs=false;
		
	if (inclSeparators==null)
		inclSeparators = false;
		
	if (inclTimezone==null)
		inclTimezone=false;
	
	var dSep = inclSeparators ? "-" : "";
	var tSep = inclSeparators ? ":" : "";
	
	var yr = d.getFullYear();
	var mo = d.getMonth() + 1;
	var dy = d.getDate();
	
	var s = yr + dSep + 
			(mo<10 ? "0" : "") + mo + dSep + 
			(dy<10 ? "0" : "") + dy 
	;
	
	if (!noTime)
	{
		var hr = d.getHours();
		var mi = d.getMinutes();
		var ss = d.getSeconds();
		s += "T" + 
			(hr<10 ? "0" : "") + hr + tSep +
			(mi<10 ? "0" : "") + mi + tSep 
		;
		
		if (inclMillisecs)
		{
			var ms = d.getMilliseconds();
			ss += (ms/1000);
		}
		s += (ss<10 ? "0" : "") + ss ;
		
		if (inclTimezone)
		{
			tzOff = d.getTimezoneOffset();
			var tzHr = Math.abs( parseInt( tzOff / 60, 10 ) );
			var tzMi = Math.abs( tzOff % 60 );
			
			s += (tzOff>0 ? "-" : "+") + 
				(tzHr<10 ? "0" : "") + tzHr + tSep +
				(tzMi<10 ? "0" : "") + tzMi
			;
		}	
	}
	
	return s;			
}

// Convert ISO8601-compliant string to JavaScript Date object.
//
Date.fromIsoString = function ( s ) 
{
	// Basically, look for "YYYY-MM-DD hh:mm:ss" and most of the acceptable variations.
//	var re = /^(\d{4})-?(\d{2})-?(\d{2})(?:[T| ]-?(\d{2})(?:\:?(\d{2})(?:\:?(\d{2})(?:\.(\d+))?)?)?(?:([Zz])|(?:([+-])(\d{2})(?:\:?(\d{2}))?))?)?/;
	var re = /^(\d{4})-?(\d{2})-?(\d{2})/;
	var a = s.match( re );
	if (a)
	{
	
		var isAbsolute = false;
		var tzOffset = 0;
		
		var yr = parseInt( a[1], 10 );
		var mo = parseInt( a[2], 10 );
		var dy = parseInt( a[3], 10 );
		var hr = 0;
		var mi = 0;
		var ss = 0;
		var ms = 0;
		
		if (a[4] != "")
		{
			hr = parseInt( a[4], 10 );
			if (a[5] != "")
			{
				mi = parseInt( a[5], 10 );
				if (a[6] != "")
				{
					ss = parseInt( a[6], 10 );
					if (a[7] != "")
					{
						ms = parseFloat( "0." + a[7], 10 );
					}	
				}
			}
			if (a[8] != "")
			{
				// UTC time (Zulu).
				isAbsolute = true;
				tzOffset = 0;
			}
			else if (a[9] != "")
			{
				// Timezone specified.
				isAbsolute = true;
				tzOffset = parseInt( a[10], 10 ) * 60;
				if (a[11] != "")
					tzOffset += parseInt( a[11], 10 );
				if (a[9] == "-")
					tzOffset = 0 - tzOffset;
			}				
		}			

		var d = new Date( yr, mo-1, dy, hr, mi, ss, ms * 1000 );

		if (isAbsolute)
		{
			var localTzOffset = d.getTimezoneOffset() * -1;
			localTzOffset -= tzOffset;
			d.setMinutes( mi + localTzOffset );
		}	
		return d;	
	}
	else
		return null;
}


function subm(frm,action,dosub,replace) 
{
	return App.submit( frm, action, dosub, replace );
}		


function tryOnSubmit()
{
	return App.onSubmit();
}

// Returns formatted string for specifed date object.
function dateToFormattedString( d ) {
	return padLeft( d.getMonth() + 1, 2, "0" ) + "/" +
		padLeft( d.getDate(), 2, "0" ) + "/" +
		new String( d.getFullYear() )
	;
}

function timeTo24time( t, ampm )
{
	var hr = Math.floor( t/60 );
	var ap = parseInt(ampm, 10);
	
	// Convert to 24-hr clock
	if(hr == 12)
	{
		if(ap == 0)
			hr = 24;
 	}
	else
		hr = hr + (12 * parseInt(ampm, 10));
	
	return hr;
}

// Returns formatted string for specifed time in minutes past midnight.
function timeToFormattedString( m, use24hr ) {
	var hr;
	var mi;
	var ampm ="";
	
	mi = m % 60;
	hr = Math.floor( m/60 );
	if (!use24hr) {
		// Twelve hour time.
		hr = Math.floor( m/60 );
		hr = hr % 12;
		if (hr==0) {
			hr = 12;
		}
		ampm = (m>719) ? "pm" : "am";
	}
	return padLeft( hr, 2, "0" ) + ":" + padLeft( mi, 2, "0" ) + ampm;	
}

// Returns formatted string for specifed date object (including time).
function dateTimeToFormattedString( d, use24hr ) {
	
	return dateToFormattedString( d ) +" "+
			timeToFormattedString( (d.getHours() * 60) + d.getMinutes(), use24hr )
	;		
}

// Attempt to create and return a date object from the specified string.  If
// unsuccessful, return null.
function stringToDate( s ) {

	var YRCUTOFF = 50;
	var a;
	var d = null;
	var yr;
	var mo;
	var dy;
	
	var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
	
	try {

		if (!d) {
			// Try m-d-y format.
			a = s.match( /(\d{1,2})[\/-](\d{1,2})[\/-](\d{1,4})/ );
			if (a != null) {
				yr = parseInt( a[3], 10 );
				if (yr < YRCUTOFF)
					yr += 2000;
				mo = parseInt( a[1], 10 );
				dy = parseInt( a[2], 10 );
				if (isDate( yr, mo, dy )) {
					d = new Date( yr, mo - 1, dy );
				}	
			} 
		}


		if (!d) {
			// Try d-mon-y format.
			a = s.match( /(\d{1,2})[\/-](\D{3})[\/-](\d{1,4})/ );
			if (a != null) {
				yr = parseInt( a[3], 10 );
				if (yr < YRCUTOFF)
					yr += 2000;
					
				// Month is alpha, may be a 3-char code.
				s = a[2].toUpperCase();
				for (mo=0; mo<12; ++mo) {
					if (s == months[mo].toUpperCase()) {
						break;
					}	
				}
				if (mo<12) {
					dy = parseInt( a[1], 10 );
					if (isDate( yr, mo+1, dy )) {
						d = new Date( yr, mo, dy );
					}	
				}	
			} 
		}

		if (!d) {
			a = s.match( /(\d{1,2})[\/-](\d{1,2})/ );
			if (a != null) {
				yr = (new Date()).getFullYear();
				mo = parseInt( a[1], 10);
				dy = parseInt( a[2], 10);
				if (isDate( yr, mo, dy )) {
					d = new Date( yr, mo - 1, dy );
				}	
			}
		}
		
		if (!d) {	 
			if (!isNaN(s)) {
				// Value is numeric.
				if (s.length == 8) {
					if (parseInt( s.substr(0,2), 10 ) > 12)
					{
						// Assume YYYYMMDD format.
						yr = parseInt( s.substr(0,4), 10 );
						mo = parseInt( s.substr(4,2), 10 );
						dy = parseInt( s.substr(6,2), 10 );
					}
					else
					{
						// Assume MMDDYYYY format.
						yr = parseInt( s.substr(4,4), 10 );
						mo = parseInt( s.substr(0,2), 10 );
						dy = parseInt( s.substr(2,2), 10 );
					}
					d = new Date( yr, mo - 1, dy );
				} else if (s.length == 6) {
					// Assume MMDDYY format.
					yr = parseInt( s.substr(4,2), 10 );
					mo = parseInt( s.substr(0,2), 10 );
					dy = parseInt( s.substr(2,2), 10 );
					if (yr < YRCUTOFF) {
						yr += 2000;
					}	
					if (isDate( yr, mo, dy )) {
						d = new Date( yr, mo - 1, dy );
					}	
				}
			}	
		}					
	} catch(e) {
		// Swallow any exceptions.
	}	
	if (!d || isNaN(d)) {
		d = null;
	}			
	return d;				
}

// Return true if year, month, day make a valid
// date, false otherwise.
function isDate( yr, mo, dy ) {
	if (mo>0 && mo<13) {
		var days = MONTHDAYS[mo-1];	
		if (mo==2 && isLeapYear( yr )) 
			++days;
		if (dy>0 && dy<=days) {
			return true;
		} else {
			return false;
		}		
	} else {
		return false;
	}			
}

// Pad string out to specified width.
function padLeft( s, width, padChar ) { 
	s = new String( s );
	var ps = "";
	if (s.length < width) {
		var t = width - s.length;
		for (var i=0; i<t; ++i) {
			ps += padChar;
		}	
		ps += s;
	} else {
		ps = s;
	}
	return ps;		
}

// Pad string out to specified width.
function padRight( s, width, padChar ) { 
	var ps = new String( s );
	if (ps.length < width) {
		var t = width - ps.length;
		for (var i=0; i<t; ++i) {
			ps += padChar;
		}	
	}
	return ps;		
}


// Convert number to string padded to specified decimals value,
// optionally inserting commas as appropriate.
function numberToString( v, numDecimals, useCommas ) {

	// Multiplier.	
	var mult = Math.pow( 10, numDecimals );

	// Split into sign value, integer and fractional part.
	var sign = (v<0) ? -1 : 1;
	
	v = Math.abs( v );
	
	var i = Math.floor( v );
	var f = Math.round( (v - i) * mult );
	
	var s = (sign==1 ? "" : "-") + i.toString();
	if (numDecimals > 0) {
		s += "." + padLeft( f, numDecimals, "0" );
	}	

	if (useCommas) {
		var p = s.indexOf( "." );
		while (p > 0) {
			p -= 3;
			if (p <= 0) {
				break;
			}
			s = s.substring(0, p) + "," + s.substring( p, s.length );
		}
	}	
	return s;	
	
	
}



// Validate a date entered in a textbox, optionally
// setting the value of the textbox to the std. date
// format.  If successful, a valid Date object is returned.
// Otherwise, null is returned.
function validateDate( elem, errMsg, setVal ) {

	var d = stringToDate( elem.value );
	
	if (!d) {
		alert( errMsg );
		return null;
	} else {
		if (setVal) 
			elem.value = dateToFormattedString(d);
		return d;
	}		
}

function isLeapYear( yr ) {
	if ((yr % 4) != 0)
		return false;
	else if ((yr % 100) != 0) 
		return true;
	else if ((yr % 400) == 0) 
		return true;
	else 
		return false;
}

// Begin mainline.

var MONTHDAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];




//-- App --------------------------------//
//
// Generic application class (singleton).
//
//---------------------------------------//
function App() {};

App.getHashFromElement = _App_getHashFromElement;
App.getArrayFromElement = _App_getArrayFromElement;
App.buildQueryString = _App_buildQueryString;
App.encodeQueryStringElement = _App_encodeQueryStringElement;
App.wrapString = _App_wrapString;

// Browser-independent methods.
App.getIFrameDocument = _App_getIFrameDocument;
App.getDocumentHeight = _App_getDocumentHeight;
App.getDocumentWidth = _App_getDocumentWidth;


App.sendHttpRequest = _App_sendHttpRequest;

//-- Form submission helpers --//
App.submit = _App_submit;
App.onSubmit = _App_onSubmit;
App.cancelNextSubmit = _App_cancelNextSubmit;
App.resetSubmit = function() { App._submitted = false; }


App._skipNextSubmit = false;	// if true, next submit will be cancelled.
App._submitted = false;			// Form has been submitted.

// Returns an associative array created from the contents of 
// an HTML SPAN or INPUT element.
function _App_getHashFromElement( elemId, rowDelim, colDelim ) {

	var i;
	var s = "";
	var a, a2;
	var c;
	var o;
	var h;
	
	if (rowDelim == null) 
		rowDelim = "^";
	if (colDelim == null) 
		colDelim = "~";
		
	h = new Object();
	o = document.getElementById( elemId );
	if (o) {
		if (o.tagName=="SPAN" || o.tagName=="PRE") {
			s = o.innerHTML;
		} else if (o.tagName=="INPUT") {
			s = o.value;
		}
	
		if (s != "") {
			a = s.split( rowDelim );
			c = a.length;
			for (i=0; i<c; ++i) {
				s = a[i];
				if (s != "") {
					a2 = (a[i]).split( colDelim );
					h[ a2[0] ] = a2[1];
				}
			}		
		}
	}				
	return h;
}

// Returns an array created from the contents of 
// an HTML SPAN or INPUT element.
function _App_getArrayFromElement( elemId, delim ) {

	var i;
	var s = "";
	var a;
	var o;
	
	if (delim == null) 
		delim = "^";
		
	o = document.getElementById( elemId );
	if (o) {
		if (o.tagName=="SPAN" || o.tagName=="PRE") {
			s = o.innerHTML;
		} else if (o.tagName=="INPUT") {
			s = o.value;
		}
	}
	if (s != "") {
		a = s.split( delim );
	} else {
		a = new Array();
	}	
	return a;
}

function _App_buildQueryString( frm ) {

	// Build query string from form inputs.
	var a = new Array();
	var elems = frm.elements;
	var elem;
	
	for (var iElem=0; iElem<elems.length; ++iElem) {
		elem = elems[iElem];
		if (!elem.name)
			break;
			
		switch (elem.tagName) {
		case "INPUT" :
			if (elem.disabled)
				break;
				
			switch (elem.type) {
			case "radio" :
			case "checkbox" : 
				if (elem.checked)
					a.push( App.encodeQueryStringElement( elem.name, elem.value ) );
				break;

			default :
				a.push( App.encodeQueryStringElement( elem.name, elem.value ) );
			}

			break;

		case "SELECT" :
			if (elem.disabled)
				break;

			if (elem.multiple) {
				var opts = elem.options;
				var opt;
				for (var i=0; i<opts.length; ++i) {
					opt = opts[i];
					if (opt.selected)
						a.push( App.encodeQueryStringElement( elem.name, opt.value ) );	
				}	
			} else {	
				a.push( App.encodeQueryStringElement( elem.name, elem.value ) );	
			}	
			break;

		case "TEXTAREA" :
			if (elem.disabled)
				break;
				
			a.push( App.encodeQueryStringElement( elem.name, elem.value ) );	
			break;
		}		
	}
	
	return a.join( "&" );
}

function _App_encodeQueryStringElement( n, v ) {
	var s = new String( v );
	return n +"="+ escape( s.replace( / /g, '+' ) );
}

// Insert newlines into a string at the specified interval.
// Attempts to keep words intact (like an HTML textarea w/
// wrap="hard").
function _App_wrapString( s, len ) {

	if (!len) {
		// Nothing to do.
		return s;
	}
		
	var a = new Array();
	
	// Split on newlines (these will be preserved).
	var aLines = s.split( "\r\n" );
	for (var i=0; i<aLines.length; ++i) {
	
		var sLine = aLines[i];

		var p1 = 0;
		var p2 = 0;
		
		while (sLine.length > len) {
			p2 = sLine.lastIndexOf( " ", len - 1 );
			if (p2 == -1) {
				// No spaces found; break line at max length.
				p2 = len;
			} else {
				// Space found; break line right after space.
				++p2;
			}
			a.push( sLine.substring(p1,p2) );
			sLine = sLine.substr(p2);
		} 
		a.push( sLine );
	
	}

	return a.join( "\n" );
}

// Gets a reference to the specified IFRAME's document object.
function _App_getIFrameDocument( iframe ) {
	var doc = null;
	if (iframe.contentDocument) {
		// NS6
		doc = iframe.contentDocument; 
	} else if (iframe.contentWindow) {
		// IE5.5, IE6
		doc = iframe.contentWindow.document;
	} else if (iframe.document) {
		// IE5
		doc = iframe.document;
	}
	return doc;	
}

// Gets the height of a document.
function _App_getDocumentHeight( doc )
{
	if (doc.body.scrollHeight)
		// IE
		return doc.body.scrollHeight;
	else
		// NS
		return doc.documentElement.offsetHeight;
}

// Gets the width of a document.
function _App_getDocumentWidth( doc )
{
	if (doc.body.scrollWidth)
		// IE
		return doc.body.scrollWidth;
	else
		// NS
		return doc.documentElement.offsetWidth;
}

// Send an HTTP request by loading the specified IFRAME element
// with the specified URL.
function _App_sendHttpRequest( frameId, url ) 
{

	var iframe = document.getElementById( frameId );
	if (iframe) 
	{
		var doc = App.getIFrameDocument( iframe );
		doc.location.replace( url );
	}	
}

// Std. submit method, usu. called from an input's
// onClick or onChange event.
//		frm= the form to submit
//		action= user-defined string
//		dosub= true if the form should be programmatically
//			submitted (e.g., if called from an event which
//			normally wouldn't cause a form submission.)
//		replace= submit via location.replace, so nothing is 
//			entered in browser history.
//
function _App_submit( frm, action, dosub, replace ) 
{

	var nm = frm.name;
	
	if (frm._formName) {
		frm._formName.value = nm;
	}	
	if (frm._action) {
		frm._action.value=action;
	}	

	if (dosub) {
		var ok = (frm.onsubmit) ? frm.onsubmit() : true;
		if (ok) {
			if (replace && frm.method=="get") {
				// Replace current page (or target iframe) w/ new one.
				
				var url = frm.action;
				var qs = App.buildQueryString(frm);
				var doc = document;

				// See if target is an iframe (note: _parent, _blank, etc. not supported).
				var tgt = frm.target;
				if (tgt != "" && tgt != "_self" ) 
				{
					// Assume target is iframe.
					var ifrm = document.getElementById( tgt );
					if (ifrm)
						doc = App.getIFrameDocument( ifrm );
				}		
				 
				// Perform a location.replace to avoid history entry.
				var a = url.split('#');
				if (a.length == 2) 
					doc.location.replace( a[0] +'?'+ qs +'#'+ a[1] );
				else 	
					doc.location.replace( a[0] +'?'+ qs );

									
			} else {	
				// Normal submit.
				frm.submit();
			}	
		}	
		return false;
	} else {		
		return true;
	}	
}

// Generic onSubmit event handler; guards against repeated submissions.
function _App_onSubmit() 
{
	if (App._submitted || App._skipNextSubmit)
	{
		App._skipNextSubmit = false;	// Reset
		return false;
	}	
		
	App._submitted = true;
	
	return true;
}	

// Cancels the next form submission.  This method sets a flag that
// is checked, then cleared by the onSubmit handler.
function _App_cancelNextSubmit() {
	App._skipNextSubmit = true;
}

