<!--

/* This script and many more are available free online at
The JavaScript Source!! http://javascript.internet.com
Created by: Ultimater :: http://ultimiacian.tripod.com/
Define getElementById for use by IE/Netscape 4
These days, it's still useful to support Windows Mobile IE
Note that unlike the real getElementById, this will cause
errors if more than one element exists with same id */

if (!document.getElementById) {
  if (document.all) {
      function getElementById() {
        if(typeof document.all[arguments[0]]!="undefined") {
            return document.all[arguments[0]]
        } else {
            return null
        }
      }
      try { 
        document.getElementById = getElementById; 
      } catch (exception) { 
        // Alex 2006-10-08: As it turns out Windows Mobile IE can't really add to the document prototype
        // As a result, document.getElementById() will NOT work but just getElementById() WILL work
      } 
  } else if(document.layers) {
      document.getElementById=function(){
        if(typeof document[arguments[0]]!="undefined") {
            return document[arguments[0]]
        } else {
            return null
        }
      }
  }
}



function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}

function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_findObj(n, d) { //v4.0 - Alex 2006-10-08: THIS MAY BE NON-OPTIMAL FOR NEWER BROWSERS - SHOULD TRY getElementById FIRST
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && document.getElementById) x=document.getElementById(n); return x;
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}



// Create a repeat-string-N-times method for all String objects
function str_repeat(n) {
   if (typeof(n) == 'undefined' || !n) { return ''; }
   var s = "", t = this.toString()
   while (--n >= 0) s += t
   return s
}

String.prototype.repeat = str_repeat;


/* 
 * Add ltrim, rtrim, and trim functions to strings
 * JCR: ISN'T WORKING IN IE 6 FOR SOME REASON, COMMENTING OUT 
 * AND REPLACING SOLE USE BELOW.
 */
/*
function am_strltrim() {
    return this.replace(/^\s+/,'');
}
function am_strrtrim() {
    return this.replace(/\s+$/,'');
}
function am_strtrim() {
    return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
String.prototype.ltrim = am_strltrim;
String.prototype.rtrim = am_strrtrim;
String.prototype.trim = am_strtrim;
*/





/* Prototype Array methods.  These become a part of every array defined on this page */
/* JCR - Copied from signup-accessors.js */

// Appends the new element to the end of the array.
function arrayAppend(newElem) {
    this.length = this.length + 1;
    this[this.length - 1] = newElem;
    return this;
}

// Returns 0-indexed position of the elem in the array.
// If not found, returns 0;
function arrayIndexOf(elem) {
    for (var i=0; i < this.length; i++) {
        if (this[i] == elem) {
            return i;
        }
    }
    return -1;
}

// Returns true or false.
function arrayHasElement(elem) {
    var str = this.toString();
    var regex = eval('/\\b' + elem + '\\b/');
    return(str.search(regex) > -1);
}

function arrayToString() {
    if (this.length) {
        return this.join(' ');
    } else {
        return ""
    }
}

// Used for comparing numeric values.
function compare(a,b) {
    return a-b;
}

// Returns intersection of two arrays of numeric values
// as a third array.  This function always returns an array
// which may be empty.
function arrayIntersect(arr) {
    var intersect = new Array();
    if (arr.length) {
        if (this.length > 0 && arr.length > 0) {
            // each is a non-zero length array.

            // make copies so we don't sort the original.
            var arr1sorted = new Array();
            arr1sorted = arr1sorted.concat(this);            
            var arr2sorted = new Array();
            arr2sorted = arr2sorted.concat(arr);
            
            arr1sorted.sort(compare);
            arr2sorted.sort(compare);
            var i=0;
            var j=0;
            while ((i<arr1sorted.length) && (j < arr2sorted.length)) {
                if (arr1sorted[i] < arr2sorted[j]) {
                    i++;
                } else if (arr1sorted[i] > arr2sorted[j]) {
                    j++;
                } else {
                    intersect.append(arr1sorted[i]);
                    i++; j++;
                }
            }
        }
    }
    return intersect;
}

// Returns union of two arrays of numeric values
// as a third array.  This function always returns an array
// which may be empty.
function arrayUnion(arr) {
    var union = new Array();
    union = union.concat(this)
    if (arr.length) {
        var arrlen = arr.length
        for(var i=0; i < arrlen; i++) {
            var val = arr[i]
            if(!union.hasElement(arr[i])) {
                //add it to union
                union[union.length] = arr[i];
            }
        }
    }
    return union;
}

Array.prototype.hasElement = arrayHasElement;
Array.prototype.toString = arrayToString;
Array.prototype.intersect = arrayIntersect;
Array.prototype.union = arrayUnion;
Array.prototype.append= arrayAppend;
Array.prototype.indexOf = arrayIndexOf;



// Array Extensions  v1.0.5
// documentation: http://www.dithered.com/javascript/array/index.html
// license: http://creativecommons.org/licenses/by/1.0/
// code by Chris Nott (chris[at]dithered[dot]com)


function isUndefined(property) {
  return (typeof property == 'undefined');
}

// Array.concat() - Join two arrays
if (isUndefined(Array.prototype.concat) == true) {
  Array.prototype.concat = function (secondArray) {
  	var firstArray = this.copy();
  	for (var i = 0; i < secondArray.length; i++) {
  		firstArray[firstArray.length] = secondArray[i];
  	}
  	return firstArray;
  };
}

// Array.copy() - Copy an array
if (isUndefined(Array.prototype.copy) == true) {
  Array.prototype.copy = function() {
  	var copy = new Array();
  	for (var i = 0; i < this.length; i++) {
  		copy[i] = this[i];
  	}
  	return copy;
  };
}

// Array.pop() - Remove the last element of an array and return it
if (isUndefined(Array.prototype.pop) == true) {
  Array.prototype.pop = function() {
  	var lastItem = null;
    if ( this.length > 0 ) {
        lastItem = this[this.length - 1];
        this.length--;
    }
    return lastItem;
  };
}

// Array.push() - Add an element to the end of an array
if (isUndefined(Array.prototype.push) == true) {
  Array.prototype.push = function() {
  	var currentLength = this.length;
  	for (var i = 0; i < arguments.length; i++) {
  		this[currentLength + i] = arguments[i];
  	}
  	return this.length;
  };
}

// Array.shift() - Remove the first element of an array and return it
if (isUndefined(Array.prototype.shift) == true) {
  Array.prototype.shift = function() {
  	var firstItem = this[0];
  	for (var i = 0; i < this.length - 1; i++) {
  		this[i] = this[i + 1];
  	}
  	this.length--;
  	return firstItem;
  };
}

// Array.slice() - Copy several elements of an array and return them
if (isUndefined(Array.prototype.slice) == true) {
  Array.prototype.slice = function(start, end) {
  	var temp;
  	
  	if (end == null || end == '') end = this.length;
  	
  	// negative arguments measure from the end of the array
  	else if (end < 0) end = this.length + end;
  	if (start < 0) start = this.length + start;
  	
  	// swap limits if they are backwards
  	if (end < start) {
  		temp  = end;
  		end   = start;
  		start = temp;
  	}
  	
  	// copy elements from array to a new array and return the new array
  	var newArray = new Array();
  	for (var i = 0; i < end - start; i++) {
  		newArray[i] = this[start + i];
  	}
  	return newArray;
  };
}

// Array.splice() - Splice out and / or replace several elements of an array and return any deleted elements
if (isUndefined(Array.prototype.splice) == true) {
  Array.prototype.splice = function(start, deleteCount) {
  	if (deleteCount == null || deleteCount == '') deleteCount = this.length - start;
  	
  	// create a temporary copy of the array
  	var tempArray = this.copy();
  	
  	// Copy new elements into array (over-writing old entries)
  	for (var i = start; i < start + arguments.length - 2; i++) {
  		this[i] = arguments[i - start + 2];
  	}
  	
  	// Copy old entries after the end of the splice back into array and return
  	for (var i = start + arguments.length - 2; i < this.length - deleteCount + arguments.length - 2; i++) {
  		this[i] = tempArray[i + deleteCount - arguments.length + 2];
  	}
  	this.length = this.length - deleteCount + (arguments.length - 2);
  	return tempArray.slice(start, start + deleteCount);
  };
}

// Array.unshift - Add an element to the beginning of an array
if (isUndefined(Array.prototype.unshift) == true) {
  Array.prototype.unshift = function(the_item) {
  	for (loop = this.length-1 ; loop >= 0; loop--) {
  		this[loop+1] = this[loop];
  	}
  	this[0] = the_item;
  	return this.length;
  };
}

/* END prototype Array methods */













/**
 * Takes an element and a pattern, returns an array with the 
 * ids of any child elements whose ids match the specified pattern.
 * Operates recursively.
 */
function am_getChildrenIdsMatching(el, pattern) {

    if (el.childNodes.length == 0) { return new Array(); }

    var ids = new Array();
    var children = el.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].id && children[i].id.match(pattern)) {
            ids.push(children[i].id);
        }
        ids = ids.concat(am_getChildrenIdsMatching(children[i], pattern));
    }
    return ids;
}

/**
 * Takes an element and an optional array of tagNames (not case sensitive)
 * returns an array with all non-TextNode child objects, optionally
 * narrowed down to only those tag types specified.
 * Operates recursively.
 */
