
//<------------------------------------------------------------------------------------------------------
// @General [-!Overview-] Generic DHTML rendering functions
// @Author: Tim Johnson - tim@johnsons-web.com
// @Protocols: The word 'series' is used for any indexable value.
//>-----------------------------------------------------------------------------------------------------
var dhtml_js = 1; // "register" this file with calling code
altRowColors = new Array("#95FF32","#88E82D");
//<------------------------------------------------------------------------------------------------------
// @syntax [-ParseCookieValue(<str-cookie_value>)-] Parse a cookie into pairse on '|' 
//>-----------------------------------------------------------------------------------------------------
function ParseCookieValue(cookie_value){
  var tmp = cookie_value.split('|'),tmp1,
			pairs = tmp.length,
			res = [];
  for(var i = 0; i < pairs; i++){
	tmp1 = tmp[i].split(':');
    res = res.concat(tmp1);
  }
  return res;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-goToURL(<str-URL>)-] Change window location to 'URL'
//>-----------------------------------------------------------------------------------------------------
function goToURL(URL) { window.location = URL; }
function ExCheckbox(ch,ndx){
  var ischecked,i,val;
  for (i = 0; i < ch.length; i++){
    val = ch[i].value;
		if(("" + val) == ("" + ndx)){ pass() }
		else{ ch[i].checked = false; }
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-pass()-] Do nothing. Handy, borrowed from python
//>-----------------------------------------------------------------------------------------------------
function pass(){}
//<------------------------------------------------------------------------------------------------------
// @syntax [-dbg(<obj>,<str-label>)-] Examine an object
//>-----------------------------------------------------------------------------------------------------
function dbg(obj,label){
  var debug = "Examining: " + label + "\n";
  for (var i in obj){
	debug += i + " -> " + obj[i] + "\n";
  }
  alert(debug);
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-StripLeading(<str>,<chars-to-strip>)-] Strip all of 'ch' from 'str'
//>-----------------------------------------------------------------------------------------------------
function StripLeading(str,ch){
  while(str.length > 1 && str.substring(0,1) == ch){
		str = str.substring(1,str.length);
    }
  return str;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-StripLeadingChars(<str>,<chars-to-strip>)-] Strip all of 'chars' from 'str'
//>-----------------------------------------------------------------------------------------------------
function StripLeadingChars(str,chars){
  while(str.length > 1 && chars.indexOf(str.substring(0,1)) != -1){
		str = str.substring(1,str.length);
    }
  return str;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-ParseIntZeroes(<str>)-] Enhanced parseInt, remove zeroes first. parseInt with 2nd arg will do the same.
//>-----------------------------------------------------------------------------------------------------
function ParseIntZeroes(str){
  var tmps = StripLeading(str,'0');
  return parseInt(tmps,10);
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TodaysDateString()-] Returns current date in 'mm/dd/yyyy' format
//>-----------------------------------------------------------------------------------------------------
function TodaysDateString(){
  var Now = new Date();  // Date() for current date
  return ((Now.getMonth() < 9 ? '0' : '') + (Now.getMonth() + 1) + "/"
		  + (Now.getDate() < 10 ? '0' : '') + Now.getDate() + "/"
		  + Now.getFullYear());
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TodaysDateArray()-] Returns current date in (mm,dd,yyyy) format
//>-----------------------------------------------------------------------------------------------------
function TodaysDateArray(){ // adjust month for first month = 1
  var Now = new Date();  // Date() for current date
  return [Now.getMonth() + 1, Now.getDate(), Now.getFullYear()];
}
function SayHello(){
  alert('Hello');
}
function NoFutureDate(field,label){
  if(!DateOrNone(field,label)) return false;
  alert('NoFutureDate()');
  var str = field.value;
  var now = TodaysDateArray();
  var parsed = str.split('/');
  mn = ParseIntZeroes(parsed[0]);
  dy = ParseIntZeroes(parsed[1]);
  yr = ParseIntZeroes(parsed[2]);
  if(yr > now[2]){ // a later year chosen
		field.value = TodaysDateString();
		}
  else if(yr == now[2]){ // same year chosen
		if(mn > now[0]) // later month, same year chosen
			field.value = TodaysDateString();
		else if(mn == now[0]) // same month
			if(dy > now[1])     // later day
				field.value = TodaysDateString();
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax  [-Throw(<str-what>,<str-where>)-] Raise an error with function name and problem
//>-----------------------------------------------------------------------------------------------------
function Throw(what,where){
  var errString = what;
  if(where){
		errString = "Function: " + where + "\n" + errString;
		}
  alert(errString);
  throw errString;
	}
var rowColorNdx = 0; 
//<------------------------------------------------------------------------------------------------------
// @syntax [-IDput(<str-ID>,<val>)-] Append 'val' to an element by 'ID'
//>-----------------------------------------------------------------------------------------------------
function IDput(ID,val){
  var target = document.getElementById(ID);
  if(target) target.appendChild(val);
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-InputElement(<str-sType>,<attrs-array>)-] 
// @description Create an form input element using raw HTML so that MSIE will accept in javascript
//>-----------------------------------------------------------------------------------------------------
function InputElement(fType,attrs){
  var estr = "<input type=\"" + fType + "\"";
  for(var i=0;i < attrs.length;i+=2){
		estr += " " + attrs[i];
		if(attrs[i + 1]){
			estr += "=" + "\"" + attrs[i +1] + "\""
			}
		}
  estr += ">"
  return estr;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-toDiv(<str-ID>,<str-text>)-]
// @description Print some text to a target, preferable a Div element
// @If 'text' is not passed, clear the target with empty string
//>-----------------------------------------------------------------------------------------------------
function toDiv(ID,text){
  var txt = '';
  if(text) txt = text;
  var target = document.getElementById(ID);
  if(target){target.innerHTML = txt;}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-PrintTo(<str-ID>,<str-text>)-]
// @description Print some text to a target, preferable a Div element
// @If 'text' is not passed, clear the target with empty string
//>-----------------------------------------------------------------------------------------------------
function PrintTo(ID,text){
  var txt = '';
  if(text) txt = text;
  var target = document.getElementById(ID);
  if(target){target.innerHTML = txt;}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Print(<str-text>,<str-obj-dst>,<attrs-array>)-]
// @description Print some `text' with attributes. 
// @If 'attrs' is an empty string, create <br>
// @If the first or second element is 'b', set as bold
//>-----------------------------------------------------------------------------------------------------
function Print(text,dst,attrs){
  var is_bold = false;
  var do_break = false;
  if(dst){
    if(attrs){
			if (First(attrs).toLowerCase() == ''){
				attrs = Rest(attrs);
				do_break = true;
				}
			if(First(attrs).toLowerCase() == 'b'){
				attrs = Rest(attrs);
				is_bold = true;
			}
			var tmp = Font(attrs,text);
			if(is_bold){
				var B = document.createElement('B');
				B.appendChild(tmp);
				tmp = B
				}
			if(TypeOf(dst) == 'string'){  // looking for ID
				var target = document.getElementById(dst);
				if(target) target.innerHTML = tmp;
				}
			else { // assume a widget
				dst.appendChild(tmp);
				}
			}
		else dst.appendChild(Text(text));
		if(do_break)
			dst.appendChild(document.createElement('BR'));
		}
  else{
		Text(text);
    if(do_break) document.createElement('BR');
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Skip(<series-val>,<int-n>)-]
// @Return copy of 'ary' skipped 'n' times
//>-----------------------------------------------------------------------------------------------------
function Skip(val,n){
  if(TypeOf(val) == 'string'){
		var newstring = val.substring(n);
		return newstring;
		}
  if(n > val.length)
		Throw("`n' greater than `val.length'","Skip")
  var newAry = new Array(val.length - n)
  var j = 0,i;
	for(i = n;i < val.length; i++){
		newAry[j] = val[i];
		j++;
		}
  return newAry;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-First(<series>)-] Return first member of a series
//>-----------------------------------------------------------------------------------------------------
function First(ary){return ary[0]}
//> Returns `ary' at its next position.
//<------------------------------------------------------------------------------------------------------
// @syntax [-Next(<Series>)-] Return remainder of a series.
//>-----------------------------------------------------------------------------------------------------
function Rest(val){
  return Skip(val,1)
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-IsDefined(<obj>)-] Is object defined?
//>-----------------------------------------------------------------------------------------------------
function IsDefined(obj){
  //if(!obj) return false;
  var type = typeof(obj);
  if(type.toUpperCase() == 'UNDEFINED')
	return false;
  else return true;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-IsUndefined(<obj>)-] Is object not defined?
//>-----------------------------------------------------------------------------------------------------
function IsUndefined(obj){
  if(!obj) return true;
  var type = typeof(obj);
  if(type.toUpperCase() == 'UNDEFINED')
		return true;
  else return false;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-strFind(<str>,<sub-str>)-] Find 'sub' in 'str'
//>-----------------------------------------------------------------------------------------------------
function strFind(str,sub){
  var found = str.indexOf(sub);
  if(found >= 0) return true;
  else return false;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-docname()-] Return document name resolved to string following last '/'
//>-----------------------------------------------------------------------------------------------------
function docName(){
  return Last(document.location.pathname.split("/"))
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Last(<ary>)-] Return last item in an array
//>-----------------------------------------------------------------------------------------------------
function Last(ary){
  return ary[ary.length - 1]
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TypeOf(<obj>)-] Get the type of the value in lower case
//>-----------------------------------------------------------------------------------------------------
function TypeOf(obj){
  if(!obj) return 'null';
  var type = typeof(obj);
  type = type.toLowerCase();
  if(type == 'object'){
		var ftype = obj.constructor;
		if(ftype == Array) return "array";
		else if(ftype = Option) return "option";
		else return "unknown";
		}
  else return type;
	}
function ListValid(lst,label,header_len){
  if(IsUndefined(lst))
		return true;
  if(lst.selectedIndex < header_len){
		return false;
		} 
	else return true;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-clearSelectList(<obj>)-] Clear a dropdown list
//>-----------------------------------------------------------------------------------------------------
function clearSelectList(ctrl){
  for (var i = ctrl.options.length; i >= 0; i--){
		ctrl.options[i] = null;
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-SelectFrom(<array>,<any-key>)-] If 'key' found, return following item
//>-----------------------------------------------------------------------------------------------------
function SelectFrom(ary,key){
  for (var i = 0; i < ary.length; i += 1)
		if (ary[i] == key)
			return ary[i + 1]
	return false;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax  [-clearSelectList(<widget>,<optional-header>)-]
// @description Clear a 'select' or drop-down list
//>-----------------------------------------------------------------------------------------------------
function clearSelectList(ele,_hdr){
	var hdr = _hdr || ["-------"];
	makeSelectList(ele,hdr);
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-makeSelectList(<widget>,<options-arrary>,<header>,<item-to-select>,<returnWidget>)-]
// @description Rebuild dropdown list 'widget' with 'options-array', with optional 'header'
//   and optional 'item' to 'select'. 
// @Returns the widget if 'returnWidget' is true or the last item created.
//>-----------------------------------------------------------------------------------------------------
function makeSelectList(ctrl,ary,hdr,_item,returnWidget){
	//alert("ctrl: " + ctrl + "\nary: " + ary + "\nhdr: " + hdr);  // DEBUG GLOBAL
  var res = null,item=null,j=0,i;
	if(_item) item = "" + _item;
  for (i = ctrl.options.length; i >= 0; i--) {
		ctrl.options[i] = null; 
		}
  if(hdr){
		for(i = 0;i < hdr.length;i++){
			if (TypeOf(hdr[i]) == 'array'){
				ctrl.options[j] = new Option(hdr[i][0]);
				ctrl.options[j].value = hdr[i][1];
				}
			else{
				ctrl.options[j] = new Option(hdr[i]);
				ctrl.options[j].value = j - 1;
				}
			j++;
			}
		}
  if(ary){
		//alert("makeSelectList: building array:\n" + ary); 
		for (i = 0; i < ary.length; i++) {
			if(TypeOf(ary[i]) == 'array') tmp = ary[i];
			else tmp = new Array(ary[i],ary[i]);
			ctrl.options[j] = new Option(tmp[0]);
			ctrl.options[j].value = tmp[1];
			if (item){
				if (item == ctrl.options[j].value ||
					item.toUpperCase() == ctrl.options[j].text.toUpperCase()){
					ctrl.options[j].selected = true;
					ctrl.selectedIndex = j;
					if(ctrl.options[j].value) res = ctrl.options[j].value;
					else res = ctrl.options[j].text; 
					}
				}
			j++;
			}
    }
  if(returnWidget) return ctrl;
  else return res;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-multSelectList(<widget>,<options-arrary>,<header>,<items-to-select>,<returnWidget>)-]
// @description Rebuild dropdown list 'widget' with 'options-array', with optional 'header'
//   and optional 'items' to 'select'. Like 'makeSelectList' with multiple option.
// @Returns the widget if 'returnWidget' is true or the last item created.
//>-----------------------------------------------------------------------------------------------------
function multSelectList(ctrl,ary,hdr,items,returnWidget){
  if(!items) items = []; 
  else if(TypeOf(items) == 'string'){
  	var tmp = items;
		items = [tmp];
  	}
  var res = null;
  var j = 0,i,val,x;
  var type;
  var selectedIndexes = new Array();
  for (i = ctrl.options.length; i >= 0; i--) ctrl.options[i] = null; 
  if(hdr){
		for(i = 0;i < hdr.length;i++){
			ctrl.options[j] = new Option(hdr[i]);
			ctrl.options[j].value = j - 1;
			j++;
			}
		}
  if(ary){
		for (i = 0; i < ary.length; i++) {
			if(TypeOf(ary[i]) == 'array')
				tmp = ary[i];
			else tmp = new Array(ary[i],ary[i]);
			ctrl.options[j] = new Option(tmp[0]);
			ctrl.options[j].value = tmp[1];
			for(x = 0; x < items.length; x++){
				val = trim(items[x]);
				if (val == ctrl.options[j].value ||
					val.toUpperCase() == ctrl.options[j].text.toUpperCase()
					){
					ctrl.options[j].selected = true;
					if(ctrl.options[j].value) 
						res = ctrl.options[j].value;
					else res = ctrl.options[j].text; 
					}
				}
			j++;
			}
    }
  if(returnWidget) return ctrl;
  else return res;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-trim(<str>)-] Trim a string
//>-----------------------------------------------------------------------------------------------------
function trim(str) { 
  if(!TypeOf(str) == 'string') return str;
  var tmp =str.replace(/^\s+/g, '');
  return tmp.replace(/\s+$/g, '');
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-Capitalize(<str-sentence>)-] capitalize every word in a string.
//>-----------------------------------------------------------------------------------------------------
function Capitalize(str_sentence) {
  return str_sentence.toLowerCase().replace(/\b[a-z]/g, convertToUpper);
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-convertToUpper(<any-args>)-] Convert first argument to upper case.
//>-----------------------------------------------------------------------------------------------------
function convertToUpper(){ return arguments[0].toUpperCase(); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-GeoCapitalize(<array>)-]
// @description capitalize every word in a string contained in index[0] of a nested array
//>-----------------------------------------------------------------------------------------------------
function GeoCapitalize(ary){
  var res =  new Array();
  for (var i = 0; i < ary.length;i++){
		var tmp = new Array();
		tmp.push(Capitalize(ary[i][0]));
		tmp.push(ary[i][1]);
		res.push(tmp);
		}
  return res;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-IDget(<str-ID>)-] Get an element by ID from current document
//>-----------------------------------------------------------------------------------------------------
function IDget(ID){ return document.getElementById(ID); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-IDgetVal(<str-ID>)-] Get an element value by ID from current document
//>-----------------------------------------------------------------------------------------------------
function IDgetVal(ID){
  var tmp = document.getElementById(ID);
  return tmp.value;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax  [-ResetList(<ID>,<header>)-] Reset a list for populating
//>-----------------------------------------------------------------------------------------------------
function ResetList(name,header){
	clearSelectList(document.getElementById(name));
	makeSelectList(document.getElementById(name),new Array(),new Array(header));
	}
function getRowColor(){
  if (rowColorNdx == 0){rowColorNdx=1}
  else rowColorNdx = 0;               // alternate colors
  var rowColor = altRowColors[rowColorNdx];
  return rowColor;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-makeRow(<table-object>)-] Create a row obj within a table object
//>-----------------------------------------------------------------------------------------------------
function makeRow(tbl){
  var rowColor = getRowColor();
  var row = tbl.insertRow(tbl.rows.length);
  row.setAttribute("bgColor",rowColor);
  return row;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-BR()-] Create a break tag
//>-----------------------------------------------------------------------------------------------------
function BR(){ return document.createElement('BR'); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-Nbsp(<int-n>)-] Create non-breaking spaces
// @references:
//    http://www.w3.org/TR/html4/sgml/entities.html
//    Keywords
//      Character entity references in HTML 4
//  @Example: &nbsp; => 	&#160; (decimal code) hex code => A0
//   '\u' + hex code padded with '00' => '\u00A0'
//>-----------------------------------------------------------------------------------------------------
function Nbsp(n){
	var nbsp = "\u00A0";
	if(n){
		var str = '';
		for(var i=0;i<n;i++){ str = str + nbsp;}
		return document.createTextNode(str);
		}
	else return document.createTextNode(nbsp);
	}
function Text(text){ return document.createTextNode(text); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-TextNode(<str-text>)-] Create a text node
//>-----------------------------------------------------------------------------------------------------
function TextNode(text){ return document.createTextNode(text); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-BText(<str-text>)-] Create a bold text node.
//>-----------------------------------------------------------------------------------------------------
function BText(text){
	var B = document.createElement('B');
	B.appendChild(document.createTextNode(text));
	return B;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-createNamedElement(<str-type>,<str-name>)-] Create an element by name and type.
//>-----------------------------------------------------------------------------------------------------
function createNamedElement(type, name) {
  var element;
  try {
    element = document.createElement('<'+type+' name="'+name+'">');
  } catch (e) { }
  if (!element || !element.name) { // Not in IE, then
    element = document.createElement(type)
    element.name = name;
  }
  return element;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TextField(<str-name>,<attrs-array>)-] Create form input text field with 'name' and attributes
//>-----------------------------------------------------------------------------------------------------
function TextField(name,ary){
  var el = document.createElement('input');
  el.setAttribute("name",name)
  for(var i=0;i < ary.length;i+=2)
		el.setAttribute(ary[i],ary[i+1]);
  return el;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Img(<array-attrs>)-] Create and img tag with attributes
//>-----------------------------------------------------------------------------------------------------
function Img(ary){
  var el = document.createElement('img');
  for(var i=0;i < ary.length;i+=2)
		el.setAttribute(ary[i],ary[i+1]);
  return el;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-HiddenField(<str-name>,<str-data>)-] Create a hidden field with name and data.
//>-----------------------------------------------------------------------------------------------------
function HiddenField(name,data){
  var el = document.createElement('input');
  el.setAttribute('type','hidden')
  el.setAttribute('name',name);
  el.setAttribute('value',data);
  return el;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-CheckBox(<str-name>,<array-attrs>,<bool-checked>,<value>,<onclickHandler>)-]
// @Description Create checkbox with name, attributes, checked, value and handler as seperate attributes.
//>-----------------------------------------------------------------------------------------------------
function CheckBox(name,ary,checked,value,onclickHandler){
  var el = document.createElement('input');
  el.setAttribute('type','checkbox');
  el.setAttribute('name',name);
  el.setAttribute('ID',name)
	if(value){ el.setAttribute('value',value); }
	if(onclickHandler){ el.setAttribute('onclick',onclickHandler); }
  if(ary)
    for(var i=0;i < ary.length;i+=2)
			el.setAttribute(ary[i],ary[i+1]);
  if(checked){
    el.defaultChecked = true; // make MSIE happy
    el.checked = true;
		}
  return el;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-excludeChkboxes(<str-formname>,<str-chkboxName>,<arry-of-others>)-]
// @description Unchecks 'others if 'chkboxName' is checked.
//>-----------------------------------------------------------------------------------------------------
function excludeChkboxes(formName,chkboxName,others){
	var chk = document[formName][chkboxName],other;
	if(chk.checked){
		for(var i=0;i < others.length;i++){
			other = document[formName][others[i]]; 
			other.checked = false;
			}
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax  [-SelectList(<array-of-attrs-and-values>,<option-items>,<array-of-headers>)-]
// @Description Build a Select (dropdown) list.
//>-----------------------------------------------------------------------------------------------------
function SelectList(attrs,items,headers){
  var el = document.createElement('select');
  for(var i=0;i < attrs.length;i+=2)
		el.setAttribute(attrs[i],attrs[i+1]);
  makeSelectList(el,items,headers);  
  return el;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-GetSelected(<str-ID>)-] Get the value selected in a drop-down list.
//>-----------------------------------------------------------------------------------------------------
function GetSelected(src_ID){
  var src = IDget(src_ID);
  var selected = src.selectedIndex;
  return src.options[selected].value;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-GetSelectListValue(<element>)-] Referencing the element (if any), return value selected. (if any)
//>-----------------------------------------------------------------------------------------------------
function GetSelectListValue(el){
  if(IsDefined(el) && IsDefined(el.selectedIndex)
    && IsDefined(el.options[el.selectedIndex].value))
    return el.options[el.selectedIndex].value;
  else return null;
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-GetSelectListText(<element>)-] Referencing the element (if any), return text selected. (if any)
//>-----------------------------------------------------------------------------------------------------
function GetSelectListText(el){
  if(IsDefined(el) && IsDefined(el.selectedIndex)
    && IsDefined(el.options[el.selectedIndex].text))
    return el.options[el.selectedIndex].text;
  else return null;
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-AddOptionValue(<src_ID>,<dst_ID>)-] Add the value selected by `src_ID' to dst_ID
//>-----------------------------------------------------------------------------------------------------
function AddOptionValue(src_ID,dst_ID){
  var src = IDget(src_ID);
  var dst = IDget(dst_ID);
  var selected = src.selectedIndex;
  var n;
  var res = isNaN(dst.value);
  if(res) n = 0;
  else n = parseInt(dst.value,10);
  if(selected){
		n += parseInt(src.options[selected].value,10);
		dst.value = n;
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-SelectedIntValue(<ID>,<useNegative>)-] Get an integer value from a select list widget. If NaN, return 0.
//>-----------------------------------------------------------------------------------------------------
function SelectedIntValue(ID,useNegative){
  var widget = document.getElementById(ID);
  var selected = widget.selectedIndex;
  if(selected < 0) return 0;
  var test;
  if(IsDefined(widget.options[selected].value))
		test = widget.options[selected].value;
  else test = widget.options[selected].text;
  if(!useNegative)
		if(test == "-1") test = "00"
  if(isNaN(test)) return 0;
  else return parseInt(test,10);
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-menu(<attrs>,<option-items>,<headers-array>,<usevalues>)-] Build drop-down list.
//>-----------------------------------------------------------------------------------------------------
function Menu(attrs,items,headers,usevalues){
  var ctrl = document.createElement('select');
  for(var i=0;i < attrs.length;i+=2)
		ctrl.setAttribute(attrs[i],attrs[i+1]);
  var j = 0;
  if(headers){
		for(var i = 0;i < headers.length;i++){
			ctrl.options[i] = new Option(headers[i]);
			j = i;
			}
		j = headers.length;
		}
  for (var i = 0; i < items.length; i++) {
		if(TypeOf(items[i]) == 'array'){
			ctrl.options[j] = new Option(items[i][0]);
			if(usevalues) ctrl.options[j].value = items[i][1];
			}
		else ctrl.options[j] = new Option(items[i]);
		j++;
    }
  return ctrl;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-RemakeMenu(<element>,<option-items>,<array-headers>)-] Rebuild select list.
//>-----------------------------------------------------------------------------------------------------
function RemakeMenu(ctrl,items,headers){
  clearSelectList(ctrl);
  var j = 0;
  if(headers){
		for(var i = 0;i < headers.length;i++){
			ctrl.options[i] = new Option(headers[i]);
			}
		j = headers.length;
		}
  for (var i = 0; i < items.length; i++) {
		if(TypeOf(items[i]) == 'array'){
			ctrl.options[j] = new Option(items[i][0]);
			ctrl.options[j].value = items[i][1];
			}
		else ctrl.options[j] = new Option(items[i]);
		j++;
    }
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-clearAutoRows(<element>,<int-num>)-] Clears rows from parent table of 'element'
//>-----------------------------------------------------------------------------------------------------
function clearAutoRows(widget,num){
  var tbl = getParentTable(widget,"TABLE"); // outer table object
  var lastRow = tbl.rows.length;
  //alert("lastRow: " + lastRow);  // DEBUG GLOBAL
  toClear = lastRow - num;
  if(toClear > 0){
		for(var i = 0;i < toClear;i++){
			tbl.deleteRow(lastRow - 1);
			lastRow--;
			}
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-clearTable(<str-ID>,<int-num>)-] Clear 'num' rows from an autopopulated table
//>-----------------------------------------------------------------------------------------------------
function clearTable(ID,num){
  var tbl = document.getElementById(ID);
  var lastRow = tbl.rows.length;
  toClear = lastRow - num;
  if(toClear > 0){
		for(var i = 0;i < toClear;i++){
			tbl.deleteRow(lastRow - 1);
			lastRow--;
			}
		}
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-getParentTable(<element>,<parent_type>)-]
// @Description Walk "up" the document tree until parent of 'parent_type' is found
//>-----------------------------------------------------------------------------------------------------
function getParentTable(widget,parent_type){
  var widg = widget;
  while(widg.parentNode.tagName.toUpperCase() != parent_type){
		widg = widg.parentNode;
		}
  return widg;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-getParent(<element>,<parent_type>)-]
// @Description Walk "up" the document tree until parent of 'parent_type' is found
//>-----------------------------------------------------------------------------------------------------
function getParent(widget,parent_type){
  var widg = widget;
  while(widg.parentNode.tagName.toUpperCase() != parent_type){
		widg = widg.parentNode;
		}
  return widg;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Button(<str-type>,<str-name>,<attrs-array>)-] 
// @Description Create a button with 'name' and alternating attributes and values
//>-----------------------------------------------------------------------------------------------------
function Button(type,name,attrs){
  var button = document.createElement('input',name);
  button.setAttribute('type',type);
  if(attrs != null){
		for(var i=0;i < attrs.length;i+=2){
			key = attrs[i].toUpperCase();
			switch(key){
				case "CLASS":
					button.className = attrs[i+1];
					break;
				case "BGC":
					button.style.backgroundColor = "lightgreen";
					button.style.Color = "darkred";
					break;
				case "CLR":
					break;
				case "ONMOUSEOVER":
					button.onMouseOver = attrs[i+1];
					break;
				case "ONMOUSEOUT":
					button.onMousOut = attrs[i+1];
					break;
        default:
				button.setAttribute(attrs[i],attrs[i+1]);
				}
			}
		}
  else
		button.setAttribute('value',type);
  return button;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TD(<row-element>,<attrs-array>,<int-ndx>)-]
// @Description Create a table cell element. 'attrs' = alternating attributes and values ===
//>-----------------------------------------------------------------------------------------------------
function TD(row,attrs,ndx){
  if(ndx == null) ndx = 0;
  var td = row.insertCell(ndx);
  if(attrs){
    for(var i=0;i < attrs.length;i+=2){
			td.setAttribute(attrs[i],attrs[i+1]);
			}
		}
  return td;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Font(<attrs-array>,<str-text>)-] Create a text node with font attributes
//>-----------------------------------------------------------------------------------------------------
function Font(attrs,text){
  var fnt = document.createElement('font');
  for (var i=0; i < attrs.length; i+=2)
		fnt.setAttribute(attrs[i],attrs[i+1]);
  fnt.appendChild(Text(text));
  return fnt;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Anchor(<str-URL>,<str-text>,<array-attrs>)-]
// @Description Create anchor element with 'text' and alternating attributes and values
//>-----------------------------------------------------------------------------------------------------
function Anchor(URL,text,attrs){
  var anch = document.createElement('A');
  anch.appendChild(document.createTextNode(text));
  anch.href = URL;
  if (attrs){
    for (var i=0; i < attrs.length; i+=2){
			anch.setAttribute(attrs[i],attrs[i+1]);
			}
		}
  return anch;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-LinkImage(<str-href>,<str-imgsrc>)-] Create anchor element with 'img'
//>-----------------------------------------------------------------------------------------------------
function LinkImage(href,imgsrc){
   var anch = document.createElement('A');
   anch.href=href;
   var img = document.createElement('img');
   img.setAttribute('src',imgsrc);
   anch.appendChild(img);
   return anch;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Table(<array-attrs>)-] Create a table element with attributes 
//>-----------------------------------------------------------------------------------------------------
function Table(attrs){
  var tab = document.createElement('table');
  if (attrs){
    for (var i=0; i < attrs.length; i+=2){
			tab.setAttribute(attrs[i],attrs[i+1]);
			}
		}
  return tab;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-TR(<tbl-element>,<attrs-array>,<int-ndx>)-]
// @Description Create table row 'tbl' = parent, 'attrs' = attributes & values, 'ndx' is ===
//>-----------------------------------------------------------------------------------------------------
function TR(tbl,attrs,ndx){
  if(ndx == null) ndx = tbl.rows.length;
  var tr = tbl.insertRow(ndx);
  if(attrs)
    for(var i=0;i < attrs.length;i+=2)
	  tr.setAttribute(attrs[i],attrs[i+1]);
  return tr;
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-Element(<str-type>,<attrs-array>)-] Create an element of any 'type' and 'attrs' 
//>-----------------------------------------------------------------------------------------------------
function Element(type,attrs){
  var el = document.createElement(type);
  if(attrs){
    for(var i=0;i < attrs.length;i+=2){
			el.setAttribute(attrs[i],attrs[i+1]);
			}
		}
  return el;
	}
// ====================================================================
//       URLEncode and URLDecode functions
//
// Copyright Albion Research Ltd. 2002
// http://www.albionresearch.com/
//
// You may copy these functions providing that 
// (a) you leave this copyright notice intact, and 
// (b) if you use these functions on a publicly accessible
//     web site you include a credit somewhere on the web site 
//     with a link back to http://www.albionresarch.com/
//
// If you find or fix any bugs, please let us know at albionresearch.com
//
// SpecialThanks to Neelesh Thakur for being the first to
// report a bug in URLDecode() - now fixed 2003-02-19.
// ====================================================================
//<------------------------------------------------------------------------------------------------------
// @syntax [-URLEncode(<text>)-] Encode for CGI serialization
//>-----------------------------------------------------------------------------------------------------
function URLEncode(plaintext){
	// The Javascript escape and unescape functions do not correspond
	// with what browsers actually do...
	var SAFECHARS = "0123456789" +					// Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";					// RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";

	//var plaintext = document.URLForm.F1.value;
	var encoded = "";
	for (var i = 0; i < plaintext.length; i++ ) {
		var ch = plaintext.charAt(i);
	    if (ch == " ") {
		    encoded += "+";				// x-www-urlencoded, rather than %20
		} else if (SAFECHARS.indexOf(ch) != -1) {
		    encoded += ch;
		} else {
		    var charCode = ch.charCodeAt(0);
			if (charCode > 255) {
			    alert( "Unicode Character '" 
                        + ch 
                        + "' cannot be encoded using standard URL encoding.\n" +
				          "(URL encoding only supports 8-bit characters.)\n" +
						  "A space (+) will be substituted." );
				encoded += "+";
			} else {
				encoded += "%";
				encoded += HEX.charAt((charCode >> 4) & 0xF);
				encoded += HEX.charAt(charCode & 0xF);
			}
		}
	} // for

	//document.URLForm.F2.value = encoded;
	//return false;
	return encoded;
};
//<------------------------------------------------------------------------------------------------------
// @syntax [-URLEncode()-] Dencode from CGI serialization
//>-----------------------------------------------------------------------------------------------------
function URLDecode( ){
   // Replace + with ' '
   // Replace %xx with equivalent character
   // Put [ERROR] in output if %xx is invalid.
   var HEXCHARS = "0123456789ABCDEFabcdef"; 
   var encoded = document.URLForm.F2.value;
   var plaintext = "";
   var i = 0;
   while (i < encoded.length) {
       var ch = encoded.charAt(i);
	   if (ch == "+") {
	       plaintext += " ";
		   i++;
	   } else if (ch == "%") {
			if (i < (encoded.length-2) 
					&& HEXCHARS.indexOf(encoded.charAt(i+1)) != -1 
					&& HEXCHARS.indexOf(encoded.charAt(i+2)) != -1 ) {
				plaintext += unescape( encoded.substr(i,3) );
				i += 3;
			} else {
				alert( 'Bad escape combination near ...' + encoded.substr(i) );
				plaintext += "%[ERROR]";
				i++;
			}
		} else {
		   plaintext += ch;
		   i++;
		}
	} // while
   document.URLForm.F1.value = plaintext;
   return false;
}
function Quote(text){
}
//<------------------------------------------------------------------------------------------------------
// @syntax [-inAry(<array>,<value>)-] Find index of value in array.
//>-----------------------------------------------------------------------------------------------------
function inAry(ary,val){
  for(var i=0;i<ary.length;i++){
		if(val == ary[i]) return i;
    }
  return -1
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-aryFind(<array>,<value>)-] Find 'value' in 'array'. Deprecated, use 'in' operator.
//>-----------------------------------------------------------------------------------------------------
function aryFind(ary,val){
  var found = inAry(ary,val);
  if(found >= 0) return true;
  else return false;
	}
//		ClearSubTotals(rate_field,hours_field,subtotal_field,prefix,psel,comments_field);
function ClearSubTotals(form,rate_field,hours_field,subtotal_field,prefix,psel,comments_field){
	form[rate_field].value = "";	
	form[hours_field].value = "";
	form[subtotal_field].value = "";
	form[comments_field].value = "";
	psel.selectedIndex = 0;
	return true;
	}
/* TODO: Set subtotal_field.readonly to true if service_selected = 8, to false otherwise */
function RateSubtotal(form,sel,rate_field,hours_field,subtotal_field,
					  grand_total_field,line_items,prefix,psel,comments_field,recalc){
  // RateSubtotal(form0,this,'1)rate','1)hours','1)sub_total','amount',5,'1)')
	// RateSubtotal(form0,                  form
	//              this,                   sel
	//              '2)rate',               rate_field
	//              '2)hours',              hours_field
	//              '2)sub_total',          subtotal_field
	//              'amount',               grand_total_field
	//              5,                      line_items
	//              ')',                    prefix
	//              form0['2)provider_ID']  psel
	//              '2)comments'            comments_field
  var service_selected = sel.selectedIndex;
  var provider_selected = psel.selectedIndex;
/*	alert('form: ' + form + "\nsel: " + sel + "\nrate_field: " + rate_field + "\nhours_field: " + hours_field +
	     "\nsubtotal_field: " + subtotal_field + "\ngrand_total_field: " + grand_total_field + 
			 "\nline_items: " + line_items + "\nprefix: " + "\npsel: " + psel + "\ncomments_field: " + comments_field + 
			 "\nservice_selected: " + service_selected + "\nprovider_selected: " + provider_selected); */
	if(service_selected < 2){
		ClearSubTotals(form,rate_field,hours_field,subtotal_field,prefix,psel,comments_field);
		GrandTotal(form,grand_total_field,line_items,prefix,subtotal_field);
		return true;
		}
	//alert("service_selected: " + service_selected);  // DEBUG GLOBAL
	if(service_selected == 9){
		form[subtotal_field].readOnly = false;	
		if(!recalc){
			form[subtotal_field].value = "";	
			}
		form[hours_field].value = "1:00";
		} 
	else {
		form[subtotal_field].readOnly = true;	
		} 
  var subfield = form[subtotal_field];
  var ratefield = form[rate_field];
  /*if(provider_selected < 2 || service_selected < 2){
		alert("HERE"); 
		subfield.value = "";
		rate_field.value = "";
		GrandTotal(form,grand_total_field,line_items,prefix,subtotal_field);
		return false;
		}  */// nothing to declare
  var ValueSelected = sel.options[service_selected].value;
  var tmp = ValueSelected.split('*'); // should parse to 2 items
  var rate = parseFloat(tmp[1]);
  if(isNaN(rate)){
		alert("Select rate for this line item is not a number!");
		return false;
		}
  var hours = form[hours_field];
  if(StrEmpty(hours.value)){
		hours.focus();
		subfield.value = "";
		GrandTotal(form,grand_total_field,line_items,prefix,subtotal_field);
		return false;
		}
  var hour_tmp = hours.value.split(':')
  if (hour_tmp.length != 2 && service_selected != 9){
	  alert("Function RateSubtotal(): 'hours must be in HH:MM format'");
	  return false;
		}
  ratefield.value = rate;
	var v1 = parseInt(hour_tmp[0],10) * rate;
	var v2 = (parseInt(hour_tmp[1],10) * rate) / 60;
	//alert("hour_tmp: " + hour_tmp +"\nv1: " + v1 + "\nv2: " + v2);  // DEBUG GLOBAL
  //  var val = eval(rate + ' * ' + hours.value);
  var val;
	if(service_selected == 9){
		val = subfield.value;
		}
	else{
		val = v1 + v2
		}
  subfield.value = MoneyRound(val);
  GrandTotal(form,grand_total_field,line_items,prefix,subtotal_field);
	}
function GrandTotal(form,grand_total_field,line_items,prefix,subtotal_field,provider_field){
  var tmp = subtotal_field.split(')');
  var suffix = tmp[1];  // field name suffix
  var line_amount;      // amount for the line
  var fsubtotal;        // subtotal field for line
  var line_number;      // line item number, 1 greater than i
  var total = 0.0;
  for(var i = 0; i < line_items; i++){
		line_number = i + 1;
		fsubtotal = '' + line_number + prefix + suffix;
		line_amount = form[fsubtotal].value;
		if(!StrEmpty(line_amount)){
			total = total + parseFloat(line_amount);
			}
		}
  form[grand_total_field].value = MoneyRound(total);
	}
function mailto_popup(mylink, windowname,across,updown,width,height) {
  var x = 0;
  if(across) x = across;
		var y = 0;
  if(updown) y = updown;
		var w = 640;
  if(width) w = width;
		var h = 480;
  if(height) h = height;
  if (! window.focus)return true;
  var href;
  if (typeof(mylink) == 'string')
		href=mylink;
  else
		href=mylink.href;
  //  href = "http://www.pageresource.com/jscript/jex5.htm"
  var params = 'width=' + w + ',height=' + h + ',scrollbars=yes,resizeable=1';
  var win = window.open(href, windowname, params);
  win.moveTo(x,y);
  return false;
	}
//<------------------------------------------------------------------------------------------------------
// @syntax [-queryElement(<str-query>,<element>)-] Append the name and value of an element to a query.
//>-----------------------------------------------------------------------------------------------------
function queryElement(query,elem){ return queryAppend(query,elem.name,elem.value); }
//<------------------------------------------------------------------------------------------------------
// @syntax [-queryAppend(<str-query>,<element>)-] Append a name and a value to a query.
//>-----------------------------------------------------------------------------------------------------
function queryAppend(query,name,value){
  return query + "&" + URLEncode(name) + "=" + URLEncode(value);
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-ParseForm(form)-] Parse a form:
// Assume all names will be an array, accounting for redundant
//   names.
// For types: select-multiple,select-one,checkbox and radio, will
// have to check for a 'checked' attribute
// The calling function will be a button. The button name will
//   be extracted by and passed any other buttons, or submit
//   buttons will be ignored. 
//>-----------------------------------------------------------------------------------------------------
function ParseForm(frm){
  var test = frm.elements;
  var res = "ELEMENTS\n--------\n"
  for(var i=0;i<test.length;i++){
  	var ele = test[i].name;
		var _type = test[i].type;
		var val = test[i].value;
		res = res + ele + " : " + _type + "=>" + val + "\n";
  	}
  alert(res);
  }
//<------------------------------------------------------------------------------------------------------
// @syntax [-obj = Assoc(<json-object>)-] Manages an Associative Array.
// @Description The array can be converted from and to a string. 
// Pair seperators are '=' and are escaped as '%3D"'
// Key/value seperators are '&' and are escaped by '%26'
// @Public Functions: tostring - writes out as a string
//                    parse - convert a string to a hash
//                    set - set (or reset) and key/value pair
//                    dbg - inspect the object
//>-----------------------------------------------------------------------------------------------------
function Assoc(thisHash){
  this.kvsep = ":";
  this.prsep = "|";
  this.kvsep_escaped = escaped_value(this.kvsep);
  this.prsep_escaped = escaped_value(this.prsep);
  if(thisHash) this.hash = thisHash;
  else this.hash = {};
  }
Assoc.prototype.parse = function(str){
  var pair,key,val;
  var tmp = str.split(this.prsep);
  for(var i = 0;i < tmp.length;i++){
    pair = tmp[i].split(this.kvsep);
		key = this.decode(pair[0]);
		val = this.decode(pair[1]);
    this.hash[key] = val;
    }
  }
Assoc.prototype.encode = function(v){
  var tmp = "" + v;
  tmp = tmp.replace(this.kvsep,this.kvsep_escaped);
  tmp = tmp.replace(this.prsep,this.prsep_escaped);
  return tmp;
  }
Assoc.prototype.decode = function(v){
  var tmp = "" + v;
  tmp = tmp.replace(this.prsep_escaped,this.prsep);
  tmp = tmp.replace(this.kvsep_escaped,this.kvsep);
  return tmp;
  }
Assoc.prototype.encode_key = function(k){return this.encode(k);}
Assoc.prototype.encode_val = function(k){return this.encode(this.hash[k]);}
Assoc.prototype.decode_key = function(k){return this.decode(k);}
Assoc.prototype.decode_val = function(k){return this.decode(this.hash[k]);}
Assoc.prototype.tostring = function(){
  var tmp = "";
  for(var k in this.hash){
    tmp = tmp + this.encode_key(k) + this.kvsep + this.encode_val(k) + this.prsep;
    }
  return tmp.substring(0,-1 + tmp.length);
  }
Assoc.prototype.dbg = function(){
  var tmp = "";
  for(var key in this.hash){
		tmp = tmp + "key [" + key + "] value [" + this.hash[key] + "]\n";
  	}
  if(tmp == "") tmp = 'Assoc object has no data';
  alert(tmp);
  }
Assoc.prototype.keys = function(){
	res = new Array();
	for(var key in this.hash){
		res.push(key)
		}
	return res;
	}
Assoc.prototype.set = function(key,val){ this.hash[key] = val; }
Assoc.prototype.get = function(key){return this.hash[key]; }
Assoc.prototype.unset = function(key){delete this.hash[key];}
function escaped_value(c){
  var h = null;
  c = c.charAt(0); // restrict input to a single character
  for (var i = 0; i < 256; ++ i){ // check all ASCII values
    h = i.toString(16).toLowerCase(); // convert i into a 2-digit hex string
		if (h.length == 1) h = "0" + h;
			h = "%" + h; // insert a % character into the string
			if (unescape(h) == c) break; // converted value matches
		}
  return h;
  }
