// PROPRIETARY - (c) 2012 by James J. Domino - Juniper Labs LLC - 310-439-1185 - Copying without written permission is strictly prohibited.
// PROPRIETARY - (c) 2010, 2009, 2008, 2007, 2006, 2005, 2004, 2003 by James J. Domino - Juniper Labs LLC - 310-439-1185

// Public Variables:
	var mnuActive = null;		// the latest mnu that is active
	var eleActive = null;
	var error_count = 0;

	var is_changed = false;
	var is_dlg_active = false;
	var in_mouse_capture = false;

	var ifrmDlg = null;
	var lastDlgResult;
	var eleDlgFocus;		// the current control that has the "focus" for one of the dialog windows
	var dlgReturnFunct;
	var have_tedit = false;

	var True = true;		// ???TODO: for the validation functions...  Need to change this!
	var False = false;

	var eleMouseCapture = null;

	var lProgressBlocks = 0;	// the current value of the progress bar (only one supported...)

	var dragDropGroupCount = 0;
	var jlEvtCount = 1;

	var page_url = '';
	var page_is_read_only = false;
	var page_help_code = null;

	var page_allow_autosize = true;
	var page_allow_x_autosize = true;
	var page_allow_y_autosize = true;
	var page_preferred_height = 800;
	var page_preferred_width = 600;

// Page Validation:
	var page_has_errors = false;
	var page_errors = '';
	var page_error_focus = null;

// Declare a few useful constants:
	// defines several types.
	var simple_type_text = 1;
	var simple_type_date = 2;
	var simple_type_int = 3;
	var simple_type_float = 4;
	var simple_type_bit = 5;

	// eventLevels
	var eventLevelMsg = 1;
	var eventLevelWarn = 2;
	var eventLevelError = 3;
	var eventLevelAudit = 4;

	var regDigit = /^\d{1}/;

	var dblclick_support = true;
	var htmledit_support = true;
	var scroll_support = true;
	var color_button_face = "#C0C0C0";
	var color_button_shadow = "#808080";

//	Shortcuts
	var jlShortcut_count = 0;
	var jlShortcuts = [];
	var divJlShortcutList = null;
	var in_shortcut = false;

// Init
	var IE = (navigator.appName=="Microsoft Internet Explorer" ? true: false);
	var IE6 = ('ActiveXObject' in window && !('XMLHttpRequest' in window));

	if ( window.document.addEventListener ) {
		window.document.addEventListener('keydown', jlShortcut_OnKeyDown, false);
		window.document.addEventListener('mousedown', doc_OnMouseDown, false);
		window.document.addEventListener('mousemove', doc_OnMouseMove, false);
		window.document.addEventListener('mouseup', doc_OnMouseUp, false);
		window.document.addEventListener('selectstart', doc_OnSelectStart, false);
		window.document.addEventListener('contextmenu', doc_OnContextMenu, false);

	} else {
		window.document.onkeydown = jlShortcut_OnKeyDown;
		window.document.onmousedown = doc_OnMouseDown;
		window.document.onmousemove = doc_OnMouseMove;
		window.document.onmouseup = doc_OnMouseUp;
		window.document.onselectstart = doc_OnSelectStart;
		window.document.oncontextmenu = doc_OnContextMenu;
	}

	// ???TODO: add code to determine if the site is in "development" mode and disable this statement or leave it in!!!
	// window.onerror = errorHandler;

function errorHandler(error_message, error_url, error_line_number) {

	// Handles an error message...

	error_count += 1;

	if ( error_count < 3 ) {			// only pass the first few errors...

		winCreate('error_client_set.asp?error_message=' + escape(error_message) + '&error_url=' + escape(error_url) + '&error_line_number=' + escape(error_line_number) + '&url=' + escape(parent.location), 400, 300);
		return true;		// true - cancel the error message

	} else {
		return false;
	}

}

function g( ele_id ) {

	var ele = window.document.getElementById( ele_id );

	if ( ele ) {
		return ele;
	} else {
		ele = window.document.getElementsByName( ele_id );

		if ( ele ) {
			return ele[0];
		}
	}

	return null;

}

function getAttr( ele, attr_name ) {

	var aa;

	try {
		if ( ele && ele.attributes ) {
			aa = ele.attributes[attr_name];
		}
	} catch (e) {
		return null;
	}

	if ( aa ) {
		return aa.value;
	} else {
		return null;
	}

}

function setAttr( ele, attr_name, new_value ) {

	ele.setAttribute(attr_name, new_value);

}

function getFld( ele_id ) {

	var ff = g(ele_id);

	if ( ff ) {
		if ( ff.value ) {
			return ff.value;
		} else {
			return '';
		}
	} else {
		return '';
	}

}

function setFld( ele_id, value ) {

	var ff = g(ele_id);

	if ( ff ) {
		ff.value = value;
	}

}

function simple_type_formatExcel( value, simple_type_id ) {

	// given a value and its "simple type", formats it for Excel

	switch ( simple_type_id ) {
		case simple_type_text:
			return xlsstr( value );
		case simple_type_date:
			return value;			// need to think more...  Excel could really goof this up!!
		case simple_type_int:
			return value;
		case simple_type_float:
			return value;
		case simple_type_bit:
			return value;
		default:
			return xlsstr( value );
	}

}

function winCreate(URL, width, height) {
	winarg = "toolbar=no,scrollbars=yes,status=no,resizable=yes,location=no,directories=no,status=no,menubar=no,copyhistory=no,width="+width+",height="+height;
	return window.open(URL, "", winarg);
}

function pageFocusSet( ctl_id, do_select ) {

	// ???TODO: add more error handling!!!
	//	if in dev mode, have it throw a "pretty" error...  Otherwise, have it ignore the issue
	//	NOTE: ???TODO:
	//	if the ctl_id is "description" or "keywords" or some other field that is used multiple times, 
	//	this will attempt to set the focus on the first element.
	//	Example...  You want to set the focus on a field called "description".  But all pages
	//	have a META name="description" tag.  The g() function will return the META field instead of
	//	your field.  The way around this is to add "id=description" to your description field
	//	because g() will find the ID attribute before looking for a matching NAME attribute.

	var ctl_focus = g(ctl_id);

	if ( ctl_focus ) {
		try {
			ctl_focus.focus();
			if ( do_select == true ) {
				ctl_focus.select();
			}
		} catch (e) {
			// report this error--if in DEV mode!
		}
	}

}

function qsGetPara(strIn, parameter_name) {

	// given a querystring (just the stuff after the ?), will return the named parameter.

	// WARNING:		will return back an array (possibly) of values if there is a comma separated list!

	var aNameValues = strIn.split('&');
	var r = '';

	var pp = 0;

	for (var i = 0; i < aNameValues.length; i++) {

		if ( aNameValues[i] != null && aNameValues[i] != '' ) {
			pp = aNameValues[i].indexOf('=');

			if ( pp >= 0 ) {
				if (aNameValues[i].slice(0, pp) == parameter_name) {
					r = aNameValues[i].slice(pp+1);
					break;
				}
			}
		}
	}

	return (r.length > 0 ? unescape(r).split(',') : '');

}

function rptMacro( sIn, sValueUsingPipes, prefix, del ) {

	// given a string, a pipe delimited list of values and a prefix, perform macro
	// substitution and return the new string

	//	ex:
	//		rptMacro( 'yada.asp?para1=%r1%&para2=%r2%&para3=%r3%', '1|2|3', 'r') --> 'yada.asp?para1=1&para2=2&para3=3'

	if ( del == null ) {
		del = '|';
	}

	var aValues = sValueUsingPipes.split(del);

	var sOut = sIn;

	for (var i = 0; i < aValues.length; i++) {
		var vv = new RegExp('%' + prefix + (i+1) + '%', "g");
		sOut = sOut.replace(vv, aValues[i]);
	}

	return sOut;

}

function urlGo( URL, check_if_ok_to_exit, evt ) {

	if ( check_if_ok_to_exit == true ) {
		if ( parent.isOkToExit() != true ) {
			return;
		}
	}

	evt = evt || window.event;

	if ( evt ) {				// if there is an event associated with this

		if ( evt.shiftKey == true ) {			// if the user is pressing the shift key
			window.open(URL, "", "");
		 	return;
		}
	}

	// Add a page_after parameter to be helpful
	if ( URL.indexOf('page_after') < 0 ) {
		if ( page_url != null && page_url != '' ) {
			if ( URL.indexOf('?') < 0 ) {
				URL += '?';
			} else {
				URL += '&';
			}
			URL += 'page_after=' + escape(page_url);
		}
	}

	parent.location = URL;

}

function getParent(el, tag_name) {

	// given an HTML DOM object, return the parent that matches the given tag

	// IN:	el - the element whose parent needs to be found
	//	tab_name = the name of the parent tag that needs to be found.

	if (el === null) 
		return null;
	else if (el.nodeType == 1 && el.tagName == tag_name)
		return el;
	else
		return getParent(el.parentNode, tag_name);		// call recursively
}

function getParentByClass( el, className) {

	// given an HTML DOM object, return the parent that matches the given tag

	// IN:	el - the element whose parent needs to be found
	//	className = the name of the parent tag that needs to be found.

	if (el === null) 
		return null;
	else if (el.nodeType == 1 && el.className == className)
		return el;
	else
		return getParentByClass(el.parentNode, className);		// call recursively

}

function getParentWithAttr( el, attr_name) {

	if ( el === null )
		return null;
	else if ( attr_name in el ) {		// if the element has this property
		return el;
	} else {
		return getParentWithAttr( el.parentNode, attr_name);
	}

}	

function getChildByTagName( el, tagName ) {

	var node = el.firstChild;

	while ( node ) {
		if ( node.nodeType != 3 && node.tagName == tagName ) {
			return node;
		} else {
			node = node.nextSibling;
		}
	}

	return null;

}

function getChild( ele, node_count ) {

	// attempts to act like a childNodes[] array--but without the whitespace that some browsers include
	// Warning...  This function will be slow!!!

	var node = null;

	if ( ele ) {

		if ( IE ) {
			return ele.childNodes[node_count];
		}

		node = ele.firstChild;

		while ( node && node.nodeType == 3 ) {
			node = node.nextSibling;
		}

		for ( tt = 0; tt < node_count; tt++ ) {
			node = node.nextSibling;
			while ( node && node.nodeType == 3 ) {
				node = node.nextSibling;
			}
		}

	}

	return node;	
}

function getFirstChild( ele ) {

	var node = null;

	if ( ele ) {
		node = ele.firstChild;

		while ( node && node.nodeType == 3 ) {
			node = node.nextSibling;
		}
	}

	return node;
}

function getLastChild( ele ) {

	var node = null;

	if ( ele ) {
		node = ele.lastChild;

		while ( node && node.nodeType == 3 ) {
			node = node.previousSibling;
		}
	}

	return node;

}

function getNextSibling( ele ) {

	var node = null;

	if ( ele ) {

		node = ele.nextSibling;

		while ( node && node.nodeType == 3 ) {
			node = node.nextSibling;
		}

	}
	return node;

}

function getPreviousSibling( ele ) {

	var node = null;

	if ( ele ) {

		node = ele.previousSibling;

		while ( node && node.nodeType == 3 ) {
			node = node.previousSibling;
		}

	}
	return node;

}

function getChildIndex( ele ) {

	var ord = -1;
	var el = ele;

	while ( el ) {
		el = el.previousSibling;
		ord++;
	}

	return ord;

}

function scrollbarWidth() {

	// ???TODO: I need a really good way to get the size of the scrollbars without resorting to just a hardcode.
	//			the problem is that many of the techniques never seem to work correctly...

	var w = document.body.offsetWidth - (2*document.body.clientLeft) - document.body.clientWidth;

	if ( w <= 0 ) {
		w = 16;
	}

	return w;

}

function scrollbarHeight() {

	// ???TODO: I need a really good way to get the size of the scrollbars without resorting to just a hardcode.
	//			the problem is that many of the techniques never seem to work correctly...

	var h = document.body.offsetHeight - (2*document.body.clientTop) - document.body.clientHeight;

	if ( h <= 0 ) {
		h = 16;
	}

	return h;

}

function mnuShow( mnuToShow, evt ) {

	// shows the given menu.

	// ???TODO:  Need to look at this code and fix it.  I need it to work no matter where or what hierarchy is involved...

	// ???TODO:	also... this code has to be tricky because under some conditions we need it to open a menu on something
	//		other than a mousedown...

	evt = evt || window.event;

	// if we are already in an active menu, don't show this one!
		if ( mnuActive != null ) {
			return;
		}

	// Save the currently active menu
		mnuActive = mnuToShow;

	// Position the menu
		mnuToShow.style.position = 'absolute';
		var ll = evtMouseX( evt ) + 4;
		var tt = evtMouseY( evt ) + 4;

	// Now, check if the menu's position would force part of it out of the client area
		if ( ll + mnuToShow.offsetWidth > windowWidth() ) {
			ll = document.body.clientWidth - mnuToShow.clientWidth - 3;
		}
		if ( ll < 0 ) {
			ll = 0;
		}
	
		if ( tt + mnuToShow.offsetHeight > windowHeight() ) {
			tt = document.body.clientHeight - mnuToShow.clientHeight - 3;
		} 
		if ( tt < 0 ) {
			tt = 0;
		}

	// Now, set the rest of the parameters!
		mnuToShow.style.zIndex = 1000;
		mnuToShow.style.left = ll + 'px';
		mnuToShow.style.top = tt + 'px';
		mnuToShow.style.visibility = 'visible';

		if (document.all && window.print) {
			if ( evt.type == 'click' ) {
				evtKill( evt );
			}
			// ???TODO: does this interfere with the document.onXXX that we need above?
			document.body.onclick = mnuHide;
			document.body.oncontextmenu = mnuHide;
		}

	return evtKill( evt );
}

function mnuHide() {

	// Check if we have an active menu
		if ( mnuActive == null )
			return;

	// Hide the menu
		mnuActive.style.visibility = "hidden";

	// check if there is a submenu
		var cc = mnuActive.current_submenu;
		while ( cc != null ) {
			cc.style.visibility = "hidden";
			cc = cc.current_submenu;
		}

	// Now, clear any event functions, if any
		if ( document.body.onclick == mnuHide ) {
			document.body.onclick = "";
		}
	
		if ( document.body.oncontextmenu == mnuHide ) {
			document.body.oncontextmenu = "";
		}

	// Clear the active menu
		mnuActive = null;

}

function intMin( intAA, intBB ) {

	if ( intAA < intBB ) {
		return intAA;
	} else {
		return intBB;
	}
}

function intMax( intAA, intBB ) {

	if ( intAA > intBB ) {
		return intAA;
	} else {
		return intBB;
	}
}

function totalOffsetTop( ele ) {

	// attempts to get the full offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	if ( IE6 ) {
		while ( e && e.tagName != 'BODY' ) {

			if ( e.style.position == 'absolute' ) {
				try {
					if ( e.style.top == null || e.style.top == '' ) {
						return out + e.offsetTop;
					} else {
						return out + parseInt(e.style.top);
					}
				} catch (e) {
				}
			} else {
				out += e.offsetTop;
			}
			e = e.offsetParent;
		}
	} else {
		while ( e && e.tagName != 'BODY' ) {

			out += e.offsetTop;
			e = e.offsetParent;
		}
	}

	return out;

}

function totalOffsetTopFromParent( ele, parent ) {

	// attempts to get the full offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	while ( e.tagName != 'BODY' && e !== parent ) {

		if ( e.style.position == 'absolute' ) {
			try {
				if ( e.style.top == null || e.style.top == '' ) {
					return out + e.offsetTop;
				} else {
					return out + parseInt(e.style.top);
				}
			} catch (e) {
			}
		} else {
			out += e.offsetTop;
		}
		e = e.offsetParent;
	}

	return out;

}

function totalOffsetLeft( ele ) {

	// attempts to get the full offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	if ( IE6 ) {
		while ( e.tagName != 'BODY' ) {

			if ( e.style.position == 'absolute' ) {
				try {
					if ( e.style.left == null || e.style.left == '' ) {
						return out + e.offsetLeft;
					} else {
						return out + parseInt(e.style.left);
					}
				} catch (e) {
					//
				}
			} else {
				out += e.offsetLeft;
			}
			e = e.offsetParent;
		}
	} else {

		while ( e.tagName != 'BODY' ) {

			out += e.offsetLeft;
			e = e.offsetParent;

		}

	}

	return out;

}

function totalOffsetLeftFromParent( ele, parent ) {

	// attempts to get the full offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	while ( e.tagName != 'BODY' && e !== parent ) {

		if ( e.style.position == 'absolute' ) {
			try {
				if ( e.style.left == null || e.style.left == '' ) {
					return out + e.offsetLeft;
				} else {
					return out + parseInt(e.style.left);
				}
			} catch (e) {
				//
			}
		} else {
			out += e.offsetLeft;
		}
		e = e.offsetParent;
	}

	return out;

}

function totalScrollTop( ele ) {

	// attempts to get the full offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	while ( e.tagName != 'BODY' ) {

		if ( e.style.position == 'absolute' ) {
			try {
				return out + e.scrollTop;
			} catch (e) {
			}
		} else {
			out += e.scrollTop;
		}
		e = e.offsetParent;
	}

	return out;

}

function totalScrollLeft( ele ) {

	// attempts to get the full scroll offset of a given element

	// ???TODO: write code to stop a possible infinite loop if the .tagName property fails!

	var e = ele;
	var out = 0;

	while ( e.tagName != 'BODY' ) {

		if ( e.style.position == 'absolute' ) {
			return out + e.scrollLeft;
		} else {
			out += e.scrollLeft;
		}
		e = e.offsetParent;
	}

	return out;

}

function winLeft() {

	// Return the Left co-ordinate of the browser window

	if (document.all) {
		return window.screenLeft;
	} else {
		return window.screenX;
	}

} 

function winTop() {

	// return the Top co-ordinate of the browser window

	if (document.all) {
		return window.screenTop;
	} else {
		return window.screenY;
	}

}

function doc_OnMouseDown( evt ) {

	evt = evt || window.event;

	if ( is_dlg_active == true ) {
		dlgHide( false );
		return false;

	} else if ( mnuActive ) {		// if there is a menu active
		var src = evtSrc( evt );
		var parent = getParentByClass(src, 'mnu');
		if ( parent == null ) {
			mnuHide();
			return evtKill( evt );
		}

	} else if ( dragDropGroupCount > 0 ) {		// if the drag/drop functions are available, let's try it!
		return jlDragDrop_OnMouseDown( evt );

	} else {
		return true;
	}

}

function doc_OnMouseMove( evt ) {

	evt = evt || window.event;

	if ( in_mouse_capture ) {
		if ( eleMouseCapture ) {

			var newevt = document.createEvent("MouseEvents");
			newevt.initMouseEvent( evt.type, false, true, document.defaultView, evt.detail, evt.screenX, evt.screenY, evt.clientX, evt.clientY, evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, evt.button, evt.relatedTarget )
			eleMouseCapture.dispatchEvent(newevt);
			return false;

		}
	} else if ( dragDropGroupCount > 0 ) {		// if the drag/drop functions are available, let's try it!
		return jlDragDrop_OnMouseMove( evt );
	} else {
		return true;
	}

}

function doc_OnMouseUp( evt ) {

	evt = evt || window.event;

	if ( in_mouse_capture ) {
		doc_OnMouseMove( evt );
	} else if ( dragDropGroupCount > 0 ) {		// if the drag/drop functions are available, let's try it!
		return jlDragDrop_OnMouseUp( evt );
	} else {
		return true;
	}

}

function doc_OnSelectStart( evt ) {

	evt = evt || window.event;

	if ( dragDropGroupCount > 0 ) {		// if the drag/drop functions are available, let's try it!
		return jlDragDrop_OnSelectStart( evt );
	} else {
		return true;
	}

}

function doc_OnContextMenu( evt ) {

	if ( dragDropGroupCount < 1 ) {
		return true;
	}

	evt = evt || window.event;

	if ( dragDropGroupCount > 0 ) {		// if the drag/drop functions are available, let's try it!
		return jlDragDrop_OnContextMenu( evt );
	} else {
		return true;
	}

}

function setMouseCapture( ele ) {

	if ( ele.setCapture ) {		// if IE
		ele.setCapture();
	} else {				// need to fake it!
		in_mouse_capture = true;
		eleMouseCapture = ele;
	}

}

function releaseMouseCapture( ele ) {

	if ( ele.releaseCapture ) {		// if IE
		ele.releaseCapture();
	} else {
		in_mouse_capture = false;
		eleMouseCapture = null;
	}

}

function dlgShow( ele, newctl, dlgWidthApprox, dlgHeightApprox, url, functReturn ) {

	// Shows the given dialog

	// Init
		dlgHide( false );			// hide any previous dlg

	// Check if we have an IFRAME available
		if ( ifrmDlg === null ) {
			ifrmDlg = document.createElement('IFRAME');
			ifrmDlg.id = 'iframeDlg';
			ifrmDlg.style.display == 'none';
			ifrmDlg.style.position = 'absolute';
			ifrmDlg.className = 'dlg';
			ifrmDlg.src = "javascript:'';";

			document.body.appendChild( ifrmDlg );

		}

	// Check if we should be able to modify this
		if ( newctl ) {
			if ( newctl.disabled == true ) {
				alert('Sorry...  This field is currently disabled and you can\'t change the value.');
				return false;
			}
		}

	// Now, init everything for the new show
		eleDlgFocus = newctl;		// save the control that has focus
		dlgReturnFunct = functReturn;

	// Set up the IFRAME
		ifrmDlg.src = url;
		ifrmDlg.style.left = intMax(totalOffsetLeft(ele) + ele.offsetWidth - dlgWidthApprox - totalScrollLeft(ele), 0) + 'px';
		ifrmDlg.style.top = intMin(intMax(totalOffsetTop(ele) + ele.offsetHeight - totalScrollTop(ele), 0), windowHeight() - dlgHeightApprox - 5) + 'px';
		ifrmDlg.style.height = dlgHeightApprox + 'px';
		ifrmDlg.style.width = dlgWidthApprox + 'px';

	// Display it
		ifrmDlg.style.display = "block";
		ifrmDlg.style.zIndex = 1000;
		ifrmDlg.style.visibility = 'visible';

	// Now, mark that a dialog box is active
		is_dlg_active = true;

	// ???TODO: add code to actively watch the mouse?  (Instead of _always_ watching the mouse...)

	return false;	// return false to get rid of default event handling!
}

function dlgHide( set_focus ) {

	if ( is_dlg_active == true ) {

		if ( ifrmDlg ) {
			// ???TODO: clear the contents of the iframe?
			ifrmDlg.style.display = "none";
			ifrmDlg.src = "javascript:'';";
		}

		is_dlg_active = false;
		// turn off active watching of the mouse?

		// ???TODO: warning!  if this control can't have focus, things get weird
		if ( set_focus == true ) {
			if ( eleDlgFocus ) {
				try {
					eleDlgFocus.focus();
				} catch(e) {
					//
				}
			}
		}

	}

}

function dlgCalendarShow( ele, newctl, functCallback ) {

	// In:	element - the control that "opened" this dlgCalendar window.
	//		newctl - the control that has the focus of the dlgCalendar "window"

	var calDate = dateClean( newctl.value );		// convert the control with focus's date into a real date variable

	// if parsing failed, use today
	if ( !calDate ) {
		calDate = dateClean( new Date() );
	}

	return dlgShow(ele, newctl, 200, 200, 'dlgCalendar.asp?as_of_date=' + escape(dateFormat( calDate )), functCallback );

}

function dlgColorShow( ele, newctl ) {

	// In:	ele - the control that "opened" this dlgColor window.
	//		newctl - the control that has the focus of the dlgColor "window"

	return dlgShow(ele, newctl, 160, 150, 'dlgColor.asp?color=' + escape(newctl.value));

}

function dlgReturn( new_value ) {

	lastDlgResult = new_value;

	if ( dlgReturnFunct ) {
		dlgReturnFunct( new_value );

	} else if ( eleDlgFocus ) {
		if ( new_value != null && new_value != '' ) {
			eleDlgFocus.value = new_value;
		}
	}

	dlgHide( true );

	frmChangedSet( true );

}

function strTrim( strIn ) {

	if ( strIn != null ) {
		return strIn.replace(/^\s+|\s+$/g, '');
	} else {
		return "";
	}

}

function statusUpdate( eventLevel, msgRaw ) {

	//	Writes updated status information to a specified location...

	var fra = g("fraResult");
	var msg = unescape(msgRaw);

	if ( fra != null ) {
		var msgHTML;

		if ( eventLevel == eventLevelError ) {
			msgHTML = '<FONT color=#ff0000>' + msg + '</font>';
		} else if ( eventLevel == eventLevelWarn ) {
			msgHTML = '<FONT color=#7F7F00>' + msg + '</font>';
		} else {
			msgHTML = msg;
		}

		fra.innerHTML = fra.innerHTML + msgHTML + '<BR>';

		try {
			fra.scrollTop = fra.scrollHeight;			// move the status list to the bottom of the display
		} catch (e) {
			// do nothing
		}

	} else {
		status = msg;
	}

	if ( eventLevel == eventLevelError ) {
		// ???TODO: should it just write the message and beep?
		alert( msg );
	}

}

function percentUpdate( valueRaw ) {

	var lNewBlocks = 0;
	var value;

	try {
		value = Math.round(parseInt(valueRaw));
	} catch (e) {
		value = 0;
	}

	var fra = g("fraPercent");

	if ( fra != null ) {
		fra.innerHTML = value + "% complete.";
	} else {
		status = value + '% complete...';
	}

	var divProgress = g("progMain");

	if ( divProgress ) {

		// save the new value
		try {
			lNewBlocks = Math.round(value/10 - .5);
		} catch (e) {
			lNewBlocks = 0;
		}

		// do some boundary checking
		if ( lNewBlocks > 10 ) {
			lNewBlocks = 10;
		} else if ( lNewBlocks < 0 ) {
			lNewBlocks = 0;
		}

		if ( lNewBlocks != lProgressBlocks ) {
			lProgressBlocks = lNewBlocks;
	
			// first, try to mark the appropriate ones that need color
			for (var i = 0; i < lProgressBlocks; i++) {
				g('progMain_seg'+i).style.backgroundColor = 'blue';
			}
		
			// now, mark any remaining items transparent
			for (var i = lProgressBlocks + 1; i < 10; i++) {
				g('progMain_seg'+i).style.backgroundColor = 'transparent';
			}

		}

	}

}

function getGotoURL() {

	var gou = g('goto_old_url');
	var url = '';

	if ( page_url == null || page_url == '' ) {
		url = document.URL;
	} else {
		url = page_url;
	}

	if ( url ) {
		gou.value = url;
	}

}

function sendThisPageCreate() {

	var url;
	var url_name;

	if ( page_url == null || page_url == '' ) {
		url = escape(document.URL);
	} else {
		url = escape(page_url);
	}

	url_name = escape(window.document.title);

	winCreate('page_send_admin.asp?url=' + url + '&url_name=' + url_name, 700, 400);

}

function popupContentShow( content_code ) {

	winCreate('popup_content_show.asp?code=' + content_code, 400, 500);
	
}

function frmChangedSet( new_value ) {

	if ( is_changed != new_value ) {

		var newDocTitle = window.document.title;

		is_changed = new_value;

		if ( is_changed == true ) {
			if ( newDocTitle.length > 0 ) {
		
				if ( newDocTitle.charAt( newDocTitle.length-1 ) != '*' ) {
					window.document.title = newDocTitle + ' *';
				}
		
			}

		} else {
			if ( newDocTitle.length > 0 ) {
		
				if ( newDocTitle.charAt( newDocTitle.length-1 ) == '*' ) {
					window.document.title = newDocTitle.substr(0, newDocTitle.length-1 );
				}
		
			}

		}

	}

}

function isOkToExit( frm ) {

	// Returns TRUE if a toolbar button (or link) is able to continue potentially leaving the page.
	// the intent is to call this function on any toolbar that may cause the page to leave or refresh.
	// this function, if it determines that the information has changed, will warn the user about unsaved changes and will ask the user
	// whether they want to save the changes, not save the changes or cancel the operation.

	// if they select YES, this page's save function will be called and this function will return true.  
	//		???TODO: note that the save may cause the page to refresh and "lose" the ability to continue the action...  but eventually, the data to save will be sent to an IFRAME and the
	//				ability to save and continue an action will be allowed.

	//	if they select NO, this function will return true

	//	if they select CANCEL, this function will return false.
	if ( is_changed == true && page_is_read_only == false ) {		// check if this page has data that has changed

		if ( frm == null ) {
			frm = g('frmMain');
		}

		if ( frm != null ) {			// check if this page has a main form

			var result = areYouSureSaveChanges();		// ask the user if they want to save their changes
				
			if ( result == 'yes' ) {				// if they say yes, run the save
				if ( doSave( frm ) == false ) {		// if the save works, return true
					return false;			// ???TODO: when everything with IFRAME's is converted, have this return true to allow the original caller to do its thing!
				}

			} else if ( result == 'no' ) {		// if they don't want to save their changes, return true
				return true;
			}

			// if we get here, return false
			return false;
		}

	}

	// if we made it here, it means there is nothing on this page to save... so continue!
	return true;

}

function areYouSure( message ) {

	// A generic function to ask the user a question and get a yes, no or cancel response.

	// Returns: 	'yes' = if user replied YES
	//				'no' = if user selected NO
	//				'cancel' = if the user selected CANCEL

	//	???TODO: may want to make this more generic...  have options for the icons, default buttons, button names, etc.

	// Init
		var now = new Date();

	// Now, show the dialog box
		if ( window.showModalDialog ) {
			is_dlg_active = true;
			window.showModalDialog("_are_you_sure.asp?message=" + message + "&now=" + now.toUTCString(), window,"dialogHeight: 170px; dialogWidth: 350px; edge: Raised; center: Yes; help: No; resizable: No; status: No;");
			is_dlg_active = false;
		} else {
			// if there isn't one
			switch ( window.prompt(message + '\r\n\r\n(Type \'y\' for yes, \'n\' for no or \'c\' for cancel)', 'y') ) {
				case 'y', 'Y', 'yes', 'YES', 'Yes':
					return 'yes';
				case 'n', 'N', 'no', 'NO', 'No':
					return 'no';
				default:
					return 'cancel';
			}
		}

	// done
		return lastDlgResult;

}

function frmReadOnlySet( frmToCheck ) {

	// changes all of the elements of a given form to DISABLED status...

	if ( frmToCheck != null ) {
		for (var tt=0; tt < frmToCheck.elements.length; tt++) {
			var frmctl = frmToCheck.elements[tt];
			if ( frmctl.tagName != "BUTTON" ) {
				frmctl.disabled = true;
			}
		}
	}

}

function spanChildrenVisibleSet( spanMain, status ) {

	if ( spanMain ) {
		for ( var cc = 0; cc < spanMain.childNodes.length; cc++ ) {
			spanMain.childNodes[cc].style.display = status;
		}
	}

}

function strHTMLTEXTAREA( strIn ) {

	var strOut = strIn;

	if ( strOut ) {

		strOut = strOut.replace(/&/g, "&amp;");
		strOut = strOut.replace(/</g, "&lt;");
		strOut = strOut.replace(/>/g, "&gt;");

	}

	return strOut;

}

function pageOnLoadFinalInit() {

	// ???TODO: This is a _horrible_ hack to get IE to resize to the contents.

	var x;
	var y;
	var is_dialog = false;

	if ( page_allow_autosize == true && allow_autosize_default == true ) {

		if ( isWindowMaximized() ) {
			// do nothing!
		} else {

			if ( window.dialogHeight != null ) {
				is_dialog = true;
			}

			// Init
			if ( page_allow_x_autosize == true ) {
				x = page_width + 25;		// 25 = yuck (chrome size)
			} else if ( is_dialog == true ) {
				x = window.dialogWidth;
			} else {
				x = page_preferred_width;
			}

			if ( page_allow_y_autosize == true ) {
				y = document.body.scrollHeight;
			} else if ( is_dialog == true ) {
				y = window.dialogHeight;
			} else {
				y = page_preferred_height;
			}

			// Tell the browser to resize
			try {
				if ( is_dialog ) {
//					window.dialogWidth = x + 'px';
					window.dialogHeight = y + 'px';
				} else {
					window.resizeTo(x, y);
				}
			} catch ( e ) {
				// do nothing
			}
		
			// Now, adjust to compensate for "chrome"
			x += ( x - windowWidth() );
			y += ( y - windowHeight() );

			x = intMin( x, screen.availWidth - winLeft() );
			y = intMin( y, screen.availHeight - winTop() );

			// Resize again, to fix any remaining issues
			try {
				if ( is_dialog ) {
//					window.dialogWidth = x + 'px';
					window.dialogHeight = y + 'px';
				} else {
					window.resizeTo(x, y);
				}
			} catch ( e ) {
				// do nothing
			}
		}

	}

}

function isWindowMaximized() {

	// this is a horrible attempt at determining if IE is maximized.
	// Microsoft doesn't provide any other way...

	if ( document.body.clientWidth + scrollbarWidth() + 5 >= screen.availWidth ) {
		return true;
	} else {
		return false;
	}

}

function chkSet( id, new_value ) {

	// sets a checkbox's value by name

	var chk = g(id);

	if ( chk != null ) {
		chk.checked = new_value;
	}

}

function radioFocusSet( radio_name, value ) {

	// this tells the system to focus on the radio button with the given value

	var radio = document.getElementsByName(radio_name);

	if ( radio ) {
		var le = radio.length;

		if ( le == undefined ) {
			try {
				radio.focus();
			} catch (e) {
				// ???TODO:
			}
		} else {
			for (var tt= 0; tt < le; tt++) {
				if ( radio[tt].value == value ) {
					try {
						radio[tt].focus();
					} catch (e) {
						// ???TODO:
					}
				}
			}
		}
	}
}

function radioValueGet( radio_name ) {

	// returns the value of the radio button that is "checked"

	var radio = document.getElementsByName(radio_name);

	if ( radio ) {
		var le = radio.length;

		if ( le == undefined ) {
			if ( radio.checked ) {
				return radio.value;
			}

		} else {
			// otherwise, iterate through the list!
			for (var tt= 0; tt < le; tt++) {
				if ( radio[tt].checked ) {
					return radio[tt].value;
				}
			}
		}
	}

	// if we got down here, we can't find the item or nothing is checked!
	return null;

}

function windowClose() {

	// Overengineered way to close windows in ie6, ie7 and ie8

	if ( IE6 ) {
		this.focus();
		self.opener = this;
		self.close();
	} else if ( IE ) {
		try {
//			window.open('','_parent','');
			window.open('','_self','');
			window.close();
		} catch (e) {
			try {
				window.opener = '';
				window.close();
			} catch (ee) {
			}
		}
	} else {
		window.opener = '';
		window.close();
		// it may still fail here!
		// alert('Sorry...  If you are reading this, it is because your browser does not support closing windows using scripts.');
	}

	return false;
}

function evtKill( evt ) {

	evtStopBubbling( evt );
	evtStopDefault( evt ); 
	try { evt.keyCode = -8; } catch (e) {}		// try killing the keycode!

	return false;		// return false normally means stop event also...
}

function evtStopDefault( evt ) {

	if (evt.preventDefault) {
		evt.preventDefault();
	} else {
		evt.returnValue = false;
	}

}

function evtStopBubbling( evt ) {

	if (evt.stopPropagation) {
		evt.stopPropagation();
	} else {
		evt.cancelBubble = true;
	}
}

function evtMousePos(evt) {

	// returns the current mouse position

	if ( evt.pageX ) {
		return {
			x:evt.pageX, 
			y:evt.pageY
		};
	} else {
		return {
			x:evt.clientX + document.body.scrollLeft - document.body.clientLeft,
			y:evt.clientY + document.body.scrollTop  - document.body.clientTop
		};
	}

}

function evtMouseX( evt ) {

	if ( evt.pageX ) {
		return evt.pageX;
	} else {
		return evt.clientX + document.body.scrollLeft;
	}

}

function evtMouseY( evt ) {

	if ( evt.pageY ) {
		return evt.pageY;
	} else {
		return evt.clientY + document.body.scrollTop;
	}

}

function evtFromElement( evt ) {

	return evt.relatedTarget || evt.fromElement;

}

function evtToElement( evt ) {

	return evt.relatedTarget || evt.toElement;

}

function evtSrc( evt ) {

	// Firefox used Target while IE and Opera use srcElement

	return evt.srcElement ? evt.srcElement : evt.target;

}

function evtMouseButton( evt ) {

	//	I use the Microsoft model of bitwise values!
	//	1 = left
	//	2 = right
	//	4 = middle

	if ( evt.which ) {
		return (evt.which < 2) ? 1 : ((evt.which == 2) ? 4 : 2);
	} else {
		return evt.button;
	}

}

function evtOffsetX( evt ) {

	if ( evt.offsetX == undefined ) {	// other
		return evt.clientX - totalOffsetLeft( evt.target );
	} else {
		return evt.offsetX;		// ie
	}

}

function addEvt( ele, evt_type, callback ) {

	// This is hacked up because IE doesn't properly set the "this" properly for event functions.

	// Note that I didn't write a delEvt because I don't seem to need it right now.  When I do, I can
	// use the jlEvtCount value to store the element and event type in an array and delete the references
	// as needed.

	if ( ele.addEventListener ) {		// if we are using a "modern" nonIE browser, use the better method!
		ele.addEventListener( evt_type, callback, false );

	} else if ( ele.attachEvent ) {		// otherwise, fight through the IE nonsense
		jlEvtCount++;
		var cnt = jlEvtCount;
		ele['jlEvt' + cnt] = callback;
		ele['evt' + cnt] = function() { ele['jlEvt' + cnt]( window.event ); };
		ele.attachEvent( 'on' + evt_type, ele['evt' + cnt] );

	} else if ( typeof( ele['on' + evt_type] ) != 'function' ) {	// otherwise, use the worst technique (first assignment wins!)
		ele['on' + evt_type] = callback;
	}

}

function getClass( ele ) {

	return ele.className;

	// NOTE:	there seemed to be a time where firefox didn't like or use className!?!
}