function am_getAllChildrenTags(el,tags) {
    if (el.childNodes.length == 0) { return new Array(); }

    var objects = new Array();
    var children = el.childNodes;
    
    if (typeof(tags) == 'undefined') {
        for (var i = 0; i < children.length; i++) {
            if (children[i].tagName) {
                objects.push(children[i]);
            }
            objects = objects.concat(am_getAllChildrenTags(children[i]));
        }
    } else {
        for (var i = 0; i < children.length; i++) {
            if (am_isElementOfType(children[i],tags)) {
                objects.push(children[i]);
            }
            objects = objects.concat(am_getAllChildrenTags(children[i], tags));
        }
    }
    return objects;
}

/**
 * Get the innerText of an element.
 * elem_or_elemID - either the id or the reference to the element whose text we're getting.
 * In IE 5+ you can use the innerText property. 
 */
function am_getInnerText(elem_or_elemID) {
	var element = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
    var text = "";
    var children = element.childNodes;
    if (element.nodeType == 3) {
        return element.nodeValue;
    }
    for (var i = 0; i < children.length; i++) {
        text = text + am_getInnerText(children[i]);
    }
    return text;
}

/**
 * Sets innerText for either Netscape/Mozilla or IE
 * elem_or_elemID - either the id or the reference to the element whose text we're setting.
 * value - the text string that will become the contents of the specified element.
 */
function am_setInnerText(elem_or_elemID, value) {
	var myElem = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
	if (myElem) {
	    if (document.all) { // IE
	    	myElem.innerText = value;
	    } else { // Moz & Netscape
		    var parsedText = document.createTextNode(value);
		    myElem.innerHTML = "";
		    myElem.appendChild( parsedText );
	    }
    }
}
function am_setInnerHTML(elem_or_elemID, value) {
	var myElem = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
	if (!myElem) { return; }
	myElem.innerHTML = value;
}


/**
 * Returns a handle to the frame named 'framename'.  If no such frame
 * exists, returns NULL.  If more than one frame with that name exists,
 * behavior is undefined.  The 'startframe' argument is optional, and
 * is used to specify the starting frame, e.g. top.frame1.frame2.  
 * The default is to start the search from the topmost frame.  The
 * purpose of this function is to allow e.g. setting the text of an
 * element in a particular frame regardless of the caller's frame depth
 */
function am_findFrame(framename, startframe) {
    if (typeof(startframe) == 'undefined') {
        var startframe = 'top';
    }
    var aFrames = eval(startframe).frames;
    if (aFrames[framename]) {
        return aFrames[framename];
    } else if (aFrames.length) {
        for (var i = 0; i < aFrames.length; i++) {
            var recurseName = aFrames[i].name;
            if (recurseName == startframe) { continue; }
            var recurseFound = am_findFrame(framename, startframe + '.' + recurseName);
            if (recurseFound) return recurseFound;
        }
    }
    return null;
}

/**
 * Set the inner text in a sibling frame.  If no frame of that name
 * exists, silently succeeds.  Replaces setTextFrame in 
 * u-admin-structure.js
 */
function am_setTextFrame(framename, elementID, value) {
    var f = am_findFrame(framename);
    if (!f) { return; }
	var myElem = f.document.getElementById(elementID);
	am_setInnerText(myElem, value);
}

/**
 * Typically used like: <body onload="am_onClose('closeHandling()')">
 * This proc is to help work around the problem of the unload handler 
 * not being called when you close the whole window (like a popup).
 * On IE this proc also tries to determine if the unload was caused by 
 * a refresh or by someone closing the window, and doesn't evaluate 
 * the specified code on a refresh.
 */
function am_onClose(code) {
    var ie = navigator.userAgent.toLowerCase().indexOf("msie") > -1;
    if (ie) {
        window.onbeforeunload = function () {
            if (event.clientY >= 0) { return; }
            eval(code);
        };
    } else {
        window.onunload = function () { eval(code); }
    }
}

/**
 * Given a DOM object, replaces occurrences of oldClass in the className
 * attribute with newClass.  Case-insensitive match of oldClass.
 *
 * oldClass - assumed to only have characters that are valid as normal characters
 *        (non-escaped) within a regular expression
 *         oldClass will not be matched on for partial strings.
 *
 * Previously named replaceClassName in utility.js, we should switch
 * everything to use this version, which can take element ids in
 * addition to elements.
 */
function am_replaceClassName(elem_or_elemID, oldClass, newClass) {
	var obj = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
	if (obj) {
    	var regexp = eval("/\\b" + oldClass + "\\b/i");
		obj.className = obj.className.replace(regexp, newClass);
    }
}

/**
 * Look for a string in an array of strings, ignoring case.
 */
function am_findStrInArray(str, arr) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].toLowerCase() == str.toLowerCase()) {
            return str;
        }
    }
    return null;
}

/**
 * Given an element (or its id) returns true if the
 * element's tagName is present in a non-case-sensitive
 * array of tagNames.  In addition, tagName of INPUT
 * supports matching on the specific input type
 * by specifying the tagName as INPUT:text
 * (The : separator character can be overriden in
 * an optional third argument)
 */
function am_isElementOfType(elem_or_elemID, tags) {
	var element = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
    if (!element.tagName ) {return false}
    var tag = element.tagName;
    if (am_findStrInArray(tag, tags)) {return true}
    if (tag == "INPUT") {
        var sepChar = arguments[3] || ':';
        tag = "INPUT" + sepChar + element.type;
        if (am_findStrInArray(tag, tags)) {return true}
    }
    return false; 
}


/*
 * Returns the selected value of a radio button group
 */
function am_getRadioValue(formName,radioName) {
    for (var i=0; i < document[formName][radioName].length; i++) {
        if (document[formName][radioName][i].checked) {
            return document[formName][radioName][i].value;
        }
    }
    return "";
}


/* 
 * Takes three arguments, textClass, imageClass, and formId.
 * textClass - Finds all elements having this class.
 * imageClass - For each matching element, modify the first img having 
 *     the imageClass class.
 * formId - Find the form having the formId ID to determine what the 
 *     image src change should be.  The change is determined by the
 *     combination of the (case-insensitively) matching span's inner
 *     text and the class of the paired tags' parent.  See example
 *     below.
 *
 * We have to do extra processing because elements can have multiple
 * space-separated classes, in which case we use the first found match
 * in the form.
 *
 * Form structure:
 *
 * <div style="display:none">
 * <form id="formId">
 *     <select name="classname">
 *         <option value="images/img1.gif">Yes</option>
 *         <option value="images/img2.gif">No</option>
 *         <option value="images/img3.gif"></option>
 *     </select>
 *     <select name="otherclass">
 *        ...
 *     </select>
 * </form>
 * </div>
 *
 * Note that storing state/image src infomation in a hidden form is a
 * hack.  Ideally we would use XML data islands but these are not yet 
 * supported by Netscape.  (It doesn't support namespaced custom tags
 * either.)
 */
function am_TextToImg(textClass, imageClass, formId) {
    // Find the form that defines our behavior
    var texttoimage = document.getElementById(formId);
    var selects = texttoimage.getElementsByTagName('SELECT');
    // Get all elements in a cross-browser way
    var aElements = document.all ? document.all
                                 : document.getElementsByTagName('*');
    for (var i = 0; i < aElements.length; i++) {

        // We only operate on elements that have the passed in textClass
        var textElement = aElements[i];
        if (!am_findStrInArray(textClass, textElement.className.split(' '))) { continue; }

        // Get the parent's class.  That, combined with element's inner
        // text determines image to display
        var parent = textElement.parentNode;
        var pclass = parent.className;

        // Get the first img tag that is a child of the text element's
        // parent and has the imageClass we passed in
        var img = null;
        var imgs = parent.getElementsByTagName('IMG');
        for (var j = 0; j < imgs.length; j++) {
            if (am_findStrInArray(imageClass, imgs[j].className.split(' '))) {
                img = imgs[j];
            }
        }
        if (null == img) { continue; }

        var state = am_getInnerText(textElement).toUpperCase();
        var found = false;
        for (var j = 0; j < selects.length; j++) {
            if (!am_findStrInArray(selects[j].name, pclass.split(' '))) { continue; }
            var options = selects[j].getElementsByTagName('OPTION');
            for (var k = 0; k < options.length; k++) {
                var optionState = am_getInnerText(options[k]).toUpperCase();
                if (state == optionState) {
                    found = true;
                    img.setAttribute('src', options[k].value);
                    if (img.getAttribute('src') == '') {
                        am_hide(img);
                    } else {
                        am_show(img);
                    }
                    am_hide(textElement);
                    break;
                }
            }
            if (found) { break; }
        }
    }
}

/**
 * Helper function like Tcl am_boolvar
 */
function am_boolvar(ob, nullReturn) {
    if (typeof(nullReturn) == 'undefined') {
        var nullReturn = false;
    }
	if (!isString(ob)) { ob ? true : false }
	if (ob == '') { return nullReturn; }
    ob = ob.toLowerCase();
    if (ob == '0' || ob == 'f' || ob == 'false' || ob == 'off' || ob == 'no' || ob == 'n') {
        return false;
    }
    return true;
}


/**
 * Return the domain of a hostname.
 *
 * Last Modified: 2004-02-01
 * Last Modified By: Jamie Rasmussen
 * Original Author: Jamie Rasmussen  (Ported from Tcl version)
 *
 * If host is an IP address, return that IP.
 * Sample output:
 *   18.202.0.68      ->  18.202.0.68
 *   68.123.127.18    ->  68.123.127.18
 *   999.999.999.999  ->  999.999.999.999
 *   am.net           ->  am.net
 *   abc.foo.bar.com  ->  foo.bar.com
 *   am.co.uk         ->  am.co.uk
 *   www.ci.la.ca.us  ->  ci.la.ca.us
 *   localhost        ->  localhost
 *   127.0.0.1:84     ->  127.0.0.1:84
 *
 * Note that IP addresses must be in decimal format, and that the proc
 * doesn't check that the IP address is in the correct range.  (We could
 * do that with a regexp, but it would be slower and very ugly.)
 * Returns passed-in hostname if can't extract domain.
 * Any port specified is passed through
 */
function am_domainFromHost (host) {

    var m = null;
    if (host.match("\\.\\d+(?:\\:\\d+)?$")) { // IP Addresss
        return host;
    } else if (host.match("^[^\\.]+\\.[^\\.]+\\.\\w\\w(?:\\:\\d+)?$")) { // Two-letter TLD
        return host;
    } else if (host.match("^[^\\.]+\\.[^\\.]+(?:\\:\\d+)?$")) {
        return host;
    } else if (m = host.match("^[^\\.]+\\.(.*)(?:\\:\d+)?$")) {
        return m[1];
    }
    return host;
}

/**
 * Last Modified: 2004-06-17
 * Last Modified By: Jeff Huber
 * Original Author: Jeff Huber (based on update_enableForm from server-check)
 *
 * Enables or disables a form. To disable a form
 * we set all the disabled property of all elements. For textboxes we set 
 * the class to disabledClassForTB if one is specified. To enable a form
 * we set all its input elements to false and set textboxes class to 
 * enabledClassForTB if it specified
 *
 * Parameters:
 * formObj - the form object
 * enableOrDisable - 1 or 0 for enable or disable respectivly
 * TBClass - clasnamme to set textboxes to. if null will not change
 * textboxes class
 */
function am_enableOrDisableForm(formObj, enableOrDisable, TBClass) {

	var inputs = formObj.elements
	var numelements = inputs.length
	for(var i=0; i<numelements; i++) {
		var input = inputs.item(i)
        input.disabled = !enableOrDisable;
        if (input.type == 'text') {
            input.className = TBClass;
        }
    }	
}

/**
 * encodeURI doesn't replace escape - it doesn't encode any of 
 * "&", ":", "/", ";", and "?". (The non-encoding of "&" is missing
 * in the documentation?)  We use encodeURIComponent instead.
 */
function am_urlencode(str) {
    return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
}
function am_urldecode(str) {
    return window.decodeURIComponent ? decodeURIComponent(str) : unescape(str);
}
function am_escapeRegexp(re) {
    return re.replace(/(\.|\*|\?|\+|\[|\]|\(|\)|\^|\$|\\|\|)/g,'\\$1');
}


/**
 * COOKIE MANIPULATION FUNCTIONS
 *
 * Some functions inspired by:
 *  http://www.webreference.com/js/column8/functions.htmlE
 *  http://www.codehouse.com/javascript/scripts/cjl/cookie/
 *
 */

/**
 * Returns an array of names of cookies.
 * Would it be useful for this to take an optional pattern argument?
 * Probably not considering the limits on number of cookies.
 */
function am_listCookies() {
    var names = new Array();
    var cookies = document.cookie.split(/\s*[,;]\s*/);
    for (var i=0; i < cookies.length; i++) {
        var cookiename = cookies[i].split(/\s*=\s*/)[0];
        names.push(am_urldecode(cookiename));
    }
    return names;
}

/**
 * Return 0 or 1 when the cookie specified does/does not exist.
 */
function am_existsCookie(name) {
    name = name.toLowerCase();
    var encname = am_urlencode(name);
    var cookienames = am_listCookies();
    for (var i=0; i<cookienames.length; i++) {
        var cn = cookienames[i].toLowerCase();
        if (cn == name || cn == encname) {
            return 1;
        }
    }
    return 0;
}

/**
 * Returns the first cookie named 'name' (case-insensitive).
 * If no such cookies, returns the value given in the optional "def" 
 * argument.  def defaults to null.  Also takes an optional "decode"
 * argument which defaults to 0, corresponding with the -decode switch
 * in the Tcl version.  If decode is true, will urldecode the value.
 *
 * The name should not be urlencoded.  If the cookie name matches the
 * URI encoding of name, it will be included in the results.  The
 * default will be returned as is, so encode it or not as fits the
 * situation.
 */
function am_getCookie(name, def, decode) {

    if (typeof(def) == 'undefined') {
        var def = null;
    }
    if (typeof(def) == 'undefined') {
        var decode = 0;
    }

    var m = null;
    var cookies = document.cookie.split(/\s*[,;]\s*/);
    var re = new RegExp('^(' + am_escapeRegexp(name) + '|' + am_urlencode(name) + ')\\s*=\\s*([^;]*)', 'i');
    for (var i=0; i<cookies.length; i++) {
        if (m = cookies[i].match(re)) {
            var val = m[2];
            if (val == "\"\"") return '';
            if (decode) { val = am_urldecode(val); }
            return val;
        }
    }

    return def;
}

/**
 * Sets a cookie, mostly compatible with the Tcl proc of the same name.
 *
 * name - required
 *
 * value - optional, default is literal ""
 *
 * maxAge - optional, specifies the maximum age of the cookies in
 *   seconds (consistent with RFC 2109). maxAge "infinite" 
 *   (case-insensitive) or any abbreviation of that string specifies 
 *   cookies that never expire.  The default behavior is to issue 
 *   session cookies.
 *
 * domain - optional, specifies the domain(s) to which this cookie 
 *   applies.  See RFC2109 for the semantics of this cookie attribute.
 *   (Basically must start with a dot and contain at least two dots.)
 *   By default, this is not set and the browser will use the current 
 *   FQDN host name.  If you set domain to ".", then we will attempt 
 *   to derive the .domain from the current location.host.
 *
 * path - optional, specifies a subset of URLs to which this cookie
 *   applies. It must be a prefix of the URL being accessed. (Default
 *   is "/")
 *
 * secure - optional, specifies to the user agent that the cookie 
 *   should only be transmitted back to the server if https. (-secure
 *   switch in Tcl version.)  Default is no, anything other than '' is
 *   considered yes.
 *
 * noencode - optional, indicates that we should not urlencode the 
 *   name/value.  (-noencode switch in Tcl version.)  Default is no
 *   (encode both), anything other than '' is considered yes.
 *
 * httponly - optional, boolean (Microsoft extension) indicates to the 
 *   client that the cookie is "non-scriptable" and should not be  
 *   revealed to the client applications via the WinInet  
 *   InternetGetCookie function.
 *
 * Other differences from Tcl version:
 *  -expire    Use am_deleteCookie instead
 *  -fromset   Not directly applicable but may want equivalent
 *  -replace   Not implemented, not really applicable (no such thing as outputheaders in JS)
 */
function am_setCookie(name, value, maxAge, domain, path, secure, noencode, httponly) {

    if (typeof(value) == 'undefined') { var value = ''; }
    if (!noencode) {
        name = am_urlencode(name);
        value = am_urlencode(value);
    }
    if (value == '') {
        var value = "\"\"";
    }

    var expires = null;
    if (typeof(maxAge) == 'undefined' || maxAge == '') {
        maxAge = null;
    } else if (String(maxAge).toLowerCase().match(/^i.*/)) {
        // netscape seemed unhappy with huge max-age, so we use
        // expires which seems to work on both netscape and IE
        maxAge = null;
        expires = new Date(2035, 0, 1);
    } else {
        // Max-Age appears to be ignored by IE6, so use the Expire header in addition.
        expires = new Date(new Date().getTime() + 1000*maxAge);
    }

    if (typeof(domain) == 'undefined') {
        var domain = '';
    } else if (domain == '.') {
        var domain = '.' + am_domainFromHost(location.host);
    }
    if (typeof(path) == 'undefined' || path == '') {
        var path = '/';
    }


    var curCookie = name + "=" + value +
      ((expires) ? "; expires=" + expires.toGMTString() : "") +
      ((maxAge) ? "; Max-Age=" + maxAge : "") +
      "; path=" + path +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : "") +
      ((httponly) ? "; HttpOnly" : "");

    document.cookie = curCookie;
}

/**
 * Sets the cookie to a past date, deleting it.
 * Parameters are passed to am_existsCookie and am_setCookie.
 */
function am_deleteCookie(name, domain, path) {
    var maxAge = new Date(1970, 0, 1).getTime() - new Date().getTime();
    if (am_existsCookie(name)) {
        am_setCookie(name, '', maxAge, domain, path);
    }
}

/**
 * Summary:
 *   var BigCookie = new am_multiCookie("BIGCOOKIE");
 *   BigCookie.exists();
 *   BigCookie.expire();
 *   BigCookie.setSubValue(key, ?value?);
 *   BigCookie.getSubValue(key, ?default?);
 *   BigCookie.keys();
 *   BigCookie.values();
 *
 *   Arguments are close to Tcl cookie procs.
 *   Path defaults to / (i.e. entire site)
 *   Domain defaults to FQDN, can get domain-only portion of the 
 *     current FQDN location.host by passing '.'
 *   Configurable separatorChar to specify the subvalue separator charactor (default is '&')
 *   Configurable joinChar to specify the key-value separator (default is '=')
 *
 * See am_setCookie for more information on arguments.
 *
 * Todo: rewrite to call setCookie, giving us more consistency on features / arguments?
 *       possibly not as we want to minimize code between get and set.  Might be useful
 *       to have a deleteSubValue(key) function? (Deleting both key and value)
 */