function setClass( ele, newClassName ) {

	ele.className = newClassName;

//	old way:
//	if ( ele.className ) {
//		ele.className = newClassName;		// ie way
//	} else {
//		ele.setAttribute('class', newClassName);		// firefox way
//	}

}

function removeAllChildNodes( node ) {

	if ( node ) {
		while ( node.hasChildNodes() ) {
			node.removeChild(node.firstChild);
		}
	}
}

function strLPad( value, padding) {

	// this is a very hardcoded procedure!!!  Do not use unless it gets improved!!
	// I need something like a VB String$() function here...

	var out = "000000000000000" + value;					// now, convert to a left padded zero string

	return out.substr(out.length-padding);				// chop the extra stuff

}

function strXOR( strIn, value ) {

	// performs an XOR on each value in this string

	// ???TODO: does charCodeAt() work on Netscape?  Note that charCodeAt() and fromCharCode() are Unicode functions.

	var out = "";

	for (var tt=0; tt < strIn.length; tt++) {
		out = out + String.fromCharCode(strIn.charCodeAt(tt) ^ value);
	}

	return out;

}

function jlShortcut_OnKeyDown( evt ) {

	var ss;
	var do_search = false;

	evt = evt || window.event;

	if ( mnuActive ) {
		return mnuActive.handleKeyDown( evt );
	}

	if ( eleActive ) {
		return eleActive.handleKeyDown( evt );
	}

	var keyCode = evt.keyCode;

	if ( jlShortcut_count < 1 ) {
		// check if there is a parent and pass it to the parent
		if ( parent ) {
			if ( parent != self ) {
				try {
					return parent.jlShortcut_OnKeyDown( evt );
				} catch (e) {
					return true;
				}
			}
		}

		return true;
	}

	switch (keyCode) {
		case 38:	// 38=Up arw
		case 40:	// 40=Down arw
		case 37:	// 37=left arw
		case 39:	// 39=right arw
		case 17:	// 17=ctrl (just the ctrl key)
		case 18:	// 18=atl (just the alt key)
		case 16:	// 16=shift (just the shift key)
		case 36:	// 36=Home
		case 35:	// 35=End
		case 45:	// 45=Insert
		case 46:	// 46=Delete
			return true;
	}

	if ( keyCode >= 112 && keyCode <= 123 ) {		// if a function key
		ss = 'f' + (keyCode - 111);
		do_search = true;
	} else if ( keyCode == 186 ) {
		ss = ';';
		do_search = true;
	} else if ( keyCode == 187 ) {
		ss = '=';
		do_search = true;
	} else if ( keyCode == 188 ) {
		ss = ',';
		do_search = true;
	} else if ( keyCode == 189 ) {
		ss = '-';
		do_search = true;
	} else if ( keyCode == 190 ) {
		ss = '.';
		do_search = true;
	} else if ( keyCode == 191 ) {
		ss = '/';
		do_search = true;
	} else if ( keyCode == 192 ) {
		ss = '`';
		do_search = true;
	} else if ( keyCode == 219 ) {
		ss = '[';
		do_search = true;
	} else if ( keyCode == 220 ) {
		ss = '\\';
		do_search = true;
	} else if ( keyCode == 221 ) {
		ss = ']';
		do_search = true;
	} else if ( keyCode == 222 ) {
		ss = '\'';
		do_search = true;
	} else if ( keyCode == 27 ) {
		ss = 'esc';
		do_search = true;
	} else {
		ss = String.fromCharCode( keyCode ).toLowerCase();
	}

	if ( evt.ctrlKey == true ) {
		ss = 'ctl+' + ss;
		do_search = true;
	}

	if ( evt.altKey == true ) {
		ss = 'alt+' + ss;
		do_search = true;
	}

	if ( evt.shiftKey == true ) {
		ss = 'shift+' + ss;
	}

	var ord = -1;

	if ( do_search == true ) {

		if ( in_shortcut ) {
			alert('killing excess shortcut!');
			return evtKill( evt );
		}

		in_shortcut = true;

		// find the last shortcut using this key
		for ( tt=1; tt <= jlShortcut_count; tt++ ) {
			if ( jlShortcuts[tt].key == ss ) {
				ord = tt;
			}
		}

		if ( ord != -1 ) {
			jlShortcutGo( ord, evt );
			in_shortcut = false;
			return evtKill( evt );
		}

		// check if there is a parent and pass it to the parent
		if ( parent ) {
			if ( parent != self ) {
				try {
					var result = parent.jlShortcut_OnKeyDown( evt );
					in_shortcut = false;
					return result;
				} catch (e) {
					in_shortcut = false;
					return true;
				}
			}
		}

		in_shortcut = false;

	}

	return true;
}

function windowHeight() {

	// ???TODO: need to look at this again...  Tired of the odd numbers and the bad definitions.
	// Then, look at the pageOnLoadFinal, tabOnResize, mnuShow and dlgShow functions to see if they can 
	// be recalibrated!

	var hh;

	if ( typeof( window.innerHeight ) == 'number') {
		hh = window.innerHeight;
	} else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
		hh = document.documentElement.clientHeight;
		if ( document.documentElement.scrollHeight >= hh ) {
			hh = hh + 16;
		}

	} else {
		hh = document.body.clientHeight;
		if ( document.documentElement.scrollHeight >= hh ) {
			hh = hh - 16 - 5;
		}
	}

	return hh;

}

function windowWidth() {

	// ???TODO: need to look at this again...  Tired of the odd numbers...

	var ww;

	if ( typeof( window.innerWidth ) == 'number') {
		ww = window.innerWidth;
	} else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
		ww = document.documentElement.clientWidth;
		if ( document.documentElement.scrollWidth >= ww ) {
			ww = ww + 16;
		}

	} else {
		ww = document.body.clientWidth;
//		if ( document.documentElement.scrollWidth >= ww ) {
//			ww = ww - 16;
//		}
	}

	return ww;

}

function contentHeight() {

	// ???TODO: redo this too.

	return document.body.offsetHeight - 2;	//???TODO: look at this magic number!

}

function contentWidth() {

	return document.body.offsetWidth - 2;	//???TODO: look at this magic number!

}

function getInnerText( ele ) {

	return (ele.textContent) ? ele.textContent : ele.innerText;

}

function setInnerText( ele, new_text ) {

	if ( typeof( ele.textContent ) != 'undefined' ) {
		ele.textContent = new_text;
	} else {
		ele.innerText = new_text;
	}

}

function dateClean( dateInRaw ) {

	// given a var, determine its type and return a clean (no time) date.

/*
	alert( dateClean( '1/1/2009' ) );	-- returns 1/1/2009
	alert( dateClean( new Date() ) );	-- returns today
	alert( dateClean( '2/29/2009' ) );	-- returns march 1st, 2009 (yikes!)
	alert( dateClean( '02/29/2008' ) );	-- returns 2/29/2008
	alert( dateClean( '29/02/2008' ) );	-- returns may of 2010 (yikes!)
*/

	if ( dateInRaw ) {

		// if it is a date object
		if ( dateInRaw instanceof Date ) {
			return new Date( dateInRaw.getFullYear(), dateInRaw.getMonth(), dateInRaw.getDate() );

		} else {	// otherwise, just try to convert it to a date
			try {
				// ???TODO: I need to do more here...  Because my systems tend to use U.S. date formats, I need to 
				// make sure that this code doesn't fail for "overseas" people. (different locales)

				var dateIn = new Date( dateInRaw );

				if ( isNaN( dateIn ) ) {
					return null;
				} else {
					return new Date( dateIn.getFullYear(), dateIn.getMonth(), dateIn.getDate() );
				}
			} catch (e) {
				return null;
			}

		}

	} else {
		return null;
	}

}

function dateFormat( dateIn ) {

/*
	alert( dateFormat( dateClean( '1/1/2009' ) ) );
	alert( dateFormat( dateClean( '12/31/2009' ) ) );
*/

	var dd = dateIn.getDate();
	var mm = dateIn.getMonth()+1;
	var yyyy = dateIn.getFullYear();

	return dateFormatMDY(mm, dd, yyyy);

}

function dateFormatMDY( mm , dd, yyyy ) {

	return ('0' + mm).slice(-2) + '/' + ('0' + dd).slice(-2) + '/' + yyyy;

}

function timeFormatHMAM( hh, mm, ampm ) {

	return ('0' + hh).slice(-2) + ':' + ('0' + mm).slice(-2) + ' ' + ampm;

}

function boolClean( value ) {

	// a "simple" function to determine if a value is true or false.
	// Used to make sure we have a clean boolean instead of one of a dozen different types!

/*
	alert( boolClean( true ) );
	alert( boolClean( false ) );
	alert( boolClean( '1' ) );
	alert( boolClean( '0' ) );
	alert( boolClean( '-1' ) );
	alert( boolClean( 'On' ) );
	alert( boolClean( 'CHecKed' ) );
	alert( boolClean( 1 ) );
	alert( boolClean( 0 ) );
	alert( boolClean( -1 ) );
	alert( boolClean( 99.99 ) );
	alert( boolClean( 0.000 ) );
*/

	switch ( typeof value ) {
		case 'boolean':
			return value;
		case 'string':
			var v = value.toLowerCase();

			switch ( v ) {
				case 't':
				case '1':
				case 'on':
				case 'true':
				case 'y':
				case 'yes':
				case 'checked':
				case '-1':
					return true;
				default: 
					return false;
			}

		case 'number':
			if ( value != 0 ) {
				return true;
			} else {
				return false;
			}
		default:
			if ( value === null ) {
				return false;
			}
	}

	return false;

}

function intClean( value ) {

	// this function may need some adjustments.  I'm using the parseInt() function
	// and it has some built-in yuckiness...

/*
	alert( intClean( 123 ) );		// returns 123
	alert( intClean( 123.623 ) );		// returns 124
	alert( intClean( '123' ) );		// returns 123
	alert( intClean( '123.623' ) );		// returns 123  (trunc's!!!)

	alert( intClean( null ) );		// returns null
	alert( intClean( true ) );		// returns 1
	alert( intClean( false ) );		// returns 0

	alert( intClean( 'adfasd' ) );		// returns null
	alert( intClean( '123abd' ) );		// returns 123
	alert( intClean( '123e+3' ) );		// returns 123
*/

	if ( value !== null ) {
		var valueOut = null;

		switch ( typeof value ) {
			case 'boolean':
				if ( value ) {
					return 1;
				} else {
					return 0;
				}

			case 'number':
				return Math.round(value);

			default:
				try {
					valueOut = parseInt( value );		// NOTE: this will TRUNC any floats!
				} catch (e) {
					valueOut = null;
				}
				if ( isNaN( valueOut ) ) {
					return null;
				} else {
					return valueOut;
				}
		}

	}

	return null;

}

function xlsstr( value ) {

	if ( value ) {
		if ( value.charAt( '"' ) == -1 ) {			// if no quotes
			if ( value.charAt( '\t' ) == -1 && value.charAt( '\r' ) == -1 && value.charAt( '\n' ) == -1 ) {		// if no tabs, CR's or LF's
				return value;
			} else {		// otherwise, we need to quote it!
				return '"' + value + '"';
			}
		} else {	// if it has double quotes, then double quote the whole thing
			return '"' + value.replace(/"/g, '""') + '"'
		}
	} else {
		return '';
	}

}

function getOuterHTML( ele ) {

	if ( ele.outerHTML ) {
		return ele.outerHTML;
	} else {
		return ele.parentNode.innerHTML;
	}

}

function ew( aa, dd, tt, nn ) {

	document.write("<a href=\"ma" + "il" + "to");
	document.write(":" + aa + "@");
	document.write(dd + "." + tt + "\">");
	if ( nn != null ) {
		document.write(nn + "</a>");
	} else {
		document.write(aa + "@" + dd + "." + tt + "</a>");
	}

}
function jlCalInit( ele_id, dateInRaw ) {

	var ele = g( ele_id );

	if ( ele != null ) {

		var dateIn = dateClean( dateInRaw );

		if ( !dateIn ) {
			dateIn = dateClean( new Date() );
		}

		// set up some variables
		ele.dateSelected = dateIn;		// this is the currently selected date

		ele.dateCurrentMonth = dateClean( dateIn );		// this is the current month shown
		ele.dateCurrentMonth.setDate(1);

		ele.selDayElement = null;		// will init later!

		ele.isKeyboardDisabled = boolClean(getAttr(ele, 'd'));

		// init functions
		ele.refresh = jlCal_refresh;

		// get the form set up
		var frm = g( ele.id + 'frm');

		if ( frm ) {
			ele.has_navigation = true;
			this.dropMonth = g(ele.id + 'dropMonth');
			this.dropYear = g(ele.id + 'dropYear');

		} else {
			ele.has_navigation = false;
			this.dropMonth = null;
			this.dropYear = null;
		}

		if ( this.dropYear ) {
			this.dropYear.value = dateIn.getFullYear();
		}

		if ( ele.addEventListener ) {
			ele.addEventListener('click', jlCal_OnClick, false);
			ele.addEventListener('dblclick', jlCal_OnDblClick, false);
			ele.addEventListener('contextmenu', jlCal_OnContextMenu, false);
			ele.addEventListener('keydown', jlCal_OnKeyDown, false);
			ele.addEventListener('mouseover', jlCal_OnMouseOver, false);
			ele.addEventListener('mouseout', jlCal_OnMouseOut, false);
			ele.addEventListener('mouseup', jlCal_OnMouseUp, false);
			ele.addEventListener('mousedown', jlCal_OnMouseDown, false);
			ele.addEventListener('selectstart', jlCal_OnSelectStart, false);

			if ( frm ) {
				frm.addEventListener('submit', jlCal_frm_OnSubmit, false);
			}

			if ( this.dropMonth ) {
				this.dropMonth.addEventListener('change', jlCal_dropMonth_OnChange, false);
			}

			if ( this.dropYear ) {
				this.dropYear.addEventListener('change', jlCal_dropYear_OnChange, false);
			}

		} else {
			ele.onclick = jlCal_OnClick;
			ele.ondblclick = jlCal_OnDblClick;
			ele.oncontextmenu = jlCal_OnContextMenu;
			ele.onkeydown = jlCal_OnKeyDown;
			ele.onmouseover = jlCal_OnMouseOver;
			ele.onmouseout = jlCal_OnMouseOut;
			ele.onmouseup = jlCal_OnMouseUp;
			ele.onmousedown = jlCal_OnMouseDown;
			ele.onselectstart = jlCal_OnSelectStart;

			if ( frm ) {
				ele.onsubmit = jlCal_frm_OnSubmit;
			}

			if ( this.dropMonth ) {
				this.dropMonth.onchange = jlCal_dropMonth_OnChange;
			}

			if ( this.dropYear ) {
				this.dropYear.onchange = jlCal_dropYear_OnChange;
			}

		}

		ele.handleKeyDown = jlCal_OnKeyDown;
		ele.disableKeyboard = jlCal_disableKeyboard;
		ele.setDateSelected = jlCal_setDateSelected;
		ele.setCurrentMonth = jlCal_setCurrentMonth;
		ele.getDateFromElement = jlCal_getDateFromElement;
		ele.setDateSelectedByElement = jlCal_setDateSelectedByElement;
		ele.getItemFromElement = jlCal_getItemFromElement;
		ele.raiseEventMonthChangeRequest = jlCal_raiseEventMonthChangeRequest;
		ele.raiseOnDblClick = jlCal_OnDblClick;
		ele.getElementByDate = jlCal_getElementByDate;
		ele.doMonthChange = jlCal_doMonthChange;

		if ( dateIn != null ) {
			ele.refresh();
			ele.selDayElement = ele.getElementByDate( dateIn );
		}

	}

}

function jlCal_OnMouseOver( evt ) {

	evt = evt || window.event;

	var src = evtSrc( evt );
	var el = this.getDateFromElement( src );

	if ( el ) {
		if ( getClass(el).charAt(0) != 'h' ) {
			setClass(el, 'h' + getClass(el));
		}
	} else if ( src.tagName == 'IMG' ) {
		var id = getAttr( src, 'i' );
		if ( id ) {
			src.style.border = "1px solid black";
			src.style.borderTop = "1px solid white";
			src.style.borderLeft = "1px solid white";
		}
	}

	return true;
}

function jlCal_OnMouseOut( evt ) {

	evt = evt || window.event;

	var src = evtSrc( evt );
	var el = this.getDateFromElement( src );

	if ( el ) {
		if ( getClass(el).charAt(0) == 'h' ) {
			setClass(el, getClass(el).substr(1));

		}			

	} else if ( src.tagName == 'IMG' ) {
		var id = getAttr( src, 'i' );

		if ( id ) {
			src.style.border = "1px solid transparent";

			if ( IE6 ) {
				src.style.borderColor = "pink";
				src.style.filter = "chroma(color=pink);";
			}
		}
	}

	return true;
}

function jlCal_OnMouseUp( evt ) {

	evt = evt || window.event;

	var src = evtSrc( evt );

	if ( src.tagName == 'IMG' ) {
		var id = getAttr( src, 'i' );

		if ( id ) {
			src.style.border = "1px solid black";
			src.style.borderTop = "1px solid white";
			src.style.borderLeft = "1px solid white";
		}
	}

	return true;
}

function jlCal_OnMouseDown( evt ) {

	evt = evt || window.event;

	var src = evtSrc( evt );

	if ( src.tagName == 'IMG' ) {
		var id = getAttr( src, 'i' );

		if ( id ) {
			src.style.border="";
			src.style.borderTop="2px solid black";
			src.style.borderLeft="2px solid black";
		}
	}

	return true;

}

function jlCal_OnContextMenu( evt ) {

	evt = evt || window.event;

	// ???TODO:

	return evtKill( evt );

}

function jlCal_OnClick( evt ) {

	evt = evt || window.event;

	if ( dblclick_support == false ) {
		return this.raiseOnDblClick( evt );

	} else {

		var src = evtSrc( evt );

		if ( src ) {
			if ( src.tagName == 'A' ) {
				return true;
			} else if ( src.parentNode ) {
				if ( src.parentNode.tagName == 'A' ) {
					return true;
				}
			}
		}

		var el = this.getDateFromElement( src );

		if ( el != null ) {
			this.setDateSelectedByElement( el, true );
			return evtKill( evt );

		} else if ( src.tagName == 'IMG' ) {
			var id = getAttr( src, 'i' );
			if ( id == 'delta' ) {
				this.raiseEventMonthChangeRequest( intClean( getAttr( src, 'v') ) );
				return false;
			} else if ( id == 'today' ) {
				var newDate = dateClean( new Date() );
				this.setDateSelected( newDate, true );
				newDate = dateFormat( newDate );

				try {
					if ( dblclick_support == false ) {
						eval( this.id + "_DayDblClicked('" + newDate + "');" );
					} else {
						eval( this.id + "_DayClicked('" + newDate + "');" );
					}

				} catch (e) {
				}
			}

		}
	}

	return true;

}

function jlCal_OnDblClick( evt ) {

	evt = evt || window.event;

	var src = evtSrc( evt );
	var el = this.getItemFromElement( src );

	// First, see if we can figure out what item was clicked
	if ( el != null ) {
		try {
			var id = getAttr(el, 'i');
			var result = eval( this.id + "_ItemDblClicked('" + id.replace(/'/g, '\\\'') + "');" );
			if ( result == true ) {			// if called returned true, skip the rest of the logic!
				evtKill( evt );
				return true;
			}
		} catch (e) {
		}

	} else if ( src.tagName == 'IMG' ) {
		var id = getAttr( src, 'i' );
		if ( id == 'delta' ) {
			this.doMonthChange( getAttr( src, 'v') );
			return false;
		} else if ( id == 'today' ) {
			var date_id = dateFormat( dateClean( new Date() ) );

			try {
				eval( this.id + "_DayDblClicked('" + date_id + "');" );
			} catch (e) {
			}

		}
	}

	// Now, try to get the day
		var el = this.getDateFromElement( evtSrc( evt ) );

		if ( el != null ) {
			try {
				var date_id = dateFormat( dateClean( getAttr(el, 'd') ) );

				eval( this.id + "_DayDblClicked('" + date_id + "');" );
			} catch (e) {
			}
			return evtKill( evt );
		}

	return true;

}

function jlCal_OnSelectStart( evt ) {

	evt = evt || window.event;

	return evtKill( evt );

}

function jlCal_OnKeyDown( evt ) {

	evt = evt || window.event;

	if ( this.isKeyboardDisabled == true ) {
		return true;

	} else if ( evt.keyCode == 27 && mnuActive != null ) {
		mnuHide();
		return false;
	}

	var kill = false;
	var el = this.selDayElement;

	if ( el != null ) {

		var rr = getParent(el, 'TR');
		var tbl = getParent(rr, 'TABLE');

		row_ord = rr.rowIndex;			// get the index for this row
		col_ord = el.cellIndex;

		if ( row_ord != -1 ) {			// if the row index makes sense
		
			switch (evt.keyCode) {
				case 27:		//27=ESC; clear the buffer and select the first row
					this.setDateSelectedByElement( null, false );
					kill = true;
					break;
					
				case 13:		// 13 = same as DblClick!
					try {
						var date_id = dateFormat( this.dateSelected );
						eval( this.id + "_DayDblClicked('" + date_id + "');" );
					} catch (e) {
						// do nothing
					}
					kill = true;
					break;

				case 33:		// 33 = page up
					if ( evt.shiftKey == true ) {
						this.raiseEventMonthChangeRequest(-12);
					} else {
						this.raiseEventMonthChangeRequest(-1);
					}
					kill = true;
					break;

				case 34:		// 34 = page down
					if ( evt.shiftKey == true ) {
						this.raiseEventMonthChangeRequest(12);
					} else {
						this.raiseEventMonthChangeRequest(1);
					}
					kill = true;
					break;

				case 37:		// 37 = left arrow
					if ( col_ord == 0 && row_ord > 2 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord-1].cells[6], false );
					} else if ( col_ord > 0 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord].cells[col_ord-1], false );
					}
					kill = true;
					break;

				case 38:		// 38=Up arw
					if ( row_ord > 2 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord-1].cells[col_ord], false );
					}
					kill = true;
					break;

				case 39:		// 39 = right arrow
					if ( col_ord == 6 && row_ord < tbl.rows.length-1 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord+1].cells[0], false );
					} else if ( col_ord < 6 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord].cells[col_ord+1], false );
					}
					kill = true;
					break;

				case 40:		// 40=Down arw
					if ( row_ord < tbl.rows.length-1 ) {
						this.setDateSelectedByElement( tbl.rows[row_ord+1].cells[col_ord], false );
					}
					kill = true;
					break;
				
				default:
					window.status = 'you hit ' + evt.keyCode;
					kill = false;
					break;
			}

		}

		if ( kill == true ) {
			return evtKill( evt );
		}

	}
	return true;

}

function jlCal_raiseEventMonthChangeRequest( delta ) {

	var selDayOut = dateClean( this.dateSelected );
	var result = true;

	if ( selDayOut != null ) {
		selDayOut = "'" + dateFormat(selDayOut) + "'";
	}

	try {
		result = eval( this.id + "_MonthChangeRequest(" + delta + ", " + selDayOut + ");" );
	} catch (e) {
		// do nothing
	}

	if ( result == undefined || result === null || result == true ) {
		this.doMonthChange( delta );
	}

}

function jlCal_getItemFromElement( el ) {

	// Given an element, will search up the document tree looking for the TR tag that has a row id

	//	RET:	returns either the element with the i= attribute or null

	while ( el != null ) {
		if ( el.tagName == 'TR' && getAttr(el, 'i') != null ) {
			return el;
		} else {
			el = getParent(el.parentNode, "TR");
		}
	}

	return null;

}

function jlCal_getDateFromElement( el ) {

	// Given an element, will search up the document tree looking for the TD element that has a day id

	//	RET:	null or the element with the d= attribute

	while ( el != null ) {
		if ( getAttr(el, 'd') != null ) {
			return el;
		} else {
			el = getParent(el.parentNode, "TD");
		}
	}

	return null;

}

function jlCal_setDateSelectedByElement( el, raise_click ) {

	// marks the given day selected

	if ( el != this.selDayElement ) {				// if there is a new selected day
		// first, unselect the last day
		if ( this.selDayElement != null ) {
			if ( getClass(this.selDayElement).charAt(0) == 'h' ) {
				setClass(this.selDayElement, getClass(this.selDayElement).substr(1));
			}
			if ( getClass(this.selDayElement).charAt(0) == 's' ) {
				setClass(this.selDayElement, getClass(this.selDayElement).substr(1));
			}
			this.selDayElement = null;
			this.dateSelected = null;
		}

		// Now, mark the new day
		if ( el != null ) {
			if ( getClass(el).charAt(0) == 'h' ) {
				setClass(el, 'hs' + getClass(el).substr(1));
			} else {
				setClass(el, 's' + getClass(el));
			}
			this.selDayElement = el;
			this.dateSelected = dateClean( getAttr(el, 'd') );
			this.selDayElement.focus();

			// signal that a date was selected!
			try {
				var date_id = dateFormat( this.dateSelected );
				eval( this.id + "_DaySelected('" + date_id + "');" );
			} catch (e) {
			}

		}
	}

	// if we should raise a click, do it!
	if ( raise_click == true ) {
		try {
			var date_id = dateFormat( this.dateSelected );
			eval( this.id + "_DayClicked('" + date_id + "');" );
		} catch (e) {
		}
	}

}

function jlCal_disableKeyboard( disable ) {

	this.isKeyboardDisabled = disable;

}

function jlCal_setCurrentMonth( dateInRaw ) {

	var dateIn = dateClean( dateInRaw );

	if ( !dateIn ) {
		return true;
	}

	var dateMonth = new Date( dateIn.getFullYear(), dateIn.getMonth(), 1 );

	if ( dateMonth.getTime() != this.dateCurrentMonth.getTime() ) {		// if we need to move months
		this.dateCurrentMonth = dateMonth;
		this.refresh();
		return false;
	} else {
		return true;
	}

}

function jlCal_setDateSelected( dateInRaw, raiseEvent ) {

	//	RET:	true - if it can't find the date!

	var dateIn = dateClean( dateInRaw );

	if ( !dateIn ) {
		return true;
	}

	var dateMonth = new Date( dateIn.getFullYear(), dateIn.getMonth(), 1 );

	if ( dateMonth.getTime() != this.dateCurrentMonth.getTime() ) {		// if we need to move months
		this.dateCurrentMonth = dateMonth;
		this.dateSelected = dateIn;
		this.refresh();

		if ( raiseEvent == true ) {

			try {
				var date_id = dateFormat( dateClean( getAttr(el, 'd') ) );

				eval( this.id + "_DayClicked('" + date_id + "');" );
			} catch (e) {
			}
		}

		return false;
	} else {
	
		var el = this.getElementByDate( dateIn );

		if ( el ) {
			this.setDateSelectedByElement( el, false );

			if ( raiseEvent == true ) {

				try {
					var date_id = dateFormat( dateClean( getAttr(el, 'd') ) );

					eval( this.id + "_DayClicked('" + date_id + "');" );
				} catch (e) {
				}
			}

			return false;
		}
	}

	return true;

}

function jlCal_getElementByDate( dateIn ) {

	var tbl = g(this.id + 'tbl');
	var dateToFind = dateFormat( dateIn );

	if ( tbl != null ) {
		for ( var rr = 2; rr < tbl.rows.length; rr++ ) {
			var row = tbl.rows[rr];
			for ( var cc = 0; cc < 7; cc++ ) {
				if ( getAttr(row.cells[cc], 'd') == dateToFind ) {
					return tbl.rows[rr].cells[cc];
				}
			}
		}
	}

	return null;

}

function jlCal_frm_OnSubmit( evt ) {

	evt = evt || window.event;

//	var dropYear = this.dropYear.blur();

	return evtKill( evt );

}

function jlCal_dropMonth_OnChange( evt ) {

	evt = evt || window.event;

	var par = getParent(this, "DIV");
	var month = this.selectedIndex;
		
	par.dateCurrentMonth.setMonth(month);
	par.refresh();
	
}

function jlCal_refresh() {

	if ( this.has_navigation ) {

		var tbody = g(this.id + 'tbody');

		var dd = new Date( this.dateCurrentMonth );
		var ss = this.dateSelected;
		var today = new Date();

		today = new Date( today.getFullYear(), today.getMonth(), today.getDate());

		var month = dd.getMonth();

		dd.setDate( dd.getDate() - dd.getDay() );

		var dayOfMonth = dd.getDate();

		for ( rr = 0; rr < 6; rr++ ) {
			var row = tbody.rows[rr];

			for ( cc=0; cc < 7; cc++ ) {
				var cell = row.cells[cc];

				var sClassName = 'oday';

				if ( dd.getTime() == today.getTime() ) {
					if ( dd.getTime() == ss.getTime() ) {
						sClassName = 'stoday';
					} else {
						sClassName = 'today';
					}
				} else if ( dd.getTime() == ss.getTime() ) {
					sClassName = 'sday';
					this.selDayElement = cell;
				} else if ( dd.getMonth() == month ) {
					sClassName = 'day';
				}

				cell.className = sClassName;
				setAttr( cell, 'd', dateFormat( dd ) );

				cell.innerText = dayOfMonth;

				dd.setDate( dayOfMonth + 1 );
				dayOfMonth = dd.getDate();

			}
		}

		g(this.id + 'dropMonth').selectedIndex = this.dateCurrentMonth.getMonth();
		g(this.id + 'dropYear').value = this.dateCurrentMonth.getFullYear();

	}

}

function jlCal_dropYear_OnChange ( evt ) {

	evt = evt || window.event;

	var par = getParent(this, "DIV");

	var year = intClean( this.value );

	if ( year === null || year < 1000 ) {
		valErr( this, "Year", "Please enter a valid year.");
		this.select();
		this.focus();
		return false;

	} else {
		valClr( this );
		par.dateCurrentMonth.setFullYear(year);
		par.refresh();
	}

	return evtKill( evt );

}

function jlCal_doMonthChange( parameterRaw ) {

	var parameter = intClean( parameterRaw );

	if ( parameter && parameter != 0 ) {
		var newMonth = dateClean( this.dateCurrentMonth );

		var month = newMonth.getMonth();

		month += parameter;

		newMonth.setMonth( month );

		this.setCurrentMonth( newMonth );

	}

}
// PROPRIETARY - (c) 2010, 2009, 2008, 2007, 2006, 2005, 2004, 2003 by James J. Domino - Juniper Labs LLC - 310-439-1185

	var page_entity_code = '';
	var page_entity_id = '';
	var page_entity_instance_id = '';

	var page_tab_querystring = '';

	var page_as_of_date = null;

	var page_save_preferred_tab = true;
	var page_last_preferred_tab = '';

function sqlstr( str_in ) {

	// ???TODO: try not to use this function!

	if ( str_in == null || str_in == '' ) {
		return 'null';
	} else {
		return '\'' + str_in.replace(/'/g, '\'\'') + '\'';
	}

}

function problemCreate() {

	var url;

	if ( page_url == null || page_url == '' ) {
		url = escape(document.URL);
	} else {
		url = escape(page_url);
	}

	winCreate('issue_new_step_01_entry.asp?entity_id_1=' + page_entity_id + '&entity_instance_id_1=' + page_entity_instance_id + '&url=' + url, 640, 480);

}

function mnuPageTop() {

	var mnu = g('mnuPageTop');
	mnuShow(mnu);
	return false;

}

function mnuPageTop_Clicked( mnu_id ) {

	if ( mnu_id == 'home' ) {
		var url = 'person_home_set.asp?';
		url += getPageInfo();

		sqlRun( url );

	} else if ( mnu_id == 'default' ) {
		var url = 'person_home_set.asp?default=true';

		sqlRun( url );

	} else {
		alert('you selected ' + mnu_id);
	}
}

function getPageInfo() {

	var qs = 'page_entity_id=' + page_entity_id + '&page_entity_instance_id=' + page_entity_instance_id
		+ '&page_title=' + escape(document.title)
		+ '&parent_location=' + escape(parent.location)
		+ '&page_url=' + escape(page_url);

	return qs;

}