function am_multiCookie(name, maxAge, domain, path, secure, separatorChar, joinChar) {
//alert(name + ' is name, ' + maxAge + ' is maxAge, ' + domain + ' is domain, ' + path + ' is path, ' + secure + ' is secure, ' + separatorChar + ' is separatorChar, ' + joinChar + ' is joinChar');
    this.affix = "";

    var expires = null;
    if (typeof(maxAge) == 'undefined' || maxAge == '') {
        maxAge = null;
    } else if (String(maxAge).toLowerCase().match(/^i.*/)) {
        // netscape seemed unhappy with huge max-age, so we use
        // expires which seems to work on both netscape and IE
        maxAge = null;
        expires = new Date(2035, 0, 1);
    } else {
        // Max-Age appears to be ignored by IE6, so use the Expire header in addition.
        expires = new Date(new Date().getTime() + 1000*maxAge);
    }
    this.affix += ((expires) ? "; expires=" + expires.toGMTString() : "") +
                 ((maxAge) ? "; Max-Age=" + maxAge : "");

    if (typeof(domain) == 'undefined' || domain == '') {
    } else if (domain == '.') {
        this.affix += "; domain=." + am_domainFromHost(location.host);
    } else {
        this.affix += "; domain=" + domain;
    }

    if (path) {
        this.affix += "; path=" + path;
    } else {
        this.affix += "; path=/";
    }
   
    if (secure) {
        this.affix += "; secure=" + secure;
    }

    if (typeof(separatorChar) == "undefined" || separatorChar == '') {
        separatorChar = '&';
    }
    if (typeof(joinChar) == "undefined" || joinChar == '') {
        joinChar = '=';
    }

    // Takes the name of the cookie we are interested in, pulls it from
    // the list of all cookies.  Takes one optional argument, justValue.
    // If justValue is true (default) only returns the value portion of
    // the cookie.  Otherwise returns the whole cookie (e.g. name=value).
    function getCookie(justValue) {
        var m = document.cookie.match(new RegExp("(" + am_escapeRegexp(name) + '|' + am_urlencode(name) + "\\s*=\\s*([^,;]*))([,;]|$)", "i"));
        var index = 1;
        if (justValue)
            index = 2;
        return m ? m[index] : null;
    }

    this.exists = function() {
        return am_existsCookie(name);
    }

    this.expire = function() {
        return am_deleteCookie(name, domain, path);
    }

    this.setSubValue = function(key, value) {
        key = am_urlencode(key);

        if (typeof(value)=='undefined' || value == '') {
            value = "\"\"";
        } else {
            value = am_urlencode(value);
        }

        var attrPair = separatorChar + key + joinChar + value;

        var ck = getCookie(false);
        if (ck) {
            // Cookie exists
            if (ck.match(new RegExp(am_escapeRegexp(separatorChar + key + joinChar)))) {
                // There is already a key-value pair matching this key
                document.cookie =
                    ck.replace(new RegExp(am_escapeRegexp(separatorChar + key + joinChar) + "[^" + separatorChar + ";]*"), attrPair) + this.affix;
            } else {
                // Append this key-value pair to the cookie value
                document.cookie =
                    ck.replace(new RegExp("(" + name + "=[^;]*)(;|$)"), "$1" + attrPair) + this.affix;
            }
        } else {
            document.cookie = name + "=" + attrPair + this.affix;
        }
    }

    // Given a key, returns its value in this cookie.  If the cookie or
    // key doesn't exist, returns the default value specified by 'def'.
    // If def isn't given, default is null.
    this.getSubValue = function(key, def) {
        key = am_urlencode(key);
        if (typeof(def) == 'undefined') {
            var def = null;
        }
        var ck = getCookie(false);
        if (!ck) return def;
        var m = ck.match(new RegExp(am_escapeRegexp(separatorChar + key + joinChar) + "([^" + separatorChar + ";]*)"));
        if (!m) return def;
        var value = m[1];
        if (!value) return def;
//        value = am_urldecode(value);
        if (value == "\"\"") return '';
        return value;
    }

    // Returns an array of the keys stored in this cookie.
    this.keys = function() {
        var k = new Array();
        var ck = getCookie(true);
        if (!ck) return k;

//        var pairs = ck.split(new RegExp(am_escapeRegexp(separatorChar) + "|" + am_urlencode(separatorChar)));
        var pairs = ck.split(new RegExp(am_escapeRegexp(separatorChar)));
        // Handle case of value starting with separator char
        if (pairs[0] != '') {
//            var p = pairs[0].split(new RegExp(am_escapeRegexp(joinChar) + "|" + am_urlencode(joinChar)));
            var p = pairs[0].split(new RegExp(am_escapeRegexp(joinChar)));
            k.push(am_urldecode(p[0]));
        }
        for (var i=1; i < pairs.length; i++) {
//            var p = pairs[i].split(new RegExp(am_escapeRegexp(joinChar) + "|" + am_urlencode(joinChar)));
            var p = pairs[i].split(new RegExp(am_escapeRegexp(joinChar)));
            k.push(am_urldecode(p[0]));
        }
        return k;
    }

    // Returns an array of the values stored in this cookie.
    this.values = function() {
        var v = new Array();
        var ck = getCookie(true);
        if (!ck) return v;
//        var pairs = ck.split(new RegExp(am_escapeRegexp(separatorChar) + "|" + am_urlencode(separatorChar)));
        var pairs = ck.split(new RegExp(am_escapeRegexp(separatorChar)));
        // Handle case of value starting with separator char
        if (pairs[0] != '') {
//            var p = pairs[0].split(new RegExp(am_escapeRegexp(joinChar) + "|" + am_urlencode(joinChar)));
            var p = pairs[0].split(new RegExp(am_escapeRegexp(joinChar)));
            v.push(p[1]);
        }
        for (var i=1; i < pairs.length; i++) {
//            var p = pairs[i].split(new RegExp(am_escapeRegexp(joinChar) + "|" + am_urlencode(joinChar)));
            var p = pairs[i].split(new RegExp(am_escapeRegexp(joinChar)));
            v.push(p[1]);
        }
        return v;
    }
}



/**
 * Helper function to get the path of frame names down to the current
 * page in the form .frame1.frame2.frame3, where frame1 would contain
 * frame2 which would contain frame3.
 */
function am_getFramePath(frame) {
    // It might be better to explicitly use window instead of this
    if (typeof(frame) == 'undefined') { var frame = this; }
    if (frame == top) { return ''; }
    try {
        return am_getFramePath(frame.parent) + '.' + frame.name;
    } catch (err) {
        // Likely IE is telling us Access is Denied for frame properties
        return '';
    }
}

/**
 * Save the location of the current page to the FRAMEPATHS cookie.
 * The location to save can be overridden with the loc parameter.
 */
function am_StoreFramePath(loc) {
    var path = am_getFramePath();
    path = path.substr(1, path.length-1);
    if ('' == path) return;
    if (typeof(loc) == 'undefined') {
        var loc = location.pathname
    }
    var FramePathsCookie = new am_multiCookie("FRAMEPATHS", "", ".");
//    FramePathsCookie.setSubValue(path, location.pathname + location.search);
    FramePathsCookie.setSubValue(path, loc);
}


/**
 * Return a boolean value telling whether the first argument is a string.
 * Modified slightly from version at:
 *      http://www.planetpdf.com/mainpage.asp?webpageid=1144
 * JCR - Modified to return null when given null
 */
function isString() {
    if (!arguments[0]) return null;
    if (typeof arguments[0] == 'string') return true;
    if (typeof arguments[0] == 'object') {
        // this could be a String object
        if (arguments[0].constructor) {
            // The following test blows in IE apparently because arguments[0].constructor
            // is undefined on the type of object being passed in (in test case the img object).
            // Thus, the surrounding extra 'if' was added.
            var criterion = arguments[0].constructor.toString().match(/string/i);
            return (criterion != null);
        }
    }
    return false;
}

/**
 * Thin wrapper to am_getDisplayType
 */
function am_getDisplayTypeById(id) {
    return am_getDisplayType(document.getElementById(id).tagName);
}

function am_getElementDisplayType(el) {
    return am_getDisplayType(el.tagName);
}

/**
 * Determine the default display type for a given tag name.
 * Default values are from http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/display.asp
 * Modified to follow http://www.w3schools.com/css/pr_class_display.asp more
 */
function am_getDisplayType(str) {
    var ie = navigator.userAgent.toLowerCase().indexOf("msie") > -1;
    var dispType = '';
    switch (str.toUpperCase()) {
        case 'ADDRESS':    case 'BLOCKQUOTE':  case 'BODY':
        case 'CENTER':     case 'COL':         case 'COLGROUP':
        case 'DD':         case 'DIR':         case 'DIV':
        case 'DL':         case 'DT':          case 'FIELDSET':
        case 'FORM':       case 'H1':          case 'H2':
        case 'H3':         case 'H4':          case 'H5':
        case 'H6':         case 'HR':          case 'IFRAME':
        case 'LEGEND':     case 'LISTING':     case 'MARQUEE':
        case 'MENU':       case 'OL':          case 'P':
        case 'PLAINTEXT':  case 'PRE':         case 'TABLE':
        case 'UL':         case 'XMP':
            dispType = 'block';
            break;
        case 'LI':
            dispType = 'list-item';
            break;
        case 'FRAME':
        case 'TBODY': case 'TFOOT': case 'THEAD':
            dispType = 'none';
            break;
        case 'TR':
            dispType = ie ? 'block' : 'table-row';
            break;
        case 'TD': case 'TH':
            dispType = ie ? 'block' : 'table-cell';
            break;
        default:
            dispType = 'inline';
    }
    return dispType;
}


/**
 * Show/Hide functions that protect against the element not existing
 * Accept either an ID or an element object directly.
 * Originally named showUIElement and hideUIElement in utility.js
 */
function am_show(elem_or_elemID, setVisibility) {
	var myElem = (isString(elem_or_elemID)) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
	if (myElem) {
		if (setVisibility) {
			myElem.style.visibility='visible';
		} else {
			myElem.style.display = am_getElementDisplayType(myElem);
		}
	}
}

function am_hide(elem_or_elemID, setVisibility) {
	var myElem = (isString(elem_or_elemID)) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
	if (myElem) {
		if (setVisibility) {
			myElem.style.visibility='hidden';
		} else {
			myElem.style.display='none';
		}
	}
}

function am_toggleShow(elem_or_elemID, setVisibility) {
    if (!am_isRendered(elem_or_elemID)) {
        am_show(elem_or_elemID, setVisibility);
    } else {
        am_hide(elem_or_elemID, setVisibility);
    }
}


/* Take a css style string like "text-align: center;text-decoration:underline"
 * and apply it to the selected element.  We can do this trivially in 
 * non-IE browsers using setAttribute on the 'style' attribute.  In IE
 * we must parse the passed in string and convert each style from its
 * CSS name to its Javascript array index (e.g. text-align becomes 
 * textAlign)
 * - Alex 2007-02-10 - I don't think this function is quite right:
 *  In IE, we really are doing what we intended which is only
 *  setting those style attributes that were specified in the css string.
 *  However, in other browsers, I think we are completely replacing the style attribute.
 *  And if we actually wanted IE to do the same thing, we could just use element.style.cssText = style
 */
function am_applyStyleString(elem_or_elemID, style) {
	var element = (isString(elem_or_elemID)) ? document.getElementById(elem_or_elemID) : elem_or_elemID;

    var ie = navigator.userAgent.toLowerCase().indexOf("msie") > -1;
    if (!ie) {
        element.setAttribute('style', style);
        return;
    }

    var aStyles = style.split(";");
    for (var i = 0; i < aStyles.length; i++) {
        var aParts = aStyles[i].split(":");
        if (aParts.length != 2) { continue; }
        var st = aParts[0];
        var val = aParts[1].replace(/^\s+/,'').replace(/\s+$/,'');

        var st_ie = '';
        var uppercaseNext = 0;
        for (var j=0; j < st.length; j++) {
            var ch = st.charAt(j);
            if (uppercaseNext) {
                st_ie += ch.toUpperCase();
                uppercaseNext = 0;
            } else if (ch == '-') {
                uppercaseNext = 1;
            } else {
                st_ie += ch;
            }
        }
        if (st_ie == "float") {st_ie = "cssFloat"}
        element.style[st_ie] = val;
    }
}

/* Take a style string like "text-align: center;text-decoration:underline"
 * and apply it to the element specified by id.  See am_applyStyleString
 * for more information.  Note that am_applyStyleString will take id or
 * element so this function is not really necessary and is deprecated.
 */
function am_applyStyleStringById(id, style) {
    am_applyStyleString(document.getElementById(id), style);
}


/* Affects all elements of the class "conditional".  This function is 
 * meant to be called in the "onLoad" attribute of the page's body tag.
 * If the element has an attribute named "conditionalExpression" then 
 * the attribute's value is evaluated.  For example, the expression 
 * might be a function with some parameters.  The expression should 
 * evaluate to 1, 0, or -1.  If it evaluates to 1, then the element's
 * class will be replaced by the value of the conditionalClass if it 
 * exists, and the element's style will be updated with the value of 
 * the conditionalStyle if it exists.  If the expression evaluates to
 * 0 or -1, nothing happens.  (It is planned that a returned value of 
 * -1 will cause the class and style to be un-applied.)
 */
function am_UpdateConditionalElements() {
    // Get all elements in a cross-browser way
    var aElements = document.all ? document.all
                                 : document.getElementsByTagName('*');

    for (var i=0; i < aElements.length; i++) {
        // ignore all elements that don't have dynamic in their class
        var el = aElements[i];

        if (!am_findStrInArray('conditional', el.className.split(' '))) { continue; }
        var condExpr  = el.getAttribute('conditionalExpression');
        if (!condExpr) { continue; }

        var condStyle = el.getAttribute('conditionalStyle');
        var condClass = el.getAttribute('conditionalClass');

        var rv = eval(condExpr);
//JCR        alert(rv + ' is rv');
        switch(rv) {
        case 1:
            if (condStyle) { am_applyStyleString(el, condStyle); }
            if (condClass) {
                // Should be appending?
                el.className = condClass;
            }
            break;
        case -1:
            // Should remove style/class
            break;
        case 0:
            break;
        }
    }
}

function am_isBlank(id) {
    var it = am_getInnerText(document.getElementById(id));
//JCR    alert(it + ' is inner text,' + (it == ''));
    return (it == '' ? 1 : 0);
}

function am_isNotBlank(id) {
    var it = am_getInnerText(document.getElementById(id));
//JCR    alert(it + ' is inner text');
    return (it != '' ? 1 : 0);
}

function am_isInError(el) {
    if (am_findStrInArray(el.name, ErrorFields)) {
        return 1;
    }
    return 0;
}

function am_toggleCheckbox(id) {
    var field = document.getElementById(id);
    if (field.value != 'on') {
        field.value = 'on';
    } else {
        field.value = 'off';
    }
}


/**
 * Generic javascript function that operates on all checkboxes in all 
 * forms in the document.
 * 
 * Parameters: 
 * "action" should be one of 'check', 'uncheck', 'reverse'.
 *     If any other value is specified, it will be assumed to be a 
 *     reference to a checkbox object, and the checkboxes will be set 
 *     to the same state as the specified checkbox.
 *
 * "disable" should be one of true, false, ''.  If empty string, will 
 * leave the checkbox in the state is was in previously.
 *
 * "prefix" limits the checkboxes to operate on. The default is '', 
 * operate on all checkboxes. Both the name and id attributes are
 * examined to see if they begin with the prefix.
 */
function am_SetCheckboxes( action, disable, prefix ) {

    if (arguments.length == 2) {
        prefix = '';
    } else if (arguments.length == 1) {
        prefix = '';
        disable = '';
    }

    for (var i=0; i < document.forms.length; i++) {
        var form = document.forms[i];
        for (var j=0; j < form.elements.length; j++) {
            var element = form.elements[j];

            if (element.type != 'checkbox') { continue; }
            if (element.name.indexOf(prefix) != 0 && element.id.indexOf(prefix) != 0) { continue; }

//            alert("operating on " + element.name); //DELETE ME

            if (action == 'check') {
                element.checked = true;
            } else if (action == 'uncheck') {
                element.checked = false;
            } else if (action == 'reverse') {
                element.checked = !element.checked;
            } else {
                element.checked = action.checked;
            }
            if (disable == '') { continue; }
            element.disabled = disable ? true : false;
        }
    }
}


/**
 * Generic javascript function that operates on all select boxes in the document. 
 * 
 * Parameters: 
 * "action" should be one of: 
 *     a string version of the selectedIndex value to which to set all selects 
 *         This assumes all selects have desired option in identical position 
 *         To pass the actual selected index in, use the toString() method 
 *     a reference to an existing select element to emulate 
 *         Copy this select's selectedIndex or value depending on isValue flag 
 *         Multi select currently NOT supported) 
 *     a string value of an option that we want to choose in each select box 
 *         This requires the isValue parameter be set to true
 * 
 * "disable" should be one of true, false, '', or skip.  If empty string, will 
 * leave the select in the state it was in previously. If it is "skip", we will
 * not set the value for any disabled selects.
 * 
 * "prefix" limits the selects to operate on. The default is '', 
 * operate on all selects. 
 * 
 * "isValue" if present this flag indicates that the provided "action" 
 * is NOT a selectedIndex but is instead the value of an option of the target select 
 * boxes to which to set all selects. (If action is an object, then we'll take the 
 * value of the currently selectedIndex.) This option would be used when selects have 
 * different options or different sort orders and we have to find the 
 * index of the given value in each one to set the selectedIndex properly. 
 */ 
function am_SetSelects( action, disable, prefix, isValue ) { 

    if (arguments.length == 2) { 
        prefix = ''; 
    } else if (arguments.length == 1) { 
        prefix = ''; 
        disable = ''; 
    } 

    // determine if the action received was an object or string 
    var isObj = !isString(action); 

    if (isValue) { 
        // if doing value matching and received an object, get value to match 
        if (isObj) 
            action = action.options[action.selectedIndex].value; 
        // otherwise action is already the value to match 
        // thus, action is now an option value we'll need to find in each select box 
    } else { 
        // if doing selectedIndex matching and received an object, get selected index 
        if (isObj) 
            action = action.selectedIndex; 
        // otherwise action is already the selected index 
        // thus, action is now the selected index to set on all select boxes 
    } 

    for (var i=0; i < document.forms.length; i++) { 
        var form = document.forms[i]; 
        for (var j=0; j < form.elements.length; j++) { 
            var element = form.elements[j]; 

            if (element.type != 'select-one') { continue; } 
            if (element.name.indexOf(prefix) != 0) { continue; } 
            if (element.disabled && disable == 'skip') { continue; }
            
            if (isValue) { 
                // go through all options looking for value that matches action and set 
                am_findSelectOptionByValue(element, action, 'select it');
            } else { 
                // try to set selected index on current element (protect against 
                // setting index higher than number of options) 
                try { 
                    element.selectedIndex = action; 
                } catch (exception) { 
                    // tried to set a bad index 
                } 
            } 

            if (disable == '' || disable == 'skip') { continue; } 
            element.disabled = disable ? true : false; 
        }
    }
} 