function toolbar_ClickedDefault( toolbar_id, btn_id, parent_entity_id, parent_entity_instance_id, page_options ) {

	// Add code to allow passing the current state (gray, etc.) of the toolbar button.

	if ( btn_id == 'search' ) {

		if ( isOkToExit() == true ) {
			parent.location = 'entity_search.asp?entity_id=' + parent_entity_id;
		}

	} else if ( btn_id == 'refresh' ) {
		if ( isOkToExit() == true ) {
			if ( page_url == null || page_url == '' ) {
				parent.location = 'entity_show.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id + page_options;
			} else {
				parent.location = page_url;
			}
		}

	} else if ( btn_id == 'new' ) {
		if ( isOkToExit() == true ) {
			parent.location = 'entity_show.asp?entity_id=' + parent_entity_id; 
		}

	} else if ( btn_id == 'delete' ) {
		// if this is a "new" instance, just jump page_cancel, page_after or even entity_search!
		if ( parent_entity_instance_id == '' ) {					// if a new instance, go to page_cancel or page_after!

			if ( window.confirm("Are you sure you want to discard your work?") == true ) {

				// first, see if they specified a page_cancel
				var sURL = qsGetPara( page_options, 'page_cancel' );
	
				// if not, see if there is a page_after
				if ( sURL == null || sURL == '' ) {
					sURL = qsGetPara( page_options, 'page_after' );
				}
	
				// otherwise, just jump to a search
				if ( sURL == null || sURL == '' ) {
					sURL = 'entity_search.asp?entity_id=' + parent_entity_id;
				}
	
				parent.location = sURL;

			}

		} else {

			if ( window.confirm("Are you sure you want to delete this entity?") == true ) {

				var delete_options = page_options;
	
				if ( delete_options.indexOf('page_after') < 0 ) {		// if there isn't a specified page_after
					delete_options += '&page_after=entity_search.asp%3Fentity_id=' + parent_entity_id;
				}
	
				if ( delete_options.indexOf('page_cancel') < 0 ) {		// if there isn't a page_cancel
					if ( page_url == null || page_url == '' ) {
						delete_options += '&page_cancel=' + escape(window.document.location.toString());
					} else {
						delete_options += '&page_cancel=' + escape(page_url);
					}
				}
				parent.location = 'entity_action_run.asp?entity_action_code=delete&entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id + '&is_confirmed=1' + delete_options;
			}
		}

	} else if ( btn_id == 'save' ) {
		doSave();

	} else if ( btn_id == 'copy' ) {
		if ( isOkToExit() == true ) {
			if ( window.confirm("Are you sure you want to copy this item?") == true ) {
	
				// ???TODO: note that I'm not keeping the current page_options!!!  In the future, I may want to look at the
				//			page_options and remove any page_after and page_cancel options!!!
	
				var copy_options = '';
				copy_options += '&page_after=entity_show.asp%3Fentity_id=' + parent_entity_id + '%26entity_instance_id=%25_1%25';
				copy_options += '&page_cancel=entity_show.asp%3Fentity_id=' + parent_entity_id + '%26entity_instance_id=' + parent_entity_instance_id;
	
				parent.location = 'entity_action_run.asp?entity_action_code=copy&entity_action_name=Copy&entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id + '&is_confirmed=1' + copy_options;
	
			}
		}

	} else if ( btn_id == 'help' ) {
		if ( page_help_code == null ) {
			if ( parent_entity_id == 0 ) {
				alert('Sorry...  No help information is available for this page.');
			} else {
				winCreate('entity_help.asp?entity_id=' + parent_entity_id, 750, 300);
			}
		} else {
			popupContentShow( page_help_code );
		}

	} else if ( btn_id == 'print' ) {
		window.document.body.focus();
		window.print();

	} else if ( btn_id == 'globalclipset' ) {
		sqlRun('global_clipboard_add.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id + '&is_set=1');
		
	} else if ( btn_id == 'globalclipadd' ) {
		sqlRun('global_clipboard_add.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id + '&is_set=0');

	} else if ( btn_id == 'globalclipdel' ) {
		sqlRun('global_clipboard_delete.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id);

	} else if ( btn_id == 'StapleUploadFile' ) {
		if ( isOkToExit() == true ) {
			winCreate('file_upload_and_staple.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id, 640, 400);
		}
	} else if ( btn_id == 'StapleGlobal' ) {
		if ( isOkToExit() == true ) {
			winCreate('global_clipboard_staple.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + parent_entity_instance_id, 640, 400);
		}
	} else if ( btn_id == 'frmedit' ) {
		winCreate('entity_set.asp?entity_id=' + parent_entity_id, 750, 300);

	} else if ( btn_id == 'taskcreate' ) {
		entityActionRun( 'Items', 'task_create', 'Create a Daily Task/Reminder', false, 'entity_action_run.asp', true );

	} else if ( btn_id == 'search_prev' ) {
		if ( isOkToExit() == true ) {
			if ( window.opener ) {
				try {
					var ei_id = window.opener.getTblPrev(parent_entity_id, parent_entity_instance_id);

					if ( ei_id == 'top' ) {
						alert('Sorry...  You have reached the top of the list.');
					} else if ( ei_id == 'wrong_entity' ) {
						alert('Sorry...  The list of items is not the right entity anymore.');
					} else if ( ei_id ) {
						document.focus();
						document.location = 'entity_show.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + ei_id;
					}
				} catch(e) {
				}
			} else {
				window.status = "Sorry...  You need to open this window from a list.";
			}
			return false;
		}

	} else if ( btn_id == 'search_next' ) {
		if ( isOkToExit() == true ) {
			if ( window.opener ) {
				try {
					var ei_id = window.opener.getTblNext(parent_entity_id, parent_entity_instance_id);

					if ( ei_id == 'bottom' ) {
						alert('Sorry...  You have reached the bottom of the list.');
					} else if ( ei_id == 'wrong_entity' ) {
						alert('Sorry...  The list of items is not the right entity anymore.');
					} else if ( ei_id ) {
						document.focus();
						document.location = 'entity_show.asp?entity_id=' + parent_entity_id + '&entity_instance_id=' + ei_id;
					}
				} catch(e) {
				}
			} else {
				window.status = "Sorry...  You need to open this window from a list.";
			}
			return false;
		}

	} else {
		alert('You pressed ' + btn_id);

	}

}

function toolbar_ClickedTableDefault( table_id, btn_id, parent_entity_code, parent_entity_id, parent_entity_instance_id, child_entity_id, sibling_entity_id, sibling_jump_ord, evt ) {

	var tbl = g(table_id);

	evt = evt || window.event;

	if ( btn_id == 'refresh' ) {

		if ( page_url == null || page_url == '' ) {
			window.document.location = window.document.location.toString();
		} else {
			window.document.location = page_url;
		}

	} else if ( btn_id == 'open' ) {

		var tbl = g( table_id );
		var count = 0;
		var have_multiple_selects = false;
		var have_shift_key_down = false;

		// check the state of the shift key
		if ( evt ) {				// if there is an event associated with this
			if ( evt.shiftKey == true ) {			// if the user is pressing the shift key
				have_shift_key_down = true;
			}
		}

		if ( tbl ) {

			count = tbl.getSelectedRowCount();
			var row_count = tbl.getRowCount();
			
			for (tt=0; tt < row_count; tt++ ) {

				if ( tbl.rowIsSelected( tt ) ) {

					var rr = tbl.getRow( tt );
					var new_page_after = '';

					// if this page has an parent_entity_id, then set up the page_after to come back here
					if ( parent_entity_id != null && parent_entity_id != '') {
						new_page_after = '&page_after=entity_show.asp%3Fentity_id=' + parent_entity_id + '%26entity_instance_id=' + parent_entity_instance_id;
					}

					var URL;

					if ( sibling_entity_id != null ) {
						try {
							if ( sibling_jump_ord == null ) {
								sibling_jump_ord = 1;
							}
							var sei_id = rr.attributes['i'].value.split('|')[sibling_jump_ord];

							URL = 'entity_show.asp?entity_id=' + sibling_entity_id + '&entity_instance_id=' + sei_id + new_page_after;
						} catch (e) {
							// ???TODO:
						}

					} else {
						URL = 'entity_show.asp?entity_id=' + child_entity_id + '&entity_instance_id=' + rr.attributes['i'].value + new_page_after;
					}

					count--;
					if ( count == 0 && !have_shift_key_down ) {
						parent.location = URL;
						break;
					} else {
						window.open(URL, "", "");
					}

				}

			}

		}

	} else if ( btn_id == 'staple' ) {
		if ( tbl.getSelectedRowCount() > 0 ) {
			entityActionRun( 'Items', 'staple_add', 'Create a staple and link all of the selected items to it.', false, 'entity_action_run.asp', false );
		} else {
			alert('Please select at least one row!');
		}

	} else if ( btn_id == 'issue_add' ) {
		if ( tbl.getSelectedRowCount() > 0 ) {
			entityActionRun( 'Items', 'issue_add', 'Add an issue and staple the selected items to it.', false, 'entity_action_run.asp', false );
		} else {
			alert('Please select at least one row!');
		}

	} else if ( btn_id == 'print' ) {
		if ( tbl ) {
			try {
				window.focus();
				window.print();
			} catch (e) {
			}
		}

	} else if ( btn_id == 'new' ) {
		var new_page_after = '';

		// if this page has an parent_entity_id, then set up the page_after to come back here
		if ( parent_entity_id != null && parent_entity_id != '') {
			new_page_after = 'entity_show.asp%3Fentity_id=' + parent_entity_id + '%26entity_instance_id=' + parent_entity_instance_id;
		}

		parent.location = 'entity_show.asp?entity_id=' + child_entity_id + '&def_' + parent_entity_code + '_id=' + parent_entity_instance_id + '&page_after=' + new_page_after;

	} else if ( btn_id == 'delete' ) {

		var frm = g('frmMain');

		if ( frm != null ) {

			if ( window.confirm("Are you sure you want to delete these " + g('page_entity_name_plural').value + "?") == true ) {

				var url_after = '';

				if ( page_url == null || page_url == '' ) {		// if we weren't given a url, we need to derive it!
					url_after = window.document.location.toString();
				} else {
					url_after = page_url;
				}

				// ???TODO: need to bulletproof these!
				g('page_after').value = url_after;
				g('page_cancel').value = url_after;

				frm.attributes['action'].value = 'entity_action_run.asp';
				g('entity_action_code').value = 'delete';
				g('entity_action_name').value = 'Delete';
				g('action').value = 'ok';			// save the hidden input tag named "action" to have an OK value!

				frm.submit();

			}

		}
		
	} else if ( btn_id == 'clipcopy' ) {
		var out = tbl.toString();

		window.clipboardData.setData("Text", out);
	
		alert('Table copied to your clipboard.  You can paste it into any application now.');

	} else if ( btn_id == 'globalclipset' ) {
		if ( tbl.getSelectedRowCount() > 0 ) {
			entityActionRun( 'Items', 'global_clipboard_set', 'Clear your global clipboard and add the selected items.', false, 'entity_action_run.asp', false );
		} else {
			alert('Please select at least one row!');
		}

	} else if ( btn_id == 'globalclipadd' ) {
		if ( tbl.getSelectedRowCount() > 0 ) {
			entityActionRun( 'Items', 'global_clipboard_add', 'Add the selected items to your global clipboard.', false, 'entity_action_run.asp', false );
		} else {
			alert('Please select at least one row!');
		}

	} else if ( btn_id == 'globalclipdel' ) {
		if ( tbl.getSelectedRowCount() > 0 ) {
			entityActionRun( 'Items', 'global_clipboard_delete', 'Remove the selected items to your global clipboard.', false, 'entity_action_run.asp', false );
		} else {
			alert('Please select at least one row!');
		}

	} else {
		alert('You pressed ' + btn_id);
	}

}

function table_RowDblClickedDefault( row_id, row_ord, parent_entity_id, parent_entity_instance_id, child_entity_id, sibling_entity_id, sibling_jump_ord, evt ) {

	// Default handling of clicking rows on a table

	var URL;

	evt = evt || window.event;

	// Build the base URL
		if ( sibling_entity_id != null ) {
			try {
				if ( sibling_jump_ord == null ) {
					sibling_jump_ord = 1;
				}

				var sei_id = row_id.split('|')[sibling_jump_ord];

				URL = 'entity_show.asp?entity_id=' + sibling_entity_id + '&entity_instance_id=' + sei_id;

			} catch (e) {
				// ???TODO:
			}

		} else {
			URL = 'entity_show.asp?entity_id=' + child_entity_id + '&entity_instance_id=' + row_id;
		}

	// If this is an event, check if the user is trying to modify the default behaviour
		if ( evt ) {				// if there is an event associated with this
	
			if ( evt.shiftKey == true ) {			// if the user is pressing the shift key
	
// ???TODO: I removed this because it was getting too annoying!!!
//				URL += '&page_after=window_close.asp'
	
				if ( parent.isOkToExit() == true ) {
					window.open(URL, "", "");
				}
	
				return evtKill( evt );

			}
		}

	// Add page after information
		if ( parent_entity_id != null && parent_entity_id != '' && parent_entity_id != '0' ) {
			var pa = g('page_after');
			if ( pa != null && pa.value != null && pa.value != '' ) {
				URL += '&page_after=' + escape(pa.value);
			} else {
				URL += '&page_after=entity_show.asp%3Fentity_id=' + parent_entity_id + '%26entity_instance_id=' + parent_entity_instance_id;
			}
		}

	// Open the URL
		urlGo( URL, true );

}

function personPreferenceValueSet( person_preference_code, value, value_name ) {

	// ???TODO: change to use something more specific...  to remove possible security issues!  (sql_run.asp --> person_preference_value_set.asp)
	// ???TODO: change this code to handle value and value_names that may contain & and other characters!

	// make sure that we have a string!
	value += '';
	value_name += '';

	sqlRun('sql_run.asp?entity_id=38&sql=exec+person_preference_value_set+@person_preference_code=' + sqlstr(person_preference_code) + ',+@value=' + escape(sqlstr(value)) + ',+@value_name=' + escape(sqlstr(value_name)) + '&task_name=Change+your+personal+preference&refresh_person_session=1');

}

function personPreferenceValueMnuBooleanSet( mnu_id ) {

	var mnuOption = g(mnu_id);
	var imgMnu = g('imgmnu' + mnu_id);
	var value = 'True';
	var value_name = 'True';

	if ( mnuOption != null ) {

		if ( mnuOption.is_on == true || mnuOption.is_on == 'True' || mnuOption.is_on == 'true' ) {
			mnuOption.is_on = false;
			value = 'False';
			value_name = 'False';
			if ( imgMnu != null ) {
				imgMnu.src = 'fx/mnuChecked0.gif';
			}
		} else {
			mnuOption.is_on = true;
			value = 'True';
			value_name = 'True';					
			if ( imgMnu != null ) {
				imgMnu.src = 'fx/mnuChecked1.gif';
			}
		}

		// ???TODO: change to use something more specific...  to remove possible security issues!  (sql_run.asp --> person_preference_value_set.asp)
		sqlRun('sql_run.asp?entity_id=38&sql=exec+person_preference_value_set+@person_preference_code=' + sqlstr(mnu_id) + ',+@value=' + value + ',+@value_name=' + sqlstr(value_name) + '&task_name=Change+your+personal+preference&refresh_person_session=1');
	}

}

function bigdropOpen( entity_id, field_id, was_text_field ) {

	//	Opens the search window for this bigdrop!

	var keyCodeToSend = '';

	// ???TODO: do I need the following?
	if ( was_text_field ) {

		var evt = evt || window.event;
	
		if ( evt != null ) {
			evtKill( evt );
			keyCodeToSend = evt.keyCode;
//			return false;
		}

	}

	// Show the dialog box
		dlgHide( false );			// hide any previous dlg

		eleDlgFocus = g(field_id);		// save the control that has focus

	// Set up the IFRAME
		var now = new Date();
		ifrmDlg = g('iframeDlg');

		ifrmDlg.src = 'dlgEntitySearch.asp?entity_id=' + entity_id
				+ '&field_id=' + escape(field_id)
				+ '&keycode=' + escape(keyCodeToSend)
				+ "&now=" + now.toUTCString();

		ifrmDlg.style.width = 550;
		ifrmDlg.style.height = 350;
		ifrmDlg.style.left=( (document.body.scrollWidth-550)/2 ) + 'px'; 
		ifrmDlg.style.top=( (document.body.scrollHeight-300)/2 ) + 'px';
		ifrmDlg.style.borderWidth = 0;

		// Display it
			ifrmDlg.style.display = "block";
			ifrmDlg.style.zIndex = 1000;

		// Now, mark the dlgExport as being active
			is_dlg_active = true;

		// ???TODO: add code to actively watch the mouse?  (Instead of _always_ watching the mouse...)

		return false;

}

function dlgEntitySearchReturn( is_cancel, entity_instance_id, entity_instance_temp_name ) {

	var ctl = eleDlgFocus;
	var field_id = ctl.name;

	dlgHide( false );

	if ( is_cancel == true ) {

		return false;

	}

	if ( ctl ) {
		ctl.value = entity_instance_id;

		var ctlText = g('text_' + field_id);

		if ( ctlText ) {
			ctlText.value = entity_instance_temp_name;

			try {
				ctlText.focus();
			} catch (e) {
				// do nothing
			}

		}

		frmChangedSet( true );
	}

	return false;

}

function doSave( frm ) {

	// tells this page to save its data

	//	RET:	true - on errors

	if ( frm == null ) {			// if they didn't specify one, assume frmMain!
		frm = g('frmMain');
	}

	// Make sure any tedit's have there data copied back!
		if ( have_tedit == true ) {
			jlTeditTransfer();
		}

	if ( frmIsValid(frm) ) {

		// ???TODO: add code to see if there is a "onFormSubmit" attribute that should be evaluated first!
		//				the idea is that there may need to be some type of "local" adjusting of values
		//				or moving data values from a set of HTML objects into a textarea or something...

		// ???TODO: add code to handle multiple submit buttons on a form.  (Need to make the form return the value of the selected <INPUT type=submit> control.

		frm.submit();

		return false;

	} else {
		return true;

	}

}

function areYouSureSaveChanges() {

	// A very specific version of the areYouSure() dialog that specifically requests if the user wants to save changes.

	// Returns: 	'yes' = if user replied YES
	//				'no' = if user selected NO
	//				'cancel' = if the user selected CANCEL

	// Init
		var now = new Date();

	// Now, show the dialog box
		is_dlg_active = true;
		window.showModalDialog("_are_you_sure.asp?entity_name=" + page_entity_name + "&now=" + now.toUTCString(), window,"dialogHeight: 143px; dialogWidth: 350px; edge: Raised; center: Yes; help: No; resizable: No; status: No;");
		is_dlg_active = false;

	// done
		return lastDlgResult;

}

function tabPreferredSave( tab_id ) {

	// send the preferred tab to the backend...

	// NOTE: there is a little bit of a performance hit here...  We are sending a message to the server each time someone its the tab!
	// should, perhaps, only do this if they leave the page!

	if ( page_save_preferred_tab == true ) {
		if ( page_last_preferred_tab != tab_id ) {
			page_last_preferred_tab = tab_id

			try {
				sqlRun('entity_preferred_tab.asp?entity_code=' + page_entity_code + '&tab_id=' + tab_id, true);
			} catch (e) {
				// do nothing
			}
		}
	}

}

function entityActionRun( entity_name_plural, entity_action_code, entity_action_name, confirm_on_front_end, page_to_execute, check_if_ok_to_exit ) {

	// This command will run a given page to execute an entity action

	// change this code to bring up a IFRAME and run the action from the iframe instead of jumping to a new page...
	// note that this code is intended to replace all instances of sqlRUN()!!!

	// ???TODO: add code to check if there are changes and prompt before running any action
	//		if ( isOkToExit() == true ) {

	var now = new Date();

	if ( check_if_ok_to_exit == true ) {
		if ( isOkToExit() == false ) {
			return false;
		}
	}

	if ( g('in_row_mode') != null ) {

		var frm = g('frmMain');

		if ( frm != null ) {

			if ( confirm_on_front_end == true ) {
				if ( window.confirm('Are you sure you want to ' + entity_action_name + ' these ' + entity_name_plural + '?') != true ) {
					return false;
				}
			}

			// ???TODO: watch out for double quotes!!!
			var url_after = '';

			// if we are in the search tool, make the URL that
			if ( page_url == null || page_url == '' ) {
				url_after = document.location.toString();
			} else {
				url_after = page_url;
			}

			// ???TODO: need to bulletproof these!
			g('page_after').value = url_after;
			g('page_cancel').value = url_after;

			// if they didn't provide a page to execute, assume the generic one
			if ( page_to_execute != null ) {
				frm.attributes['action'].value = page_to_execute;
			} else {
				frm.attributes['action'].value = 'entity_action_run.asp';
			}

			// save the action code and name
			g('entity_action_code').value = entity_action_code;
			g('entity_action_name').value = entity_action_name;

			if ( confirm_on_front_end == false ) {
				g('action').value = 'need_ok';			// save the hidden input tag named "action" to have an OK value!
			} else {
				g('action').value = 'ok';
			}

			frm.submit();

		}

	} else {			// if not in row mode, need to build a URL and jump to it!

		if ( confirm_on_front_end == true ) {
			if ( window.confirm('Are you sure you want to ' + entity_action_name + ' these ' + entity_name_plural + '?') != true ) {
				return false;
			}
		}

		var url = '';

		if ( page_to_execute != null ) {
			url = page_to_execute;
		} else {
			url = 'entity_action_run.asp';
		}

		url += '?entity_id=' + page_entity_id;
		if ( typeof(page_entity_instance_id) != 'undefined' ) {
			url += '&entity_instance_id=' + page_entity_instance_id;
		}
		if ( typeof(page_entity_instance_ts) != 'undefined' ) {
			url += '&entity_instance_ts=' + page_entity_instance_ts;
		}
		url += '&entity_action_code=' + escape(entity_action_code);
		url += '&entity_action_name=' + escape(entity_action_name);
		url += '&page_after=' + escape(page_url);			// if a delete action, this may never return!
		url += '&page_cancel=' + escape(page_url);

		if ( confirm_on_front_end == true ) {
			url += '&action=ok';
		} else {
			url += '&action=need_ok';
		}

		parent.location = url;

	}

}

function copyIdToClipboard() {

	try {
		var out = page_entity_instance_id;

		window.clipboardData.setData("Text", out);		

		window.status = 'This ' + page_entity_name + '\'s id (' + out + ') has been copied to the clipboard.';

	} catch (e) {
		window.status = 'Having problems copying the id.';
	}

}

function sqlRun( url, hide_busy ) {
 
    var ifrm = g('iframeQuery');
    var now = new Date();

	if ( ifrm ) {
		ifrm.src = "javascript:'';";
		if ( hide_busy != true ) { ifrm.style.left=( (document.body.scrollWidth-busy_dialog_width)/2 ) + 'px'; ifrm.style.top=( (document.body.scrollHeight-busy_dialog_height)/2 ) + 'px'; ifrm.style.display='block'; ifrm.style.zIndex = 1000; }
		ifrm.src = url + "&now=" + now.toUTCString();
	} else {
		// ???TODO: error!
	}

}

function tabOnResize() {

	// ???TODO: need to redo this!!!

	var right = windowWidth() - 18;

	// Let's do another check--to make sure things don't get out of hand!
	if ( typeof( page_width ) != 'undefined' ) {
		if ( right > page_width ) {
			right = page_width - 18;
		}
	}

	var hdrMain = g('hdrMain');
	var tblMain = g('tblMain');
	var toolbarMain = g('toolbarMain');
	var divContent = g('divContent')

	if ( hdrMain ) {
		var top = totalOffsetTop(hdrMain) + hdrMain.clientHeight;
	} else if ( toolbarMain ) {
		var top = totalOffsetTop(toolbarMain) + toolbarMain.clientHeight;
	}

	var bottom = windowHeight() - 2;

	// if there is a table, reposition it
	if ( tblMain ) {
		tblMain.move(0, top, right, bottom);

	} else if ( divContent ) {			// if there is just a Content DIV, reposition it
		divContent.style.position = 'absolute';
		divContent.style.left = '0px';
		divContent.style.top = top + 'px';

		try {
			if ( IE6 ) {
				divContent.style.height = (document.body.clientHeight - top) + 'px';
				divContent.style.width = (document.body.clientWidth) + 'px';
			} else {
				divContent.style.right = '2px';		// for new browsers...  Just specify the right and bottom directly.  It will calculate the width by itself!
				divContent.style.bottom = '2px';
			}
		} catch (e) {
		}

	}

	if ( toolbarMain ) {
		try {
			toolbarMain.style.width = right + 'px';
		} catch (e) {
			// do nothing
		}
	}

}


function getTblPrev( entity_id, entity_instance_id ) {

	if ( typeof(page_entity_id) != 'undefined' ) {
		if ( entity_id == page_entity_id ) {

			var tbl = g('tblMain');
			var focus_row_ord = tbl.getFocusRowOrd();
	
			if ( focus_row_ord > 0 ) {
				tbl.clearRowsSelected();
	
				tbl.setFocusRow( focus_row_ord - 1);
	
				var row_id = getAttr(tbl.getRow( tbl.getFocusRowOrd() ), 'i');
	
				return row_id;
			} else {
				return 'top';
			}
		} else {
			return 'wrong_entity';
		}
	}

}

function getTblNext( entity_id, entity_instance_id ) {

	if ( typeof(page_entity_id) != 'undefined' ) {
		if ( entity_id == page_entity_id ) {

			var tbl = g('tblMain');
			var focus_row_ord = tbl.getFocusRowOrd();
	
			if ( focus_row_ord < tbl.getRowCount()-1 ) {
				tbl.clearRowsSelected();
	
				tbl.setFocusRow( focus_row_ord + 1);
	
				var row_id = getAttr(tbl.getRow( tbl.getFocusRowOrd() ), 'i');
	
				return row_id;
			} else {
				return 'bottom';
			}
		} else {
			return 'wrong_entity';
		}
	}

}

function jlDbtnInit( btn_id ) {

	var ele = g( btn_id );

	if ( ele != null ) {

		ele.go = jlDbtn_Go;

		// set up the events
			if ( ele.addEventListener ) {
				ele.addEventListener('click', jlDbtn_OnClick, false);
				ele.addEventListener('dblclick', jlDbtn_OnClick, false);
				ele.addEventListener('mouseover', jlDbtn_OnMouseOver, false);
				ele.addEventListener('mouseout', jlDbtn_OnMouseOut, false);
				ele.addEventListener('mouseup', jlDbtn_OnMouseUp, false);
				ele.addEventListener('mousedown', jlDbtn_OnMouseDown, false);
				ele.addEventListener('contextmenu', jlDbtn_OnContextMenu, false);
			} else {
				ele.onclick = jlDbtn_OnClick;
				ele.ondblclick = jlDbtn_OnClick;
				ele.onmouseover = jlDbtn_OnMouseOver;
				ele.onmouseout = jlDbtn_OnMouseOut;
				ele.onmouseup = jlDbtn_OnMouseUp;
				ele.onmousedown = jlDbtn_OnMouseDown;
				ele.oncontextmenu = jlDbtn_OnContextMenu;
			}

		// ???TODO: add this to a shortcut list!

	}

}

function jlDbtn_OnClick( evt ) {

	evt = evt || window.event;

	this.go( evt );

}

function jlDbtn_OnMouseOver( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid black";
	this.style.borderTop = "1px solid white";
	this.style.borderLeft = "1px solid white";

}

function jlDbtn_OnMouseOut( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid transparent";

	if ( IE6 ) {
		this.style.borderColor = "pink";
		this.style.filter = "chroma(color=pink);";
	}

}

function jlDbtn_OnMouseUp( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid black";
	this.style.borderTop = "1px solid white";
	this.style.borderLeft = "1px solid white";

}

function jlDbtn_OnMouseDown( evt ) {

	evt = evt || window.event;

	this.style.border="";
	this.style.borderTop="2px solid black";
	this.style.borderLeft="2px solid black";

}

function jlDbtn_OnContextMenu( evt ) {

	evt = evt || window.event;

	return evtKill( evt );

}

function jlDbtn_Go( evt ) {

	var type = getAttr( this, 't');
	var url = null;
	var fldName = getAttr( this, 'fc');
	var ctl = this;
	var value = null;
	var ww = 200;
	var hh = 200;

	if ( fldName ) {
		ctl = g(fldName);
		if ( ctl ) {
			value = ctl.value;
		}
	}

	switch ( type ) {
		case 'date':
			ww = 200;
			hh = 200;
			url = 'dlgCalendar.asp?as_of_date=' + escape(value);
			break;
		case 'time':
			ww = 150;
			hh = 180;
			url = 'dlgTime.asp?as_of_time=' + escape(value);
			break;
		case 'color':
			ww = 160;
			hh = 150;
			url = 'dlgColor.asp?color=' + escape(value);
			break;
		case 'ei':
			bigdropOpen(getAttr(this, 'e'), fldName, false);
			return;
	}

	if ( url ) {
		var now = new Date();
		url += "&now=" + now.toUTCString();
		dlgShow( this, ctl, ww, hh, url, null );
	}

}
function jlInputInit() {

	eles = document.body.getElementsByTagName('INPUT');

	for (tt=0; tt < eles.length; tt++ ) {
		var ele = eles[tt];

		if ( getAttr(ele, 'r') == '1' ) {
			continue;
		}

		ele.isValid = jlInput_isValid;

		if ( ele.addEventListener ) {

			ele.addEventListener( 'focus', jlInput_OnFocus, false);
			ele.addEventListener( 'blur', jlInput_OnBlur, false);
			ele.addEventListener( 'keypress', jlInput_OnKeyPress, false);
			ele.addEventListener( 'paste', jlInput_OnPaste, false);
			ele.addEventListener( 'change', jlInput_OnChange, false);
			if ( ele.type == 'checkbox' ) {
				ele.addEventListener( 'click', jlInput_chk_OnClick, false);
			}
		} else {

			ele.onfocus = jlInput_OnFocus;
			ele.onblur = jlInput_OnBlur;
			ele.onkeypress = jlInput_OnKeyPress;
			ele.onpaste = jlInput_OnPaste;
			ele.onchange = jlInput_OnChange;
			if ( ele.type == 'checkbox' ) {
				addEvt( ele, 'click', jlInput_chk_OnClick );
			}
		}

	}

}


function jlInput_chk_OnClick( evt ) {

	// special code for checkboxes!

	evt = evt || window.event;

	frmChangedSet( true );

	return true;

}

function jlInput_OnKeyPress( evt ) {

	// check if this is a keypress that has no effect!

	// ???TODO: this is ugly.  I put this in for the search tool because someone could just jump to it
	//			and press the Enter key and have it try to do a search.
	//			the better solution is to have this event.keyCode stuff go away and change the
	//			search tool to make sure they entered something into the criteria if the # instances are over
	//			the "magic" number.

	evt = evt || window.event;

	if ( evt ) {
		if ( evt.keyCode == 13 ) {
			return;
		}
	}

	frmChangedSet( true );

	return true;

}

function jlInput_OnPaste( evt ) {

	evt = evt || window.event;

	frmChangedSet( true );

	return true;

}

function jlInput_OnChange( evt ) {

	evt = evt || window.event;

	frmChangedSet( true );

	return true;
}

function jlInput_OnFocus( evt ) {

	//	Selects all text in this field and shows the TITLE of the field in the status bar

	evt = evt || window.event;

	window.status = this.title;
	// ???TODO: add code to select all text!
	// this.selectAll(); or something?

	return true;

}

function jlInput_OnBlur( evt ) {

	evt = evt || window.event;

	return this.isValid();

}

function jlInput_isValid() {

	var scriptValid = getAttr(this, 'onIsValid');
	var result = false;

	if ( scriptValid != null && scriptValid != '' ) {
		result = !eval(scriptValid);
	}

	return result;

}

	var jlJump_elements = [];
	var jlJump_count = 0;

	var divJumpList = null;

function jlJumpInit( jump_id ) {

	var ele = g( jump_id );

	if ( ele != null ) {
		// set up the events
			if ( ele.addEventListener ) {
				ele.addEventListener('click', jlJump_OnClick, false);
				ele.addEventListener('dblclick', jlJump_OnClick, false);
				ele.addEventListener('mouseover', jlJump_OnMouseOver, false);
				ele.addEventListener('mouseout', jlJump_OnMouseOut, false);
				ele.addEventListener('mouseup', jlJump_OnMouseUp, false);
				ele.addEventListener('mousedown', jlJump_OnMouseDown, false);
				ele.addEventListener('contextmenu', jlJump_OnContextMenu, false);
				ele.addEventListener('selectstart', jlJump_OnSelectStart, false);
			} else {
				ele.onclick = jlJump_OnClick;
				ele.ondblclick = jlJump_OnClick;
				ele.onmouseover = jlJump_OnMouseOver;
				ele.onmouseout = jlJump_OnMouseOut;
				ele.onmouseup = jlJump_OnMouseUp;
				ele.onmousedown = jlJump_OnMouseDown;
				ele.oncontextmenu = jlJump_OnContextMenu;
				ele.onselectstart = jlJump_OnSelectStart;
			}

		// ???TODO: add this to a shortcut list!

	}

	// Add this jump to the list of jumps available on this page!
	jlJump_count++;
	jlJump_elements[jlJump_count] = jump_id;
	
}

function jlJump_OnClick( evt ) {

	evt = evt || window.event;

	jlJumpGo( this );

}

function jlJump_OnMouseOver( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid black";
	this.style.borderTop = "1px solid white";
	this.style.borderLeft = "1px solid white";

}

function jlJump_OnMouseOut( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid transparent";

	if ( IE6 ) {
		this.style.borderColor = "pink";
		this.style.filter = "chroma(color=pink);";
	}

}

function jlJump_OnMouseUp( evt ) {

	evt = evt || window.event;

	this.style.border = "1px solid black";
	this.style.borderTop = "1px solid white";
	this.style.borderLeft = "1px solid white";

}

function jlJump_OnMouseDown( evt ) {

	evt = evt || window.event;

	this.style.border="";
	this.style.borderTop="2px solid black";
	this.style.borderLeft="2px solid black";

}

function jlJump_OnContextMenu( evt ) {

	evt = evt || window.event;

	return evtKill( evt );
}

function jlJump_OnSelectStart( evt ) {

	evt = evt || window.event;

	return evtKill( evt );
}

function jumpMnuShow( evt ) {

	// This will create and show a menu of jumps available on this page

	if ( divJumpList == null ) {

		divJumpList = document.createElement('DIV');
		divJumpList.id = 'jlJumpMnu';
		divJumpList.className = 'mnu';
		divJumpList.style.position = 'absolute';
		divJumpList.style.visibility = 'hidden';

		document.body.appendChild(divJumpList);

		var ll = '';

		for ( tt=1; tt <= jlJump_count; tt++ ) {
			var name = getAttr( g( jlJump_elements[tt] ), 'title');

			ll += '<tr class="mnuRow" i=' + tt + '><td><img src=fx/p.gif width=16 height=16></td><td>' + name + '</td></tr>';

		}

		divJumpList.innerHTML = '<table COLS=2 border=0 cellpadding=0 cellspacing=0 valign=top>' + ll + '</table>';

		jlMnuInit( 'jlJumpMnu' );

	}

	mnuShow( divJumpList, evt );

}

function jlJumpMnu_Clicked( mnu_id ) {

	var ele = g( jlJump_elements[mnu_id] );

	jlJumpGo( ele );

}

function jlJumpGo( ele ) {

	var url = getAttr( ele, 'url');

	if ( url != null ) {			// if they put a hard coded URL, use it
		urlGo(url, true);

	} else {			// otherwise, find the related field

		fld_code = getAttr(ele, 'fc');

		if ( fld_code != null ) {		// if we have a field name

			var fld = g( fld_code );	// get the actual field
			var eiid = '';
	
			if ( fld ) {
				eiid = fld.value;
				var url = 'entity_show.asp?entity_id=' + ele.eid + '&entity_instance_id=' + eiid + '&page_after=';

				urlGo(url, true);
			}
		}
	}

}

function jlMnuInit( mnu_id ) {

	var ele = g( mnu_id );

	if ( ele != null ) {

		ele.current_submenu = null;
		ele.handleKeyDown = jlMnu_handleKeyDown;
		ele.setCheckbox = jlMnu_setCheckbox;
		ele.getCheckbox = jlMnu_getCheckbox;
		ele.lastItemOrd = -1;

		var rows = ele.childNodes[0].rows;

		for ( rr=0; rr < rows.length; rr++ ) {
			var item = rows[rr];

			// if it is a mnu separator bar
			if ( item.className == 'mnuSep' ) {
				if ( item.addEventListener ) {
					item.addEventListener('click', jlMnuSep_killEvent, false);
					item.addEventListener('contextmenu', jlMnuSep_killEvent, false);
					item.addEventListener('mouseover', jlMnuSep_killEvent, false);
					item.addEventListener('mouseout', jlMnuSep_killEvent, false);
					item.addEventListener('mouseup', jlMnuSep_killEvent, false);
					item.addEventListener('mousedown', jlMnuSep_killEvent, false);
					item.addEventListener('selectstart', jlMnuSep_killEvent, false);
				} else {	// ie way (allows "this" to work)
					item.onclick = jlMnuSep_killEvent;
					item.oncontextmenu = jlMnuSep_killEvent;
					item.onmouseover = jlMnuSep_killEvent;
					item.onmouseout = jlMnuSep_killEvent;
					item.onmouseup = jlMnuSep_killEvent;
					item.onmousedown = jlMnuSep_killEvent;
					item.onselectstart = jlMnuSep_killEvent;
				}

			} else {
				if ( item.addEventListener ) {
					item.addEventListener('click', jlMnuItem_mouseOnClick, false);
					item.addEventListener('contextmenu', jlMnuItem_mouseOnContextMenu, false);
					item.addEventListener('mouseover', jlMnuItem_mouseOnMouseOver, false);
					item.addEventListener('mouseout', jlMnuItem_mouseOnMouseOut, false);
					item.addEventListener('mouseup', jlMnuItem_mouseOnMouseUp, false);
					item.addEventListener('mousedown', jlMnuItem_mouseOnMouseDown, false);
					item.addEventListener('selectstart', jlMnuItem_mouseOnSelectStart, false);
				} else {
					item.onclick = jlMnuItem_mouseOnClick;
					item.oncontextmenu = jlMnuItem_mouseOnContextMenu;
					item.onmouseover = jlMnuItem_mouseOnMouseOver;
					item.onmouseout = jlMnuItem_mouseOnMouseOut;
					item.onmouseup = jlMnuItem_mouseOnMouseUp;
					item.onmousedown = jlMnuItem_mouseOnMouseDown;
					item.onselectstart = jlMnuItem_mouseOnSelectStart;
				}

				item.submenu = null;

				var sm_id = getAttr(item, 'sm');
	
				if ( sm_id != null && sm_id != '' ) {
					var sm = g( sm_id );
					item.submenu = sm;
					sm.parentmenu = item;
				}

				item.style.margin = '2px';

			}

		}

	}

}

function jlMnu_handleKeyDown( evt ) {

	evt = evt || window.event;

	if ( this.current_submenu != null ) {
		return this.current_submenu.handleKeyDown( evt );
	}

	var rows = this.childNodes[0].rows;
	var keyCode = evt.keyCode;


	// check on the type of key
	if ( keyCode == 27 ) {
		mnuHide();
		return evtKill( evt );
	}

	// handle the key
	switch (keyCode) {
		case 13:	// 13=Enter
			if ( this.lastItemOrd != -1 ) {
				// Hide the menu
				mnuHide();
	
				// Now, call the Clicked function for this menu
				try {
					eval( this.id + "_Clicked('" + getAttr(rows[this.lastItemOrd], 'i') + "');");
				} catch (e) {
				}
			}
			break;

		case 38:	// 38=Up arw
		case 40:	// 40=Down arw
			var nextItemOrd = jlMnuItemNext( rows, this.lastItemOrd, keyCode == 38 ? -1 : 1 );					

			if ( nextItemOrd != -1 && nextItemOrd != this.lastItemOrd ) {
				if ( this.lastItemOrd != -1 ) {
					jlMnuSelSet( rows[ this.lastItemOrd ], false);
				}
				this.lastItemOrd = nextItemOrd;
				jlMnuSelSet( rows[ this.lastItemOrd ], true);
			}
			break;

		case 36:	// 36=Home
			if ( this.lastItemOrd >= 0 ) {
				jlMnuSelSet( rows[ this.lastItemOrd ], false);
			}
			this.lastItemOrd = 0;
			jlMnuSelSet( rows[ this.lastItemOrd ], true);
			break;

		case 35:	// 35=End
			if ( this.lastItemOrd >= 0 ) {
				jlMnuSelSet( rows[ this.lastItemOrd ], false);
			}
			this.lastItemOrd = rows.length-1;
			jlMnuSelSet( rows[ this.lastItemOrd ], true);
			break;

		case 37:	// 37=left arw
			if ( this.parentmenu != null ) {
				// ???TODO: this.parentmenu
			}
			break;

		case 39:	// 39=right arw
			if ( this.lastItemOrd >= 0 ) {
				if ( rows[this.lastItemOrd].submenu != null ) {
					// ???TODO: rows[this.lastItemOrd].submenu
				}
			}
			break;
	}

	// ???TODO: add code to respond to other characters.  If they press a key, find the first menu item that starts with that key (like dropdowns)

	return false;

}

function jlMnu_setCheckbox( mnu_id, is_checked ) {

	var rows = this.childNodes[0].rows;
	var ll = rows.length;

	for ( tt = 0; tt < ll; tt++ ) {
		if ( getAttr(rows[tt], 'i') == mnu_id ) {
			rows[tt].childNodes[0].childNodes[0].src = 'fx/mnuChecked' + ( is_checked ? '1' : '0' ) + '.gif';
			setAttr(rows[tt], 'is_on', ( is_checked ? 1 : 0) );
			return true;
		}
	}
	return false;
}

function jlMnu_getCheckbox( mnu_id ) {

	var rows = this.childNodes[0].rows;
	var ll = rows.length;

	for ( tt = 0; tt < ll; tt++ ) {
		if ( getAttr(rows[tt], 'i') == mnu_id ) {
			var vv = getAttr(rows[tt], 'is_on');
			if ( vv == 1 || vv == '1' || vv == 'true' || vv == true ) {
				return true;
			} else {
				return false;
			}
		}
	}

}

function jlMnuItemNext( rows, current_ord, delta ) {

	var ord = current_ord;

	if ( delta > 0 ) {
		for ( ii = current_ord + delta; ii < rows.length; ii += delta ) {
			if ( rows[ii].className != 'mnuSep' ) {
				return ii;
			}
		}
		return -1;
	} else {
		for ( ii = current_ord + delta; ii >= 0; ii += delta ) {
			if ( rows[ii].className != 'mnuSep' ) {
				return ii;
			}
		}
		return -1;
	}

}

function jlMnuSelSet( row_ele, is_selected ) {

	var newClassName = "";

	if ( is_selected ) {
		newClassName = "s";
		newClassName = "s";
	}

	row_ele.childNodes[0].className = newClassName;
	row_ele.childNodes[1].className = newClassName;

}

function jlMnuItem_mouseOnMouseOver( evt ) {

	evt = evt || window.event;

	var divMain = getParent(this, 'DIV');

	divMain.lastItemOrd = this.rowIndex;

	this.childNodes[0].className = "s";
	this.childNodes[1].className = "s";

	// hide an "old" submenu, if needed
	if ( divMain.current_submenu != null ) {				// if there is a submenu showing
		if ( divMain.current_submenu != this.submenu ) {			// and it isn't ours
			divMain.current_submenu.style.visibility = 'hidden';
			divMain.current_submenu = null;
		} else {
			return false;
		}
	}

	// if we have a submenu
	if ( this.submenu != null ) {

		if ( this.submenu.style.visibility != 'visible' ) {			// and it isn't already visible

			// position it
			this.submenu.style.position = 'absolute';
			var ll = totalOffsetLeft(this) + this.scrollWidth - 5;
			var tt = totalOffsetTop(this);

			// Now, check if the menu's position would force part of it out of the client area
			if ( ( ll + this.submenu.offsetWidth ) > windowWidth() ) {
				ll = window.document.body.clientWidth - this.submenu.clientWidth - 3;
			}

			if ( (tt + this.submenu.offsetHeight) > windowHeight() ) {
				tt = window.document.body.clientHeight - this.submenu.clientHeight - 3;
			} else if ( tt < 0 ) {
				tt = 0;
			}

			// Now, set the rest of the parameters!
			this.submenu.style.zIndex = 1001;
			this.submenu.style.left = ll + 'px';
			this.submenu.style.top = tt + 'px';
			this.submenu.style.visibility = 'visible';

			// and save the fact that we have a submenu open
			divMain.current_submenu = this.submenu;

		}	
	}		

	window.status = this.title;

}

function jlMnuItem_mouseOnMouseOut( evt ) {

	evt = evt || window.event;

	this.childNodes[0].className = "";
	this.childNodes[1].className = "";

	if ( this.submenu != null ) {

		if ( this.submenu.style.visibility == 'visible' ) {			// if out submenu is visible

			var elTo = evtToElement(evt);
			if ( elTo.tagName != 'TR' ) {
				var tdNew = getParent(elTo, "TR");
			}

			// if we are looking at ourselves, exit immediately
			if ( tdNew == this ) {
				return false;
			}

			// Figure out if they are going to our submenu
			var divTo = elTo;
			if ( divTo.tagName != 'DIV' ) {
				divTo = getParent(divTo, 'DIV');
			}
	
			if ( divTo == this.submenu ) {			// if they are moving the the submenu, leave it visible!
				// do nothing
			} else {
				this.submenu.style.visibility = 'hidden';
			}
		}
	}

	window.status = "";
}

function jlMnuItem_mouseOnMouseUp( evt ) {

	evt = evt || window.event;

	if ( this.submenu == null ) {
		this.childNodes[0].style.border = "1px solid transparent";
	
		this.childNodes[1].style.border = "1px solid transparent";
	}
}

function jlMnuItem_mouseOnMouseDown( evt ) {

	evt = evt || window.event;

	if ( this.submenu == null ) {
		this.childNodes[0].style.borderTop="2px solid black";
		this.childNodes[0].style.borderLeft="2px solid black";
		this.childNodes[0].style.borderRight="0px";
		this.childNodes[0].style.borderBottom="0px";
	
		this.childNodes[1].style.borderTop="2px solid black";
		this.childNodes[1].style.borderLeft="2px solid Highlight";
		this.childNodes[1].style.borderRight="0px";
		this.childNodes[1].style.borderBottom="0px";
	}

}

function jlMnuItem_mouseOnContextMenu( evt ) {

	evt = evt || window.event;

	if ( this.submenu == null ) {
		// Hide the menu
		mnuHide();
	
		var divMain = getParent(this, 'DIV');

		// Now, call the Clicked function for this menu
		try {
			eval( divMain.id + "_Clicked('" + getAttr(this, 'i') + "');");
		} catch (e) {
		}

		return evtKill( evt );
	} else {
		return evtKill( evt );
	}

}

function jlMnuItem_mouseOnClick( evt ) {

	evt = evt || window.event;

	if ( this.submenu == null ) {
		if ( this.className != 'mnuSep' ) {

			// Hide the menu
			mnuHide();
	
			var divMain = getParent(this, 'DIV');

			// Now, call the Clicked function for this menu
			try {
				eval( divMain.id + "_Clicked('" + getAttr(this, 'i') + "');");
			} catch (e) {
			}

		} else {
			return evtKill( evt );
		}

	} else {
		return evtKill( evt );
	}

}

function jlMnuItem_mouseOnSelectStart( evt ) {

	evt = evt || window.event;

	return evtKill( evt );

}

function jlMnuSep_killEvent( evt ) {

	evt = evt || window.event;

	return evtKill( evt );

}
function jlOtlInit( otl_id ) {

	var ele = g( otl_id );

	if ( ele != null ) {
	
		ele.lastItem = null;
		ele.itemShow = jlOtl_itemShow;
		ele.itemToggle = jlOtl_itemToggle;
		ele.imgFromItem = jlOtl_imgFromItem;
		ele.firstItem = jlOtl_firstItem;
		ele.lastVisibleItem = jlOtl_lastVisibleItem;
		ele.itemHasChildren = jlOtl_itemHasChildren;
		ele.childFromItem = jlOtl_childFromItem;
		ele.firstChildFromItem = jlOtl_firstChildFromItem;
		ele.lastChildFromItem = jlOtl_lastChildFromItem;
		ele.tdFromItem = jlOtl_tdFromItem;
		ele.parentFromItem = jlOtl_parentFromItem;
		ele.handleOnClick = jlOtl_OnClick;

		if ( ele.addEventListener ) {
			ele.addEventListener('click', jlOtl_OnClick, false);
			ele.addEventListener('dblclick', jlOtl_OnDblClick, false);
			ele.addEventListener('selectstart', jlOtl_OnSelectStart, false);
			ele.addEventListener('keydown', jlOtl_OnKeyDown, false);
			ele.addEventListener('focus', jlOtl_OnFocus, false);
			ele.addEventListener('blur', jlOtl_OnBlur, false);
			ele.addEventListener('contextmenu', jlOtl_OnContextMenu, false);

		} else {
			ele.onclick = jlOtl_OnClick;
			ele.ondblclick = jlOtl_OnDblClick;
			ele.onselectstart = jlOtl_OnSelectStart;
			ele.onkeydown = jlOtl_OnKeyDown;
			ele.onfocus = jlOtl_OnFocus;
			ele.onblur = jlOtl_OnBlur;
			ele.oncontextmenu = jlOtl_OnContextMenu;
		}
	}

}
	

function jlOtl_OnFocus( evt ) {
	// ???TODO: "paint" dotted line around the row with focus!

	evt = evt || window.event;

}
	
function jlOtl_OnBlur( evt ) {
	// ???TODO: remove dotted line around the row with focus!

	evt = evt || window.event;

}
	
function jlOtl_OnContextMenu( evt ) {

	//	Handles right mouse clicks!

	evt = evt || window.event;

	var el = evtSrc( evt );
	var divItem;

	if ( el.tagName == "INPUT" || el.tagName == "TEXTAREA" ) {
		return true;
	}

	divItem = getParent( el, 'DIV' );

	if ( divItem == null ) {
		return false;
	} else if ( divItem.className != 'otlItem' ) {
		return false;
	} else {
		try {	
			eval( this.id + "_ItemContextMenuOpen('" + getAttr(divItem, 'rid') + "');" );
		} catch (e) {
			// do nothing
		}

	}

	// stop default key handling...
	return evtKill( evt );
}
	
function jlOtl_OnClick( evt ) {
	
	evt = evt || window.event;

	var el = evtSrc( evt );
	var divItem;
	
	// Get the item, given the type of control clicked
		switch ( el.tagName ) {
			case 'IMG':
				// get the Item
				divItem = getParent( el, 'DIV' );

				// did we get a otl item?
				if ( divItem == null ) {
					return false;
				} else if ( divItem.className != 'otlItem' ) {
					return false;
				}
	
				// does this item have children?
				if ( this.itemHasChildren( divItem ) ) {
					this.itemToggle( getAttr(divItem, 'rid') );
				}
				break;
	
			case 'TD':
			case 'TR':
				divItem = getParent( el, 'DIV' );
	
				// Check if we have a item
				if ( divItem == null ) {
					return false;
				} else if ( divItem.className != 'otlItem' ) {
					return false;
				}
	
				this.itemShow( getAttr(divItem, 'rid') );
				break;
		}
	
	return false;
	
}
	
function jlOtl_OnDblClick( evt ) {
	
	evt = evt || window.event;

	var el = evtSrc( evt );
	var divItem;
	
	this.handleOnClick( evt );
	
	divItem = getParent( el, 'DIV' );
	
	// Check if we have a item
	if ( divItem == null ) {
		return false;
	} else if ( divItem.className != 'otlItem' ) {
		return false;
	}
	
	// does this item have children?
	if ( this.itemHasChildren( divItem ) ) {
		this.itemToggle( getAttr(divItem, 'rid') );
	} else {
		try {
			eval( this.id + "_ItemDblClicked('" + getAttr(divItem, 'rid') + "');");
		} catch (e) {
			// ???TODO:
		}
	}
	
	return false;

}

function jlOtl_OnKeyDown( evt ) {
	
	evt = evt || window.event;

	var divItem = this.lastItem;
	var keyCode = evt.keyCode;

	// Now, let's see what the keyCode was!
		switch ( keyCode ) {
			case 32:			// 32=space
				this.itemToggle( getAttr(divItem, 'rid') );
				kill_event = true;
				break;

			case 13:		// 13 = same as DblClick!
				this.itemToggle( getAttr(divItem, 'rid') );
				kill_event = true;
				break;

			case 37:		// 37 = left arrow
				if ( this.itemHasChildren( divItem ) ) {
					if ( this.childFromItem( divItem ).style.display != "none" ) {
						this.itemToggle( getAttr(divItem, 'rid') );
					}
				}
				kill_event = true;
				break;

			case 39:		// 39 = right arrow
				if ( this.itemHasChildren( divItem ) ) {
					if ( this.childFromItem( divItem ).style.display == "none" ) {
						this.itemToggle( getAttr(divItem, 'rid') );
					}
				}
				kill_event = true;
				break;

			case 36:		// 36 = Home
				if ( this.firstItem() == divItem ) {
					// do nothing?
				} else if ( this.firstItem() != null ) {
					this.itemShow( getAttr( this.firstItem(), 'rid') );
				}
				kill_event = true;
				break;

			case 35:		// 35 = End
				if ( this.lastVisibleItem() == divItem ) {
					// do nothing?
				} else if ( this.lastVisibleItem() != null ) {
					this.itemShow( getAttr( this.lastVisibleItem(), 'rid') );
				}
				kill_event = true;
				break;

			case 38:		// 38=Up arw
				var found = false;
				var divPrev = getPreviousSibling( divItem );
				if ( divPrev != null ) {
					if ( this.itemHasChildren( divPrev ) ) {
						if ( this.childFromItem( divPrev ).style.display != "none" ) {
							var par = this.lastChildFromItem( divPrev );
							while (par) {
								if ( this.itemHasChildren( par ) ) {
									if ( this.childFromItem( par ).style.display != "none" ) {
										par = this.lastChildFromItem( par );
									} else {
										this.itemShow( getAttr(par, 'rid') );
										par = null;
									}
								} else {
									this.itemShow( getAttr(par, 'rid') );
									par = null;
								}
							}
						} else {
							this.itemShow( getAttr( divPrev, 'rid') );
						}
					} else {
						this.itemShow( getAttr( divPrev, 'rid') );
					}
				} else if ( this.parentFromItem( divItem ) != null ) {			// if this item doesn't have a previous sibling, jump to the parent
					this.itemShow( getAttr( this.parentFromItem( divItem ), 'rid') );
				}
				kill_event = true;
				break;

			case 40:		// 40=Down arw
				var found = false;
				if ( this.itemHasChildren( divItem ) ) {
					if ( this.childFromItem( divItem ).style.display != "none" ) {
						this.itemShow( getAttr( this.firstChildFromItem( divItem ), 'rid') );
						found = true;
					}
				}

				if ( !found ) {
					var divNext = getNextSibling( divItem );
					if ( divNext != null ) {
						this.itemShow( getAttr( divNext, 'rid') );
					} else {
						var par = this.parentFromItem( divItem );
	
						while ( par ) {
							if ( getNextSibling( par ) != null ) {
								this.itemShow( getAttr( getNextSibling( par ), 'rid') );
								par = null;
							} else {
								par = this.parentFromItem( par );
							}
						}
					}
				}

				kill_event = true;
				break;
				
			default:
				kill_event = false;
				break;
		}	
	
	// Kill the event, if required
		if ( kill_event == true ) {
			return evtKill( evt );
		}
	
}

function jlOtl_itemToggle( report_folder_id ) {
		
	// alert('itemToggle(' + report_folder_id + ')');

	var divItem = g( this.id + '_' + report_folder_id );

	if ( divItem ) {

		if ( this.itemHasChildren( divItem ) ) {

			var divChild = this.childFromItem( divItem );
			var img = this.imgFromItem( divItem );
	
			if ( divChild ) {
				if ( divChild.style.display == "none" ) {
					divChild.style.display = "block";
					img.src = "fx/icoMinus.gif";
		
				} else {
					divChild.style.display = "none";
					img.src = "fx/icoPlus.gif";
				}
	
			}

		}

	}

}

function jlOtl_imgFromItem( divItem ) {

	// given an item, returns the image

	return divItem.childNodes[0].rows[0].cells[0].childNodes[1];

}

function jlOtl_firstItem() {

	return getChild(this, 0);

}

function jlOtl_lastVisibleItem() {

	var divItem = getLastChild( this );

	if ( divItem == null) {
		return null;
	} else if ( divItem.tagName != 'DIV' ) {
		return null;
	}

	while ( divItem ) {				// while we have an item to check
		if ( this.itemHasChildren( divItem ) ) {			// if this item has children
			if ( this.childFromItem( divItem ).style.display != "none" ) {		// and the children are visible, move to the child and repeat
				divItem = this.lastChildFromItem( divItem );
			} else {
				return divItem;			// otherwise, return this item
			}
		} else {
			return divItem;		// otherwise, return this item
		}

	}

	return null;

}

function jlOtl_itemHasChildren( divItem ) {

	if ( divItem ) {
		if ( getAttr(divItem, 'has_children') == true || getAttr(divItem, 'has_children') == 'True' ) {
			return true;
		} else {
			return false;
		}
	} else {
		return false;
	}

}

function jlOtl_childFromItem( divItem ) {

	// given an item, return the child DIV

	return getChild( divItem, 1);		// was divItem.childNodes[1];

}

function jlOtl_firstChildFromItem( divItem ) {

	return getChild(getChild(divItem, 1), 0);

}

function jlOtl_lastChildFromItem( divItem ) {

	return getLastChild( getChild( divItem, 1) );

}

function jlOtl_tdFromItem( divItem ) {

	// given an item, returns the TD that contains the item's name

	return divItem.childNodes[0].rows[0].cells[1];

}

function jlOtl_parentFromItem( divItem ) {

	// given an item, returns its parent

	var par = divItem.parentNode;

	while ( par ) {

		if ( par == this ) {
			return null;
		}

		if ( par.tagName == 'DIV' ) {

			if ( par.id.substring(0, this.id.length+1) == this.id + '_' ) {
				return par;
			}

		}

		par = par.parentNode;

	}

	return null;

}

function jlOtl_itemShow( report_folder_id ) {

	// Sets the focus on the given item.
	// NOTE: if this item is hidden because its parent isn't open, this function will open it!

	// alert('itemShow(' + report_folder_id + ')');

	var divItem = g(this.id + '_' + report_folder_id);

	if ( divItem == null ) {
		return false;
	}

	if ( this.lastItem ) {
		this.tdFromItem(this.lastItem).style.background = "";
		this.tdFromItem(this.lastItem).style.color = "";
	}

	this.tdFromItem(divItem).style.background = "Highlight";
	this.tdFromItem(divItem).style.color = "HighlightText";

	window.status = divItem.title;

	this.lastItem = divItem;

	// make sure that this item's tree branches are all open!!!
	var par;

	par = divItem.parentNode;

	while ( par ) {

		if ( par == this ) {
			break;
		}

		if ( par.tagName == 'DIV' ) {
			if ( par.id.substring(0, this.id.length+1) == this.id + '_' ) {
				if ( this.childFromItem( par ).style.display != "block" ) {
					this.itemToggle( par.id.substr(this.id.length+1) );
				}
			}
		}

		par = par.parentNode;

	}

	// ???TODO: run the otlMain_ItemClicked() event!!!
	try {
		eval( this.id + "_ItemClicked('" + getAttr(divItem, 'rid') + "');");
	} catch (e) {
		// ???TODO:
	}

	try {
		this.tdFromItem(divItem).focus();
	} catch (e) {
		// do nothing...  this can happen if the user is going fast
	}

}

function jlOtl_OnSelectStart( evt ) {
	
	// stop the selection of any text within the body of this table

	evt = evt || window.event;
	
	var tmp = evtSrc( evt );
	
	if ( tmp != null ) {
		if ( tmp.tagName == "INPUT" ) {
			return true;
		}
	}
	
	return evtKill( evt );
}
function jlPanelInit( new_div_id, panel_selector_element_prefix, new_panel_info, unselected_panel_selector_element_class, selected_panel_selector_element_class, do_transitions, new_transition_time ) {

	var ele = g( new_div_id );

	if ( ele != null ) {
		var img = ele.childNodes[0];

		if ( img.tagName != 'IMG' ) {
			// generate an error!
		}

		// save some properties
		ele.panel_selector_element_prefix = panel_selector_element_prefix;
		ele.unselected_panel_selector_element_class = unselected_panel_selector_element_class;
		ele.selected_panel_selector_element_class = selected_panel_selector_element_class;
		ele.transition_time = new_transition_time;
		ele.panel_info = new_panel_info;
		ele.panel_count = new_panel_info.length;
		ele.current_panel_id = 1;
		ele.do_auto_panel_transitions = do_transitions;
		ele.auto_transition_time = 5000;

		// link up some member functions
		ele.doAutoTransition = jlPanel_doAutoTransition;
		ele.panelTransition = jlPanel_Transition;
		ele.setPanel = jlPanel_setPanel;
		ele.imgOpacitySet = jlPanel_imgOpacitySet;
		ele.goCurrent = jlPanel_goCurrent;
		ele.go = jlPanel_go;

		// if we are doing auto transitions, start them now
		if ( ele.do_auto_panel_transitions ) {
			setTimeout(' try { ' + new_div_id + '.doAutoTransition() } catch (e) {}', ele.auto_transition_time);
		}
	}

	
}

//	class methods
function jlPanel_doAutoTransition() {

	// This gets the system to automatically move to the next panel

	if ( this.do_auto_panel_transitions == true ) {

		// calculate the new panel's id
		var new_panel_id = this.current_panel_id + 1;
		if ( new_panel_id > this.panel_count ) {
			new_panel_id = 1;
		}

		// tell the system to go to this panel
		this.setPanel( new_panel_id, this.do_auto_panel_transitions);

		// schedule the next auto transition
		setTimeout(' try { ' + this.id + '.doAutoTransition() } catch (e) {}', this.auto_transition_time);
	}

}

function jlPanel_Transition(image_file_name, transition_time_in_ms) { 

	//	Given a background DIV, an image within the DIV, the new image file name and the transition time in milliseconds, 
	//	schedule a set of calls to transition in the new image

	var img = this.childNodes[0];

	var step_time_in_ms = Math.round(transition_time_in_ms / 100); 
	 
	//	set the current image as background of the DIV
	this.style.backgroundImage = 'url(' + img.src + ')'; 
	 
	//	set the IMG to transparent
	setOpacity(img, 0); 
	 
	//	set the new image on the "hidden" image
	img.src = image_file_name; 

	//	schedule a bunch of changes in opacity
	//	???TODO: may want to adjust this...  Worried about killing older browsers!
	for ( tt = 1; tt <= 100; tt++) { 
		setTimeout('try { ' + this.id + '.imgOpacitySet(' + tt + ') } catch (e) {}', tt * step_time_in_ms ); 
	} 

}

function jlPanel_setPanel( new_panel_id, do_auto_trans ) {

	// Tells the system to move to the given panel
	// Also allows the caller to turn on/off auto transitions

	this.do_auto_panel_transitions = do_auto_trans;

	if ( this.current_panel_id != new_panel_id ) {

		var pi = this.panel_info[ new_panel_id-1 ];

		// First, run the transition
		this.panelTransition(pi.file_name, this.transition_time);

		// Now, upgrade the "bullet" for the panel name
		g( this.panel_selector_element_prefix + this.current_panel_id).className = this.unselected_panel_selector_element_class;
		g( this.panel_selector_element_prefix + new_panel_id).className = this.selected_panel_selector_element_class;

		this.current_panel_id = new_panel_id;

	}

}

function jlPanel_goCurrent() {

	this.go( this.current_panel_id );

}

function jlPanel_go( panel_id ) {

	this.do_auto_panel_transitions = false;

	var pi = this.panel_info[ panel_id-1 ];

	if ( pi != null && pi.url != '' ) {
		document.location = pi.url;
	}

}

function jlPanel_imgOpacitySet( opacity ) {

	var img = this.childNodes[0];

	setOpacity( img, opacity );

}

//	Private Functions

function setOpacity( ele, opacity) { 

	// set the opacity of the given element

	var styleImg = ele.style;

	// set the opacity for the image.  We are doing it multiple times to support several browsers
	styleImg.opacity = (opacity / 100); 
	styleImg.MozOpacity = (opacity / 100); 
	styleImg.KhtmlOpacity = (opacity / 100); 
	styleImg.filter = 'alpha(opacity=' + opacity + ')'; 

} 

function jlSelectInit() {

	eles = document.body.getElementsByTagName('SELECT');

	for (tt=0; tt < eles.length; tt++ ) {
		var ele = eles[tt];

		if ( getAttr(ele, 'r') == '1' ) {
			continue;
		}

		ele.in_search_mode = false;
		ele.search_buffer = "";
		ele.firstIndex = null;
		ele.lastIndex = null;

		var ss = getAttr(ele, 'showStatus');
		if ( ss != null ) {
			setAttr( ele, 'showStatus', eval(ss.value) );
		} else {
			ele.showStatus = true;
		}

		ele.find = jlSelect_find;
		ele.CurIndexSet = jlSelect_CurIndexSet;

		if ( ele.addEventListener ) {
			ele.addEventListener('keypress', jlSelect_OnKeyPress, false);
			ele.addEventListener('keydown', jlSelect_OnKeyDown, false);
			ele.addEventListener('focus', jlSelect_OnFocus, false);
			ele.addEventListener('change', jlSelect_OnChange, false);
			ele.addEventListener('click', jlSelect_OnClick, false);
			ele.addEventListener('blur', jlSelect_OnBlur, false);
		} else {
			ele.onkeypress = jlSelect_OnKeyPress;
			ele.onkeydown = jlSelect_OnKeyDown;
			ele.onfocus = jlSelect_OnFocus;
			ele.onchange = jlSelect_OnChange;
			ele.onclick = jlSelect_OnClick;
			ele.onblur = jlSelect_OnBlur;
		}


		// Init
		ele.firstIndex = ele.selectedIndex;
		ele.lastIndex = ele.firstIndex;

	}
	
}

function jlSelect_OnChange( evt ) {

	evt = evt || window.event;

	frmChangedSet( true );

	return true;

}

function jlSelect_OnClick( evt ) {

	// turn off search mode if they start clicking!

	evt = evt || window.event;

	this.in_search_mode = false;

	try {
		if ( !IE6 ) {
			this.focus();
		}
	} catch (e) {
	}

	return true;
}

function jlSelect_OnFocus( evt ) {

	// Init

	evt = evt || window.event;

	this.firstIndex = this.selectedIndex;
	this.lastIndex = this.selectedIndex;
	this.search_buffer = "";		// Start at the first character
	this.in_search_mode = false;		// Start out not in search mode

	return true;
}

function jlSelect_OnKeyDown( evt ) {

	// Handle special keys that only appear during the KeyDown

	evt = evt || window.event;

	if ( evt.ctrlKey == true || evt.altKey == true ) {
		return true;
	}

	switch ( evt.keyCode ) {
		case 8:			// 8=backspace; remove a character from the buffer and start the search over
			if ( this.search_buffer.length > 0 ) {
				this.search_buffer = this.search_buffer.substring(0, this.search_buffer.length - 1);
				this.find( this.search_buffer, true);
			}
			return evtKill( evt );

		case 13:		// 13 = Enter
			try { evt.keyCode = 9; } catch (e) {}	// convert to a tab!
			break;

		case 27:		// 27 = Escape; Clear the buffer and select the first item!
			this.search_buffer = "";
			this.find( this.search_buffer, true);

			return evtKill( evt );

		case 38:		// 38 = Up			// if they hit an arrow, clear the buffer!!!
		case 40:		// 40 = Down
		case 37:		// 37 = left
		case 39:		// 39 = right
		case 35:		// 35 = End
		case 36:		// 36 = home
		case 33:		// 33 = page up
		case 34:		// 34 = page down
					// ???TODO: add others!!!  Also... Check if all browsers use these codes!
			this.search_buffer = "";
			this.in_search_mode = false;
			return true;
	}

	return true;
}

function jlSelect_OnKeyPress( evt ) {

	evt = evt || window.event;

	if ( evt.ctrlKey == true || evt.altKey == true ) {
		return true;
	}

	var keyCode = (evt.keyCode)? evt.keyCode: ((evt.charCode)? evt.charCode: evt.which);

	switch ( keyCode ) {
		case 9:			// 9 = tab key
		case 16:		// 16 = Shift key
		case 17:		// 17 = control key
		case 13:		// 13 = Enter
		case 27:		// 27 = Escape
		case 38:		// 38 = Up			// if they hit an arrow, clear the buffer!!!
		case 40:		// 40 = Down
		case 37:		// 37 = left
		case 39:		// 39 = right
		case 35:		// 35 = End
		case 36:		// 36 = home
		case 33:		// 33 = page up
		case 34:		// 34 = page down
			return true;
		case 8:			// 8 = backspace
			return evtKill( evt );
	}

	this.in_search_mode = true;		// turn on search mode

	// Add the character to the search string
	this.search_buffer = this.search_buffer + String.fromCharCode( keyCode );

	if ( this.search_buffer.length == 1) {
		this.find( this.search_buffer, true);
	} else {
		this.find( this.search_buffer, false);
	}

	evtStopDefault( evt );
	evtStopDefault( evt );

	return false;
}

function jlSelect_OnBlur( evt ) {

	evt = evt || window.event;

	// If we are in search mode and lose focus, make sure the item we last selected is still selected.
	// ???TODO: why?
	if (this.in_search_mode && this.lastIndex != this.selectedIndex) {
		this.selectedIndex = this.lastIndex;
		this.options[this.lastIndex].selected = true;
	}

	// Raise an event, if needed
	if ( this.firstIndex != this.selectedIndex ) {
		frmChangedSet( true );

		var obc = getAttr(this, 'OnBlurChanged');
		if ( obc != null && obc != '' ) {
			eval(obc);
		}
	}

	return true;
}

// ==============
//	Helper functions:

function jlSelect_find( strToFind, start_at_beginning ) {

	// Find the current string within the dropdown list's text

	var le = strToFind.length;			// Length of search string
	var sPos;

	if ( start_at_beginning == true ) {			// if we want to start from the beginning
		sPos = 0;
	} else {				// otherwise, start at the current position (to speed the search!)
		sPos = this.lastIndex;
	}

	if ( this.showStatus == true )			// Show status information
		window.status = "Search: " + strToFind;

	if ( strToFind.length == 0 ) {			// if it is empty, select the first one
		return this.CurIndexSet(0);

	} else {		// otherwise, do the search
		strToFind = strToFind.toLowerCase();		// be case-insensitive

		var option_count = this.options.length;

		if ( option_count >= 0 ) {

			for (var i=sPos; i < option_count; i++) {
				var option_to_check = this.options[i];

				// check for a match
				var sOption = (option_to_check.textContent) ? option_to_check.textContent : option_to_check.innerText;
	
				if ( sOption.length >= le ) {				// if it is a good length (not too small)
					if (strToFind == sOption.substr(0, le).toLowerCase()) {		// check if there are equal
						return this.CurIndexSet(i);

					}
				}

			}
		}

		// ???TODO: write code to beep and then open the dropdown!
		return this.CurIndexSet(0);			// For now, select the first item in the list.
	}
}

function jlSelect_CurIndexSet(index)
{

	// Set the current item

	if (index >= 0 && index < this.options.length) {
		this.lastIndex = index;
		this.selectedIndex = index;
		this.lastIndex = index
		this.options[index].selected = true;
	}
	return true;
}

function jlShortcutAdd( key, ele_id, title ) {

	jlShortcut_count++;

	jlShortcuts[ jlShortcut_count ] = {
		key: key, 
		ele_id: ele_id, 
		title: title, 
		funct: null, 
		eval_funct: null
		};
}

function jlShortcutJavascriptAdd( key, funct, title ) {

	jlShortcut_count++;

	jlShortcuts[ jlShortcut_count ] = {
		key: key, 
		ele_id: null, 
		title: title, 
		funct: funct, 
		eval_funct: null
		};

}

function jlShortcutEvalAdd( key, eval_funct, title ) {

	jlShortcut_count++;

	jlShortcuts[ jlShortcut_count ] = {
		key: key, 
		ele_id: null, 
		title: title, 
		funct: null, 
		eval_funct: eval_funct
		};

}

function jlShortcutMnuShow( evt ) {

	// This will create and show a menu of shortcuts available on this page

	if ( divJlShortcutList === null ) {

		divJlShortcutList = document.createElement('DIV');
		divJlShortcutList.id = 'jlMnuShortcut';
		divJlShortcutList.className = 'mnu';
		divJlShortcutList.style.position = 'absolute';
		divJlShortcutList.style.visibility = 'hidden';

		document.body.appendChild(divJlShortcutList);

		var ll = '';

		for ( tt=1; tt <= jlShortcut_count; tt++ ) {
			var name = jlShortcuts[tt].title;

			if ( name.indexOf(jlShortcuts[tt].key) == -1 ) {
				 name += ' (' + jlShortcuts[tt].key + ')';
			}

			ll += '<tr class="mnuRow" i=' + tt + '><td><img src=fx/p.gif width=16 height=16></td><td>' + name + '</td></tr>';

		}

		divJlShortcutList.innerHTML = '<table COLS=2 border=0 cellpadding=0 cellspacing=0 valign=top>' + ll + '</table>';

		jlMnuInit( 'jlMnuShortcut' );

	}

	mnuShow( divJlShortcutList, evt );

}

function jlMnuShortcut_Clicked( mnu_id, evt ) {

	jlShortcutGo( mnu_id, evt );

}

function jlShortcutGo( shortcut_ord, evt ) {

	var sc = jlShortcuts[ shortcut_ord ];

	if ( sc.funct !== null ) {
		sc.funct( evt );
	} else if ( sc.eval_funct !== null ) {
		eval( 'try { ' + sc.eval_funct + ' } catch (e) {}' );

	} else {

		var docItem = g(sc.ele_id);

		//	NOTE:	need to think about what other "controls" could get the focus!

		if ( docItem.onclick ) {
			return docItem.onclick();

		} else if ( docItem.tagName == 'IMG' ) {				// if an image, check if a toolbar button...  (may want to look at the class name?)
			var par = getParent( docItem, "TABLE");

			if ( par === null ) {
				docItem.focus();
				return true;
			}

			// Now, call the Clicked function for this menu
			return eval( par.id + "_Clicked('" + docItem.id + "', evt);");

		} else if ( docItem.href ) {

			if ( docItem.target ) {
				window.open(docItem.href, "", "");
			} else {
				parent.location = docItem.href;
			}

		} else if ( docItem.tagName == 'INPUT' ) {

			if ( docItem.type == 'submit' ) {

				var par = getParent( docItem, "FORM");
	
				if ( par === null ) {			// if we don't have a form, just focus on the control
					docItem.focus();

				} else {						// otherwise, check if the data is valid and then submit the form!
					doSave( par );
				}
			} else {
				docItem.focus();
			}
				
		} else {
			docItem.focus();
		}

	}

}
function jlTabsInit( tabs_id, skip_resize ) {

	var ele = g( tabs_id );

	if ( ele != null ) {
		// set up the function calls
		ele.calcMetrics = jlTabs_calcMetrics;
		ele.IdtoOrd = jlTabs_IdtoOrd;
		ele.Item = jlTabs_Item;
		ele.ItemById = jlTabs_ItemById;
		ele.Count = jlTabs_Count;
		ele.CurTab = jlTabs_CurTab;
		ele.CurTabSet = jlTabs_CurTabSet;
		ele.CurTabSetById = jlTabs_CurTabSetById;
		ele.Shortcutted = jlTabs_Shortcutted;
		ele.resize = jlTabs_resize;
		ele.move = jlTabs_move;
		ele.getBtnById = jlTabs_getBtnById;
		ele.getFrameById = jlTabs_getFrameById;
		ele.FrameRefreshById = jlTabs_FrameRefreshById;
		ele.getBtns = jlTabs_getBtns;
		ele.getFrames = jlTabs_getFrames;

		ele.wwFrameDIV = 400;
		ele.wwFrameIFRAME = 398;
		ele.hhFrame = 350;

		ele.calcMetrics();

		// initialize the buttons
		var btns = ele.getBtns();

		for (var tt = 0; tt < btns.childNodes.length; tt++) {

			var btn = btns.childNodes[tt];

			if ( btn ) {
				var id = getAttr(btn, 'i');

				if ( id != null ) {
					if ( btn.addEventListener ) {
						btn.addEventListener('mouseover', jlTabBtn_OnMouseOver, false);
						btn.addEventListener('mouseout', jlTabBtn_OnMouseOut, false);
						btn.addEventListener('click', jlTabBtn_OnClick, false);
						btn.addEventListener('selectstart', jlTabBtn_OnSelectStart, false);
					} else {
						btn.onmouseover = jlTabBtn_OnMouseOver;
						btn.onmouseout = jlTabBtn_OnMouseOut;
						btn.onclick = jlTabBtn_OnClick;
						btn.onselectstart = jlTabBtn_OnSelectStart;
					}

					btn.setIsEnabled = jlTabBtn_setIsEnabled;

					var sc = getAttr( btn, 'shortcut' );
// ???TODO: or...  instead of sc, use:
// 'alt+' + (tt+1) - Alt plus a number!
// using other shortcuts seems to be if'y now that IE8 has taken most of them!
					if ( sc != null ) {
						jlShortcutEvalAdd( sc, "g('" + ele.id + "').Shortcutted('" + id + "');", btn.title );
					}

				}

			}
		}

		// check if there is a default tab
		var dt = getAttr(ele, 'default_tab_id');

		if ( dt != null ) {
			ele.CurTabSetById( dt );
		}

		if ( skip_resize == null || skip_resize == false ) {
			ele.resize();
		}

	}

}

function jlTabs_calcMetrics() {

	// This function attempts to calculate various important metrics for this tab

	this.wwFrameDIV = this.offsetWidth;
	this.wwFrameIFRAME = this.wwFrameDIV - 2;		// - 2 = iframes just always have a border to remove!
	this.hhFrame = this.offsetHeight - this.childNodes[0].clientHeight;		// the main DIV's height minus the table for the tab buttons

}

function jlTabs_CurTabSetById( tab_id ) {

	//	???TODO: do we want to return a True if the tab_id wasn't found?

	var ord = this.IdtoOrd( tab_id );

	if ( ord < 0 ) {			// if we didn't find the tab they wanted, set it to the first tab
		ord = 0;
	}

	this.CurTabSet( ord );

}

function jlTabs_getBtns() {

	// Returns the cells of the table that contains the tab buttons

	return this.childNodes[0];

}

function jlTabs_getFrames() {

	// returns the main container for all of the content frames

	return this.childNodes[1];

}

function jlTabs_CurTabSet( tab_ord ) {

	// sets the given tab as current

	var btns = this.getBtns();
	var frames = this.getFrames();
	var btn = btns.childNodes[tab_ord];

	if ( btn != null ) {
		var tab_id = getAttr(btn, 'i');

		// if they are selecting a new tab, save the value!
		if ( this.CurTabId != tab_id ) {

			// ???TODO: need to add the tab control's name here!  (to allow supporting more than one tab!)
			var ctlTabId = g('tab_id');			// attempt to save the tab_id in a hidden form variable
	
			if ( ctlTabId != null ) {
				ctlTabId.value = tab_id
			}
			this.CurTabId = tab_id;
		}
	}

	for (var tt = 0; tt < btns.childNodes.length; tt++) {

		// select the tab button and related tab container
		var btn = btns.childNodes[tt];
		var frame = this.getFrameById( getAttr(btn, 'i'));

		if ( tt == tab_ord ) {		// if this is the correct tab

			btn.style.backgroundColor = color_button_face;
			btn.style.borderBottom = "1px solid " + color_button_face;
			btn.style.zIndex = 1001;

			// now, show the tab container and kick off its contents, if needed
			if ( frame != null ) {

				if ( frame.tagName == 'IFRAME' ) {			// if it is an iframe

					// first, kick off the process to show its contents
					var _src = getAttr(frame, 'futuresrc');
		
					if ( _src != "" ) {
						frame.src = _src;
						setAttr(frame, 'futuresrc', "");
					}
	
					// make sure it is sized correctly
					try {
						frame.style.width = this.wwFrameIFRAME + 'px';
						frame.style.height = this.hhFrame + 'px';
					} catch (e) {
						//
					}

				} else {
					try {
						frame.style.width = this.wwFrameDIV + 'px';
						frame.style.height = this.hhFrame + 'px';
					} catch (e) {
						//
					}
				}

				// now, set its Z-order and make it visible
				frame.style.zIndex = 1000;
				frame.style.visibility = "visible";
			}

			// TODO: check if we need to set focus on a control of this newly displayed frame!
			try {
				eval( this.id + "_CurTabSet('" + getAttr(btn, 'i') + "');" );
			} catch (e) {
				// do nothing
			}

		} else if ( getClass(btn) == 'tabbtn' ) {

			btn.style.backgroundColor = "#F0F0F0";
			btn.style.borderBottom = "1px solid " + color_button_shadow;
			btn.style.zIndex = (1000-tt);

			if ( frame != null ) {
				if ( frame.style.visibility != "hidden" ) {
					frame.style.visibility = "hidden";
					frame.style.zIndex = -1;
				}
			}

		}

	}

}

function jlTabs_ItemById( tab_id ) {

	// given an item's id, returns the item

	return this.Item( this.IdtoOrd( tab_id ) );

}

function jlTabs_IdtoOrd( tab_id ) {

	// given an item's id, returns the item

	//	RET:	-1 = if the ord hasn't been found

	var btns = this.getBtns();

	for (var tt=0; tt < btns.childNodes.length; tt++ ) {
		if ( getAttr(btns.childNodes[tt], 'i') == tab_id ) {
			return tt;
			break;
		}
	}

	return -1;

}

function jlTabs_Item( tab_ord ) {

	// given an item's id, returns the item

	var btns = this.getBtns();

	return btns.childNodes[tab_ord];

}

function jlTabs_Count() {

	return this.getBtns().childNodes.length;

}

function jlTabs_getFrameById( tab_id ) {

	var frames = this.getFrames();

	for (var tt=0; tt < frames.childNodes.length; tt++) {
		if ( getAttr(frames.childNodes[tt], 'i') == tab_id ) {
			return frames.childNodes[tt];
			break;
		}
	}

}

function jlTabs_getBtnById( tab_id ) {

	var btns = this.getBtns();

	for (var tt=0; tt < btns.childNodes.length; tt++) {
		if ( getAttr(btns.childNodes[tt], 'i') == tab_id ) {
			return btns.childNodes[tt];
			break;
		}
	}

}

function jlTabs_resize() {

	// moves the tabs to the given location

	// Recalcs the widths needed and set the last "blank" td with the appropriate size...

	var btns = this.getBtns();
	var frames = this.getFrames();
	var ww = 0;
	var count = 0;

	// First, calculate some important metrics
		this.calcMetrics();

		frames.style.width = this.wwFrameDIV + 'px';
		if ( this.hhFrame > 0 ) {
			frames.style.height = this.hhFrame + 'px';
		}

	// Calc the size of the rest of the tabs...
		count = frames.childNodes.length;
		if ( count < btns.childNodes.length ) {
			count = btns.childNodes.length;
		}

		for (var tt = 0; tt < count; tt++) {

			var btn = btns.childNodes[tt];
			var frame = frames.childNodes[tt];

			if ( frame ) {
				if ( frame.tagName == 'IFRAME' ) {
					frame.style.width = this.wwFrameIFRAME + 'px';
					frame.style.height = this.hhFrame + 'px';
				} else if ( frame.tagName == 'DIV' ) {
					frame.style.width = this.wwFrameDIV + 'px';
					frame.style.height = this.hhFrame + 'px';
				}

			}

		}

}

function jlTabs_move( left, top, right, bottom, position_method ) {

	// moves the tabs to the given location

	var btns = this.getBtns();

	// Set the parent size and location
		if ( position_method ) {
			this.style.position = position_method;
		}
		this.style.left = left + 'px';
		this.style.top = top + 'px';
		this.style.width = (right - left) + 'px';
		this.style.height = (bottom - top) + 'px';

	this.resize();

}

function jlTabs_CurTab() {

	return this.CurTabId;

}

function jlTabs_FrameRefreshById( frame_id ) {

	var iframe = this.getFrameById( frame_id );

	if ( iframe ) {

		if ( iframe.tagName == 'IFRAME' ) {

			if ( getAttr(iframe, 'futuresrc') == null || getAttr(iframe, 'futuresrc') == '' ) {
				setAttr(iframe, 'futuresrc', iframe.src);
				iframe.src = "javascript:'';";
			}

		}

	}

}

function jlTabs_Shortcutted( btn_id ) {

	var btn = this.getBtnById( btn_id );

	if ( btn != null ) {

		if ( getAttr(btn, 'is_disabled') != "1" ) {

			var ok_to_set = true;

			// Try to run the "ok to click" function of the main form
			try {
				ok_to_set = eval( this.id + "_TabOkToSet('" + btn_id + "');");
			} catch (e) {
				// note:  if the ???_TabOkToSet() function fails, the event will _not_ get cancelled.
				ok_to_set = true;
			}

			if ( ok_to_set == null || ok_to_set == true ) {

				// Now, set the current tab to this one
				try {
					this.CurTabSetById( btn_id );
				} catch (e) {
				}

				// save the preferred tab?
				tabPreferredSave( btn_id );

				// now, call something to allow the page to do something, if necessary
				try {
					eval( this.id + "_TabShortcutted('" + btn_id + "');");
				} catch (e) {
					// do nothing
				}

			}

		}

	}

}

function jlTabBtn_OnMouseOver( evt ) {

	evt = evt || window.event;

	var tab_id = getAttr(this, 'i');
	var parent = getParentByClass( this, "tabs");

	window.status = this.title;

	if ( parent.CurTabId != tab_id ) {
		this.style.backgroundColor = '#FFFFC0';
	}

}

function jlTabBtn_OnMouseOut( evt ) {

	evt = evt || window.event;

	var tab_id = getAttr(this, 'i');
	var parent = getParentByClass( this, "tabs");

	window.status = "";
	if ( parent.CurTabId != tab_id ) {
		this.style.backgroundColor = "#F0F0F0";
	}

}

function jlTabBtn_setIsEnabled( is_enabled ) {

	if ( is_enabled ) {
		this.is_disabled = "0";
		// ???TODO:
	} else {
		this.is_disabled = "1";
		// ???TODO:
	}

}

function jlTabBtn_OnClick( evt ) {

	evt = evt || window.event;

	if ( getAttr(this, 'is_disabled') != "1" ) {

		var id = getAttr( this, 'i');

		// First, get the parent tabbtn
		var parent = getParentByClass( this, "tabs");

		var ok_to_set = true;

		if ( parent == null ) {
			return;
		}

		// Try to run the "ok to click" function of the main form
		try {
			ok_to_set = eval( parent.id + "_TabOkToSet('" + id + "');");
		} catch (e) {
			// note:  if the ???_TabOkToSet() function fails, the event will _not_ get cancelled.
			ok_to_set = true;
		}

		if ( ok_to_set == null || ok_to_set == true ) {

			// Now, set the current tab to this one
			try {
				parent.CurTabSetById(id);
			} catch (e) {
			}
	
			// save the preferred tab?
			tabPreferredSave( id );

			// now, call something to allow the page to do something, if necessary
			try {
				eval( parent.id + "_TabClicked('" + id + "');");
			} catch (e) {
				// do nothing
			}

		}

	}

	return evtKill( evt );

}

function jlTabBtn_OnSelectStart( evt ) {

	// stop the selection of any text within the body of this table

	evt = evt || window.event;

	return evtKill( evt );
}

function tabAutoFocus() {

	if ( !page_stop_autofocus ) {

		var tbl = g('tblMain');
	
		if ( tbl ) {
			try {
				tbl.setFocus();
			} catch (e) {
				// do nothing
			}
		}

	}

}

function tabOnPageFocus() {

	var tbl = g('tblMain');

	if ( tbl ) {
		try {
			tbl.setFocus();
		} catch (e) {
			// do nothing
		}
	}

}
function jlTblInit( tbl_id ) {

	// Initializes the table class.
	// The table class is actually a DIV that has four separate tables:
	//	1. A table contains a NAV toolbar
	//	2. a table contains a title
	//	3. a table contains the header 
	//	4. a table with the actual data.

	// This method allows us to make the toolbar, title and header row fixed while providing a scrolling area for the data table.

	var ele = g(tbl_id);
	var id = ele.id;

	ele.max_cols_to_sort = 3;
	ele.had_first_resize = false;
	ele.default_is_desc = 1;			// 0 = defaults to ascending; 1 = defaults to desc
	ele.autoSize = getAttr(ele, 'autoSize');

	ele.inSearchMode = false;
	ele.searchBuffer = "";	
	ele.showStatus = true;
	ele.isKeyboardDisabled = getAttr(ele, 'isKeyboardDisabled');
	ele.lastRowOrd = -1;
	ele.nocolgroup = boolClean( getAttr(ele, 'nocolgroup') );

	ele.getCell = jlTbl_getCell;
	ele.getColCode = jlTbl_getColCode;
	ele.getColCount = jlTbl_getColCount;
	ele.setKeyboardDisable = jlTbl_setKeyboardDisable;
	ele.setFocus = jlTbl_setFocus;
	ele.getWidthWanted = jlTbl_getWidthWanted;
	ele.getColHeaders = jlTbl_getColHeaders;

	ele.find = jlTbl_find;

	ele.move = jlTbl_move;
	ele.print = jlTbl_print;
	ele.resize = jlTbl_resize;
	ele.getRow = jlTbl_getRow;
	ele.getRowId = jlTbl_getRowId;
	ele.getRows = jlTbl_getRows;
	ele.setFocusRow = jlTbl_setFocusRow;
	ele.clearRowsSelected = jlTbl_clearRowsSelected;
	ele.getRowCount = jlTbl_getRowCount;
	ele.getFocusRowOrd = jlTbl_getFocusRowOrd;

	ele.rowIsSelected = jlTbl_rowIsSelected;
	ele.getSelectedRowCount = jlTbl_getSelectedRowCount;
	ele.getFirstSelectedRowOrd = jlTbl_getFirstSelectedRowOrd;
	ele.sortTable = jlTbl_sortTable;
	ele.sortdata = jlTbl_sortdata;
	ele.setRowWidthByScale = jlTbl_setRowWidthByScale;

	ele.addSortColumn = jlTbl_addSortColumn;
	ele.filterByColumn = jlTbl_filterByColumn;
	ele.Sort = jlTbl_Sort;
	ele.toString = jlTbl_toString;
	ele.loadSortColumns = jlTbl_loadSortColumns;
	ele.saveSortColumns = jlTbl_saveSortColumns;

	ele.clearSearchBuffer = jlTbl_clearSearchBuffer;
	ele.showSearchBuffer = jlTbl_showSearchBuffer;
	ele.doRaiseClicked = jlTbl_doRaiseClicked;

	ele.autoSize = getAttr(ele, 'autoSize');
	ele.variable_row = boolClean( getAttr(ele, 'variable_row') );
	ele.extra_row_margin = intClean( getAttr(ele, 'extra_row_margin') );
	ele.max_row_height = intClean( getAttr(ele, 'max_row_height') );

	// Init
		colgroup = jlTbl_getColHeaderCOLGROUP( ele );

		for (var i=0; i < colgroup.childNodes.length; i++){
			var col = colgroup.childNodes[i];
			var ww = col.width;
			col.widthWanted = intClean(ww);			// ???TODO: careful...  If they use %'s, we may "blow up!"
									// was: 100 * here!?!
		}

	// Set up the TH events and variables
		var cells = jlTbl_getColHeaderRowCells( ele );

		for (var i=0; i < cells.length; i++){
			var th = cells[i];

			th.in_resize = false;
			th.was_in_resize = false;
			th.pos = null;
			th.size = null;
			th.st = getAttr(th, 'st');
			th.fn = getAttr(th, 'fn');

			if ( th.addEventListener ) {
				th.addEventListener('click', jlTblTH_OnClick, false);
				th.addEventListener('dblclick', jlTblTH_OnClick, false);
				th.addEventListener('mousedown', jlTblTH_OnMouseDown, false);
				th.addEventListener('mouseup', jlTblTH_OnMouseUp, false);
				th.addEventListener('mousemove', jlTblTH_OnMouseMove, false);
				th.addEventListener('contextmenu', jlTblTH_OnContextMenu, false);
			} else {
				th.onclick = jlTblTH_OnClick;
				th.ondblclick = jlTblTH_OnClick;
				th.onmousedown = jlTblTH_OnMouseDown;
				th.onmouseup = jlTblTH_OnMouseUp;
				th.onmousemove = jlTblTH_OnMouseMove;
				th.oncontextmenu = jlTblTH_OnContextMenu;
			}

		}

	// Force the size to be the specified one
		if ( ele.width ) {
			ele.style.width = ele.width + 'px';
		}

		if ( ele.height ) {
			ele.style.height = ele.height + 'px';
		}

	// Init various events
		// get the body span scrolling action captured
		var divData = jlTbl_getDIVData( ele );

		if ( divData.addEventListener ) {
			divData.addEventListener('scroll', jlTbldivData_OnScroll, false);
		} else {
			divData.onscroll = jlTbldivData_OnScroll;
		}

		// capture all of the events for the TBODY
		var tBody = jlTbl_getDataTBODY( ele );

		if ( tBody.addEventListener ) {
			tBody.addEventListener('click', jlTBODY_OnClick, false);
			tBody.addEventListener('dblclick', jlTBODY_OnDblClick, false);
			tBody.addEventListener('contextmenu', jlTBODY_OnContextMenu, false);
			tBody.addEventListener('keydown', jlTBODY_OnKeyDown, false);
			tBody.addEventListener('keypress', jlTBODY_OnKeyPress, false);
			tBody.addEventListener('focus', jlTBODY_OnFocus, false);
			tBody.addEventListener('blur', jlTBODY_OnBlur, false);
			tBody.addEventListener('selectstart', jlTBODY_OnSelectStart, false);
			tBody.addEventListener('mouseover', jlTBODY_OnMouseOver, false);
			tBody.addEventListener('mouseout', jlTBODY_OnMouseOut, false);
		} else {
			tBody.onclick = jlTBODY_OnClick;
			tBody.ondblclick = jlTBODY_OnDblClick;
			tBody.oncontextmenu = jlTBODY_OnContextMenu;
			tBody.onkeydown = jlTBODY_OnKeyDown;
			tBody.onkeypress = jlTBODY_OnKeyPress;
			tBody.onfocus = jlTBODY_OnFocus;
			tBody.onblur = jlTBODY_OnBlur;
			tBody.onselectstart = jlTBODY_OnSelectStart;
			tBody.onmouseover = jlTBODY_OnMouseOver;
			tBody.onmouseout = jlTBODY_OnMouseOut;
		}

		tBody.setRowIsSelectedByEle = jlTBODY_setRowIsSelectedByEle;
		tBody.getRowIsSelectedByEle = jlTBODY_getRowIsSelectedByEle;
		tBody.getRowIsOddByEle = jlTBODY_getRowIsOddByEle;
		tBody.setRowRangeIsSelected = jlTBODY_setRowRangeIsSelected;
		tBody.clearRowRangeIsSelected = jlTBODY_clearRowRangeIsSelected;

		// if this client doesn't have scroll support, let's get the up/down buttons running!
		if ( scroll_support == false ) {
			var btnUP = g( ele.id + 'BTNUP' );

			if ( btnUP ) {
				if ( btnUP.addEventListener ) {
					btnUP.addEventListener('click', jlTblBtnUP_OnClick, false);
					btnUP.addEventListener('dblclick', jlTblBtnUP_OnClick, false);
				} else {
					btnUP.onclick = jlTblBtnUP_OnClick;
					btnUP.ondblclick = jlTblBtnUP_OnClick;
				}

			}

			var btnDN = g( ele.id + 'BTNDN' );

			if ( btnDN ) {
				if ( btnDN.addEventListener ) {
					btnDN.addEventListener('click', jlTblBtnDN_OnClick, false);
					btnDN.addEventListener('dblclick', jlTblBtnDN_OnClick, false);
				} else {
					btnDN.onclick = jlTblBtnDN_OnClick;
					btnDN.ondblclick = jlTblBtnDN_OnClick;
				}

			}

		}

}

function jlTbl_Sort( ColsToSort, bDoSort ) {

	// Sets the sort order for this table

	// IN:	ColsToSort = an object with a few arrays...

	//	bDoSort = true - do the sort
	//		false - just mark the appropriate columns and DO NOT sort.
	//			(this option is useful for "init'ing" the table when the sort was done by SQL.)

	// ???TODO: 

	alert('???TODO: write this function!');

}


function jlTbl_compare(n1, n2) {

	// A simple function that is used for the Sort method of an array to compare two items

	value1 = n1.sd;
	value2 = n2.sd;

	if ( value1 > value2 ) {
		return +1;
	} else if ( value1 < value2 ) {
		return -1;
	} else {
		return 0;
	}

}

function jlTbl_loadSortColumns() {

	// Loads the current sort order into an object

	var tHeadParent;
	var ColsToSort = new Object;

	// Get the references to the header and the column to sort
		tHeadParent = jlTbl_getTHEAD( this );

		if (tHeadParent == null)		// if we don't have one, exit
			return;

	// init the array
		ColsToSort.ord = new Array(this.max_cols_to_sort);
		ColsToSort.IsDesc = new Array(this.max_cols_to_sort);
		ColsToSort.DataType = new Array(this.max_cols_to_sort);
		ColsToSort.field_name = new Array(this.max_cols_to_sort);

		for (var tt=0; tt < this.max_cols_to_sort; tt++) {
			ColsToSort.ord[tt] = -1;
			ColsToSort.IsDesc[tt] = this.default_is_desc;
			ColsToSort.DataType[tt] = 4;
			ColsToSort.field_name[tt] = "";
		}

	// Now, read each column looking for ones that are getting sorted
		var cells = jlTbl_getColHeaderRowCells( this );
		for (var tt=0; tt < cells.length; tt++) {
			var i = cells[tt].sort_order;

			// if we have a sort order, save it to the array
			if ( i != null ) {
				if ( i > -1 && i < this.max_cols_to_sort ) {
					ColsToSort.ord[i] = tt;
					ColsToSort.IsDesc[i] = parseInt(cells[tt].is_desc);
					ColsToSort.DataType[i] = parseInt(cells[tt].st);
					ColsToSort.field_name[i] = cells[tt].fn;
				}
			}

		}

	return ColsToSort;

}

function jlTbl_saveSortColumns( ColsToSort ) {

	// saves the sort order information for all of the columns.
	// For the primary (first) sort column, this code will add the appropriate arrow

	var tHeadParent;
	var thItem;
	var so;

	var arrow;

	// Get the references to the header and the column to sort
		tHeadParent = jlTbl_getTHEAD( this );
		var cells = jlTbl_getColHeaderRowCells( this );

		if (tHeadParent == null)		// if we don't have one, exit
			return;

		if (tHeadParent.arrow == null) {
			tHeadParent.arrow = new Array(this.max_cols_to_sort);
		}

	// Now, iterate through the items
		for (var so=0; so < this.max_cols_to_sort; so++ ) {

			var ord = ColsToSort.ord[so];

			if ( ord > -1 ) {
				thItem = cells[ord];

				if (thItem != null) {

					// Check if there is already an arrow
					if (tHeadParent.arrow[so] != null) {
						tHeadParent.arrow[so].parentNode.removeChild(tHeadParent.arrow[so]);
					}

					// Set the arrow's direction
					arrow = document.createElement("IMG");
					setClass(arrow, "arrow");
					arrow.style.display = 'inline';

					// Set the arrow's direction
					if (ColsToSort.IsDesc[so] == 0) {
						arrow.src = "fx/icoSort" + (so+1) + "a.gif"
					} else {
						arrow.src = "fx/icoSort" + (so+1) + "d.gif"
					}

					// Save the arrow
					tHeadParent.arrow[so] = arrow;
					thItem.appendChild(arrow);

				}
			}

		}

	// Now, set the hidden attribute values
		// first, clear any values
		for (var tt=0; tt < cells.length; tt++) {
			cells[tt].sort_order = -1;
			cells[tt].is_desc = this.default_is_desc;
		}

	// Now, go through the list and set the values
		// I'm going backwards to make sure that a dup column selected for sorting doesn't wipe out its new sort_order
		for (var tt=this.max_cols_to_sort-1; tt >= 0 ; tt--) {
			if ( ColsToSort.ord[tt] > -1 ) {
				cells[ColsToSort.ord[tt]].sort_order = tt;
				cells[ColsToSort.ord[tt]].is_desc = ColsToSort.IsDesc[tt];
			}
		}

}

function jlTbl_filterByColumn( nCol ) {

	// ???TODO: this is a very slow method of filtering...
	//		I can improve this code dramatically by using the linked lists that tables provide!

	var first_row_selected = this.getFirstSelectedRowOrd();
	var imgFilter;

	if ( first_row_selected >= 0 ) {

		var rr = this.getRow(first_row_selected);
		var cell = rr.cells[nCol];
		var filter_value = (cell.textContent) ? cell.textContent : cell.innerText;

		var tHeadParent = jlTbl_getTHEAD( this );
		var tblData = jlTbl_getDataTBODY( this );
		var cells = jlTbl_getColHeaderRowCells( this );
		var thItem = cells[nCol];

		// set up the filter image
		imgFilter = document.createElement("IMG");
		imgFilter.style.display = 'inline';
		setClass(imgFilter, "filter");
		imgFilter.src = "fx/icoFilter1.gif"
		thItem.appendChild(imgFilter);

		// Now, delete the rows that don't match!
		var rows = tblData.rows;
		var count = 0;

		if ( rows.length > 0 ) {			// check if we have any rows!

			for ( var tt = rows.length-1; tt >= 0 ; tt-- ) {
				var cell = rows[tt].cells[nCol];
				var value = (cell.textContent) ? cell.textContent : cell.innerText;
				if ( value != filter_value ) {
					tblData.deleteRow(tt);
				}
			}

			if ( rows.length > 0 ) {
				// this sillyness forces the table to refresh
				rows[0].cells[0].innerHTML = rows[0].cells[0].innerHTML;
			}

		}

	}

}

function jlTbl_addSortColumn(nCol) {

	// Adds the given column to the sort order.
	// this function will determine which columns are used for the sort and will set up
	// the direction and which column should have the arrow.

	var tHeadParent;
	var thItem;

	var newSortFieldList;

	// Get a reference to the THEAD of the table and to the column the user wants to sort
		tHeadParent = jlTbl_getTHEAD( this );

		if (tHeadParent == null)		// if we don't have one, exit
			return;

		thItem = jlTbl_getColHeaderRowCells(this)[nCol];

		if (thItem == null)
			return;

	// save the column id
		this.first_sort_column_ord = nCol;

	// Figure out what is the current sort order
		var ColsToSort = this.loadSortColumns();

	// Now, figure out if this is just a direction change
		if ( ColsToSort.ord[0] == -1 ) {			// if we don't have a first column sorted
			ColsToSort.ord[0] = nCol;
			ColsToSort.IsDesc[0] = this.default_is_desc;
			ColsToSort.DataType[0] = thItem.st;
			ColsToSort.field_name[0] = thItem.fn;

		} else if ( ColsToSort.ord[0] == nCol ) {		// if we do, and it is the one the user selected
			if ( ColsToSort.IsDesc[0] != 0) {
				ColsToSort.IsDesc[0] = 0;
			} else {
				ColsToSort.IsDesc[0] = 1;
			}
		} else {			// if this is the first time for this column
			// delete any dup's
			for (var tt=0; tt < this.max_cols_to_sort; tt++) {
				if ( ColsToSort.ord[tt] == nCol ) {
					for ( var xx = tt; xx < this.max_cols_to_sort-1; xx++) {
						ColsToSort.ord[xx] = ColsToSort.ord[xx+1];
						ColsToSort.IsDesc[xx] = ColsToSort.IsDesc[xx+1];
						ColsToSort.DataType[xx] = ColsToSort.DataType[xx+1];
						ColsToSort.field_name[xx] = ColsToSort.field_name[xx+1];
					}						
					ColsToSort.ord[this.max_cols_to_sort-1] = -1;
				}
			}

			// make room at the beginning
			for (var tt=this.max_cols_to_sort-1; tt > 0; tt--) {
				ColsToSort.ord[tt] = ColsToSort.ord[tt-1];
				ColsToSort.IsDesc[tt] = ColsToSort.IsDesc[tt-1];
				ColsToSort.DataType[tt] = ColsToSort.DataType[tt-1];
				ColsToSort.field_name[tt] = ColsToSort.field_name[tt-1];
			}

			// make the first position the column selected
			ColsToSort.ord[0] = nCol;
			ColsToSort.IsDesc[0] = this.default_is_desc;
			ColsToSort.DataType[0] = thItem.st;
			ColsToSort.field_name[0] = thItem.fn;

		}

	// Set the new order
		this.saveSortColumns(ColsToSort);
		ColsToSort = this.loadSortColumns();			// get the cleaned up list  ???TODO: this is a little slower.. but helps to eliminate when a single column selected more than once

	// tell the outside world about the new order, if necessary
		newSortFieldList = "";

		for (var tt=0; tt < this.max_cols_to_sort; tt++) {
			if ( ColsToSort.field_name[tt].length != 0 ) {
				if ( newSortFieldList.length != 0 ) {
					newSortFieldList += ', ';
				}
				newSortFieldList += ColsToSort.field_name[tt];
				if ( ColsToSort.IsDesc[tt] != 0 ) {
					newSortFieldList += ' DESC'
				}
			}
		}

		// Now, see if there is a function that we can call with the new sort
		try {
			eval( this.id + "_ColumnSortChanged('" + newSortFieldList + "');" );
		} catch (errorObject) {
			// do nothing
		}

	// Sort the table using the new methods
		this.sortTable(ColsToSort);
}

function jlTbl_sortTable(ColsToSort, compareFunction) {

	// 	Sorts the table

	//	IN:	ColsToSort - an array of columns that should be used to gather sort data.
	//			if not null, the "sd" (Sort Data) property will have the gathered information
	//			If null, the compareFunction needs to use other means to sort the data

	//		compareFunction - the function that should be used for doing comparisons

	var row_total_footer;

	var tBody = jlTbl_getDataTBODY( this )
	var tblRows = tBody.rows;

	var divData = jlTbl_getDIVData( this );
	var oldScrollLeft = divData.scrollLeft;
	var num_rows = tblRows.length;

	if ( num_rows == 0 )			// check for an empty table
		return;
	//endif

	var rows = new Array();
	var lastRowOrd = this.lastRowOrd;

	// Move the data out of the HTML table
		var last_row = num_rows-1;

		var row_to_copy = tblRows[0];

		for (var i = 0; i < num_rows; i++) {
			if ( i == last_row && getAttr(row_to_copy, 'is_total_footer') == 1 ) {			// if this is the last row, and it is a footer
				row_total_footer = row_to_copy.cloneNode(true);
			} else {
				rows[i] = row_to_copy.cloneNode(true);

				if ( i == lastRowOrd ) {
					rows[i].hasFocus = true;
				}

				if ( ColsToSort ) {			// if we were given an array, use it to gather information
					rows[i].sd = this.sortdata( ColsToSort, row_to_copy.cells );
				}

			}
			row_to_copy = row_to_copy.nextSibling;

			while ( row_to_copy && row_to_copy.nodeType == 3 ) {
				row_to_copy = row_to_copy.nextSibling;
			}

		}

	// delete the TBODY
	removeAllChildNodes( tBody );
	tBody = jlTbl_getDataTBODY( this );

	// Sort it
		if ( compareFunction == null ) {
			rows.sort(jlTbl_compare);
		} else {
			rows.sort(compareFunction);
		}

	// Write it back
		var is_odd = true;
		var clsName = '';
		lastRowOrd = -1;

		for (var i=0; i < rows.length; i++) {
			var rowToAdd = rows[i];

			if ( rowToAdd.sd ) {
				try {
					delete rowToAdd.sd;
				} catch (e) {
					rowToAdd.sd = null;
				}
			}

			if ( rowToAdd.hasFocus ) {
				lastRowOrd = i;
				try {
					delete rowToAdd.hasFocus;
				} catch (e) {
					rowToAdd.hasFocus = null;
				}
			}

			var is_selected = (getClass(rowToAdd).charAt(1) == 's');		// if selected

			if ( is_odd ) {
				clsName = 'o';
				is_odd = false;
			} else {
				clsName = 'e';
				is_odd = true;
			}

			setClass(rowToAdd, clsName + ( is_selected ? 's' : '' ));

			tBody.appendChild(rowToAdd);
		}

		if ( row_total_footer != null ) {
			tBody.appendChild(row_total_footer);
		}

	// get the scroll in sync, if needed
		var divHeader = jlTbl_getDIVHeader( this );
		var divHeaderSlave = jlTbl_getDIVHeaderSlave( this );
		divData.scrollLeft = oldScrollLeft;
		divHeaderSlave.style.left = (-oldScrollLeft) + 'px';

	// clear the search buffer
		this.clearSearchBuffer();

	// perform a resize--in case the header changed size!
		this.resize();

	// reset the focus
		if ( lastRowOrd >= 0 ) {
			this.setFocusRow( lastRowOrd );
		}

}

function jlTbl_getRowCount() {

	var tblData = jlTbl_getDataTBODY( this );

	var rows = tblData.rows;

	return rows.length;

}

function jlTbl_getColCount() {

	var cells = jlTbl_getColHeaderRowCells( this );
	return cells.length;

}

function jlTbl_sortdata( ColsToSort, cells ) {

	// Given the columns to sort, the direction and data types, return a single string that can represent this row in a sort

	var out = "";
	var is_negative = false;
	var regexpHTML = /<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>/;	// remove comma's, dollar signs and any HTML
	var regexpNumber = /,|$/g

	for (var tt=0; tt < this.max_cols_to_sort; tt++ ) {
		if ( ColsToSort.ord[tt] != -1 ) {

			var cell = cells[ColsToSort.ord[tt]];
			var value1 = (cell.textContent) ? cell.textContent : cell.innerText;

			if ( value1 != undefined ) {
				value1 = value1.replace(regexpHTML, "");
			}

			switch ( Number(ColsToSort.DataType[tt]) ) {
				case simple_type_int:		// if a number
				case simple_type_float:
					value1 = value1.replace(regexpNumber, "");
					value1 = Number(value1);

					if ( value1 < 0 ) {
						is_negative = true; 
					} else {
						is_negative = false;
					}
					value1 = Math.round(10000 * value1);		// first, do a little "rounding"
					value1 = strLPad(Math.abs(value1), 15)
					// I removed this...  This allows sorting by magnitude instead of just by amount
					// if ( is_negative == true ) {					// if negative, have it sort first
					// 	value1 = "1" + value1; 
					// } else {
					//	value1 = "2" + value1;	
					//}
					break;

				case simple_type_date:		// if a date
					var dd = new Date(value1);
					value1 = strLPad(dd.getFullYear(), 4) + strLPad( dd.getMonth(), 2) + strLPad( dd.getDate(), 2) + strLPad( dd.getHours(), 2) + strLPad( dd.getMinutes(), 2) + strLPad( dd.getSeconds(), 2) + strLPad( dd.getMilliseconds(), 3);
					break;

				default:		// if a string
					value1 = value1.toLowerCase();
					break;
			}

			if ( ColsToSort.IsDesc[tt] != 0 ) {
				if (value1 == null || value1 == "" ) {
					value1 = strXOR(" ", 127);
				} else {
					value1 = strXOR(value1, 127);		// we are only doing reverses for the first 7 bits!!!
				}
			}

			out = out + value1 + ' ';	// ???TODO: add a better field separator?

		}
	}

	return out;
}

function jlTbldivData_OnScroll() {

	var divMain = this.parentNode;
	var divHeaderSlave = jlTbl_getDIVHeaderSlave( divMain );

	divHeaderSlave.style.left = (-this.scrollLeft) + 'px';

}

function jlTbl_setRowWidthByScale( yScale, wanted ) {

	// sets the width of each column using a scaling factor

	var cgHead = jlTbl_getColHeaderCOLGROUP( this );
	var cgBody = jlTbl_getDataCOLGROUP( this );
	var total = 0;

	for (var i=0; i < cgHead.childNodes.length; i++) {
		var cH = cgHead.childNodes[i];
		var cB = cgBody.childNodes[i];

		var newWidth = Math.round(intMax(1, yScale * cH.widthWanted), 0);

		cH.style.width = newWidth + 'px';
		cB.style.width = newWidth + 'px';

		total += newWidth;
	}

	// this makes sure we match up perfectly
	if ( cgHead.childNodes.length > 0 ) {
		if ( total != wanted ) {
			newWidth = newWidth + (wanted - total);		// adjust it!

			cH.style.width = newWidth + 'px';
			cB.style.width = newWidth + 'px';
		}
	}
}

function jlTbl_resize() {

	// ???TODO: revist this stupid function...  I can't tell if it is working correctly!
	//	it seems to be sizing all tables too large and forcing horz. scrollbars!

	var divHeader = jlTbl_getDIVHeader( this );
	var divHeaderSlave = jlTbl_getDIVHeaderSlave( this );
	var tblHeader = jlTbl_getColHeaderTABLE( this );
	var divData = jlTbl_getDIVData( this );
	var tblBody = jlTbl_getDataTBODY( this );
	var tblData = jlTbl_getDataTABLE( this );

	var wanted = 0;
	var ww;
	var ww2;
	var hh;

	var rowOne;
	
	// Get the width and height
		if ( this.style.width ) {
			ww = parseInt(this.style.width);
		} else if ( this.width ) {
			ww = parseInt(this.width);
		} else {
			ww = this.offsetWidth;
		}

		if ( this.style.height ) {
			hh = parseInt(this.style.height);
		} else if ( this.height ) {
			hh = parseInt(this.height);
		} else {
			hh = this.offsetHeight;
		}

		if ( hh == 0 ) {
			hh = 350;
			this.style.height = hh + 'px';
		}

	// Calculate a new width
		if ( tblBody.rows.length > 0 ) {			// check if we have any rows!  If yes, get a reference to the first row
			rowOne = tblBody.rows[0];
		}	

		if ( this.autoSize == "true" || ( this.had_first_resize == false && this.autoSize == "autoonce") ) {
			wanted = this.getWidthWanted();

			divData.style.width = ww + 'px';
	
			ww2 = ww - scrollbarWidth();
	
			var yScale = (ww2 / wanted);

			this.setRowWidthByScale(yScale, ww2);

			this.had_first_resize = true;
	
		} else {
			ww2 = ww;
		}

	// set the header area
		divHeader.style.left = '0px';
		divHeader.style.width = ww + 'px';
		divHeader.style.top = '0px';
		divHeader.style.height = tblHeader.clientHeight + 'px';

		divHeaderSlave.style.left = '0px';
		divHeaderSlave.style.right = ww + 'px';
		divHeaderSlave.style.top = '0px';
		divHeaderSlave.style.bottom = tblHeader.clientHeight + 'px';

	// Set the body span
		divData.style.left = '0px';
		divData.style.width = ww + 'px';
//		divData.style.top = tblHeader.clientHeight;		// we are letting the Relative value set this

	// Check if the rows should be resized!
		if ( this.variable_row == true ) {
			var extraMargin = this.extra_row_margin;

			var maxHeight = this.max_row_height;

			if ( maxHeight == null || maxHeight == 0 ) {
				maxHeight = 1000;
			}

			// Now, process the rows
			var row_count = tblData.rows.length;
			var hhRow = 16;
	
			if ( row_count >= 0 ) {
		
				for (var rr=0; rr < row_count; rr++) {
		
					var row_to_copy = tblData.rows[rr];			// select the first row
		
					hhRow = 0;
					row_to_copy.style.height = 12;		// force the height and have it re-calculate!

					for (var cc=0; cc < row_to_copy.cells.length; cc++) {
						var td = row_to_copy.cells[cc];
						td.style.whiteSpace = 'normal';
						if ( td.scrollHeight > hhRow ) {
							hhRow = td.scrollHeight;
						}
					}

					if ( hhRow != 0 ) {
						if ( hhRow > maxHeight ) {
							hhRow = maxHeight;
						}
						row_to_copy.style.height = (hhRow + extraMargin) + 'px';
					}

				}
		
			}
		} else {
			var row_to_copy = tblData.rows[0];			// select the first row

			if ( row_to_copy != null ) {
				row_to_copy.style.height = '16px';
			}
		}

	// set the height to the appropriate location
		if ( (tblData.clientHeight + scrollbarWidth() ) > hh ) {
			if ( hh - tblHeader.clientHeight > 0 ) {
				divData.style.height = (hh - tblHeader.clientHeight) + 'px';
			} else {
				divData.style.height = '0px';
			}
		} else {
			divData.style.height = (tblData.clientHeight + scrollbarWidth()) + 'px';
		}

	divHeader.style.clip="rect( 0px " + ww2 + "px auto 0px)";
	
	divHeaderSlave.style.left = (-divData.scrollLeft) + 'px';

}

function jlTbl_move( left, top, right, bottom ) {

	// Moves the table to these specific coordinates

	// Move the main DIV to the new location
		this.style.position = "absolute";
		this.style.top = top + 'px';
		this.style.left = left + 'px';
		this.style.width = intMax(1, right - left) + 'px';
		this.style.height = intMax(1, bottom - top) + 'px';

		this.had_first_resize = false;

	// Do a resize
		this.resize();
}

function jlTbl_getCell( xx, yy ) {

	var row = this.getRow( yy );

	if ( row ) {
		return row.cells[xx];
	}

}

function jlTbl_clearRowsSelected() {

	var tbody = jlTbl_getDataTBODY( this );

	tbody.clearRowRangeIsSelected( 0, tbody.rows.length-1 );

}

function jlTbl_setFocusRow( row_ord ) {

	// this changes the focus to the given row and saves the row_ord in lastRowOrd!
	//	NOTE:	this causes a "flash" effect...  but it appears there is no way around that!

	var tBody = jlTbl_getDataTBODY( this );
	var rows = tBody.rows;

	if ( row_ord >= 0 && row_ord < rows.length ) {			// check if the row_ord is in range!
		tBody.setRowIsSelectedByEle( rows[row_ord], true);
		var row = tBody.rows[row_ord];
		var divData = jlTbl_getDIVData( this );
		var oldScrollLeft = divData.scrollLeft;		// now, get the current scrolling value

		// check if we need to scroll the table
		if ( row.offsetTop < divData.scrollTop ) {
			row.scrollIntoView(true);		// true = make it at the top!
		} else if ( (row.offsetTop + row.clientHeight) > divData.scrollTop + divData.clientHeight ) {
			row.scrollIntoView(false);		// false = do not make it at the top!
		}
		row.cells[0].focus();			// ???TODO: should it focus on a different cell?
		this.lastRowOrd = row_ord;

		if ( divData.scrollLeft != oldScrollLeft ) {
			divData.scrollLeft = oldScrollLeft;
		}

	}	

}

function jlTbl_setFocus() {

	try {
		var tbody = jlTbl_getDataTBODY( this );

		var lastRowOrd = this.lastRowOrd;

		if ( lastRowOrd == null || lastRowOrd < 0 ) {
			lastRowOrd = 0;
		}

		this.setFocusRow( lastRowOrd );

	} catch (e) {
		// ???TODO:
	}

}

function jlTbl_toString() {

	var now = new Date();

	var out = [];
	var regexpNBSP = /&nbsp;/;	// nonblank space
	var regexpCRLF = /\r\n/;		// a CR/LF combination
	var simple_types = new Array;

	var headerCells = jlTbl_getColHeaderRow(this).cells;
	var tblData = jlTbl_getDataTBODY( this );

	// Initialize the output with the table's title, options and time of this copy
		out[out.length] = "\"Table Title:\"\t" + xlsstr( getAttr(this, 'table_title') );
		out[out.length] = "\r\n";
		out[out.length] = "\"Options:\"\t" + xlsstr( getAttr(this, 'table_options') );
		out[out.length] = "\r\n";
		out[out.length] = "\"Time:\"\t" + xlsstr( dateFormat( now ) + ' ' + now.toLocaleTimeString() );
		out[out.length] = "\r\n";

	// Now, get the simple types--and get the header names!
		for (var cc=0; cc < headerCells.length; cc++) {			// 0 = header row
			simple_types[cc] = parseInt( headerCells[cc].st );		// get the simple_type_id property
			var cell = headerCells[cc];
			var value = (cell.textContent) ? cell.textContent : cell.innerText;
// I had this in the code because the old framework used to write nonblank spaces everywhere.
//			if ( value != undefined ) {
//				value = value.replace(regexpNBSP, "");
//			}
			out[out.length] = simple_type_formatExcel(value, simple_type_text) + "\t";
		}
		out[out.length] = "\r\n";

	// Now, save the data
	var row_count = tblData.rows.length;

	if ( row_count >= 0 ) {

		for (var rr=0; rr < row_count; rr++) {

			var row_to_copy = tblData.rows[rr];			// select the first row

			for (var cc=0; cc < row_to_copy.cells.length; cc++) {
				var cell = row_to_copy.cells[cc];
				var value = (cell.textContent) ? cell.textContent : cell.innerText;
				if ( value != undefined ) {
					value = value.replace(regexpNBSP, "");
				}
				out[out.length] = simple_type_formatExcel(value, simple_types[cc]) + "\t";
			}
			out[out.length] = "\r\n";

		}

	}

	return out.join('');

}

function jlTbl_print( show_preview, width, height ) {

	// This is a silly hack to get printing tables to work.
	// For printing, we want the various parts of the table to auto-expand

	var id = this.id;

	var hh1 = g(id).style.height;
	var hh2 = g(id + 'DIVTB').style.height;

	g(id).style.height = 'auto';
	g(id + 'DIVTB').style.height = 'auto';

	window.print();

	g(id).style.height = hh1;
	g(id + 'DIVTB').style.height = hh2;

}

function jlTbl_getColCode( col_id ) {

	var tblHeader = jlTbl_getColHeaderRow( this );

	return tblHeader.cells[col_id].fn;

}

function jlTbl_setKeyboardDisable( disable ) {

	this.isKeyboardDisabled = boolClean( disable );
	
}

function jlTbl_getFocusRowOrd() {

	// returns the index of the row that currently has focus

	return this.lastRowOrd;

}

function jlTbl_getColHeaders() {

	return jlTbl_getColHeaderRow( this );

}

function jlTbl_getRows() {

	return jlTbl_getDataTABLERows( this );

}

function jlTbl_getRow( rowIndexBase0 ) {

	var rows = jlTbl_getDataTABLERows( this );

	if ( rows ) {
		try {
			return rows[rowIndexBase0];
		} catch (e) {
			return null;
		}
	}

}

function jlTbl_getRowId( rowIndexBase0 ) {

	var rows = jlTbl_getDataTABLERows( this );

	if ( rows ) {
		try {
			return getAttr(rows[rowIndexBase0], 'i');
		} catch (e) {
			return null;
		}
	}

}

function jlTbl_rowIsSelected( row_ord ) {

	var tbody = jlTbl_getDataTBODY( this );

	var rows = tbody.rows;

	if ( rows.length > 0 ) {			// check if we have any rows!
		if ( row_ord < rows.length ) {			// check if this row exists
			return tbody.getRowIsSelectedByEle( rows[row_ord] );
		}
	}

	return false;
}

function jlTbl_getSelectedRowCount() {

	var tbody = jlTbl_getDataTBODY( this );
	var rows = tbody.rows;
	var count = 0;

	if ( rows.length > 0 ) {			// check if we have any rows!

		for ( var tt = 0; tt < rows.length; tt++ ) {
			if ( tbody.getRowIsSelectedByEle( rows[tt] ) == true ) {
				count++;
			}
		}

	}

	return count;

}

function jlTbl_getFirstSelectedRowOrd() {

	var tBody = jlTbl_getDataTBODY( this );
	var rows = tBody.rows;

	if ( rows.length > 0 ) {			// check if we have any rows!

		for ( var tt = 0; tt < rows.length; tt++ ) {
			if ( tBody.getRowIsSelectedByEle( rows[tt] ) == true ) {
				return tt;
			}
		}

	}

	return -1;

}

function jlTbl_getWidthWanted() {

	var out = 0;

	var cgHead = jlTbl_getColHeaderCOLGROUP( this );

	for (var i=0; i < cgHead.childNodes.length; i++){
		var cH = cgHead.childNodes[i];
		out += cH.widthWanted;
	}

	return out;

}

function jlTblTH_OnMouseUp( evt ) {

	evt = evt || window.event;

	if ( this.in_resize == true ) {

		releaseMouseCapture( this );
		this.in_resize = false;
		this.was_in_resize = true;
		el = null;

		var divMain = getParentByClass( this, "tbl");

		if ( divMain == null ) {
			return;
		}

		var ccHead = jlTbl_getColHeaderCOLGROUP( divMain ).childNodes[ this.cellIndex ];
		var ccBody = jlTbl_getDataCOLGROUP( divMain ).childNodes[ this.cellIndex ];

		ccBody.width = ccHead.width;
		ccBody.style.width = ccHead.width + 'px';

		var tblHead = getParent(this, 'TABLE');
		var tblBody = getParent(ccBody, 'TABLE');

		tblBody.style.width = tblHead.style.width;

		// Refresh the "original" width wanted
		var newWidth = parseInt(this.style.width);		// get the new width
		var oldTotalWidth = divMain.getWidthWanted();				// get the old total width wanted
		var widthCurrent = getParent(this, 'DIV').offsetWidth;		// get the current width data area
		var newWidthWanted = Math.round(oldTotalWidth * (newWidth / widthCurrent));

		this.widthWanted = newWidthWanted;	

		divMain.resize();

		// kill any additional events!
		return evtKill( evt );

	} else {
		this.style.borderTop="1px solid white";
		this.style.borderLeft="1px solid white";
		this.style.borderRight="1px solid black";
		this.style.borderBottom="1px solid black";
		this.style.backgroundColor="threedface";
	}

}

function jlTblTH_OnMouseDown( evt ) {

	evt = evt || window.event;

	var tbl = getParent( this, 'TABLE');
	var offsetX = evtOffsetX( evt );

	if ( evtSrc(evt).tagName == 'INPUT' ) {
		return false;

	} else if ( Math.abs( this.offsetWidth - offsetX ) < 15 ) {
		this.in_resize = true;
		this.pos = evt.clientX;
		this.size = this.offsetWidth;
		tbl.size = tbl.offsetWidth;

		evtKill( evt );

		setMouseCapture( this );

	} else {

		this.style.borderTop="2px solid black";
		this.style.borderLeft="2px solid black";
		this.style.borderRight="0px";
		this.style.borderBottom="0px";
		this.style.backgroundColor="#e1e1e1";

	}

}

function jlTblTH_OnMouseMove( evt ) {

	evt = evt || window.event;

	var tbl = getParent(this, 'TABLE');
	var divMain = getParentByClass(tbl, 'tbl');

	if ( this.in_resize == true ) {

		var delta = 0;

		delta = (evt.clientX - this.pos);

		// First, resize the column (COL in the COLGROUP)
		var ww = intMax(this.size + delta, 0);

		var cc = jlTbl_getColHeaderCOLGROUP( divMain ).childNodes[ this.cellIndex ];

		cc.width = ww;
		cc.style.width = ww + 'px';

		// Now, resize the total table
		var ww = intMax(tbl.size + delta, 0);

		tbl.style.width = ww + 'px';

		// kill any other events
		return evtKill( evt );

	} else {

		var offsetX = evtOffsetX( evt );

		if ( Math.abs( this.offsetWidth - offsetX ) < 15 ) {
			this.style.cursor = "e-resize";
		} else if ( this.style.cursor != "pointer" ) {
			this.style.cursor = "pointer";
		}
	}

}

function jlTblTH_OnClick( evt ) {

	evt = evt || window.event;

	var divMain;
	var hdr;
	var nCol;

	// Check if we were in a resize event...  Stop the processing of the click!
		if ( this.was_in_resize ) {
			this.was_in_resize = false;
			return evtKill( evt );
		}

	// First, get the main table
		divMain = getParentByClass( this, "tbl")

		if ( divMain == null ) {
			return;
		}

	// check if they clicked on a checkbox or something else!
	if ( evtSrc(evt).tagName == 'INPUT' ) {
		nCol = this.cellIndex;

		try {
			return eval( divMain.id + "_THInputClicked(" + nCol + ");");
		} catch (e) {
			// do nothing
		}

		return false;
	}

	// Get the index of the column
		nCol = -1;
		nCol = this.cellIndex;

		if ( nCol == -1 ) {
			return;
		}

	// Now...  Call the user's _THClick function...  If it errors or doesn't exist, call the default behavior!
		var result = true;

		try {
			result = eval( divMain.id + "_THClick(" + nCol + ");");
		} catch (e) {
		}

		try {
			if ( result == true ) {
				divMain.addSortColumn(nCol);
			}

		} catch(e) {
			// do nothing
		}

}

function jlTblTH_OnContextMenu( evt ) {

	evt = evt || window.event;

	var divMain;
	var hdr;
	var nCol;

	// Get the index of the column
		nCol = -1;
		nCol = this.cellIndex;

		if ( nCol == -1 ) {
			return evtKill( evt );
		}

	// First, get the main table
		divMain = getParentByClass( this, "tbl")

		if ( divMain == null ) {
			return;
		}

	// Now...  call ???TODO:
		try {
			divMain.filterByColumn(nCol);
		} catch(e) {
			// do nothing
		}

		return evtKill( evt );

}

function jlTBODY_OnSelectStart( evt ) {

	evt = evt || window.event;

	// stop the selection of any text within the body of this table

	var el = evtSrc( evt );

	if ( el != null ) {
		if ( el.tagName == "INPUT" || el.tagName == "TEXTAREA" ) {
			return true;
		}
	}

	return evtKill( evt );
}

function jlTBODY_OnMouseOver( evt ) {

	// show the contents of the cell in the window status bar

	evt = evt || window.event;

	var el = evtSrc( evt );

	if ( el.tagName == 'TD' ) {
		// if the text within the TD is too long, copy it to the title!
		if ( el.scrollWidth > el.offsetWidth ) {
			el.title = (el.textContent) ? el.textContent : el.innerText;
		}
	}

}

function jlTBODY_OnMouseOut( evt ) {

	// clear the window status bar

	evt = evt || window.event;

	var el = evtSrc( evt );

	if ( el.tagName == 'TD' ) {
		if ( el.title ) {
			try {
				delete el.title;
			} catch (e) {
				el.title = null;
			}
		}
	}

}

function jlTBODY_OnFocus( evt ) {

	evt = evt || window.event;

	// ???TODO: "paint" dotted line around the row with focus!
}

function jlTBODY_OnBlur( evt ) {

	evt = evt || window.event;

	// ???TODO: remove dotted line around the row with focus!
}

function jlTBODY_OnKeyDown( evt ) {

	evt = evt || window.event;

	var tBody = this;
	var tblData = getParent( tBody, "TABLE");
	var divMain = getParentByClass( tblData, "tbl")

	if ( divMain.isKeyboardDisabled == true ) {
		return;

	} else if ( evt.keyCode == 27 && mnuActive != null ) {
		mnuHide();
		return;
	}

	var tmp = evtSrc( evt );

	if ( tmp.tagName == 'INPUT' ) {
		return true;
	}

	var row_ord = -1;

	var kill_event = false;

	row_ord = divMain.lastRowOrd;		// get the index for the row that should have focus

	if ( row_ord != -1 ) {

		var el = tBody.rows[row_ord];

		switch (evt.keyCode) {
			case 8:			// 8=backspace; remove a char from the buffer and search
				if ( divMain.searchBuffer.length > 0 ) {
					divMain.searchBuffer = divMain.searchBuffer.substring(0, divMain.searchBuffer.length - 1);
					divMain.find( divMain.searchBuffer, true);
				}
				kill_event = true;
				break;

			case 27:		//27=ESC; clear the buffer and select the first row
				divMain.clearSearchBuffer();
				tBody.setRowIsSelectedByEle( tBody.rows[row_ord], false);
				tBody.setRowIsSelectedByEle( tBody.rows[0], true);
				divMain.setFocusRow( 0 );
				kill_event = true;
				break;
					
			case 13:		// 13 = same as DblClick!
				try {
					var rlevel = getAttr(el, 'rlevel');
					if ( rlevel == null ) { rlevel = 0; }
					eval( divMain.id + "_RowDblClicked('" + getAttr(el, 'i').replace(/'/g, '\\\'') + "', " + row_ord + ", " + rlevel + ");" );
				} catch (e) {
					// do nothing
				}
				kill_event = true;
				break;

			case 36:		// 36 = Home
				if ( evt.shiftKey == true ) {
					if ( divMain.lastRowOrd != -1 ) {
						tBody.setRowRangeIsSelected( 0, row_ord);
					}
				} else {
					tBody.clearRowRangeIsSelected( 0, tBody.rows.length-1 );
					tBody.setRowIsSelectedByEle( tBody.rows[0], true);
				}
				divMain.setFocusRow( 0 );
				divMain.doRaiseClicked( getAttr(tBody.rows[0], 'i'), 0);
				kill_event = true;
				divMain.clearSearchBuffer();
				divMain.inSearchMode = false;
				break;

			case 35:		// 35 = End
				if ( evt.shiftKey == true ) {
					if ( divMain.lastRowOrd != -1 ) {
						tBody.setRowRangeIsSelected(row_ord, tBody.rows.length-1);
					}
				} else {
					tBody.clearRowRangeIsSelected( 0, tBody.rows.length-1 );
					tBody.setRowIsSelectedByEle( tBody.rows[tBody.rows.length-1], true);
				}
				divMain.setFocusRow( tBody.rows.length-1 );
				divMain.doRaiseClicked( getAttr(tBody.rows[tBody.rows.length-1], 'i'), tBody.rows.length-1);
				kill_event = true;
				divMain.clearSearchBuffer();
				divMain.inSearchMode = false;
				break;

			case 38:		// 38=Up arw
				if ( row_ord > 0 ) {
					if ( evt.shiftKey == false ) {
						tBody.clearRowRangeIsSelected( 0, tBody.rows.length-1 );
					}
					tBody.setRowIsSelectedByEle( tBody.rows[row_ord-1], true);
					divMain.setFocusRow( row_ord-1 );
					divMain.doRaiseClicked( getAttr(tBody.rows[row_ord-1], 'i'), row_ord-1);
				}
				kill_event = true;
				divMain.clearSearchBuffer();
				divMain.inSearchMode = false;
				break;

			case 40:		// 40=Down arw
				if ( row_ord < tBody.rows.length-1 ) {
					if ( evt.shiftKey == false ) {
						tBody.clearRowRangeIsSelected( 0, tBody.rows.length-1 );
					}
					tBody.setRowIsSelectedByEle( tBody.rows[row_ord+1], true);
					divMain.setFocusRow( row_ord+1 );
					divMain.doRaiseClicked( getAttr(tBody.rows[row_ord+1], 'i'), row_ord+1 );
				}
				kill_event = true;
				divMain.clearSearchBuffer();
				divMain.inSearchMode = false;
				break;
				
			default:
				kill_event = false;
				break;
		}

	}

	if ( kill_event == true ) {
		return evtKill( evt );
	}

}

function jlTBODY_OnKeyPress( evt ) {

	evt = evt || window.event;

	var divMain = getParentByClass( this, "tbl");
	var keyCode = (evt.keyCode)? evt.keyCode: ((evt.charCode)? evt.charCode: evt.which);

	var tmp = evtSrc( evt );

	if ( tmp.tagName == 'INPUT' ) {
		return true;
	}

	if ( divMain.isKeyboardDisabled == true ) {
		return;
	}

	switch ( keyCode ) {
		case 9:			// 9 = tab key
		case 16:		// 16 = Shift key
		case 17:		// 17 = control key
		case 13:		// 13 = Enter
		case 27:		// 27 = Escape
		case 38:		// 38 = Up			// if they hit an arrow, clear the buffer!!!
		case 40:		// 40 = Down
		case 37:		// 37 = left
		case 39:		// 39 = right
		case 35:		// 35 = End
		case 36:		// 36 = home
		case 33:		// 33 = page up
		case 34:		// 34 = page down
			return true;
		case 8:			// 8 = backspace
			return evtKill( evt );
	}

	divMain.inSearchMode = true;		// turn on search mode

	// Add the character to the search string
	divMain.searchBuffer = divMain.searchBuffer + String.fromCharCode( keyCode );

	if ( divMain.searchBuffer.length == 1) {
		divMain.find( divMain.searchBuffer, true);
	} else {
		divMain.find( divMain.searchBuffer, false);
	}

	// stop default key handling...
	return evtKill( evt );
}

function jlTBODY_OnContextMenu( evt ) {

	//	Handles right mouse clicks!

	evt = evt || window.event;

	var tmp = evtSrc(evt);

	if ( tmp.tagName == "INPUT" || tmp.tagName == "TEXTAREA" ) {
		return true;
	}

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( this, "tbl")

	el = getParent(tmp, "TR");	// get the row

	if ( el != null ) {			// if they are on a row

		var rowIndex = el.rowIndex;

		if ( divMain.nocolgroup ) {		// if there isn't a COLGROUP, it means there is a hidden TR that is setting the widths!
			rowIndex--;
		}

		if ( getAttr(el, 'is_total_footer') != 1 && getAttr(el, 'is_total_header') != 1 ) {			// if it isn't a footer row

			// if they aren't clicking on a selected element
			if ( !this.getRowIsSelectedByEle( el ) ) {
				this.clearRowRangeIsSelected( 0, tblData.rows.length);			// clear the table and set this row selected
				this.setRowIsSelectedByEle( el, true);
			} 

			divMain.setFocusRow( rowIndex );
			divMain.doRaiseClicked( getAttr(el, 'i'), rowIndex );
		}

		try {	
			eval( divMain.id + "_ContextMenuOpen('" + getAttr(el, 'i').replace(/'/g, '\\\'') + "', " + rowIndex + ");" );
		} catch (e) {
			// do nothing
		}

	}

	// stop default key handling...
	return evtKill( evt );
}

function jlTBODY_OnDblClick( evt ) {

	evt = evt || window.event;

	var tmp = evtSrc( evt );

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( this, "tbl");

	el = getParent(tmp, "TR");	// get the row

	if ( el != null ) {

		var rowIndex = el.rowIndex;

		if ( divMain.nocolgroup ) {		// if there isn't a COLGROUP, it means there is a hidden TR that is setting the widths!
			rowIndex--;
		}

		try {
			var rlevel = getAttr(el, 'rlevel');
			if ( rlevel == null ) { rlevel = 0; }

			eval( divMain.id + "_RowDblClicked('" + getAttr(el, 'i').replace(/'/g, '\\\'') + "', " + rowIndex + ", " + rlevel + ");" );

		} catch (e) {
			// do nothing
		}
		return evtKill( evt );
	}

	// stop default key handling...
	return evtKill( evt );
}

function jlTBODY_OnClick( evt ) {

	// Handles all clicks on the table

	evt = evt || window.event;

	var ev = evt;
	var el;
	var tHeadParent;

	var tmp = evtSrc( ev );

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( tblData, "tbl")

	var last;
	var current;
	var tt; 

	// Check what was clicked
		// ???TODO: I need to figure out whether or not the given clicked item is within the table or not.
		//			if it isn't within the table, then we should get out!!!
		// the following is a weak attempt to solve the problem!
		if ( tmp.tagName == 'TBODY' ) {				// this was put in to prevent a problem when you press Control and click the mouse and move!
			return false;
		}
		
	el = getParent(tmp, "TR");	// get the row

	if ( el != null ) {

		var rowIndex = el.rowIndex;

		if ( divMain.nocolgroup ) {		// if there isn't a COLGROUP, it means there is a hidden TR that is setting the widths!
			rowIndex--;
		}

		if ( dblclick_support == false ) {		// if double click not supported
			if ( divMain.lastRowOrd == rowIndex ) {		// and we are on the same row

				// check if we are less than 750 milliseconds from the last click!
				var now = new Date();

				if ( this.lastClickDateTime ) {
					if ( (now.getTime() - this.lastClickDateTime.getTime()) <= 750 ) {		// if smaller than 750 milliseconds, treat as a double click!

						var rlevel = getAttr(el, 'rlevel');
						if ( rlevel == null ) { rlevel = 0; }

						eval( divMain.id + "_RowDblClicked('" + getAttr(el, 'i').replace(/'/g, '\\\'') + "', " + rowIndex + ", " + rlevel + ");" );

						return evtKill( evt );
					}
				}
			}
		}

		var isTotal = 0;

		if ( getAttr(el, 'is_total_footer') != null ) {
			isTotal = getAttr(el, 'is_total_footer');
		} else if ( getAttr(el, 'is_total_header') != null ) {
			isTotal = getAttr(el, 'is_total_header');
		}

		if ( isTotal != 1 ) {

			// if the ctrl key isn't pressed, clear all 
			if ( ev.shiftKey == true ) {
				if ( divMain.lastRowOrd != -1 ) {
					this.setRowRangeIsSelected(divMain.lastRowOrd, rowIndex);
				}

			} else if ( ev.ctrlKey == false ) {
				this.clearRowRangeIsSelected( 0, this.rows.length);
				this.setRowIsSelectedByEle( el, true);
			} else {
				this.setRowIsSelectedByEle(el, !this.getRowIsSelectedByEle( el ));
				// ???TODO: this is controversal...  Note that I'm not changing the focus here!
				divMain.doRaiseClicked( getAttr(el, 'i'), rowIndex );
				return false;
			}

			divMain.setFocusRow( rowIndex );
			divMain.doRaiseClicked( getAttr(el, 'i'), rowIndex );

			if ( dblclick_support == false ) {
				this.lastClickDateTime = new Date();
			}

		}
	}

	// a quick hack to allow INPUT (checkboxes?) and other objects to function within table bodies!
	if ( tmp.tagName == 'INPUT' || tmp.tagName == 'IMG' || tmp.tagName == 'A' ) {
		tmp.focus();
		return true;
	}

	return false;
}

function jlTBODY_clearRowRangeIsSelected( rowIndexA, rowIndexB ) {

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( tblData, "tbl");
	var row_count = this.rows.length;

	if ( row_count >= 0 ) {
		var row_to_check = this.rows[0];

		for (var tt = 0; tt < row_count; tt++ ) {
			if ( this.getRowIsSelectedByEle( row_to_check ) == true ) {
				this.setRowIsSelectedByEle( row_to_check, false);
			}
			row_to_check = row_to_check.nextSibling;
			while ( row_to_check && row_to_check.nodeType == 3 ) {
				row_to_check = row_to_check.nextSibling;
			}
		}
	}

}

function jlTBODY_setRowRangeIsSelected( rowIndexA, rowIndexB ) {

	var tblData = this;
//	var tblData = getParent( this, "TABLE");

	if ( rowIndexA <= rowIndexB ) {
		var row = tblData.rows[rowIndexA];

		for (var tt=rowIndexA; tt <= rowIndexB; tt++ ) {
			this.setRowIsSelectedByEle( row, true);
			row = row.nextSibling;
			while ( row && row.nodeType == 3 ) {
				row = row.nextSibling;
			}
		}

	} else {
		var row = tblData.rows[rowIndexB];

		for (var tt=rowIndexB; tt <= rowIndexA; tt++ ) {
			this.setRowIsSelectedByEle( row, true);
			row = row.nextSibling;
			while ( row && row.nodeType == 3 ) {
				row = row.nextSibling;
			}
		}
	}
}

function jlTBODY_getRowIsOddByEle( el ) {

	if ( getClass(el).charAt(0) == 'o')
		return true;
	else
		return false;
	//endif

}

function jlTBODY_getRowIsSelectedByEle( el ) {

	if ( getClass(el).charAt(1) == 's') {
		return true;
	} else {
		return false;
	}

}

function jlTBODY_setRowIsSelectedByEle( el, is_selected ) {

	// Sets the state of a given row

	// IN:	el - the row element
	//	is_selected - true - if the row should be selected

	var className;

	// First, derive the class name
		if ( this.getRowIsOddByEle( el ) ) {
			className = "o";
		} else {
			className = "e";
		}

		if ( is_selected ) {
			className = className + 's';
		}

		if ( getClass(el) != className ) {
			setClass(el, className);
		}

	// If selected, add a hidden <INPUT> that shows that this row has been selected
		if ( is_selected == true ) {

			var do_add = true;

			// find the hidden <INPUT> and change it
			for (var tt=0; tt < el.cells[0].childNodes.length; tt++) {

				// ???TODO: the following was on this line...  it was used to handle two inputs in the same column.
				// However...  Should just change this code to _always_ have a checkbox as the first column!
				// && el.cells[0].childNodes[tt].name == 'row_' + getAttr(el, 'i') ) {
				if ( el.cells[0].childNodes[tt].nodeName == "INPUT" || el.cells[0].childNodes[tt].nodeName == "TEXTAREA" ) {
					el.cells[0].childNodes[tt].value = "1";
					do_add = false
					break;
				} 

			}

			if ( do_add == true ) {
				var selected = document.createElement("INPUT");
				selected.type = "hidden";
				selected.value = 1;
				selected.name = "row_" + getAttr(el, 'i');
				if ( getAttr(el, 'ts') ) {		// if there is a timestamp, add it to the name of the field
					selected.name += "-" + getAttr(el, 'ts');
				}
				el.cells[0].appendChild(selected);
			}

		} else {			// if not selected
			// find the hidden <INPUT> and delete it
			for (var tt=0; tt < el.cells[0].childNodes.length; tt++) {

				// ???TODO: see above...  && el.cells[0].childNodes[tt].name == 'row_' + getAttr(el, 'i')
				if ( el.cells[0].childNodes[tt].nodeName == "INPUT" || el.cells[0].childNodes[tt].nodeName == "TEXTAREA" ) {
					el.cells[0].childNodes[tt].parentNode.removeChild(el.cells[0].childNodes[tt]);
					break;
				} 

			}

		}
}

function jlTblBtnUP_OnClick( evt ) {

	// Handles all clicks on the table

	evt = evt || window.event;

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( tblData, "tbl")
	var divData = jlTbl_getDIVData( divMain );
	var tbody = jlTbl_getDataTBODY( divMain );

	var hh = parseInt(divData.style.height) - 15;

	if ( typeof( divData.lastScrollTop ) == 'number' ) {
		divData.lastScrollTop = intMax(divData.lastScrollTop - hh, 0);
	} else {
		divData.lastScrollTop = hh;
	}

	divData.scrollTop = divData.lastScrollTop;

	return evtKill(evt);
}

function jlTblBtnDN_OnClick( evt ) {

	// Handles all clicks on the table

	evt = evt || window.event;

	var tblData = getParent( this, "TABLE");
	var divMain = getParentByClass( tblData, "tbl")
	var divData = jlTbl_getDIVData( divMain );
	var tbody = jlTbl_getDataTBODY( divMain );

	var hh = parseInt(divData.style.height) - 15;

	if ( typeof( divData.lastScrollTop ) == 'number' ) {
		divData.lastScrollTop = divData.lastScrollTop + hh;
	} else {
		divData.lastScrollTop = hh;
	}

	divData.scrollTop = divData.lastScrollTop;

	return evtKill(evt);
}

function jlTbl_find( sToFind, start_at_beginning ) {

	// Find the current string within the dropdown list's text

	//	RET:	-1 = not found, otherwise the row ordinal value (0 based)

	var l = sToFind.length;			// Length of search string
	var sPos;

	var tblData = jlTbl_getDataTABLE( this, "TABLE");
	var tbody = jlTbl_getDataTBODY( this );

	var row_ord = this.lastRowOrd;

	var first_sort_column_ord = this.first_sort_column_ord;

	if ( first_sort_column_ord == null ) {
		first_sort_column_ord = 0;
	}

	if ( start_at_beginning == true || this.lastRowOrd == -1 ) {			// if we want to start from the beginning
		sPos = 0;
	} else {				// otherwise, start at the current position (to speed the search!)
		sPos = this.lastRowOrd;
	}

	this.showSearchBuffer(sToFind);

	if ( sToFind.length == 0 ) {			// if it is empty, select the first one

		if ( row_ord != -1 ) {
			tbody.setRowIsSelectedByEle( tbody.rows[row_ord], false);
		}
		tbody.setRowIsSelectedByEle( tbody.rows[0], true);
		this.setFocusRow( 0 );

		return -1;

	} else {		// otherwise, do the search

		sToFind = sToFind.toLowerCase();		// be case-insensitive

		var row_count = tbody.rows.length;

		if ( row_count >= 0 ) {

			var row_to_check = tbody.rows[sPos];

			for (var i=sPos; i < row_count; i++) {
				// check for a match
				var cell = row_to_check.cells[first_sort_column_ord];
				var sOption = (cell.textContent) ? cell.textContent : cell.innerText;

				if ( sOption.length >= l ) {				// if it is a good length (not too small)
					if (sToFind == sOption.substr(0,l).toLowerCase()) {		// check if there are equal
						if ( row_ord != -1 ) {
							tbody.setRowIsSelectedByEle( tbody.rows[row_ord], false);
						}
						tbody.setRowIsSelectedByEle( row_to_check, true);
						this.setFocusRow( row_to_check.rowIndex );
						return row_to_check.rowIndex;
					}
				}
				row_to_check = row_to_check.nextSibling;
				while ( row_to_check && row_to_check.nodeType == 3 ) {
					row_to_check = row_to_check.nextSibling;
				}
			}
		}

		// select the first item...
		if ( row_ord != -1 ) {
			tbody.setRowIsSelectedByEle( tbody.rows[row_ord], false);
		}
		tbody.setRowIsSelectedByEle( tbody.rows[0], true);
		this.setFocusRow(0);

		return -1;
	}
}

function jlTbl_clearSearchBuffer() {

	// clears the search buffer and clears the window status, if required

	if ( this.showStatus == true ) {
		if ( window.status == "Search: " + this.searchBuffer ) {
			window.status = "";
		}
	}

	this.searchBuffer = "";

}

function jlTbl_showSearchBuffer( sToFind ) {

	// shows the current contents of the search buffer, if required

	if ( this.showStatus == true ) {
		window.status = "Search: " + sToFind;
	}

}

function jlTbl_doRaiseClicked( row_id, row_ord ) {

	var divMain = this;

	try {
		eval( divMain.id + "_RowClicked('" + row_id.replace(/'/g, '\\\'') + "', " + row_ord + ");" );
	} catch (e) {
		// do nothing
	}

}

//	This is a set of sub-element "finding" functions.  Given the main DIV, these functions attempt to return a specific
//	element.

	// The table class should be constructed like this:

	//	<div id=tblMain name=tblMain class=tbl autosize="" table_title="" table_options="" nocolgroup=0><div>
	//	<table id=???TBLNAV>
	//	<tr><td align="right" colspan=?><button>up</button><button>down</button></td></tr>
	//	</table>
	//	<table id=???TBLTITLE>
	//	<tr id=tblMainTRTH><td colspan=11>
	//		<table width="100%">  contains an 'extended" header! </table>
	//		</td>
	//	</tr>
	//	</table></div><div id=tblMainDIVTH class=tblTH><div class=tblTHSlave><table id="tblMainTBLTH" class=tblTH><COLGROUP id=tblMainHCG><COL width=207>...</COLGROUP><thead id="tblMainTHTH">
	//	<tr><TH st=1 fn="entity_name" title="Name">Name</TH>
	//	...
	//	</tr></thead></table></div></div><DIV id="tblMainDIVTB" class=tblTBODY><table class=tblTBODY><COLGROUP id=tblMainBCG><COL width=207>...</COLGROUP><tbody id="tblMainTBODY" tabindex=0>
	//	<tr class=o i='238'>...td and /td</tr>
	//	</tbody>
	//	</table></div></div>

function jlTbl_getColHeaderTABLE( divMain ) {

	return g(divMain.id + 'TBLTH');

}

function jlTbl_getColHeaderRow( divMain ) {

	if ( divMain.nocolgroup ) {
		return g(divMain.id + 'TBLTH').rows[1];
	} else {
		return g(divMain.id + 'TBLTH').rows[0];
	}
}

function jlTbl_getColHeaderRowCells( divMain ) {

	return jlTbl_getColHeaderRow( divMain ).cells;

}

function jlTbl_getTHEAD( divMain ) {

	return jlTbl_getColHeaderTABLE(divMain).tHead;

}

function jlTbl_getColHeaderCOLGROUP( divMain ) {

	return g( divMain.id + 'HCG' );

}

function jlTbl_getDIVData( divMain ) {

	return g( divMain.id + 'DIVTB' );

}

function jlTbl_getDataTABLE( divMain ) {

	return getChild( jlTbl_getDIVData( divMain ), 0);

}

function jlTbl_getDataTABLERows( divMain ) {

	return jlTbl_getDataTBODY( divMain ).rows;

}

function jlTbl_getDataCOLGROUP( divMain ) {

	return g( divMain.id + 'BCG' );

}

function jlTbl_getDataTBODY( divMain ) {

	return g(divMain.id + 'TBODY');

}

function jlTbl_getDIVHeader( divMain ) {

	return g( divMain.id + 'DIVTH' );

}

function jlTbl_getDIVHeaderSlave( divMain ) {

	return getChild( jlTbl_getDIVHeader( divMain ), 0);

}
var rng = null;
var sel = null;

function jlTeditInit( ele_id ) {

	// check if the device supports HTML editing
	if ( htmledit_support == false ) {
		return;
	}

	var ele = g(ele_id);

	if ( ele != null ) {

		var elepar = ele.parentNode;

		if ( elepar.tagName == 'TD' ) {
			elepar = getParent(elepar, 'TABLE').parentNode;
		}

		var vv = ele.value;		// get the value

		if ( vv != null ) {
			// if the value is has the NO flag, skip this textarea because tedit needs to handle it
			if ( vv.indexOf('<' + '!--no--' + '>') != -1 ) {
				return;
			}
		}

		// clean up the value for editing
		if ( vv != null ) {
			vv = vv.replace(/&lt;/g, '<');
			vv = vv.replace(/&gt;/g, '>');
			vv = vv.replace(/&amp;/g, '&');
		}

		have_tedit = true;

		ele.tabIndex = -1;	// stop the user from tabbing to this item

		var divEdit = document.createElement('DIV');

		divEdit.id = 'div' + ele_id;
		divEdit.ta_id = ele_id;
		divEdit.className = 'tedit';
		divEdit.style.position = 'absolute';
		divEdit.style.left = (totalOffsetLeftFromParent(ele, elepar) - 1) + 'px';	// ???TODO: I need this -1...  for the textbox border?
		divEdit.style.top = totalOffsetTopFromParent(ele, elepar) + 'px';
		divEdit.style.width = ele.offsetWidth;
		divEdit.style.height = ele.offsetHeight;
		divEdit.style.zIndex = (ele.style.zIndex?ele.style.zIndex:0);

		elepar.appendChild( divEdit );

		divEdit.dlgResult = jlTedit_dlgResult;
		divEdit.doToolbarRefresh = jlTedit_doToolbarRefresh;

		var divToolbar = document.createElement('DIV');
		divToolbar.id = 'toolbar' + ele_id;
		divToolbar.className = 'toolbar';
		divToolbar.style.left = '0px';
		divToolbar.style.top = '0px';
		divToolbar.style.width = ele.offsetWidth + 'px';
		divToolbar.lastInTD = false;

		var sToolbar = '<table width="100%" cellspacing="0" cellpadding="0"><tr><td>'
		sToolbar += tbtnCreate('html', 'HTML', 'HTML Mode', '', false, 'Turn on HTML mode.');
//		sToolbar += tbtnSpaceCreate();
//		sToolbar += tbtnCreate('Undo', 'Undo', 'Undo', '', false, 'Allows you to undo your last action.');
		sToolbar += tbtnSpaceCreate();

		sToolbar += tbtnCreate('Bold', 'Bold', 'Bold', '', false, 'Make the text bold.');
		sToolbar += tbtnCreate('Italic', 'Italic', 'Italics', '', false, 'Make the text italics.');
		sToolbar += tbtnCreate('Underline', 'Underline', 'Underline', '', false, 'Make the text underlined.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('JustifyLeft', 'JustLeft', 'Left Justify', '', false, 'Make the text left justified.  All text will be aligned on the left and the right side will be "jagged".');
		sToolbar += tbtnCreate('JustifyCenter', 'Center', 'Center', '', false, 'Make the text centered.');
		sToolbar += tbtnCreate('JustifyRight', 'JustRight', 'Right Justify', '', false, 'Make the text right justified.  All text will be aligned on the right and the left side will be "jagged".');
		sToolbar += tbtnCreate('JustifyFull', 'Just', 'Justify', '', false, 'Make the text align on both the left and right sides--like a Newspaper column.');
//		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('InsertOrderedList', 'Numbered', 'Numbered', '', false, 'Makes the selected text a numbered list.');
		sToolbar += tbtnCreate('InsertUnorderedList', 'Bullet', 'Bullet', '', false, 'Makes the selected text a bulletted (unnumbered) list.');
//		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('Outdent', 'Outdent', 'Outdent', '', false, 'Decreases the amount of indenting for the selected text.');
		sToolbar += tbtnCreate('Indent', 'Indent', 'Indent', '', false, 'Indents the selected text.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('ForeColor', 'ForeColor', 'Foreground Color', '', false, 'Allows you to set the color of the text.');
		sToolbar += tbtnCreate('BackColor', 'BackColor', 'Background Color', '', false, 'Allows you to set the color of the background.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('linkAdd', 'LinkAdd', 'Link', '', false, 'Allows adding a link to the selected text.');
		sToolbar += tbtnCreate('fileLinkAdd', 'FileLinkAdd', 'Add link to file', '', false, 'Allows adding a link to a file.');
		sToolbar += tbtnCreate('imageAdd', 'ImageAdd', 'Add Image', '', false, 'Allows adding an image to the text.');
		sToolbar += tbtnCreate('imageExtAdd', 'ImageAdd', 'Add External Image', '', false, 'Allows adding an image to the text.  Use this if you want to add an image that doesn\'t exist on this site.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('tableAdd', 'TableAdd', 'table', '', false, 'Allows adding a table to the text.');
//		sToolbar += tbtnCreate('tableProperties', 'tableProperties', 'Adjust table properties', '', true, 'Allows adjusting the properties of a table.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('tableRowDelete', 'tableRowDelete', 'Delete Table Row', '', true, 'Deletes a row of a table.');
		sToolbar += tbtnCreate('tableRowInsertBefore', 'tableRowInsertBefore', 'Insert Table Row Before Current Row', '', true, 'Inserts a row before the current row.');
		sToolbar += tbtnCreate('tableRowInsertAfter', 'tableRowInsertAfter', 'Insert table row after current row', '', true, 'Inserts a row after the current row.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('tableColumnDelete', 'tableColumnDelete', 'Delete Table Column', '', true, 'Deletes a column of a table.');
		sToolbar += tbtnCreate('tableColumnInsertBefore', 'tableColumnInsertBefore', 'Insert Table Column Before Current Column', '', true, 'Inserts a new column before the current column.');
		sToolbar += tbtnCreate('tableColumnInsertAfter', 'tableColumnInsertAfter', 'Insert table column after current column', '', true, 'Inserts a new column after the current column.');
		sToolbar += tbtnSpaceCreate();
		sToolbar += tbtnCreate('clean', 'htmlclean', 'Clean the HTML of bad stuff pasted from other applications.', '', false, 'Cleans the HTML of bad or useless tags and attributes.');
		sToolbar += '</td></tr><tr><td>';
		sToolbar += styleCreate( divEdit.id );
		sToolbar += '&nbsp;&nbsp;';
		sToolbar += fontCreate( divEdit.id );
		sToolbar += fontsizeCreate( divEdit.id );

		sToolbar += '&nbsp;&nbsp;';
//		sToolbar += borderCreate( divEdit.id );
		
		sToolbar += '</td></tr></table>';

		divToolbar.innerHTML = sToolbar;
		divEdit.appendChild( divToolbar );

		// look up the useful dropdowns!
		divEdit.dropFS = g( divEdit.id + 'dropFontSize' );
		divEdit.dropFN = g( divEdit.id + 'dropFont' );
		divEdit.dropFB = g( divEdit.id + 'dropStyle' );
		divEdit.toolbarMain = g( 'toolbar' + ele_id );

		jlToolbarInit( 'toolbar' + ele_id );
		divToolbar.Clicked = tedittoolbar_Clicked;

		var iframeEdit = document.createElement('IFRAME');
		iframeEdit.id = 'iframe' + ele_id;
		iframeEdit.style.width = ele.offsetWidth + 'px';
		iframeEdit.style.height = (ele.offsetHeight - divToolbar.offsetHeight) + 'px';
		iframeEdit.style.backgroundColor = "#FFFFFF";	// Safari seems to need this
		iframeEdit.style.overflow = 'auto';

		divEdit.appendChild( iframeEdit );
		
		var cw = iframeEdit.contentWindow;
		var d = cw.document;
		d.open();
		var sHTML = '';

		if ( IE6 ) {
			// do nothing.  Note...  We should also do nothing for IE7!!!  (need quirks mode...)
		} else {
			sHTML += '<?xml version="1.0" encoding="UTF-8"?>\r\n<!DOCTYPE html>\r\n';
		}

		sHTML += '<html>\r\n<head>\r\n<link rel="stylesheet" type="text/css" href="/c/_style.css">\r\n<script>\r\nfunction ew() {};\r\n</script>\r\n</head>\r\n<body style="background-color: #FFFFFF;">';
		sHTML += vv;
		sHTML += '</body>\r\n</html>'
		d.write(sHTML);
		d.close();

		try{ d.designMode="on"; } catch(e){ cw.designMode="on"; }

		// I put this in because I noticed an IE 6 machine errored on this line.
		try{ d.havePasteEventInit = false; } catch(e) {}

		if ( d.addEventListener ) {
			d.addEventListener( 'keypress', jlTedit_iframe_OnKeyPress, false);
			d.addEventListener( 'keydown', jlTedit_iframe_OnKeyDown, false);
			d.addEventListener( 'keyup', jlTedit_iframe_OnKeyUp, false);

			d.addEventListener( 'click', jlTedit_iframe_OnClick, false);
			d.addEventListener( 'dblclick', jlTedit_iframe_OnDblClick, false);
			d.addEventListener( 'mouseup', jlTedit_iframe_OnMouseUp, false);
			iframeEdit.addEventListener( 'focus', jlTedit_iframe_OnFocus, false);
			iframeEdit.addEventListener( 'blur', jlTedit_iframe_OnBlur, false);
			// NOTE: The blur event doesn't seem to work in safari.  Need to add a listener to something else?

		} else {
			d.onkeypress = jlTedit_iframe_OnKeyPress;
			d.onkeydown = jlTedit_iframe_OnKeyDown;
			d.onkeyup = jlTedit_iframe_OnKeyUp;

			d.onclick = jlTedit_iframe_OnClick;
			d.ondblclick = jlTedit_iframe_OnDblClick;
			d.onmouseup = jlTedit_iframe_OnMouseUp;
			iframeEdit.onfocus = jlTedit_iframe_OnFocus;
			iframeEdit.onblur = jlTedit_iframe_OnBlur;
		}
	}

}

function jlTeditTransfer() {

	eles = document.body.getElementsByTagName('TEXTAREA');

	for (tt=0; tt < eles.length; tt++ ) {
		var ele = eles[tt];

		if ( ele.className == 'tedit' ) {
			var id = ele.id || ele.name;

			var ifrm = g('iframe' + id);

			if ( ifrm ) {
				var body = ifrm.contentWindow.document.body;

				if ( body.inHTMLMode != true ) {
					var vv = body.innerHTML;

					if ( ele.value != vv ) {
						ele.value = vv;
						frmChangedSet( true );
					}
				}
			}
		}
	}

}

function tbtnCreate( btn_id, icon_name, name, shortcut, is_disabled, title ) {

	var cn = 'btn';		// set the default class name
	var attr = '';		// set the default attributes

	if ( is_disabled ) {
		cn = 'dbtn';
		attr = ' d=1';
	}

	return '<img i="' + btn_id + '" src="fx/tbtn' + icon_name + '1.gif" alt="' + name + '" UNSELECTABLE=on class=' + cn + attr + ' title="' + title + '">'

}

function tbtnSpaceCreate() {

	return '<img class=sep src="fx/tbtnSpace.gif" alt="">';

}

function styleCreate( ele_id ) {

	var sOut = '';

	sOut += '<select tabIndex=-1 id="' + ele_id + 'dropStyle" r=1 onchange="jlTedit_drop_OnChange( this, \'FormatBlock\' );">';
	sOut += '<option value="">Style</option>';
	sOut += '<option value="<p>">Paragraph</option>';
	sOut += '<option value="<h1>">Heading 1</option>';
	sOut += '<option value="<h2>">Heading 2</option>';
	sOut += '<option value="<h3>">Heading 3</option>';
	sOut += '<option value="<h4>">Heading 4</option>';
	sOut += '<option value="<h5>">Heading 5</option>';
	sOut += '<option value="<pre>">Program Code/Fixed</option>';
	sOut += '</select>';

	return sOut;

}

function fontCreate( ele_id ) {

	var sOut = '';

	sOut += '<select tabIndex=-1 id="' + ele_id + 'dropFont" r=1 onchange="jlTedit_drop_OnChange( this, \'FontName\' );">';
	sOut += '<option value="">Font</option>';
	sOut += '<option value="Arial, Arial, Helvetica, sans-serif">Arial</option>';
	sOut += '<option value="Arial Black, Arial Black, Gadget, sans-serif">Arial Black</option>';
	sOut += '<option value="Comic Sans MS, Comic Sans MS, cursive">Comic Sans</option>';
	sOut += '<option value="Courier New, Courier New, Courier, monospace">Courier New</option>';
	sOut += '<option value="Georgia, Georgia, serif">Georgia</option>';
	sOut += '<option value="Impact, Impact, Charcoal, sans-serif">Impact</option>';
	sOut += '<option value="Lucida Console, Monaco, monospace">Lucida Console</option>';
	sOut += '<option value="Lucida Sans Unicode, Lucida Grande, sans-serif">Lucida Sans Unicode</option>';
	sOut += '<option value="Palatino Linotype, Book Antiqua, Palatino, serif">Palatino</option>';
	sOut += '<option value="Tahoma, Geneva, sans-serif">Tahoma</option>';
	sOut += '<option value="Times New Roman, Times, serif">Times New Roman</option>';
	sOut += '<option value="Trebuchet MS, Helvetica, sans-serif">Trebuchet</option>';
	sOut += '<option value="Verdana, Verdana, Geneva, sans-serif">Verdana</option>';

	sOut += '</select>';

	return sOut;

}

function fontsizeCreate( ele_id ) {

	// ???TODO: my preference is to have a set of point values (8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72

	var sOut = '';

	sOut += '<select tabIndex=-1 id="' + ele_id + 'dropFontSize" r=1 onchange="jlTedit_drop_OnChange( this, \'FontSize\' );">';
	sOut += '<option value="">Size</option>';
	sOut += '<option value="1">1</option>';
	sOut += '<option value="2">2</option>';
	sOut += '<option value="3">3</option>';
	sOut += '<option value="4">4</option>';
	sOut += '<option value="5">5</option>';
	sOut += '<option value="6">6</option>';
	sOut += '<option value="7">7</option>';
	sOut += '</select>';

	return sOut;

}

function borderCreate( ele_id ) {

	var sOut = '';

	sOut += 'Border: <select tabIndex=-1 id="' + ele_id + 'dropBorder" r=1>';

	sOut += '</select>';

	return sOut

}

function tblCreate( column_count, row_count ) {

	var qq = [];

	qq[qq.length] = '<table border="1"><tbody>';

	for (yy=0; yy < row_count; yy++) {
		qq[qq.length] = '<tr>';
		for (xx=0; xx < column_count; xx++) {
			qq[qq.length] = '<td>&nbsp;</td>';
		}
		qq[qq.length] = '</tr>';
	}

	qq[qq.length] = '</tbody></table>';

	return qq.join('');

}

function imgCreate( url, alt, width, height, lock_aspect_ratio ) {

	var sOut = '<img src="' + url + '" alt="' + alt + '"';

	if ( width != null && width != '' ) {
		sOut += ' width="' + width + '"';
	}

	if ( height != null && height != '' ) {
		sOut += ' height="' + height + '"';
	}

	if ( lock_aspect_ratio == true ) {
		sOut += ' lar=true';
	}

	sOut += '>';

	return sOut

}

function fileLinkCreate( url, alt, ext ) {

	url = url.replace(/\\/g, "/");

	var pp = url.lastIndexOf('/');
	var fn = ext + ' file';

	if ( pp != -1 ) {
		fn = url.substr(pp+1);
	}

	var sOut = '<a target=_blank href="' + url + '">' + fn + '(<img style="display: inline;" src="fx/icoft' + ext + '16.gif" alt="' + alt + '" width=16 height=16>)</a>';

	return sOut

}

function tedittoolbar_Clicked( btn_id ) {

	var divParent = getParentByClass(this, 'tedit');
	var btn = this.getBtnById( btn_id );
	var iframeEdit = divParent.childNodes[1];
	var cw = iframeEdit.contentWindow;
	var d = cw.document;
	var do_dlg = false;
	var url = null;
	var ww = 0;
	var hh = 0;

	// Figure out what has been selected
		if (window.getSelection) {
			sel = cw.getSelection();
		} else { 
			sel = d.selection;
			rng = sel.createRange();
		}

	switch ( btn_id ) {
		case 'ForeColor':
			do_dlg = true;
			url = 'dlgColor.asp';
			ww = 160;
			hh = 150;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'BackColor':
			do_dlg = true;
			url = 'dlgColor.asp';
			ww = 160;
			hh = 150;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'tableAdd':
			do_dlg = true;
			url = 'dlgTableCreate.asp';
			ww = 160;
			hh = 200;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'tableProperties':
			do_dlg = true;
			url = 'dlgTableProperties.asp';
			ww = 300;
			hh = 200;
//			while ( 
			divParent.lastDlgCommand = btn_id;
			break;

		case 'imageAdd':
			do_dlg = true;
			url = 'dlgImage.asp';
			ww = 550;
			hh = 400;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'imageExtAdd':
			do_dlg = true;
			url = 'dlgImageExternal.asp';
			ww = 550;
			hh = 450;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'fileLinkAdd':
			do_dlg = true;
			url = 'dlgUserFile.asp';
			ww = 550;
			hh = 400;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'tableColumnDelete':
		case 'tableColumnInsertBefore':
		case 'tableColumnInsertAfter':

		case 'tableRowDelete':
		case 'tableRowInsertBefore':
		case 'tableRowInsertAfter':
			var cell = null;
			if ( sel.anchorNode ) {
				cell = getParent(sel.anchorNode, "TD");
			} else if ( rng.parentElement ) {
				cell = getParent(rng.parentElement(), "TD");
			} else if ( rng.item ) {
				cell = getParent(rng.item(0), "TD");
			}
			if ( cell == null ) {
				alert('sorry...  Please put the caret in a single cell of the table.');
			} else {
				doTableAction( cell, btn_id );
			}
			return false;
			break;

		case 'linkAdd':
			do_dlg = true;
			url = 'dlgLink.asp';
			ww = 550;
			hh = 100;
			divParent.lastDlgCommand = btn_id;
			break;

		case 'clean':
			teditClean( d.body );
			return false;

		case 'html':
			var ta = g(divParent.ta_id);

			if ( d.body.inHTMLMode == true ) {
				var txt = ta.value;
				d.body.innerHTML = txt;
				iframeEdit.style.display = 'block';
				d.body.inHTMLMode = false;
			} else {
				var txt = d.body.innerHTML;
				var toolbar = g('toolbar' + divParent.ta_id);
				ta.value = txt;
				ta.style.position = 'absolute';
				ta.style.top = (toolbar.offsetHeight + 2) + 'px';
				ta.style.left = iframeEdit.style.left;
				ta.style.width = iframeEdit.style.width;
				ta.style.height = iframeEdit.style.height;
				iframeEdit.style.display = 'none';
				d.body.inHTMLMode = true;
				try { ta.focus(); } catch(e) {};
			}
			return false;
	}

	if ( do_dlg == true ) {
		dlgShow( btn, btn, ww, hh, url, divParent.dlgResult );
		return false;
	}

	// Now, execute the command
	if (!document.all) d.execCommand('useCSS',false, true);
	d.execCommand(btn_id, false, '');
	cw.focus();

}

function teditClean( body ) {

	var txt = body.innerHTML;

	if ( txt != null ) {
		txt = txt.replace(/[’‘]/g, "'");
		txt = txt.replace(/[”“]/g, '"');
		txt = txt.replace(/…/g, '...');
		txt = txt.replace(/–/g, '-');
		txt = txt.replace(/½/g, '1/2');
		txt = txt.replace(/¼/g, '1/4');
		txt = txt.replace(/¾/g, '3/4');
		txt = txt.replace(/<\/?(\?xml|xml|[ovwxp]:|meta|st\d:)[^>]*>/gi, "");
		txt = txt.replace(/<font[^>]*><\/font[^>]*>/gi, "");
		txt = txt.replace(/<font[^>]*>&nbsp;<\/font[^>]*>/gi, "&nbsp;");

		body.innerHTML = txt;

		eleClean( body );

	}

}

function eleClean( ele ) {

	// First, clean any children of "Word" nonsense

	var ll = ele.childNodes.length;

	for ( var tt = 0; tt < ele.childNodes.length; tt++ ) {

		if ( ele.childNodes.nodeType != 3 ) {		// ignore text nodes
			eleClean( ele.childNodes[tt] );
		}

	}

	// Now, clean this node
	if ( ele.attributes != null ) {
		for ( var tt=ele.attributes.length-1; tt >= 0; tt-- ) {
			attr = ele.attributes[tt];

			if ( attr.name.substring(0, 3) == 'Mso' ) {
				ele.removeAttribute(tt);
			} else if ( attr.name.substring(0, 5) == 'Apple' ) {
				ele.removeAttribute(tt);
			} else if ( attr.name == 'style' ) {
				if ( attr.value != null ) {
					var ss = attr.value.split(';');
					for ( var qq = 0; qq < ss.length; qq++ ) {
						if ( strTrim(ss[qq]).substring(0,4) == 'mso-' ) {
							ss[qq] = null;
						}
					}
					var out = ss.join(';');

					if ( out == null || out.length == 0 ) {
						ele.removeAttribute(attr.name);
					} else if ( attr.value != out ) {
						attr.value = out;
					}

				}

			} else if ( attr.name == 'class' ) {
				if ( attr.value != null ) {
					if ( attr.value.substring(0,3) == 'Mso' ) {
						ele.removeAttribute(attr.name);
					}
				}
			}
		}
	}

}

function doTableAction( cell, action_id ) {

	var cellIndex = cell.cellIndex;
	var tr = getParent(cell, "TR");
	var rowIndex = tr.rowIndex;
	var tbl = getParent(tr, "TABLE");

	switch ( action_id ) {
		case 'tableColumnDelete':
			if ( tr.cells.length == 1 ) {
				tbl.parentNode.removeChild( tbl );
			} else {
				for ( tt = 0; tt < tbl.rows.length; tt++ ) {
					tbl.rows[tt].deleteCell( cellIndex );
				}
			}
			break;

		case 'tableRowDelete':
			if ( tbl.rows.length == 1 ) {
				tbl.parentNode.removeChild( tbl );
			} else {
				tbl.deleteRow( rowIndex );
			}
			break;

		case 'tableColumnInsertAfter':
			cellIndex++;
		case 'tableColumnInsertBefore':
			for ( tt = 0; tt < tbl.rows.length; tt++ ) {
				var newTD = tbl.rows[tt].insertCell( cellIndex );
				newTD.innerHTML = "&nbsp;";
			}
			break;

		case 'tableRowInsertAfter':
			rowIndex++;
		case 'tableRowInsertBefore':
			var le = tr.cells.length;
			var newTR = tbl.insertRow( rowIndex );
			for (tt=0; tt < le; tt++) {
				var newTD = newTR.insertCell( tt );
				newTD.innerHTML = "&nbsp;";
			}
			break;
	}

	return false;
}

function jlTedit_drop_OnChange( dropElement, command_name ) {

	var divParent = getParentByClass(dropElement, 'tedit');
	var iframeEdit = divParent.childNodes[1];
	var cw = iframeEdit.contentWindow;
	var d = cw.document;

	if ( dropElement.value != null && dropElement.value != '' ) {
		if (!document.all) d.execCommand('useCSS', false, true);
		d.execCommand(command_name, false, dropElement.value);
	}
	dropElement.value = '';
	cw.focus();

}

function jlTedit_dlgResult( new_value ) {

	var divParent = getParentByClass(eleDlgFocus, 'tedit');
	var iframeEdit = divParent.childNodes[1];
	var cw = iframeEdit.contentWindow;
	var d = cw.document;

	if (rng) {
		rng.select();
	}

	if (!document.all) d.execCommand('useCSS', false, true);

	switch ( divParent.lastDlgCommand ) {
		case 'tableAdd':
			if ( new_value != null ) {
				var sHTML = tblCreate( new_value.x, new_value.y);
				try {
					d.execCommand("inserthtml", false, sHTML);
				} catch (e) {
					if (document.selection) { 
						rng.pasteHTML( sHTML + rng.htmlText);
					}
				}
			}
			break;

		case 'imageEdit':
			if ( new_value != null ) {
				if ( divParent.lastElement != null ) {
					var el = divParent.lastElement;
					el.src = new_value.url;
					el.alt = new_value.alt;
					el.width = new_value.width;
					el.height = new_value.height;
				}
			}
			break;

		case 'imageAdd':
			if ( new_value != null ) {
				var sHTML = imgCreate( new_value.url, new_value.alt, new_value.width, new_value.height, new_value.lock_aspect_ratio );
				try {
					d.execCommand("inserthtml", false, sHTML);
				} catch (e) {
					if (document.selection) { 
						rng.pasteHTML( sHTML + rng.htmlText);
					}
				}

			}
			break;

		case 'imageExtAdd':
			if ( new_value != null ) {
				var sHTML = imgCreate( new_value.url, new_value.alt, new_value.width, new_value.height, new_value.lock_aspect_ratio );
				try {
					d.execCommand("inserthtml", false, sHTML);
				} catch (e) {
					if (document.selection) { 
						rng.pasteHTML( sHTML + rng.htmlText);
					}
				}

			}
			break;

		case 'fileLinkAdd':
			if ( new_value != null ) {
				var sHTML = fileLinkCreate( new_value.url, new_value.alt, new_value.ext );
				try {
					d.execCommand("inserthtml", false, sHTML);
				} catch (e) {
					if (document.selection) { 
						rng.pasteHTML( sHTML + rng.htmlText);
					}
				}

			}
			break;

		case 'linkEdit':
			if ( new_value != null ) {
				if ( divParent.lastElement != null ) {
					var el = divParent.lastElement;
					el.href = new_value.url;
					if ( new_value.new_window ) {
						el.target = "_blank";
					} else {
						try { el.removeAttribute('target'); } catch (e) {}
					}
				}
			}
			break;

		case 'linkAdd':
			if ( new_value != null ) {
				var sHTML = '<A HREF="' + new_value.url + '"';
				if ( new_value.new_window ) {
					sHTML += ' target="_blank"';
				}
				sHTML += '>';
				try {
					if ( sel.type && sel.type != "Text" && sel.type != "Range" ) { 
						sel=""; 
					}
					d.execCommand("inserthtml", false, sHTML + sel);
				} catch (e) {
					if (document.selection) { 
						rng.pasteHTML( sHTML + rng.htmlText);
					}
				}
			}
			break;	

		default:
			d.execCommand(divParent.lastDlgCommand, false, new_value);
	}

	cw.focus();
	divParent.doToolbarRefresh( null );

}

//	Events for the iFrame!
//	Note...  For IE, this gets tricky because you need to get the event information from the iFrame content window!

function jlTedit_iframe_OnKeyPress( evt ) {

//	var pw = (this.defaultView? this.defaultView: this.parentWindow);

//	evt = evt || pw.event;

	return true;
}

function jlTedit_iframe_OnKeyDown( evt ) {

	var pw = (this.defaultView? this.defaultView: this.parentWindow);

	evt = evt || pw.event;

	return jlShortcut_OnKeyDown( evt );

}

function jlTedit_iframe_OnKeyUp( evt ) {

	var pw = (this.defaultView? this.defaultView: this.parentWindow);
	var iframeEdit = pw.frameElement;
	var divParent = getParentByClass( iframeEdit, 'tedit');

	evt = evt || pw.event;

	divParent.doToolbarRefresh( evt );

	return true;

}

function jlTedit_iframe_OnClick( evt ) {

	var pw = (this.defaultView? this.defaultView: this.parentWindow);
	var iframeEdit = pw.frameElement;
	var divParent = getParentByClass( iframeEdit, 'tedit');

	evt = evt || pw.event;

	if ( is_dlg_active == true ) {
		dlgHide( false );
		return false;
	}

}

function jlTedit_doToolbarRefresh( evt ) {

	var iframeEdit = this.childNodes[1];
	var divToolbar = this.childNodes[0];
	var cw = iframeEdit.contentWindow;
	var d = cw.document;

	// figure out the current selection
	if (window.getSelection) {
		sel = cw.getSelection();
	} else { 
		sel = d.selection;
		rng = sel.createRange();
	}

	var fs = d.queryCommandValue("FontSize");
	var fn = d.queryCommandValue("FontName");
	var bold = d.queryCommandValue("Bold");
	var underline = d.queryCommandValue("Underline");
	var italic = d.queryCommandValue("Italic");
	var formatBlock = d.queryCommandValue("FormatBlock");
	var jl = d.queryCommandValue("JustifyLeft");
	var jr = d.queryCommandValue("JustifyRight");
	var jc = d.queryCommandValue("JustifyCenter");
	var jf = d.queryCommandValue("JustifyFull");

	// Update the font size
	if ( this.dropFS ) {
		if ( fs ) {
			this.dropFS.value = fs;
		} else {
			this.dropFS.value = "";
		}
	}

	if ( fn ) {
		fn = fn.toLowerCase();
		if ( this.dropFN ) {
			for ( oo = 1; oo < this.dropFN.options.length; oo++ ) {
				var opt_value = this.dropFN.options[oo].value.toLowerCase() + ',';
				if ( opt_value.indexOf(fn) >= 0 ) {
					this.dropFN.value = this.dropFN.options[oo].value;
					break;
				}
			}
		}
	}

	if ( this.dropFB ) {
		switch ( formatBlock ) {
			case 'Normal': this.dropFB.value = '<p>'; break;
			case 'Heading 1': this.dropFB.value = '<h1>'; break;
			case 'Heading 2': this.dropFB.value = '<h2>'; break;
			case 'Heading 3': this.dropFB.value = '<h3>'; break;
			case 'Heading 4': this.dropFB.value = '<h4>'; break;
			case 'Heading 5': this.dropFB.value = '<h5>'; break;
			case 'Formatted': this.dropFB.value = '<pre>'; break;
			default: 
				this.dropFB.value = "";
		}
	}

	this.toolbarMain.setBtnPushed( 'Bold', bold);
	this.toolbarMain.setBtnPushed( 'Underline', underline);
	this.toolbarMain.setBtnPushed( 'Italic', italic);
	this.toolbarMain.setBtnPushed( 'JustifyLeft', jl);
	this.toolbarMain.setBtnPushed( 'JustifyRight', jr);
	this.toolbarMain.setBtnPushed( 'JustifyCenter', jc);
	this.toolbarMain.setBtnPushed( 'JustifyFull', jf);

//window.status = 'this.id=' + this.id + ';fs=' + fs + '; fn=' + fn + '; bold=' + bold + '; underline=' + underline + '; italic=' + italic + '; FormatBlock=' + formatBlock;

	// get the current selected item and see if there is a cell involved!
	var el;
	if ( sel.anchorNode ) {
		el = sel.anchorNode;
	} else if ( rng.parentElement ) {
		el = rng.parentElement();
	} else if ( rng.item ) {
		el = rng.item(0);
	}

	var cell = getParent( el, "TD" );

	//	If we are in a TD, show table formatting options!
	var inTD = false;
	if ( cell ) {
		inTD = true;
	}

	if ( divToolbar.lastInTD != inTD ) {
//		divToolbar.setBtnIsDisabled( 'tableProperties', !inTD );

		divToolbar.setBtnIsDisabled( 'tableColumnDelete', !inTD );
		divToolbar.setBtnIsDisabled( 'tableColumnInsertBefore', !inTD );
		divToolbar.setBtnIsDisabled( 'tableColumnInsertAfter', !inTD );

		divToolbar.setBtnIsDisabled( 'tableRowDelete', !inTD );
		divToolbar.setBtnIsDisabled( 'tableRowInsertBefore', !inTD );
		divToolbar.setBtnIsDisabled( 'tableRowInsertAfter', !inTD );

		divToolbar.lastInTD = inTD;
	}

}

function jlTedit_iframe_OnDblClick( evt ) {

	var pw = (this.defaultView? this.defaultView: this.parentWindow);
	var iframeEdit = pw.frameElement;
	var divParent = getParentByClass( iframeEdit, 'tedit');
	var divToolbar = divParent.childNodes[0];

	evt = evt || pw.event;

	var el = evtSrc( evt );

	if ( el.tagName == 'IMG' ) {
		url = 'dlgImageExternal.asp?url=' + escape(el.src) + '&alt=' + escape(el.alt) + '&width=' + escape(el.width) + '&height=' + escape(el.height);
		divParent.lastDlgCommand = 'imageEdit';
		divParent.lastElement = el;
		dlgShow( divToolbar, divToolbar, 550, 450, url, divParent.dlgResult );
		return false;

	} else if ( el.tagName == 'A' ) {
		url = 'dlgLink.asp?url=' + escape(el.href) + '&target=' + escape(el.target);
		divParent.lastDlgCommand = 'linkEdit';
		divParent.lastElement = el;
		dlgShow( divToolbar, divToolbar, 550, 100, url, divParent.dlgResult );
		return false;

	} else if ( el.tagName == 'TABLE' ) {
		// ???TODO: alert('show the table dialog now!');
	}

}

function jlTedit_iframe_OnMouseUp( evt ) {

	var pw = (this.defaultView? this.defaultView: this.parentWindow);
	var iframeEdit = pw.frameElement;
	var divParent = getParentByClass( iframeEdit, 'tedit');

	evt = evt || pw.event;

	divParent.doToolbarRefresh( evt );

}

function jlTedit_iframe_OnFocus( evt ) {

	var d = this.contentWindow.document;

	if ( d.havePasteEventInit == false ) {
		d.havePasteEventInit = true;
		var bb = d.body;

		bb.onPasteSet = true;
		bb.iframe_id = this.id		// save the id--in case we need it later

		if ( bb.addEventListener ) {
			bb.addEventListener( 'paste', jlTedit_body_OnPaste, false);
		} else {
			bb.onpaste = jlTedit_body_OnPaste;
		}
	}

	return true;

}

function jlTedit_iframe_OnBlur( evt ) {

	// NOTE:	this is the actual iframe object!

	var ele_id = this.id.substr(6);
	var ta = g(ele_id);

	var vv = this.contentWindow.document.body.innerHTML;

	if ( ta.value != vv ) {
		ta.value = vv;
		frmChangedSet( true );
	}

	return true;
}

function jlTedit_body_OnPaste( evt ) {

	// Handle a paste event.  We need to clean up the HTML pasted in case it came from Microsoft Word

	// I must apologize...  This is hideous--but I don't have much choice.

	var body = this;

	// Tell the system to do a clean after the paste has finished
	setTimeout("teditClean( g('" + body.iframe_id + "').contentWindow.document.body );", 10);

	return true;
}

function jlTextareaInit() {

	eles = document.body.getElementsByTagName('TEXTAREA');

	for (tt=0; tt < eles.length; tt++ ) {
		var ele = eles[tt];

		ele.isValid = jlTextarea_isValid;
		ele.allow_tab = boolClean( getAttr(ele, 'allow_tab') );

		// if they want a tedit, check if we should hook into it anyway.
		if ( ele.className == 'tedit' ) {
			var vv = ele.value;		// get the value
			if ( vv != null ) {
				// if the value is missing the NO flag, skip this textarea because tedit needs to handle it
				if ( vv.indexOf('&lt;' + '!--no--' + '&gt;') == -1 ) {
					continue;
				}
			}
		}

		if ( ele.addEventListener ) {

			ele.addEventListener( 'focus', jlTextarea_OnFocus, false);
			ele.addEventListener( 'blur', jlTextarea_OnBlur, false);
			ele.addEventListener( 'keypress', jlTextarea_OnKeyPress, false);
			ele.addEventListener( 'keydown', jlTextarea_OnKeyDown, false);
			ele.addEventListener( 'paste', jlTextarea_OnPaste, false);
			ele.addEventListener( 'change', jlTextarea_OnChange, false);

		} else {

			ele.onfocus = jlTextarea_OnFocus;
			ele.onblur = jlTextarea_OnBlur;
			ele.onkeypress = jlTextarea_OnKeyPress;
			ele.onkeydown = jlTextarea_OnKeyDown;
			ele.onpaste = jlTextarea_OnPaste;
			ele.onchange = jlTextarea_OnChange;

		}

	}

}


function jlTextarea_OnKeyPress( evt ) {

	// check if this is a keypress that has no effect!

	// ???TODO: this is ugly.  I put this in for the search tool because someone could just jump to it
	//			and press the Enter key and have it try to do a search.
	//			the better solution is to have this event.keyCode stuff go away and change the
	//			search tool to make sure they entered something into the criteria if the # instances are over
	//			the "magic" number.

	evt = evt || window.event;

	if ( evt ) {
		if ( evt.keyCode == 13 ) {
			return;
		}
	}

	frmChangedSet( true );

}

function jlTextarea_OnKeyDown( evt ) {

	evt = evt || window.event;

	if ( this.allow_tab == true ) {

		if ( evt.keyCode == 9 ) {
			if ( evt.shiftKey ) {
				this.selection = window.document.selection.createRange();
				setTimeout("textareaHandleTab('" + this.name + "', true);", 0);
			} else {
				this.selection = window.document.selection.createRange();
				setTimeout("textareaHandleTab('" + this.name + "', false);", 0);
			}
			return evtKill( evt );
		}

	}

}


function jlTextarea_OnPaste( evt ) {

	evt = evt || window.event;

	frmChangedSet( true );

}

function jlTextarea_OnChange( evt ) {

	evt = evt || window.event;

	frmChangedSet( true );

}

function jlTextarea_OnFocus( evt ) {

	//	Selects all text in this field and shows the TITLE of the field in the status bar

	evt = evt || window.event;

	window.status = this.title;
	// ???TODO: add code to select all text!
	// this.selectAll(); or something?

}

function jlTextarea_OnBlur( evt ) {

	evt = evt || window.event;

	return this.isValid();

}

function jlTextarea_isValid() {

	var scriptValid = getAttr(this, 'onIsValid');
	var result = false;

	if ( scriptValid != null && scriptValid != '' ) {
		result = !eval(scriptValid);
	}

	return result;

}

function textareaHandleTab(id, has_shift_pressed) {

	// ???TODO: need to see if this is cross-browser compliant.
	// I'm sure that it isn't!!!

	var sel;

	if ( has_shift_pressed == false ) {			// insert tab before selected text
		if ( document.all[id].selection.text.length != 0 ) {		// if they have some text selected
			sel = document.all[id].selection.text;
			sel = String.fromCharCode(9) + sel;

			for ( var x = 0; x < sel.length; x++ ) {
				if (( sel.charCodeAt(x) == 10 ) && ( sel.charCodeAt(x-1)==13 ) ) {
					sel = sel.substring(0, x+1) + String.fromCharCode(9) + sel.substring(x+1, sel.length)
				}
			}
			if ( IE6 || window.opera ) {
				document.all[id].selection.text = sel;
			} else {
				document.all[id].selection.text = sel + String.fromCharCode(13) + String.fromCharCode(10);
			}

		} else {				// if they don't have any text selected, just add a tab
			document.all[id].selection.text = String.fromCharCode(9);
		}

	} else {			// if has a shift, remove tabs from selected text

		if ( document.all[id].selection.text.length != 0 ) {		// if nothing selected
			sel = document.all[id].selection.text;

			if ( sel.charCodeAt(0) == 9 ) {
				sel = sel.substring(1, sel.length);
			}

			for ( var x=0; x < sel.length; x++) {
				if ( (sel.charCodeAt(x) == 9) && (sel.charCodeAt(x-1)==10) && (sel.charCodeAt(x-2)==13) ) {
					sel = sel.substring(0, x) + sel.substring(x+1, sel.length);
				}
			}

			if ( IE6 || window.opera ) {
				document.all[id].selection.text = sel;
			} else {
				document.all[id].selection.text = sel + String.fromCharCode(13) + String.fromCharCode(10);
			}
		} else {
			// ???TODO: nothing selected... so what should we do?
			// ???TODO: need to check the context and see if we should erase the tab at the current location or something...
		}
	}

	document.all[id].focus();

}
function jlToolbarInit( toolbar_id ) {

	var ele = g( toolbar_id );

	if ( ele != null ) {

		ele.getBtnIsDisabled = jlToolbar_getBtnIsDisabled;
		ele.setBtnIsDisabled = jlToolbar_setBtnIsDisabled;
		ele.setBtnPushed = jlToolbar_setBtnPushed;

		ele.getBtnById = jlToolbar_getBtnById;
		ele.Shortcutted = jlToolbar_Shortcutted;

		var tbarRow = ele.childNodes[0].rows[0];

		for ( jj=0; jj < tbarRow.cells.length; jj++ ) {
			var td = tbarRow.cells[jj];
			for ( ii=0; ii < td.childNodes.length; ii++ ) {
				var btn = td.childNodes[ii];

				if ( btn.tagName == 'IMG' && getAttr( btn, 'i') != null ) {

					if ( btn.addEventListener ) {
						btn.addEventListener('mouseover', jlToolbarBtn_OnMouseOver, false);
						btn.addEventListener('mouseout', jlToolbarBtn_OnMouseOut, false);
						btn.addEventListener('mouseup', jlToolbarBtn_OnMouseUp, false);
						btn.addEventListener('mousedown', jlToolbarBtn_OnMouseDown, false);
						btn.addEventListener('click', jlToolbarBtn_OnClick, false);
					} else {
						btn.onmouseover = jlToolbarBtn_OnMouseOver;
						btn.onmouseout = jlToolbarBtn_OnMouseOut;
						btn.onmouseup = jlToolbarBtn_OnMouseUp;
						btn.onmousedown = jlToolbarBtn_OnMouseDown;
						btn.onclick = jlToolbarBtn_OnClick;
					}

					var sc = getAttr( btn, 'shortcut' );
					if ( sc != null ) {
						jlShortcutEvalAdd( sc, "g('" + ele.id + "').Shortcutted('" + getAttr( btn, 'i') + "');", btn.title );
					}
				}
			}
		}
	}

}

function jlToolbar_getBtnIsDisabled( btn_id ) {

	var btn = this.getBtnById( btn_id );

	if ( btn ) {
		if ( getAttr(btn, 'd') != '1' ) {
			return false;
		} else {
			return true;
		}
	}

}

function jlToolbar_setBtnIsDisabled( btn_id, is_disabled ) {

	var btn = this.getBtnById( btn_id );

	if ( btn ) {
		if ( is_disabled == true ) {
			setAttr( btn, 'd', '1');
			var cn = btn.className;

			if ( cn == null ) {
				cn = 'dbtn';
			} else if ( cn.charAt(0) != 'd' ) {
				cn = 'd' + cn;
			}
			btn.className = cn;
		} else {
			setAttr( btn, 'd', '0');
			var cn = btn.className;

			if ( cn == null ) {
				cn = 'btn';
			} else if ( cn.charAt(0) == 'd' ) {
				cn = cn.substr(1);
			}
			btn.className = cn;
		}
	}

}

function jlToolbar_setBtnPushed( btn_id, is_pushed ) {

	var btn = this.getBtnById( btn_id );

	if ( btn ) {
		setAttr( btn, 'p', is_pushed);
		if ( is_pushed == true ) {
			btn.style.backgroundColor = '#e0e0e0';
		} else {
			btn.style.backgroundColor = this.style.backgroundColor;
		}
	}

}

function jlToolbar_getBtnById( btn_id ) {

	var tbarRow = this.childNodes[0].rows[0];

	for ( jj=0; jj < tbarRow.cells.length; jj++ ) {
		var td = tbarRow.cells[jj];
		for ( ii=0; ii < td.childNodes.length; ii++ ) {
			var tbtn = td.childNodes[ii];

			if ( tbtn.tagName == 'IMG' && getAttr( tbtn, 'i') == btn_id ) {
				return tbtn;
			}
		}
	}

	return null;
}

function jlToolbar_Shortcutted( btn_id ) {

	var btn = this.getBtnById( btn_id );

	if ( getAttr(btn, 'd') != '1' ) {
		if ( this.Clicked ) {
			this.Clicked( getAttr(btn, 'i') );
		} else {
			// Now, call the Clicked function for this menu
			eval( this.id + "_Clicked('" + getAttr(btn, 'i') + "');");
		}
	} else {
		alert("Sorry...  This toolbar button is disabled!");
	}

}

function jlToolbarBtn_OnMouseOver() {

	if ( getAttr(this, 'd') != '1' ) {
		this.style.border = "1px solid black";
		this.style.borderTop = "1px solid white";
		this.style.borderLeft = "1px solid white";
	}

	window.status = this.title;

}

function jlToolbarBtn_OnMouseOut() {

	this.style.border = "1px solid threedface";

//	I decided to leave the background color alone... 10/26/2010
//	if ( getAttr(this, 'p') == true ) {
//		this.style.backgroundColor = "#f0f000";	//"#e0e0e0";
//	} else {
//		this.style.backgroundColor = "threedface";
//	}

	window.status = "";
}

function jlToolbarBtn_OnMouseUp() {

	if ( getAttr(this, 'd') != '1' ) {

		this.style.border = "1px solid black";
		this.style.borderTop = "1px solid white";
		this.style.borderLeft = "1px solid white";
		this.style.backgroundColor = "threedface";

	}

}

function jlToolbarBtn_OnMouseDown() {

	if ( getAttr(this, 'd') != '1' ) {
		this.style.border="0px";
		this.style.borderTop="2px solid black";
		this.style.borderLeft="2px solid black";
		this.style.backgroundColor="#e1e1e1";
	}

}

function jlToolbarBtn_OnClick() {

	if ( getAttr(this, 'd') != '1' ) {
		// First, get the parent jlToolbarBtn
		var parent = getParent( this, "DIV" );

		if ( parent == null )
			return;

		// Now, call the Clicked function for this menu
		if ( parent.Clicked ) {
			parent.Clicked( getAttr(this, 'i') );
		} else {
			eval( parent.id + "_Clicked('" + getAttr(this, 'i') + "');");
		}
	}

}
function valErr( ctl, attr_name, error_message ) {

	// changes the style of the control to "validation_error" and changes its title to be the error message

	if ( getClass(ctl) != "validation_error" ) {
		setClass(ctl, "validation_error");
		ctl.old_title = ctl.title;
		ctl.title = error_message + " (" + ctl.old_title + ")";
	} else {
		ctl.title = error_message + " (" + ctl.old_title + ")";
	}

	status = attr_name + ": " + error_message;
	// beep();

	// Now, add to the page error list
		if ( page_errors != '' ) {
				page_errors += '\r\n';
		}
		page_errors += attr_name + ' - ' + error_message;

	// Check if we should focus on this item
		if ( page_error_focus == null ) {		// if there wasn't a previous error, set the focus to this control!
			page_error_focus = ctl;
		}

	// Mark that this page has errors!
		page_has_errors = true;

}

function valClr( ctl ) {

	// changes the style to "editable" and clears the title...  (or restores original title!!!)

	if ( getClass(ctl) == "validation_error" ) {
		setClass(ctl, "editable");
		ctl.title = ctl.old_title;
	}

	status = "";
}

function frmIsValid( frmToCheck ) {

	// Tests to see if the form has completely valid informtion

	// RET:	true - if the data is valid

	var has_error = false;
	page_has_errors = false;
	page_errors = '';
	page_error_focus = null;

	for (var ctrlIndex=0; ctrlIndex < frmToCheck.elements.length; ctrlIndex++) {

		var scriptValid = getAttr(frmToCheck.elements[ctrlIndex], 'onIsValid');

		if ( scriptValid != null && scriptValid != '' && scriptValid != undefined ) {

			if ( frmToCheck.elements[ctrlIndex].disabled == true ) {
				// I know that I will regret this... 2009/03/25
				valClr( frmToCheck.elements[ctrlIndex] );
			} else {
				scriptValid = scriptValid.replace(/this,/g, 'frmToCheck.elements[' + ctrlIndex + '], ');

				try {
					has_error = eval(scriptValid);			// the eval() will return TRUE if there are errors
				} catch (e) {
					valErr( frmToCheck.elements[ctrlIndex], frmToCheck.elements[ctrlIndex].name, "Validation function failed!" );
					has_error = true;
				}
			}
		}

	}

	// Call the onIsValid() function for the FORM element, if any
		var scriptValid = getAttr(frmToCheck, 'onIsValid');

		if ( scriptValid != null && scriptValid != '' && scriptValid != undefined ) {

			scriptValid = scriptValid.replace(/this,/g, 'frmToCheck, ');

			try {
				has_error = eval(scriptValid);			// the eval() will return TRUE if there are errors
			} catch (e) {
				has_error = true;
			}

		}

	// Show an alert if there were any validation errors!
		if ( page_has_errors ) {
			alert('This page has one or more fields with validation errors:\r\n\r\n' + page_errors + '\r\n\r\nPlease correct them and try again.  For more information click the Help toolbar button.');
		}

	// set the focus on the first error, if needed
		if ( page_error_focus != null ) {
			try {
				// ???TODO: check if we need to change tabs to focus!!!
				page_error_focus.focus();
			} catch (e) {
				// ???todo
			}
		}

	return !page_has_errors;
}

function tinyintCheck( attr_name, ctl, is_nullable ) {

	// Checks the given field for errors

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	re = /^\d+$/;

	str = str.toString();

	if (!str.match(re)) {
		valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
		return true;
	}

	if ( str.length > 3 ) {
		valErr(ctl, attr_name, "The value that you have entered is too long.  (Is " + str.length + " and needs to be 3 or less.)");
		return true;
	}

	// Check the value
		vv = parseInt( str );

		if ( vv > 255 ) {
			valErr(ctl, attr_name, 'This value is too large.  You can only enter a number from 0 to 255.');
			return true;
		} else if ( vv < 0 ) {
			valErr(ctl, attr_name, 'This value is too small.  You can only enter a number from 0 to 255.');
			return true;
		}

	valClr( ctl );
	return false;

}

function smallintCheck( attr_name, ctl, is_nullable, allow_negative ) {

	// Checks the given field for errors

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	// Figure out what pattern to try to match
	if ( allow_negative == true ) {
		re = /^[-]?\d+$/;
	} else {
		re = /^\d+$/;
	}

	str = str.toString();
	str = str.replace(/,/g, "");

	if (!str.match(re)) {
		valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
		return true;
	}

	// Check the value
		vv = parseInt( str );

		if ( vv < 0 ) {
			if ( allow_negative == false ) {
				valErr(ctl, attr_name, 'This value can not be negative.');
				return true;
			} else if ( vv < -32768 ) {
				valErr(ctl, attr_name, 'This value is too small.  You can only enter a number from 0 to 255.');
				return true;
			}
		} else if ( vv > 32767 ) {
			valErr(ctl, attr_name, 'This value is too large.  You can only enter a number from -32,768 to 32,767.');
			return true;
		}

	valClr( ctl );
	return false;

}

function intCheck( attr_name, ctl, is_nullable, allow_negative ) {

	// Checks the given field for errors

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	// Figure out what pattern to try to match
	if ( allow_negative == true ) {
		re = /^[-]?\d*$/;
	} else {
		re = /^\d*$/;
	}

	str = str.toString();
	str = str.replace(/,/g, "");

	if (!str.match(re)) {
		valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
		return true;
	}

	// Check the value
		vv = parseInt( str );

		if ( vv > 2147483647 ) {
			valErr(ctl, attr_name, 'This value is too large.  You can only enter a number from -32,768 to 32,767.');
			return true;
		} else if ( vv < -2147483648 ) {
			valErr(ctl, attr_name, 'This value is too small.  You can only enter a number from 0 to 255.');
			return true;
		}

	valClr( ctl );
	return false;

}

function bigintCheck( attr_name, ctl, is_nullable, allow_negative ) {

	// Checks the given field for errors

	// ???TODO: add code to remove the comma!!!

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	// Figure out what pattern to try to match
	if ( allow_negative == true ) {
		re = /^[-]?\d*$/;
	} else {
		re = /^\d*$/;
	}

	str = str.toString();
	str = str.replace(/,/g, "");

	if (!str.match(re)) {
		valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
		return true;
	}

	// Check the value
		vv = parseInt( str );

		if ( vv > 9223372036854775807.0 ) {
			valErr(ctl, attr_name, 'This value is too large.');
			return true;
		} else if ( vv < -9223372036854775808.0 ) {
			valErr(ctl, attr_name, 'This value is too small.');
			return true;
		}

	valClr( ctl );
	return false;

}

function realCheck( attr_name, ctl, is_nullable, allow_negative ) {

	// Checks the given field for errors

	// ???TODO: add code to remove the comma!!!

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	// Check if the string matches the pattern for a real number
		re = /^[-]?\d*\.?\d*$/;

		str = str.toString();
		str = str.replace(/,/g, "");
		str = str.replace(/[$]/g, "");
	
		if (!str.match(re)) {
			valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
			return true;
		}

	// Check the value
		vv = parseFloat( str );

		if ( vv < 0 ) {
			if ( allow_negative == false ) {
				valErr(ctl, attr_name, 'This value must be a positive number.');
				return true;
			} else if ( vv < -3.40E+38 ) {
				valErr(ctl, attr_name, 'This value is too small.  You can only enter a number from 0 to 255.');
				return true;
			}
		} else if ( vv > 3.40E+38 ) {
			valErr(ctl, attr_name, 'This value is too large.  You can only enter a number from -32,768 to 32,767.');
			return true;
		}

	// if we made it here, it looks good!
		valClr( ctl );
		return false;

}

function floatCheck( attr_name, ctl, is_nullable, allow_negative ) {

	// Checks the given field for errors

	// ???TODO: add code to remove the comma!!!

	var str = ctl.value;
	var re;
	var vv;

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;
	}

	// Check if the string matches the pattern for a float number
		re = /^[-]?\d*\.?\d*$/;

		str = str.toString().replace(/,/,'');
		str = str.replace(/,/g, "");
		str = str.replace(/[$]/g, "");
	
		if (!str.match(re)) {
			valErr( ctl, attr_name, "This is not a correct number.");		// ???TODO: come up with a better message!
			return true;
		}

	// Check the value
		vv = parseFloat( str );

		if ( isNaN(vv) ) {
			valErr(ctl, attr_name, 'This value must be a number.');
			return true;
		} else if ( vv < 0 ) {
			if ( allow_negative == false ) {
				valErr(ctl, attr_name, 'This value must be a positive number.');
				return true;
			} else if ( vv < -1.79E+308 ) {
				valErr(ctl, attr_name, 'This value is too small.  You can only enter a number from 0 to 255.');
				return true;
			}
		} else if ( vv > 1.79E+308 ) {
			valErr(ctl, attr_name, 'This value is too large.  You can only enter a number from -32,768 to 32,767.');
			return true;
		}

	// if we made it here, it looks good!
		valClr( ctl );
		return false;

}

function timeCheck( attr_name, ctl, is_nullable ) {

	// Checks the given field for errors

	// ???TODO: add code to support miliary times.

	var hh, mm, ampm;
	var value = strTrim(ctl.value);

	// ???TODO: fix this...  not feeling clever enough right now...
	var reTime1 = /\b\d{1,2}[:]\d{2}[ ][ap]\b/;			// 1:30 p
	var reTime1m = /\b\d{1,2}[:]\d{2}[ ][ap][m]\b/;		// 1:30 pm

	var reTime2 = /\b\d{1,2}[:]\d{2}[ap]\b/;				// 1:30p
	var reTime2m = /\b\d{1,2}[:]\d{2}[ap][m]\b/;			// 1:30pm

	var reTime3 = /\b\d{1,2}[ap]\b/;		// 1p
	var reTime3m = /\b\d{1,2}[ap][m]\b/;		// 1pm

	var reTime4 = /\b\d{1,2}[ ][ap]\b/;		// 1 p
	var reTime4m = /\b\d{1,2}[ ][ap][m]\b/;		// 1 pm

	// check for null situation
	if ( value.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	value = value.toLowerCase();

	// check for basic format
	if ( value == 'noon' ) {
		value = timeFormatHMAM(12, 0, 'pm');
	} else if ( value == 'mid' || value == 'midnight' ) {
		value = timeFormatHMAM(12, 0, 'am');
	} else if ( !reTime1.test(value) && !reTime1m.test(value) && !reTime2.test(value) && !reTime2m.test(value) && !reTime3.test(value) && !reTime3m.test(value) && !reTime4.test(value) && !reTime4m.test(value) ) {
		valErr(ctl, attr_name, "Incorrect time format.  Please enter the time using the format \"h:mm a\" or \"h:mm p\".)");
		return true;

	} else {			// ok... it conforms to our format... so far...
		var delim1 = value.indexOf(':');
		var delim2 = value.indexOf(' ');
		var ampmPosition = delim2 + 1;		// assume the a or p is right after the space

		// if we didn't find the space, find the first a or p!
		if ( delim2 == -1 ) {
			delim2 = value.indexOf('a');
			if ( delim2 == -1 ) {
				delim2 = value.indexOf('p');
			}
			ampmPosition = delim2;
		}

		// if we didn't find a colon--then their isn't any minutes!
		if ( delim1 == -1 ) {
			delim1 = delim2;
		}

		hh = parseInt(value.substring(0, delim1), 10);
		if ( delim1 != delim2 ) {
			mm = parseInt(value.substring(delim1+1, delim2), 10);
		} else {
			mm = 0;
		}
		ampm = value.substr(ampmPosition, 1);

		if ( hh < 1 || hh > 12 ) {
			valErr(ctl, attr_name, "Please check the hours value. (1 to 12)");
			return true;
		} else if ( mm < 0 || mm > 59 ) {
			valErr(ctl, attr_name, "Please check the minutes value. (0 to 59)");
			return true;
		} else if ( ampm == 'a' ) {
			ampm = 'am';
		} else if ( ampm == 'p' ) {
			ampm = 'pm';
		} else {			// this shouldn't happen due to the pattern check above!
			valErr(ctl, attr_name, "Please check the am/pm value. (am or pm)");
			return true;
		}

		// create a "properly" formatted time!
		value = timeFormatHMAM(hh, mm, ampm);

	}

	ctl.value = value;

	valClr( ctl );
	return false;

}

function strCheck( attr_name, ctl, max_length, is_nullable ) {

	// Checks the given field for errors
	// ???TODO: call the strTrim() function!?!

	// check for null situation
	if ( ctl.value.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	if ( ctl.value.length > max_length ) {
		valErr(ctl, attr_name, "The value that you have entered is too long.  (Is " + ctl.value.length + " and needs to be " + max_length + " or less.)");
		return true;
	}

	valClr( ctl );
	return false;
}

function fileCheck( attr_name, ctl, is_nullable ) {

	// Checks the given field for errors

	var sFileName = '';
	var vv = ctl.value;

	if ( vv != null ) {
		sFileName = vv.toString();
	}

	// check for null situation
	if ( sFileName.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a value for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	// ???TODO: can we add a check on the file?  perhaps its size or something?

	valClr( ctl );
	return false;
}

function emailCheck( attr_name, ctl, max_length, is_nullable ) {

	// checks if the user entered a proper email address

	// first, do some basic string checks
	if ( strCheck( attr_name, ctl, max_length, is_nullable ) ) {
		return true;
	}

	// Now, do some simple checks
	var str = ctl.value;

	var re = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;		// TODO: make sure this works correctly!
	if (!str.match(re)) {
		valErr( ctl, attr_name, "Please check your email address.  It should be in name@domain.com format.");
		return true;
	}

	valClr( ctl );
	return false;
}

function colorCheck( attr_name, ctl, is_nullable ) {

	// checks if the user entered a proper email address

	// first, do some basic string checks
	if ( strCheck( attr_name, ctl, 7, is_nullable ) ) {
		return true;
	}

	// Now, do some simple checks
	var str = ctl.value;

	if ( str.length != 0 ) {

		var re = /^#[0-9a-fA-F]{6}$/;		// TODO: make sure this works correctly!
		if (!str.match(re)) {
			valErr( ctl, attr_name, "Please check your color value.  It should be in the form of the symbol pound (#) followed by 6 hexidecimal digits.");
			return true;
		}

	}

	valClr( ctl );
	return false;
}

function creditcardCheck( attr_name, ctl, max_length, is_nullable ) {

	// do some _very_ basic credit card field checks!

	var str = ctl.value;
	var re = /^\d*$/;

	// Check for "PRESENT" value
	if ( str == 'PRESENT' ) {
		valClr( ctl );
		return false;
	}

	// First, replace various values
		str = str.replace(/[ ]/g, '');
		str = str.replace(/[-]/g, '');

		// if the cleanup removed some characters, replace it
		if ( str != ctl.value ) {
			ctl.value = str;
		}

	// check for null situation
	if ( str.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a credit card number.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	if ( str.length > max_length ) {
		valErr(ctl, attr_name, "This credit card number is too long.  (Is " + str.length + " and needs to be " + max_length + " or less.");
		return true;
	}

	str = str.toString();

	if (!str.match(re)) {
		valErr( ctl, attr_name, "This is not a correct credit card number.");		// ???TODO: come up with a better message!
		return true;
	}

	valClr( ctl );
	return false;

}

function smalldateCheck( attr_name, ctl, is_nullable ) {

	// checks if the given control has a valid date (mm/dd/yyyy or mm-dd-yyyy or the two digit year equiv.'s)
	// Also allows a "relative" mode where it will add or subtract days from today.

	var mm, dd, yyyy;
	var value = ctl.value;

	var reLong = /\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/;
	var reShort = /\b\d{1,2}[\/-]\d{1,2}[\/-]\d{2}\b/;

	var reRelative = /[+-]\d+/;

	// check for null situation
	if ( ctl.value.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a date for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	// Check if the length is too long
	if ( ctl.value.length > 10 ) {
		valErr(ctl, attr_name, "The value that you have entered is too long.  (Is " + ctl.value.length + " and needs to be 10 or less.)");
		return true;
	}

	// Now, do some date checks
	var valid = (reLong.test(value)) || (reShort.test(value));
	var dwWanted = daytoOrd( value );

	if ( dwWanted != -1 ) {			// if they provided a day of the week

		var today = new Date();
		var dw = today.getDay();

		if ( dw < dwWanted ) {
			today.setDate( today.getDate() + (dwWanted-dw) );
		} else {
			today.setDate( today.getDate() + (7-dw) + dwWanted );
		}
			
		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();

		if ( yyyy < 1900 || yyyy >= 2079 ) {
			valErr(ctl, attr_name, "The year is out of range.  (Must be in the range of 1900 to 2078.)");
			return true;
		} else {
			ctl.value = dateFormatMDY(mm, dd, yyyy);
			valClr( ctl );
		}

	} else if ( reRelative.test(value) ) {			// check if they are doing a +# or -#

		// Get today
		var today = new Date();

		// Now, add the relative date
		today.setDate( today.getDate() + ( (value.substring(0, 1) == '-') ? -1 : 1 ) * parseInt( value.substring(1), 10 ) );

		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();

		if ( yyyy < 1900 || yyyy >= 2079 ) {
			valErr(ctl, attr_name, "The year is out of range.  (Must be in the range of 1900 to 2078.)");
			return true;
		} else {
			ctl.value = dateFormatMDY(mm, dd, yyyy);
			valClr( ctl );
		}
		
	} else if (valid) {
		var delimChar = (value.indexOf("/") != -1) ? "/" : "-";
		var delim1 = value.indexOf(delimChar);
		var delim2 = value.lastIndexOf(delimChar);
		mm = parseInt(value.substring(0, delim1), 10);
		dd = parseInt(value.substring(delim1+1, delim2), 10);
		yyyy = parseInt(value.substring(delim2+1), 10);
		// handle two-digit year
		if (yyyy < 100) {
			var today = new Date();
			// get current century floor (e.g., 2000)
			var currCent = parseInt(today.getFullYear() / 100) * 100;
			// two digits up to this year + 15 expands to current century
			var threshold = (today.getFullYear() + 15) - currCent;
			if (yyyy > threshold) {
				yyyy += currCent - 100;
			} else {
				yyyy += currCent;
			}
		}

		if ( yyyy < 1900 || yyyy >= 2079 ) {
			valErr(ctl, attr_name, "The year is out of range.  (Must be in the range of 1900 to 2078.)");
			return true;
		}

		var testDate = new Date(yyyy, mm-1, dd);
		if (testDate.getDate() == dd) {
			if (testDate.getMonth() + 1 == mm) {
				if (testDate.getFullYear() == yyyy) {
					// fill field with database-friendly format
					ctl.value = dateFormatMDY(mm, dd, yyyy);
					valClr( ctl );
					return false;
				} else {
					valErr(ctl, attr_name, "Please check the year value.");
					return true;
				}
			} else {
				valErr(ctl, attr_name, "Please check the month value.");
				return true;
			}
		} else {
			valErr(ctl, attr_name, "Please check the day value.");
			return true;
		}
	} else {
		valErr(ctl, attr_name, "Incorrect date format.  Please enter the date using mm/dd/yyyy format.");
		return true;
	}

	valClr( ctl );
	return false;
}

function daytoOrd( day_name ) {

	// given the name of a day of the week, return its ordinal value (0 = Sunday)

	//	RET: 	-1 = didn't recognize the day
	//		0 = Sunday; 6 = Saturday

	if ( day_name ) {
		switch ( day_name.toLowerCase() ) {
			case 'sun':
			case 'sunday':
			case 'su':
				return 0;

			case 'mon':
			case 'monday':
			case 'mo':
				return 1;

			case 'tue':
			case 'tuesday':
			case 'tu':
				return 2;

			case 'wed':
			case 'wednesday':
			case 'we':
				return 3;

			case 'thu':
			case 'thursday':
			case 'thurs':
			case 'th':
				return 4;

			case 'fri':
			case 'friday':
			case 'fr':
				return 5;

			case 'sat':
			case 'saturday':
			case 'sa':
				return 6;

			default:
				return -1
		}
			
	} else {
		return -1;
	}

}

function dateCheck( attr_name, ctl, is_nullable ) {

	// checks if the given control has a valid date (mm/dd/yyyy or mm-dd-yyyy or the two digit year equiv.'s)
	// Also allows a "relative" mode where it will add or subtract days from today.

	var mm, dd, yyyy;
	var value = ctl.value;

	var reLong = /\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/;
	var reShort = /\b\d{1,2}[\/-]\d{1,2}[\/-]\d{2}\b/;

	var reRelative = /[+-]\d+/;

	// check for null situation
	if ( ctl.value.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a date for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	// Check if the length is too long
	if ( ctl.value.length > 10 ) {
		valErr(ctl, attr_name, "The value that you have entered is too long.  (Is " + ctl.value.length + " and needs to be 10 or less.)");
		return true;
	}

	// Now, do some date checks
	var valid = (reLong.test(value)) || (reShort.test(value));
	var dwWanted = daytoOrd( value );

	if ( dwWanted != -1 ) {			// if they provided a day of the week

		var today = new Date();
		var dw = today.getDay();

		if ( dw < dwWanted ) {
			today.setDate( today.getDate() + (dwWanted-dw) );
		} else {
			today.setDate( today.getDate() + (7-dw) + dwWanted );
		}
			
		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();

		if ( yyyy < 1900 || yyyy >= 2079 ) {
			valErr(ctl, attr_name, "The year is out of range.  (Must be in the range of 1900 to 2078.)");
			return true;
		} else {
			ctl.value = dateFormatMDY(mm, dd, yyyy);
			valClr( ctl );
		}

	} else if ( reRelative.test(value) ) {			// check if they are doing a +# or -#

		// Get today
		var today = new Date();

		// Now, add the relative date
		today.setDate( today.getDate() + ( (value.substring(0, 1) == '-') ? -1 : 1 ) * parseInt( value.substring(1), 10 ) );

		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();

		ctl.value = dateFormatMDY(mm, dd, yyyy);
		valClr( ctl );
		
	} else if (valid) {
		var delimChar = (value.indexOf("/") != -1) ? "/" : "-";
		var delim1 = value.indexOf(delimChar);
		var delim2 = value.lastIndexOf(delimChar);
		mm = parseInt(value.substring(0, delim1), 10);
		dd = parseInt(value.substring(delim1+1, delim2), 10);
		yyyy = parseInt(value.substring(delim2+1), 10);
		// handle two-digit year
		if (yyyy < 100) {
			var today = new Date();
			// get current century floor (e.g., 2000)
			var currCent = parseInt(today.getFullYear() / 100) * 100;
			// two digits up to this year + 15 expands to current century
			var threshold = (today.getFullYear() + 15) - currCent;
			if (yyyy > threshold) {
				yyyy += currCent - 100;
			} else {
				yyyy += currCent;
			}
		}

		var testDate = new Date(yyyy, mm-1, dd);
		if (testDate.getDate() == dd) {
			if (testDate.getMonth() + 1 == mm) {
				if (testDate.getFullYear() == yyyy) {
					// fill field with database-friendly format
					ctl.value = dateFormatMDY(mm, dd, yyyy);
					valClr( ctl );
					return false;
				} else {
					valErr(ctl, attr_name, "Please check the year value.");
					return true;
				}
			} else {
				valErr(ctl, attr_name, "Please check the month value.");
				return true;
			}
		} else {
			valErr(ctl, attr_name, "Please check the day value.");
			return true;
		}
	} else {
		valErr(ctl, attr_name, "Incorrect date format.  Please enter the date using mm/dd/yyyy format.");
		return true;
	}

	valClr( ctl );
	return false;
}

function datetimeCheck( attr_name, ctl, is_nullable ) {

	// checks if the given control has a valid date (mm/dd/yyyy or mm-dd-yyyy or the two digit year equiv.'s)
	// Also allows a "relative" mode where it will add or subtract days from today.

	var mm, dd, yyyy, hh, mm, ampm;
	var value = ctl.value;

	var reLongPureDate = /^\d{1,2}[\/-]\d{1,2}[\/-]\d{4}$/;
	var reShortPureDate = /\d{1,2}[\/-]\d{1,2}[\/-]\d{2}$/;

	var reLong = /^\d{1,2}[\/-]\d{1,2}[\/-]\d{4}[ ]\d{1,2}:\d{1,2}[ ][apAP][mM]?$/;
	var reShort = /^\d{1,2}[\/-]\d{1,2}[\/-]\d{2}[ ]\d{1,2}:\d{1,2}[ ][apAP][mM]?$/;

	var reRelative = /[+-]\d+/;

	var timeFormatted = '';

	// check for null situation
	if ( ctl.value.length == 0 ) {

		if ( is_nullable == false ) {
			valErr(ctl, attr_name, "Please enter a date for this field.");
			return true;
		}

		valClr( ctl );
		return false;

	}

	// Check if the length is too long
	if ( ctl.value.length > 19 ) {
		valErr(ctl, attr_name, "The value that you have entered is too long.  (Is " + ctl.value.length + " and needs to be 19 or less.)");
		return true;
	}

	// Now, do some date checks
	var valid = (reLong.test(value)) || (reShort.test(value) || reLongPureDate.test(value) || reShortPureDate.test(value));
	var dwWanted = daytoOrd( value );

	if ( dwWanted != -1 ) {			// if they provided a day of the week

		var today = new Date();
		var dw = today.getDay();

		if ( dw < dwWanted ) {
			today.setDate( today.getDate() + (dwWanted-dw) );
		} else {
			today.setDate( today.getDate() + (7-dw) + dwWanted );
		}
			
		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();
		hh = today.getHours();
		nn = today.getMinutes();

		if ( hh > 12 ) {
			ampm = 'pm';
			hh -= 12;
		} else {
			ampm = 'am';
		}

		if ( yyyy < 1900 || yyyy >= 2079 ) {
			valErr(ctl, attr_name, "The year is out of range.  (Must be in the range of 1900 to 2078.)");
			return true;
		} else {
			timeFormatted = " " + timeFormatHMAM(hh, nn, ampm);
			ctl.value = dateFormatMDY(mm, dd, yyyy) + timeFormatted;
			valClr( ctl );
		}

	} else if ( reRelative.test(value) ) {			// check if they are doing a +# or -#

		// Get today
		var today = new Date();

		// Now, add the relative date
		today.setDate( today.getDate() + ( (value.substring(0, 1) == '-') ? -1 : 1 ) * parseInt( value.substring(1), 10 ) );

		// Now, break out the components
		mm = today.getMonth()+1;
		dd = today.getDate();
		yyyy = today.getFullYear();
		hh = today.getHours();
		nn = today.getMinutes();

		if ( hh > 12 ) {
			ampm = 'pm';
			hh -= 12;
		} else {
			ampm = 'am';
		}

		// Finally, format the time and date and set the control to it
		timeFormatted = " " + timeFormatHMAM(hh, nn, ampm);
		ctl.value = dateFormatMDY(mm, dd, yyyy) + timeFormatted;
		valClr( ctl );
		
	} else if (valid) {
		var delimChar = (value.indexOf("/") != -1) ? "/" : "-";
		var delim1 = value.indexOf(delimChar);
		var delim2 = value.lastIndexOf(delimChar);
		var delimtime = value.indexOf(" ");
		var delimNN = value.indexOf(":");
		var delim4 = value.lastIndexOf(" ");
		mm = parseInt(value.substring(0, delim1), 10);
		dd = parseInt(value.substring(delim1+1, delim2), 10);
		yyyy = parseInt(value.substring(delim2+1), 10);

		// handle the time first
		if ( delimtime != -1 ) {			// if we have a space
			hh = parseInt(value.substring(delimtime + 1, delimNN), 10);
			nn = parseInt(value.substring(delimNN + 1, delim4), 10);
			ampm = value.substring(delim4+1).toLowerCase();
			if ( hh < 1 || hh > 12 ) {
				valErr(ctl, attr_name, "Please check the hours value.  Needs to be from 1 to 12.");
				return true;
			} else if ( nn < 0 || nn > 59 ) {
				valErr(ctl, attr_name, "Please check the minutes value.  Needs to be from 0 to 59.");
				return true;
			} else if ( ampm == 'a' || ampm == 'p' ) {
				ampm += 'm';
			} else if ( ampm != 'am' && ampm != 'pm' ) {
				valErr(ctl, attr_name, "Please check the am/pm area.  Needs to be either am or pm.");
				return true;
			}
			timeFormatted = " " + timeFormatHMAM(hh, nn, ampm);
		} else {
			timeFormatted = "";
		}
		// handle two-digit year
		if (yyyy < 100) {
			var today = new Date();
			// get current century floor (e.g., 2000)
			var currCent = parseInt(today.getFullYear() / 100) * 100;
			// two digits up to this year + 15 expands to current century
			var threshold = (today.getFullYear() + 15) - currCent;
			if (yyyy > threshold) {
				yyyy += currCent - 100;
			} else {
				yyyy += currCent;
			}
		}

		// now, handle the date part
		var testDate = new Date(yyyy, mm-1, dd);
		if (testDate.getDate() == dd) {
			if (testDate.getMonth() + 1 == mm) {
				if (testDate.getFullYear() == yyyy) {
					// fill field with database-friendly format
					ctl.value = dateFormatMDY(mm, dd, yyyy) + timeFormatted;
					valClr( ctl );
					return false;
				} else {
					valErr(ctl, attr_name, "Please check the year value.");
					return true;
				}
			} else {
				valErr(ctl, attr_name, "Please check the month value.");
				return true;
			}
		} else {
			valErr(ctl, attr_name, "Please check the day value.");
			return true;
		}
	} else {
		valErr(ctl, attr_name, "Incorrect date/time format.  Please enter the date using mm/dd/yyyy hh:mm am format.  Or, you can use + or - and a number if relative to today.");
		return true;
	}

	valClr( ctl );
	return false;
}

function jlDIVPanelInit( div_id, panel_selector_element_prefix, unselected_panel_selector_element_class, selected_panel_selector_element_class, transition_time_in_ms, do_auto_transitions, auto_transition_time_in_ms ) {

	var ele = g( div_id );

	if ( ele != null ) {
		// save some properties
		ele.panel_selector_element_prefix = panel_selector_element_prefix;
		ele.unselected_panel_selector_element_class = unselected_panel_selector_element_class;
		ele.selected_panel_selector_element_class = selected_panel_selector_element_class;
		ele.transition_time_in_ms = transition_time_in_ms;
		// first, get rid of the non DIV nodes
		for ( var tt=ele.childNodes.length-1; tt >=0 ; tt-- ) {
			if ( ele.childNodes[tt].nodeType == 3 ) {
				ele.removeChild( ele.childNodes[tt] );
			}
		}
		ele.panel_count = ele.childNodes.length;
		ele.old_panel_id = 0;
		ele.current_panel_id = 1;
		ele.do_auto_panel_transitions = do_auto_transitions;
		ele.auto_transition_time_in_ms = auto_transition_time_in_ms;

		ele.opacity_per_step = 10;		// has to divide 100 evenly!  Note:  If you increase this value, it can get too choppy.  If you decrease it, it can kill the CPU!

		for ( var tt = 0; tt < ele.panel_count; tt++ ) {
			var div = ele.childNodes[tt];
			div.style.left = '0px';
			div.style.top = '0px';
			div.style.width = ele.style.width;
			div.style.height = ele.style.height;
			div.style.overflow = 'auto';
			if ( div.style.backgroundColor == '' || div.style.backgroundColor == null ) {
				div.style.backgroundColor = '#ffffff';
			}
			div.style.zIndex = ele.panel_count - tt;
			div.style.position = 'absolute';
			if ( tt != 0 ) {
				setOpacity(div, 0); 
			}
		}

		// link up some member functions
		ele.doAutoTransition = jlDIVPanel_doAutoTransition;
		ele.setPanel = jlDIVPanel_setPanel;
		ele.divOpacitySet = jlDIVPanel_divOpacitySet;
		ele.divDone = jlDIVPanel_divDone;

		// if we are doing auto transitions, start them now
		if ( ele.do_auto_panel_transitions ) {
			setTimeout(' try { ' + div_id + '.doAutoTransition() } catch (e) {}', ele.auto_transition_time_in_ms);
		}
	}

	
}

//	class methods
function jlDIVPanel_doAutoTransition() {

	// This gets the system to automatically move to the next panel

	if ( this.do_auto_panel_transitions == true ) {

		// calculate the new panel's id
		var new_panel_id = this.current_panel_id + 1;
		if ( new_panel_id > this.panel_count ) {
			new_panel_id = 1;
		}

		// tell the system to go to this panel
		this.setPanel( new_panel_id, this.do_auto_panel_transitions);

		// schedule the next auto transition
		setTimeout(' try { ' + this.id + '.doAutoTransition() } catch (e) {}', this.auto_transition_time_in_ms);

	}

}

function jlDIVPanel_setPanel( new_panel_id, continue_auto_transitions ) {

	// Tells the system to move to the given panel
	// Also allows the caller to turn on/off auto transitions

	this.do_auto_panel_transitions = continue_auto_transitions;

	if ( this.current_panel_id != new_panel_id ) {

		this.old_panel_id = this.current_panel_id;
		this.current_panel_id = new_panel_id;

		// First, run the transition
		var divOld = null;
		var transition_time_in_ms = this.transition_time_in_ms;

		var divNew = this.childNodes[new_panel_id-1];
		var old_panel_id = this.old_panel_id;
		if ( old_panel_id != 0 ) {
			divOld = this.childNodes[old_panel_id-1];
			divOld.style.zIndex = 1;
			g( this.panel_selector_element_prefix + old_panel_id).className = this.unselected_panel_selector_element_class;
		}

		// Now, upgrade the "bullet" for the panel name
		g( this.panel_selector_element_prefix + new_panel_id).className = this.selected_panel_selector_element_class;

		// do some math
		var opacity_per_step = this.opacity_per_step
		var step_count = 100 / opacity_per_step;
		var step_time_in_ms = Math.round(transition_time_in_ms / step_count); 

		divNew.style.zIndex = this.panel_count;
	 
		// schedule a bunch of changes in opacity
		var time = 0;
		for ( var tt = 0; tt <= 100; tt+=opacity_per_step) { 
			setTimeout('try { ' + this.id + '.divOpacitySet(' + tt + ') } catch (e) {}', time ); 
			time += step_time_in_ms;
		}
		setTimeout('try { ' + this.id + '.divDone(); } catch (e) {};', time );

	}

}

function jlDIVPanel_divOpacitySet( opacity ) {

	var new_panel_id = this.current_panel_id;
	var divNew = this.childNodes[new_panel_id-1];

	setOpacity( divNew, opacity );

}

function jlDIVPanel_divDone() {

	// This function will reset the old panel so that it is ready for the next transition

	var old_panel_id = this.old_panel_id;

	if ( old_panel_id != 0 ) {
		var divOld = this.childNodes[old_panel_id-1];

		setOpacity(divOld, 0);
		divOld.style.zIndex = 1;
	}

}

//	Private Functions

function setOpacity( ele, opacity) { 

	// set the opacity of the given element

	var styleDIV = ele.style;

	// set the opacity for the image.  We are doing it multiple times to support several browsers
	styleDIV.opacity = (opacity / 100); 
	styleDIV.MozOpacity = (opacity / 100); 
	styleDIV.KhtmlOpacity = (opacity / 100); 
	styleDIV.filter = 'alpha(opacity=' + opacity + ')'; 

} 