/**
 * Commonly used to make text next to a radio button or checkbox
 * ("inputs" below refers to these two types only) act as if it was 
 * part of that input element.  Sample usage:
 *
 * <span onMouseUp="return am_clickChildCheckOrRadio(this);">
 *   <input type="checkbox" onClick="alert('something')">Some text
 * </span>
 *
 * Note the use of onMouseUp and not onClick - if you use onClick
 * you would get a loop.  For future, think of ways to prevent
 * bubble up from causing a loop.
 *
 * Takes one required argument, par, in which you should pass <this>.  
 * We use parameter to make sure that the input's onClick doesn't get 
 * called twice if it was the element that triggered the onMouseUp.
 *
 * Takes one optional argument, pattern, which should be a regular
 * expression.  If specified, only operates on elements whose name
 * matches that pattern.  Don't forget to escape backslashes.
 *
 * The onClick handler for the inputs is called as necessary.  If 
 * multiple checkboxes are encountered, each is clicked.  If multiple
 * radio buttons are encountered, the first will be clicked.  
 * Multiple radio buttons are not recommended, as this effect can be
 * unexpected.  Mixing checkboxes and radio buttons is also not
 * recommended.
 *
 * This function will currently only affect inputs that are both
 * enabled and rendered.  We may want to make this configurable in the
 * future.
 *
 * IE6 oddity - Having an alert during onMouseUp/Down processing seems
 * to cancel the processing of the events.  Make sure you avoid them.
 */
function am_clickChildCheckOrRadio (par) {

    var pattern  = arguments[1];

    // Determine the element clicked on in a cross-browser way.
    var e = window.event || arguments.callee.caller.arguments[0];
    var src = e.srcElement || (e.target.nodeType == 3 ? e.target.parentNode : e.target);
    // In Mozilla, could use e.currentTarget as par instead of having to pass this.

    var ie = document.all && navigator.userAgent.indexOf("Opera") == -1;

    var aInputs = par.getElementsByTagName('INPUT');
    for (var i = 0; i < aInputs.length; i++) {
        var input = aInputs[i];

        // Don't call the onClick handler of the Input in these cases:
        //  1. Element was the original source of the click.
        //  2. We have specified a pattern for the name and this doesn't match it.
        //  3. Input is disabled, or not currently rendered.
        if (input == src) { continue; }
        
        if (pattern && (!input.name || !input.name.match(pattern))) { continue; }
        if (input.disabled || !am_isRendered(input)) { continue; }

        if (input.type == 'radio') {
            input.checked = true;
            if (input.onclick) input.onclick();
            break;
        } else if (input.type == 'checkbox') {
            input.checked = !input.checked;
            if (input.onclick) input.onclick();
        }
    }
    return true;
}


// Return the id of an element, or, if it has none, the id of the nearest
// ancestor with one.
//
function am_getFirstId(element) {
    if (element.id)
        return element.id;
    return element.parentNode ? am_getFirstId(element.parentNode) : null;
}

// Unfortunately IE didn't have Array.unshift until 5.5
// It should be possible to actually override Array.unshift
// with something like this, but that might be confusing.
//
function am_arrayUnshift(item, arr) {
    for (var i=arr.length; i >= 1; i--) {
        arr[i] = arr[i-1];
    }
    arr[0] = item;
    arr.length = arr.length + 1;
}

// Unfortunately document.getElementsByName() behaves quite differently
// in IE and Netscape.  This is our replacement for consistent behavior.
// Unfortunately Jamie did not document the exact problems encountered
// by IE since it does seem to work at least in simple scenarios.
// In a document with a lot of elements, this function is WAY TOO SLOW.
function am_getElementsByName(name) {
    var ie = navigator.userAgent.toLowerCase().indexOf("msie") > -1;
    if (!ie) {
        return document.getElementsByName(name);
    }

    // Get all elements in a cross-browser way
    var aElements = document.all ? document.all
                                 : document.getElementsByTagName('*');
    var arr = new Array();
    for (var i=0; i < aElements.length; i++) {
        var el = aElements[i];
        if (el.name == null || el.name != name) { continue; }
        arr[arr.length] = el;
        arr.length = arr.length + 1;
    }
    return arr;
}

// Thin wrapper for am_HighlightById with am_getFirstId
// designed to fire as an event handler. 
// Note that it executes am_HighlightById in the context of the
// object referred to by the 'this' keyword.  When you assign
// this event handler inline in HTML, 'this' refers to the global window.
// In Mozilla (but I don't think in IE), when you attach the event handler
// in Javascript, 'this' will refer to the DOM element that is the event target.
// This is actually NOT what you want because the context is used to maintain
// a static property that stores the previous Element that needs to be deHighlighted.
// So, when you attach am_Highlight as an event handler in Javascript, attach
// am_Highlight.bind(contextObject) instead of just am_Highlight, where contextObject
// can be window for the global scope or any arbitrary object or DOM element
// (which would allow you to maintain multiple
//  highlight/dehighlight groups in the same window)
// See http://www.digital-web.com/articles/scope_in_javascript/ for more details
//
// Returns the id of the element that was previously highlighted.  This
// can be used to "back out" a highlight.  (null if no previously
// highlighted element.)
//
function am_Highlight() {
    // Access event id on both IE (window.event) and Netscape
    var event = window.event || arguments.callee.caller.arguments[0];
    var element = event.srcElement || event.currentTarget || event.target;
    var id = am_getFirstId(element);
    if (null == id) return null;

    am_arrayUnshift(id, arguments);
    // apply method of Function requires IE 5.5 or newer
    return am_HighlightById.apply(this, arguments);
}

if (isUndefined(Function.prototype.bind) == true) {
    Function.prototype.bind = function(contextObj) { 
        var method = this, 
        temp = function() { 
            return method.apply(contextObj, arguments); 
        }; 
      
        return temp; 
    } 
}

var am_Highlight_previousElement = '';

// Takes an element id and an arbitrary number of arguments.
// Undocumented arguments below are currently unused, and will be ignored.
// Arguments are interpreted as:
//   id - Affect the element with this id
//   highlightStyle
//   deHighlightStyle
//   highlightClass - The target element's class will be set to this
//   deHighlightClass - The previous target element's class will be set to this
//   highlightBehavior
//   deHighlightBehavior
//   Reserved1
//   Reserved2
//   ElementPrefix1 - Function will also affect element with id 'ElementPrefix1 + id' if it exists
//   ElementPrefix2 - Function will also affect element with id 'ElementPrefix2 + id' if it exists
//   ...
//   ElementPrefixN - Function will also affect element with id 'ElementPrefixN + id' if it exists
//
// Note this function maintains a static property that stores the
// previous element that needs to be deHighlighted in the object
// referred to by the 'this' keyword (which by default is the global window).
// If you use this as an event handler and assign it inline in HTML,
// 'this' also refers to the global window.  For assignment via Javascript,
// it's better to use the am_Highlight wrapper (via am_Highlight.bind).
// If you wanted to maintain more than one highlight/dehighlight group
// of elements in the same window to avoid clashes between the groups,
// you need to apply this function in the context of some arbitrary object
// or DOM element for all but one (or all) groups, i.e.:
// am_HighlightById.apply(contextObjectForThisGroup, arguments) or
// am_HighlightById.call(contextObjectForThisGroup, id, highlightStyle, dehighlightStyle, ...)
//
// Returns the id of the element that was previously highlighted.  This
// can be used to "back out" a highlight.  (null if no previously
// highlighted element.)
//
function am_HighlightById(id) {
    var element = document.getElementById(id);
    if (null == element) { return null; }

    var highlightStyle = arguments[1] || '';
    var deHighlightStyle = arguments[2] || '';
    var highlightClass = arguments[3] || '';
    var deHighlightClass = arguments[4] || '';
    var highlightBehavior = arguments[5] || '';
    var deHighlightBehavior = arguments[6] || '';
    var Reserved1 = arguments[7] || '';
    var Reserved2 = arguments[8] || '';
    
    var previousElement = this.am_Highlight_previousElement;

    am_formatElement(previousElement, deHighlightStyle, deHighlightClass, deHighlightBehavior);
    am_formatElement(element, highlightStyle, highlightClass, highlightBehavior);

    // If any prefixes were passed in, operate on them too.
    var el;
    for (var i = 9; i < arguments.length; i++) {
        if (previousElement != '' && previousElement.id != '') {
            el = document.getElementById(arguments[i] + previousElement.id);
            am_formatElement(el, deHighlightStyle, deHighlightClass, deHighlightBehavior);
        }
        el = document.getElementById(arguments[i] + element.id);
        am_formatElement(el, highlightStyle, highlightClass, highlightBehavior);
    }

    // Save current element for next invocation to deHighlight
    var rv = previousElement;
    this.am_Highlight_previousElement = element;
    if (!rv) return null;
    return rv.id;
}

// Takes an element name and an arbitrary number of arguments.
// Undocumented arguments below are currently unused, and will be ignored.
// Arguments are interpreted as:
//   name - Affect all elements with this name
//   highlightStyle
//   deHighlightStyle
//   highlightClass - The target elements' class will be set to this
//   deHighlightClass - The previous target elements' class will be set to this
//   highlightBehavior
//   deHighlightBehavior
//   Reserved1
//   Reserved2
//   ElementPrefix1 - Function will also affect elements with name 'ElementPrefix1 + name' if they exists
//   ElementPrefix2 - Function will also affect elements with name 'ElementPrefix2 + name' if they exists
//   ...
//   ElementPrefixN - Function will also affect elements with name 'ElementPrefix3 + name' if they exists
//
function am_HighlightByName(name) {
    var elements = am_getElementsByName(name);
    if (null == elements) { return null; }

    var highlightStyle = arguments[1] || '';
    var deHighlightStyle = arguments[2] || '';
    var highlightClass = arguments[3] || '';
    var deHighlightClass = arguments[4] || '';
    var highlightBehavior = arguments[5] || '';
    var deHighlightBehavior = arguments[6] || '';
    var Reserved1 = arguments[7] || '';
    var Reserved2 = arguments[8] || '';

    var previousElement = this.am_Highlight_previousElement;

    if (previousElement != '' && previousElement.name != '') {
        var previousElements = am_getElementsByName(previousElement.name);
        am_formatElementArray(previousElements, deHighlightStyle, deHighlightClass, deHighlightBehavior);
    }
    am_formatElementArray(elements, highlightStyle, highlightClass, highlightBehavior);

    // If any prefixes were passed in, operate on them too.
    var els;
    for (var i = 9; i < arguments.length; i++) {
        if (previousElement != '' && previousElement.name != '') {
            els = am_getElementsByName(arguments[i] + previousElement.name);
            am_formatElementArray(els, deHighlightStyle, deHighlightClass, deHighlightBehavior);
        }
        els = am_getElementsByName(arguments[i] + name);
        am_formatElementArray(els, highlightStyle, highlightClass, highlightBehavior);
    }
    
    // Save current element for next invocation to deHighlight
    var rv = previousElement;
    this.am_Highlight_previousElement = elements[0];
    return rv;
}

// This function takes either an element or an element id which is expected
// to have two or more children.  Each of those children elements is further
// expected to have as a child or descendenent an <input type="radio"> tag, i.e.:
// (use of am_clickChildCheckOrRadio in the example below is not required)
//  <td id="myElement" onClick="am_HighlightChildWithCheckedRadio(this, 'color:black', 'color:gray');">
//      <span onMouseUp="am_clickChildCheckOrRadio(this);" onKeyUp="am_clickChildCheckOrRadio(this);">
//          <input type="radio" name="myRadio" value=t>Text, select/input form fields, etc.
//      </span>
//      <span onMouseUp="am_clickChildCheckOrRadio(this);" onKeyUp="am_clickChildCheckOrRadio(this);">
//          <input type="radio" name="myRadio" value=f>Text, select/input form fields, etc.
//      </span>
//  </td>
//  <script language="Javascript">am_HighlightChildWithCheckedRadio('myElement', 'color:black', 'color:gray');</script>
// The idea is the radio control indicates alternate options in a complex widget and
// this function emphasizes the items in the span that has the checked the radio
// and de-emphasizes the other spans where the radios are not checked.
// All items in the immediate child of our element (i.e. span in our example) and all descendents
// will have the requested formatting applied to them subject to tag type restrictions (see tags parameter below)
// Optional arguments are:
//   highlightStyle
//   deHighlightStyle
//   highlightClass - The target elements' class will be set to this
//   deHighlightClass - The class of elements in the non-selected children will be set to this
//   highlightBehavior
//   deHighlightBehavior
//   Reserved1
//   Reserved2
//   tags - space-separated list of tag types to be affected (if omitted, all will be affected)
//          Note that specific input types can be specified like this: INPUT:text
function am_HighlightChildWithCheckedRadio(elem_or_elemID) {
    var element = (isString(elem_or_elemID)) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
    if (null == element) { return null; }

    var highlightStyle = arguments[1] || '';
    var deHighlightStyle = arguments[2] || '';
    var highlightClass = arguments[3] || '';
    var deHighlightClass = arguments[4] || '';
    var highlightBehavior = arguments[5] || '';
    var deHighlightBehavior = arguments[6] || '';
    var Reserved1 = arguments[7] || '';
    var Reserved2 = arguments[8] || '';
    var tagList = arguments[9];
    if (tagList != null) {
        var tags = tagList.split(' ');
    } else {
        var tags = tagList;
    }
    
    var children = element.childNodes;
    for (var j = 0; j < children.length; j++) {
        var child = children[j];
        // skip text nodes
        if (child.nodeType == 3) {continue}
        var inputs = child.getElementsByTagName("INPUT");
        for (var i = 0; i < inputs.length; i++) {
            if (inputs[i].type != 'radio') {continue}
            if (inputs[i].checked) {
                am_formatElementAndAllChildrenTags(child, highlightStyle, highlightClass, highlightBehavior, tags);
                break
            } else {
                am_formatElementAndAllChildrenTags(child, deHighlightStyle, deHighlightClass, deHighlightBehavior, tags);
                break
            }
        }
    }
}

// Apply either a style, a class, or behavior to an element.
// (behavior is a method defined on the element object)
function am_formatElement(element, style, className, behavior) {
    if (!element) { return; }

    if (style != '') {
        am_applyStyleString(element, style);
    }

    if (className != '') {
        element.className = className;
    }

    if (behavior != '') {
        element[behavior]();
    }
}

// Loop over an array of elements, formatting each with am_formatElement
//
function am_formatElementArray(arr, style, className, behavior) {
    if (arr == null) { return; }
    for (var i = 0; i < arr.length; i++) {
        am_formatElement(arr[i], style, className, behavior);
    }
}

// Format element and all children tags with am_formatElement
// you can optionally narrow down the list of affected tags
// to the tag types (not case-sensitive) in the optional tags array parameter
function am_formatElementAndAllChildrenTags(element, style, className, behavior, tags) {
    if (tags != null) {
        if (am_isElementOfType(element, tags)) {
            am_formatElement(element, style, className, behavior);
        }
    } else {
        am_formatElement(element, style, className, behavior);
    }
    am_formatElementArray(am_getAllChildrenTags(element,tags), style, className, behavior);
}



/********************************************************
 * Copyright (C) 2002-2003, CodeHouse.com. All rights reserved.
 * CodeHouse(TM) is a registered trademark.
 *
 * THIS SOURCE CODE MAY BE USED FREELY PROVIDED THAT
 * IT IS NOT MODIFIED OR DISTRIBUTED, AND IT IS USED
 * ON A PUBLICLY ACCESSIBLE INTERNET WEB SITE.
 * 
 * CodeHouse.com JavaScript Library Module: Get Current Style Method
 *
 * You can obtain this script at http://www.codehouse.com
 ********************************************************/
function CJL_getCurrentStyle(elem, prop)
{
   if( elem.currentStyle )
   {  
      var ar = prop.match(/\w[^-]*/g);
      var s = ar[0];
      
      for(var i = 1; i < ar.length; ++i)		   
      {
         s += ar[i].replace(/\w/, ar[i].charAt(0).toUpperCase());
      }
           
      return elem.currentStyle[s]
   }
   else if( document.defaultView && document.defaultView.getComputedStyle )
   {
      return document.defaultView.getComputedStyle(elem, null).getPropertyValue(prop);
   }
}


/**
 * Determine if an element is rendered.  An element is not rendered
 * if it has style display=none, visibility=hidden, or is a form 
 * field of type hidden, or if any of its parent elements have style
 * display=none or visibility=hidden.  (In IE 6, display style for form
 * fields of type "hidden" is inline, in Mozilla 1.6 is "none".)
 * Recursive
 */
function am_isRendered(elem_or_elemID) {
    var el = (isString(elem_or_elemID)) ? document.getElementById(elem_or_elemID) : elem_or_elemID;

    var visibility = CJL_getCurrentStyle(el, 'visibility');
    var display = CJL_getCurrentStyle(el, 'display');
    if (display == 'none' || 
        visibility == 'hidden' ||
        el.type == 'hidden') {
        return 0;
    }
    if (el.parentNode != document) {
        return am_isRendered(el.parentNode);
    }
    return 1;
}


/**
 * Given either an element or an id, return the next visible and 
 * enabled element in the tab order.  This proc follows the Mozilla
 * method of treating negative tabIndexes exactly like 0.  In IE,
 * objects with a negative tabIndex are completely omitted from the
 * tabbing order.  We may want to make this configurable.
 *
 * Takes one optional argument, loopAtEnd, which defaults to true.
 * If loopAtEnd is false, will return null at the end of the tab order.
 * Otherwise, will return the first element in the tab order.
 *
 * Tab Order is:
 * 
 * Objects with a positive tabIndex are selected in increasing order 
 *   and in source order to resolve duplicates.
 * Objects with an tabIndex of less than or equal to zero are selected 
 *   in source order, and come after positive tabIndexes in tabbing 
 *   order.
 *
 * TODO: Refactor this so we can have an am_prevElementInTabOrder too,
 *       which we will likely trigger on backspace in some cases.
 */
function am_nextElementInTabOrder(elem_or_elemID, loopAtEnd) {
	var el = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;
    if (typeof(loopAtEnd) == 'undefined') {
        var loopAtEnd = true;
    }

    // Loop over all of the elements in order - should probably cache this
    var f = el.form;
    var tabOrders = new Array();
    var elsInOrder = new Array();
    var namesInOrder = new Array();

    for (var i = 0; i < f.elements.length; i++) {
        var fel = f.elements[i];
        if (fel.disabled || !am_isRendered(fel)) { continue; }  // Only include visible elements
        var tabOrder = fel.tabIndex;
        if (tabOrder <= 0) {
            elsInOrder.push(fel);
            namesInOrder.push(fel.name);
        } else {
            for (var j=0; j < elsInOrder.length; j++) {
                var ti = tabOrders[j];
                if (!ti || ti > tabOrder) break;
            }
            // have found place to insert current element at j.
            tabOrders.splice(j, 0, tabOrder);
            elsInOrder.splice(j, 0, fel);
            namesInOrder.splice(j, 0, fel.name);
        }
    }
//    alert(namesInOrder.toString());

    var nextElementIndex = elsInOrder.indexOf(el) + 1;
    if (nextElementIndex == elsInOrder.length) {
        if (!loopAtEnd) return null;
        nextElementIndex = 0;
    }
    return elsInOrder[nextElementIndex];
}

/**
 * Designed to be used like onKeyPress="am_nextOnMaxLength();"
 * If the current key press will have us reaching the source element's
 * maxlength, focus on the next element in the tab order.
 */
function am_nextOnMaxLength() {

    // Access event id on both IE (window.event) and Netscape
    var event = window.event || arguments.callee.caller.arguments[0];
    var el = event.srcElement || event.currentTarget || event.target;

    var maxLength = el.getAttribute('maxLength');
    if (maxLength != null && el.value.length + 1 == maxLength) {
        am_nextElementInTabOrder(el).focus();
    }
    return true;
}


/**
 * Focus on the first form field in a page that needs it.
 * In all cases, only focusses on form fields that are enabled and
 * visible to user.
 *
 * Takes three optional arguments.
 *   focusClass   If specified, focus on first field with this class.
 *       If not specified, focus on first blank field.
 *   focusButton  If true (default), failure to find any matching field
 *       will cause function to give focus to first available submit.
 *   focusBlank   If true (default), failure to find the class will
 *       fallback to focussing on blanks.
 *
 * TODO: SHOULD PROBABLY RESPECT TAB ORDER
 */
function am_firstFocus() {

    var focusClass  = arguments[0];
    var focusButton = typeof(arguments[1]) != 'undefined' ? arguments[1] : true;
    var focusBlank  = typeof(arguments[2]) != 'undefined' ? arguments[2] : true;
    var firstButton = null;
    var firstBlank = null;

    // Get all elements in a cross-browser way
    var aElements = document.all ? document.all
                                 : document.getElementsByTagName('*');
    for (var i = 0; i < aElements.length; i++) {
        var el = aElements[i];

        // Only operate on form elements that are enabled and visible
        if (!el.form) { continue; }                           
        if (el.disabled || !am_isRendered(el)) { continue; }

        if (!firstButton && el.type && (el.type == "submit" || el.type == "button")) { 
            // Save the first button we find
            firstButton = el;
        }

        if (focusClass) {
            if (am_findStrInArray(focusClass, el.className.split(' '))) {
                el.focus(); return;
            } else if (!firstBlank && el.value == '') {
                firstBlank = null;
            }
        } else if (el.value == '') {
            el.focus(); return;
        }
    }

    // No matching fields - go to our fallbacks as specified by options
    if (focusBlank && firstBlank) { firstBlank.focus(); return; }
    if (focusButton && firstButton) { firstButton.focus(); return; }
}


// This script and many more are available free online at
// The JavaScript Source!! http://javascript.internet.com
// Original:  jgw (jgwang@csua.berkeley.edu )
// Web Site:  http://www.csua.berkeley.edu/~jgwang/
function checkCapsLock( e ) {
	var myKeyCode=0;
	var myShiftKey=false;

        // Doesn't work in Netscape 7.02, which doesn't seem to be able
        // to get shift status from either e.modifiers or e.shiftKey

	// Internet Explorer 4+
	if ( document.all ) {
		myKeyCode=e.keyCode;
		myShiftKey=e.shiftKey;

	// Netscape 4
	} else if ( document.layers ) {
		myKeyCode=e.which;
		myShiftKey=( myKeyCode == 16 ) ? true : false;

        // Mozilla 1.4+
	} else if ( document.getElementById ) {
                // Problem: e.shiftKey is always false on NS 7.02
		myKeyCode=e.which;
		myShiftKey = e.shiftKey;
	}


	// Upper case letters are seen without depressing the Shift key, therefore Caps Lock is on
	if ( ( myKeyCode >= 65 && myKeyCode <= 90 ) && !myShiftKey ) {
		return true;

	// Lower case letters are seen while depressing the Shift key, therefore Caps Lock is on
	} else if ( ( myKeyCode >= 97 && myKeyCode <= 122 ) && myShiftKey ) {
		return true;
	}
	return false;
}


/**
 * Designed to be called onKeyPress from input fields:
 *  onkeypress="return am_checkForCapsAndEnter(capsWarningObject);"
 *
 * If Enter key is pressed, checks whether form is ready to be
 * submitted.  If so, submits, otherwise, focusses on the next element
 * in the tab order.
 *
 * First optional argument is the object to show/hide which presumably 
 * contains a "CAPS Lock is On" warning.  Second optional argument is
 * whether to loop in the tab order and is passed through to 
 * am_nextElementInTabOrder.
 *
 * Otherwise, if CAPS lock is detected to be on/off (using the 
 * checkCapsLock function) and an object to unhide is provided, it will
 * be shown/hidden.
 *
 * To determine if form is ready to submit, gets an expression to
 * evaluate from the 'enableSubmitFunction' attribute of the source
 * element's form.  The expression should evaluate to true if the form
 * is ready to be submitted, false otherwise.
 */
function am_checkForCapsAndEnter(warnElement, loopAtEnd) {

    var e = window.event || arguments.callee.caller.arguments[0];
    var el = e.srcElement || e.currentTarget || e.target;
    var enableSubmitProc = el.form.getAttribute('enableSubmitFunction');
    if (typeof(warnElement) == 'undefined') {
        var warnElement = null;
    }

    var characterCode; // literal character code will be stored in this variable

    if (e && e.which) { // if which property of event object is supported (NN4)
        characterCode = e.which;
    } else {
        e = window.event || arguments.callee.caller.arguments[0];
        characterCode = e.keyCode; // character code is contained in IE's keyCode property
    }

    // Submit the form on enter key
    if (characterCode == 13) {
        var readyForSubmit = eval(enableSubmitProc);
        if (readyForSubmit) {
            el.form.submit();
        } else {
            var nextEl = am_nextElementInTabOrder(el, loopAtEnd);
            if (nextEl) nextEl.focus();
        }
        return readyForSubmit;
    } else if (warnElement) {
       if (checkCapsLock(e)) {
            am_show(warnElement);
        } else {
            am_hide(warnElement);
        }
    }
    return true;
}


/**
 * Dynamically add option elements to a select box
 * in a browser-independent way. The supposedly cross-browser
 * version of this method had issues in IE and thus the IE-specific code had to be
 * brought back.
 *
 * Parameters:
 *		objSelect - select object to update with new option objects
 *		value - value for this option
 *		label - display label for option
 *		indentSpaces - integer indicating how many spaces to indent the label with (default = 0)
 */
function am_addSelectOption(oSelect, value, label, indentSpaces) {
	if (document.all) {
		var newElem = document.createElement("OPTION");
		newElem.text = " ".repeat(indentSpaces) + label;
		newElem.value = value;
		oSelect.options.add(newElem);
 	} else {
		// this method initially appeared cross-browser, but options created
		// this way could not get the .selected set in IE
		var newElem = document.createElement("OPTION");
		newElem.innerHTML = "&nbsp;".repeat(indentSpaces) + label;
		newElem.value = value;
		oSelect.appendChild(newElem);
    }
}

/**
 * Dynamically remove option elements from a select box
 * in a browser-independent way.
 */
function am_removeSelectOptionByIndex(oSelect, optIndex) {
	if (document.all) {
	    oSelect.options.remove(optIndex);
 	} else {
 	    oSelect.options[optIndex] = null
	}
}

/**
 * Returns the index of the first option in a select object
 * whose value case-insensitively matches the value in optionvalue
 * Return -1 if not found
 */
function am_findSelectOptionByValue(oSelect, optionvalue, shouldSelect) {
	var retVal = -1;
	var numOptions = oSelect.options.length
	for (var i=0; i < numOptions; i++) {
		if (oSelect.options.item(i).value.toLowerCase() == optionvalue.toLowerCase()) {
			retVal = i
			if (shouldSelect) { oSelect.selectedIndex = i; }
			break;
		}
	}
	return retVal;
}

/**
 * Port from Tcl, original code was from OpenACS
 */
function util_commify_number (num) {
    num = num.toString();
	var oldnum = num;

	// Regular Expression taken from Mastering Regular Expressions (Jeff Friedl)
	// matches optional leading negative sign plus any other 3 digits, starting from end
    while ( (num = num.replace(/^(-?[0-9]+)([0-9][0-9][0-9])/, '$1,$2')) != oldnum ) {
        oldnum = num;
    }
    return num
}

/*
 * Call from onLoad event handle for the IFRAME tag.
 */
function setIframeHeight(iframe) {
    if (!iframe) {
        if (window && window.frameElement) {
            iframe = window.frameElement;
        }
    }
    if (iframe) {
        iframeDoc = iframe.contentWindow.document;
        iframeDoc.body.style.overflow = "hidden";
        oldheight = iframe.style.height;
        iframe.style.height = iframeDoc.body.scrollHeight;
    }
}

// -->