// Requires amutils.js
/* ---------------------------------------------------------------------------
// Smart Save Buttons
//
// Enable/Disable the Save or Reset buttons depending on whether new info has been
//   entered in the form.
// TODO: Extend to provide enable/disable depending on whether another custom validator approves or not
//       as opposed to just having data changed (typically used with keepEnabled buttons)

The following methods are provided:

initSubmitButton(formId, buttonIdPrefix, [idPattern [,arrCtls [,bOverrideDisable]]]) -
   initializes the state of the submit buttons and saves form element values for future reference.
   Submit buttons monitor certain form fields specified by the idPatern and/or arrCtls so that
   you could have different submit buttons on the same HTML form that are each monitoring
   different fields (e.g. in user admin).  arrCtls is an optional performance improvement so that
   the function does not need to iterate through the entire form in order to find form
   elements to monitor.  If arrCtls matches different items than that specified by idPattern,
   the result is not well-defined.  If you use arrCtls, you should make sure that it
   matches the idPattern provided. bOverrideDisable when true keeps the buttons from
   ever getting disabled, but all other functionality (tracking of changes from init state, etc.)
   is still available

initSubmitButtonKeepEnabled(formId, buttonIdPrefix) - initSubmitButton(formId, buttonIdPrefix, null,null, true)
   provided to avoid having to change past usages of initSubmitButton.

refreshSubmitButton(obj [,arrCtls]) - called by every form element that is monitored by the submit buttons
   to update the status of the submit buttons.
submitSubmitButton(oButton, validateFn, formId) - called by the actual submit button to do pre-processing
   (user-defined validation function passed in by pointer; build list of modified fields,
   available to the server in the hidden element id <formId>_modified_fields; disable the
   buttons to prevent double submission) and then actual form submission. This, of course,
   submits the entire form and then re-initializes all submit buttons on that form.

Helpers: These typically should not be called outside of savebutton.js
 canonicalElementValue(formId, buttonIdPrefix, element, fSet);
 setSaveButtonsEnabled(formId, buttonIdPrefix, enabled) - sets the state of the save buttons
   matching the appropriate prefix for the specified form. Must call initSubmitButton(formId,...)
   prior to using this.
 buildModifiedFields(formId);
 initAllSubmitButtons
 getButtonIdPrefixForElement

Usage:
 - Call initSubmitButton from your onLoad.
 - Include refreshSubmitButton(this) on every element that is monitored by the Save buttons or use page-wide monitoring.
 - If you need notification of modified fields, include a hidden field which can be named
     as needed, but id'ed as follows:
      <formId>_modified_fields_notsaved
 - Use the 'return submitSubmitButton(this, [optional validateFn], [optional formId if outside of a form]);'
    method on the onClick event of each Save button.
 - Define your own validation function if necessary. Declaration is <validateFn>(formId)

//----------------------------------------------------------------------------
*/

// Thin wrapper around initSubmitButton that overrides the disabling of the
//   Submit buttons.
function initSubmitButtonKeepEnabled(formId, buttonIdPrefix) {
	initSubmitButton(formId, buttonIdPrefix, null, null, true);
}

// Button-enabling functions
function setSaveButtonsEnabled(formId, buttonIdPrefix, enabled) {
	// REFACTOR FOR GENERALIZED EN/DISABLE IMAGE
	// Previously we assumed one image, now we need to:
	//  - assume 2 images, 1st enabled, 2nd disabled
	//  - keep backward compatibility for single image
	//  - allow plain input buttons instead of buttons (what about buttons with no images?)
	//  - allow the css classes to use for Enabled and Disabled states to be specified
	//    in a custom attribute on each button via getButtonClass
	// Alex: Buttons are now allowed to be placed outside of *any* form and still be controlled as long as prefix matches
	//       This needs to be tested for performance implications, esp. for INPUT

	if (!mOverrideDisable[formId][buttonIdPrefix]) {
		// var frm = document.getElementById(formId);
		// Get all buttons, instead of just those for this form
		var buttons = document.getElementsByTagName('BUTTON');
		var inputs = document.getElementsByTagName('INPUT');

        // iterate through all button elements
		for (var i=0; i<buttons.length ; i++) {
			// if the current button id matches the id passed in, and it's in this form or in no form, we've found the element
			if ((!buttons[i].form || buttons[i].form.id == formId) && buttons[i].id.substring(0, buttonIdPrefix.length) == buttonIdPrefix) {
				var btnSave = buttons[i];
                var imgs = btnSave.getElementsByTagName('IMG');

				if (btnSave.getAttribute('ABSDISABLED')  &&  btnSave.getAttribute('ABSDISABLED').toLowerCase() == 'true') {
    				// permanently disabled as per absdisabled attribute
                    btnSave.className = getButtonClass(btnSave, false);
                    switchElements(imgs[1], imgs[0]);
                    btnSave.disabled = true;

				} else {
				    // not permanently disabled, so check if need to change btnSave
                    // and if so set these values
                    if(btnSave.disabled != !enabled) {
                        btnSave.className = getButtonClass(btnSave, enabled);
                        if (imgs.length == 2) {
                            // 2 images: first enabled, 2nd disabled
                            if (!enabled)
                                switchElements(imgs[1], imgs[0]);
                            else
                                switchElements(imgs[0], imgs[1]);
                        } else if (imgs.length == 1) {
                            // single image for backward compatibility with early dev versions
                            imgs[0].src = (!enabled) ? (IMAGEPATH + 'btn-save-d.gif') : (IMAGEPATH + 'btn-save.gif');
                        }
                        btnSave.disabled = !enabled;
                    }
				}
			}
		}
        // iterate through all input elements
		for (var i=0; i<inputs.length ; i++) {
			if ((!inputs[i].form || inputs[i].form.id == formId) && inputs[i].id.substring(0, buttonIdPrefix.length) == buttonIdPrefix) {
				var btnSave = inputs[i];
				if (btnSave.getAttribute('ABSDISABLED')  &&  btnSave.getAttribute('ABSDISABLED').toLowerCase() == 'true') {
                    btnSave.className = getButtonClass(btnSave, false);
                    btnSave.disabled = true;
				} else {
                    if(btnSave.disabled != !enabled) {
                        btnSave.className = getButtonClass(btnSave, enabled);
                        btnSave.disabled = !enabled;
                    }
				}
			}
		}
	}
}

/**
 * Determine the proper button class by first attempting to read
 * it from custom attributes on the button element, and if that fails
 * then use defaults listed here.
 * Expected custom attributes on button element:
 *      btnEnabledClass
 *      btnDisabledClass
 * Parameters:
 *      button object
 *      boolean enabled specifying to return the enabled class (true) or disabled one (false)
 * Returns: a string class name
 */
function getButtonClass(oButton, bEnabled) {
    var defEn = 'button1';
    var defDis = 'button1G';
    var en = oButton.getAttribute('btnEnabledClass');
    var dis = oButton.getAttribute('btnDisabledClass');

    if (bEnabled)
        return (en) ? en : defEn;
    else
        return (dis) ? dis : defDis;
}

// This array is 3-D and indexed first by the formId, then by the buttonIdPrefix and then some construction of the form element name.
var resetValues = new Array();

//var mFormId;
//var mButtonIdPrefix = new Array();
// This array is 2-D indexed by the formId and the buttonIdPrefix. This allows handling multiple forms on one page and multiple subsets of fields with different monitoring buttons.
var mIdPattern = new Array();
var mButtonIdPrefixes = new Array(); // Will be a 2-D array, indexed by integer that lists all buttonIdPrefixes
                                     //  for a particular formId. We have this because we are using the buttonIdPrefix
                                     //  as the second index, but we need some way of tracking the text string
                                     //  that we can iterate through. This would be solved by a .keys collection,
                                     //  which we don't have.

var mOverrideDisable = new Array();  // Will be 2-D array indexed by formId, buttonIdPrefix.

// Initialize the array containing the form values as they loaded. This
//   array is used to enable/disable the Save buttons.
// ASSUMPTIONS:
//   1) Form elements which have the string "notsaved" in their id will be ignored
//      whether or not they match the pattern.
// params: formId Id of the <FORM...> that the Submit button is for. We track values on
//               a per-form basis.
//         buttonIdPrefix Id prefix of the submit buttons.  For multiple
//                        buttons the ids can contain a unique portion after the prefix
//         idPattern (Optional) RegEx object to use to match ids of elements to
//                   include in logic to enable button. If it doesn't exist
//                   all form elements will be considered.
//		   arrCtls (optional) array of form controls (objects) to initialize.
//					 If provided, only these and not all elements in the form are searched.
//                   A perf improvement.
//         bOverrideDisable (optional). If true, then we never disable the buttons--neither on
//                   init nor on refreshSubmitbutton
// return: none
// Example Usage:
// (A) initSubmitButton("form", "btnSave");
//        All elements in the form with id="form" that do not have
//          "notsaved" in their ids will affect all the submit buttons
//          which have ids that begin with "btnSave".
// (B) initSubmitButton("form", "btnSave", /^save|^test/);
//        Same as Example A plus the the ids of the elements will
//          have to start with "save" or "test" to affect the buttons.
function initSubmitButton(formId, buttonIdPrefix, idPattern, arrCtls, bOverrideDisable) {
	//alert('initSubmit formId=' + formId + ', buttonIdPrefix=' + buttonIdPrefix + ', idPattern=' + idPattern);
	//mFormId = formId;
	//mButtonIdPrefix[formId]=buttonIdPrefix;  // unused now that we are distinguishing fields within the same form
	                                           // as monitored by a certain button.

	// Initialize if this is the first time this formId is being called.
	if (!mIdPattern[formId]) {
		mIdPattern[formId] = new Array();
		resetValues[formId] = new Array();
		mButtonIdPrefixes[formId] = new Array();
		mOverrideDisable[formId] = new Array();
	}

	// If we have not init'ed with this buttonIdPrefix then append it to the
	// variable that keeps track of which buttonIdPrefixes have been initialized.
	if (!mButtonIdPrefixes[formId].hasElement(buttonIdPrefix)) {
		mButtonIdPrefixes[formId].push(buttonIdPrefix);
	}

    // JCR - When idPattern wasn't being passed, this was getting set
    // to undefined, which caused an error on Netscape.  (Not sure
    // why IE didn't error.)
	mIdPattern[formId][buttonIdPrefix] = idPattern ? idPattern : null;
	resetValues[formId][buttonIdPrefix] = new Array();
	mOverrideDisable[formId][buttonIdPrefix] = bOverrideDisable;

	var frmMain = document.getElementById(formId);

	// initialize for load/reset
	if (!bOverrideDisable)
		setSaveButtonsEnabled(formId, buttonIdPrefix, false);

	if(arrCtls) {
		var numElements = arrCtls.length;
		for(var i = 0; i<numElements; i++) {
			// use this helper function to save the current value into the values array
			canonicalElementValue(formId, buttonIdPrefix, arrCtls[i], true);
		}
	} else {
		var numElements = frmMain.length;
		for(var i = 0; i<numElements; i++) {
			// use this helper function to save the current value into the values array
			canonicalElementValue(formId, buttonIdPrefix, frmMain.elements.item(i), true);
		}
	}
}

// This function cycles through all of the form elements and checks the
//   values against the values saved in the init function to determine if buttons
//   should be enabled/disabled.  This function will be called for each monitored form element.
//
//  obj - triggering object
//  arrCtls - (Optional) ctls to search for changes. If this is defined, not
//            all form elements need to be searched.
function refreshSubmitButton(obj, arrCtls) {
    //alert('refreshing ' + obj.id);
	var change = false
	var orgChange = false;
	var ctlChange = false

	var formId;
	if (obj.form) {
		formId = obj.form.id;
	} else {
		// Find the first form element that is an input, select or textarea and set its form as the form.
		// because refreshSubmitButton might be on a TD or a DIV.
		// Assume for our purposes, that this is the form element in question.
		var elems = obj.getElementsByTagName('INPUT');
		if (elems.length > 0) {
			formId = elems[0].form.id;
			obj = elems[0];
		} else {
			elems = obj.getElementsByTagName('TEXTAREA');
			if (elems.length>0) {
				formId = elems[0].form.id;
				obj = elems[0];
			} else {
				elems = obj.getElementsByTagName('SELECT');
				if (elems.length>0) {
					formId= elems[0].form.id;
					obj = elems[0];
				} else {
					alert('No form element found.');
				}
			}
		}
	}

	// Find the monitoring buttons for this form element.
	var buttonIdPrefix = getButtonIdPrefixForElement(formId, obj.id);
	//alert('buttonIdPrefix=' + buttonIdPrefix);

	// Only need to check all elements if the triggering element has been
	// set back to the reset value
	ctlChange = canonicalElementValue(formId, buttonIdPrefix, obj, false)
	if(!ctlChange) {
		if(arrCtls) {
		    // Only check the form elements that have been passed into the function
			var numElements = arrCtls.length
			for(var i = 0; i<numElements; i++) {
				// use this helper function to check the current value against the values array
				// canonicalElementValue will only check items that match the idPattern for this buttonIdPrefix. Leave
				//   the test in there, since it is efficiently located right at the top.
				change |= canonicalElementValue(formId, buttonIdPrefix, arrCtls[i], false);
				if (change) break; // if there is a change we can break out since we're doing an OR
			}
		} else {
		    // Only check the form elements that match the idPattern for the monitoring buttons.
			var frmMain = document.getElementById(formId);
			var numElements = frmMain.length
			for(var i = 0; i<numElements; i++) {
	   	    	var item = frmMain.elements.item(i);
				// use this helper function to check the current value against the values array
				// canonicalElementValue will only check items that match the idPattern for this buttonIdPrefix. Leave
				//   the test in there, since it is efficiently located right at the top.
				change |= canonicalElementValue(formId, buttonIdPrefix, item, false);
				if (change) break; // if there is a change we can break out since we're doing an OR
			}
		}
	} else {
		// if the triggering control has changed we can immediately return true
		change = true
	}
	//alert("save change as " + change);
	setSaveButtonsEnabled(formId, buttonIdPrefix, change);
}


// This function encapsulates all of the rules for seeing if a particular
//   field has been changed  It provides both the functionality to store the
//   current version (fSet==true) and to check the current against the saved
//   value (fSet==false).
// note: Element with "notsaved" anywhere in the ID will be short circuited so
//   they will not effect the Submit button's disabled state
// returns: In fSet==false mode, the function returns true if there was a
//   change since the element was saved.
function canonicalElementValue(formId, buttonIdPrefix, element, fSet) {
	var div;
    if (element.id.indexOf("notsaved")!=-1) return false;
	if(element.tageName=='FIELDSET') return false; //dont record state of fieldsets
	if (mIdPattern[formId][buttonIdPrefix]!=null && !mIdPattern[formId][buttonIdPrefix].test(element.id)) return false;
	if(element.type&& (element.type.toLowerCase() == "radio" || element.type.toLowerCase() == "checkbox")) {
		// use the name and value pair to get unique "name"
		//   for checkboxes and radio buttons
		if (fSet) {
			resetValues[formId][buttonIdPrefix][element.name + "_" + element.value] = element.checked;
		} else {
			return (resetValues[formId][buttonIdPrefix][element.name + "_" + element.value] != element.checked);
		}
	} else if(element.tagName=="SELECT" && element.multiple) {
		// build string representing the current selection
		var options = element.options;
		var value = "";
		var numOptions = element.length;
		for (var j=0; j<numOptions; j++) {
			var opt = options.item(j);
			if(opt.selected) {
				value = value + opt.text + opt.value;
			}
		}

		if (fSet) {
			resetValues[formId][buttonIdPrefix][element.name] = value;
		} else {
			return (resetValues[formId][buttonIdPrefix][element.name] != value);
		}
	} else if ((div=document.getElementById("Edit_" + element.name)) != null) {
		// found a div with an id = "Edit_" + element.name
		//  Assume this div is contenteditiable and use
		//  its contents instead of element's value
		if (fSet) {
//			alert('setting: ' + div.innerHTML);
			resetValues[formId][buttonIdPrefix][div.id] = div.innerHTML;
		} else {
//			alert('saved: ' + resetValues[formId][buttonIdPrefix][div.id] + '\n\ncurrent: ' + div.innerHTML);
            /* debug code detecting NN issue on reset to saved value
		    var sav = resetValues[formId][buttonIdPrefix][div.id]
		    alert('are saved and current equal?' + sav.length + ' ' + div.innerHTML.length)
		    var eq = sav == div.innerHTML
		    var diff = sav.length - div.innerHTML.length
			alert(eq + ' diff=' + diff);
			if (diff > 0) {
			    for (var m=0; m< sav.length; m++) {
			        if (!(  (sav.substring(m, m+1) == div.innerHTML.substring(m, m+1))  ))
			            alert('missmatch ' + m + ' ' + escape(sav.substring(m, m+1)) + ' ' + escape(div.innerHTML.substring(m, m+1)));
			    }
			}
			document.getElementById('test_notsaved').value = ('saved: ' + resetValues[formId][buttonIdPrefix][div.id] + '\n\ncurrent: ' + div.innerHTML);
			*/
			return (resetValues[formId][buttonIdPrefix][div.id] != div.innerHTML);
		}
	} else if (element.name != "") {
		if (fSet) {
			resetValues[formId][buttonIdPrefix][element.name] = element.value;
		} else {
			return (resetValues[formId][buttonIdPrefix][element.name] != element.value);
		}
	} else {
		return false;
	}
}


/**
 * Accessor to read value from server-loaded values stored during
 * init by Canonical.
 *
 * TODO: FILL OUT THE REMAINDER OF THIS ACCESSOR IF IT IS GOING TO
 * be used anywhere other than m-policy.htm for the div innerHTML.
 *
 */
function getStoredValueFromCanonical(formId, elementId) {

	var btnPfix = getButtonIdPrefixForElement(formId, elementId)
	var element = document.getElementById(elementId);
	var value = '';
	var div;

	// different 3rd D element for radio/checkbox, select?, contenteditable div and other
	if (element.type.toUpperCase() == 'CHECKBOX'  ||  element.type.toUpperCase() == 'RADIO') {
		alert('check');

	} else if ((div=document.getElementById("Edit_" + element.name)) != null) {
		value = resetValues[formId][buttonIdPrefix][div.id];

	} else {
		alert('other');

	}
	return value;
}

//submitSubmitButton(oButton, validateFn, formId) - called by the submit button to do pre-processing
//   (user-defined validation function passed in by pointer; build list of modified fields,
//   available to the server in the hidden element id <formId>_modified_fields; disable the
//   button to prevent double submission) and then actual form submission.
//
// oButton      - button that is triggering the submit
// validateFn   - Optional. a function pointer to a function that returns whether the
//              form data is valid. validateFn(formId);
// formId       - Optional - normally formId is derived from oButton.form.id, but you can supply/override the formId
//              This is especially useful for buttons that are placed outside of a form
//
// TODO: Add boolean parameter to remove name attribute from non-modified fields to only submit modified ones
//
function submitSubmitButton(oButton, validateFn, formId) {

	if (!formId) {
	    var formId = oButton.form.id;
	}
	var oForm = document.getElementById(formId);
	
	var bIsValid = true;
	if (validateFn != null) {
		// Tell validateFn which
		mCurrentValidateForm = oForm;  // What's this variable for?
		var validateFnCall = validateFn + "('" + oForm.id + "')"
		bIsValid = eval(validateFnCall);
	}
	if (bIsValid) {
		var modifiedFieldsHidden = document.getElementById(formId + '_modified_fields_notsaved');
		// JCR - From Alex modification
		if (buildModifiedFields(formId,modifiedFieldsHidden)) {
			var resetModified = true
		} else {
			var resetModified = false
		}
		// END JCR
		var otherModifiedFieldsHidden = document.getElementById(formId + '_other_modified_fields_notsaved');
		if (otherModifiedFieldsHidden) {
//JCR			otherModifiedFieldsHidden.value = buildQueryString(formName, excludeFormName, bSkipNonMod);
			otherModifiedFieldsHidden.value = buildQueryString('ALL', oForm.name, true);
//JCR			alert('Wrote other mod fields:\n' + document.getElementById(formId + '_other_modified_fields_notsaved').value);
		}
		initAllSubmitButtons(formId);

    	oForm.submit();

		if (resetModified) {
			modifiedFieldsHidden.value="";
		}
		if (otherModifiedFieldsHidden)
			otherModifiedFieldsHidden.value="";

		return true;
	} else {
		return false;
	}
}

/**
 * THIS IS ALSO IN UTILITY.JS - SHOULD MAKE it am_switch and move to amutils.js
 * Helper to simplify the act of showing one element to hide another.
 * Makes use of show/hideUIElement which has been refactored to accept
 * either an element ID (as before) or an object directly.
 * PARAMETERS:
 *      showThis - an element or element id
 *      hideThis - an element or element id
 */
function switchElements(showThis, hideThis) {
//    hideUIElement(hideThis);
//    showUIElement(showThis);
    am_hide(hideThis);
    am_show(showThis);
}

/**
 * Reset a form element "el" to its starting value.  If "attribute" is
 * specified, instead try to retrieve the value to set to from the 
 * attribute of that name in the element.  If there is no such
 * attribute, look for an element that has the same id plus the suffix
 * ':attribute'.  If such an element is found, copy its value.
 * "el" can be either an element reference or the id of an element.
 * TODO: Maybe should have an optional parameter that determines
 * whether we should do the copyFrom lookup.
 */
function am_resetFormElement(elem_or_elemID, attribute) {
	var el = isString(elem_or_elemID) ? document.getElementById(elem_or_elemID) : elem_or_elemID;

    if (el.disabled) {
        return;
    }

    var type = el.type.toUpperCase();
    if (attribute) {
        var resetTo = el.getAttribute(attribute);
        if (resetTo == null) {
            if (!el.id) return;
            var copyFrom = document.getElementById(el.id + ':' + attribute);
            if (!copyFrom) return;
            switch (type) {
                case 'CHECKBOX': case 'RADIO':
                    el.checked = copyFrom.checked;
                    break;
                case 'TEXT': case 'TEXTAREA': case 'HIDDEN': case 'PASSWORD':
                    el.value = copyFrom.value;
                    break;
                case 'SELECT-ONE': case 'SELECT-MULTIPLE':
                    for (var i=0, l=el.options.length; i < l; i++)
                        try { el.options[i].selected = copyFrom.options[i].defaultSelected; } catch (exception) {}
                    break;
            }
            return
        }
        switch (type) {
            case 'CHECKBOX': case 'RADIO':
                // Force boolean because strings '0' and 'false' are true
                el.checked = am_boolvar(resetTo);
                break;
            case 'TEXT': case 'TEXTAREA': case 'HIDDEN': case 'PASSWORD':
                el.value = resetTo;
                break;
            case 'SELECT-ONE': case 'SELECT-MULTIPLE':
                am_findSelectOptionByValue(el, resetTo, 'and select it');
                break;
        }
    } else {
        // No attribute specified, so reset this element to its starting value
        switch (type) {
            case 'CHECKBOX': case 'RADIO':
                el.checked = el.defaultChecked;
                break;
            case 'TEXT': case 'TEXTAREA': case 'HIDDEN': case 'PASSWORD':
                el.value = el.defaultValue;
                break;
            case 'SELECT-ONE': case 'SELECT-MULTIPLE':
                for (var i=0, l=el.options.length; i < l; i++)
                    el.options[i].selected = el.options[i].defaultSelected;
                break;
        }
    }
}

/**
 * Wraps am_resetFormElement for convenient use with buttons.
 * Typical usage: 
 *  <button id='btnPrefix' onClick="am_resetForm(this);">  or
 *  <button id='btnPrefix' onClick="am_resetForm(this, 'templateValue');">
 * (You probably want a resetSubmitButton, a this.blur(), etc. too)
 *
 * oButton is an element reference to the button triggering the reset.
 * The button must have an id attribute.  The button id and the id of 
 * the form that the button belongs to will be used to determine the 
 * RegEx pattern of form field ids that this button affects.
 *
 * attribute is passed through to am_resetFormElement.  Specify '' to
 * reset to page load values, or the name of an attribute to use in
 * setting the form value.  (See am_resetFormElement for more)
 *
 * formId can be used to specify the id of a form explicitly, useful 
 * when you would like the button itself to be outside of a form.
 */
function am_resetForm(oButton, attribute, formId) {
    if (!formId) {
        var formId = oButton.form.id;
    }
    var oForm = document.getElementById(formId);
    var idPattern = getElementIdPatternForButton(formId, oButton.id);
    
    var numElements = oForm.length
    for(var i = 0; i < numElements; i++) {
        var el = oForm.elements.item(i);

        // Skip any form elements that don't match the pattern
        // registered for this button.
        if (idPattern != null && el.id && !idPattern.test(el.id)) { continue; }
        am_resetFormElement(el, attribute);
    }
    return false;
}


/**
 * Build an escaped query string of all form fields/values in a given
 * form. If no form is specified then qs will include all fields in all
 * forms on the page.
 *
 * All of the following parameters are optional:
 * formName - the name of the desired form or the keyword of 'ALL' to indicate
 *	  all forms on page. If blank then 'ALL' will be used.
 * excludeFormName - a form name to exclude.
 * bSkipNonMod - pass in true or false to indicate skip non-modified fields
 */
function buildQueryString(formName, excludeFormName, bSkipNonMod) {
	formName = (formName) ? formName : 'ALL';
	excludeFormName = (excludeFormName) ? excludeFormName : '';

	var myForms = new Array();  // array of form objects from which to build the qs
	if (formName == 'ALL') {
		var allForms = document.getElementsByTagName('FORM');
		for (var i = 0; i < allForms.length; i++) {
			// go through all forms and add them to array unless excluded by name
			if (allForms[i].name != excludeFormName)
				myForms.push(allForms[i]);
		}
	} else {
		if (formName != excludeFormName)
			myForms.push(document.forms[formName]);
	}

	var qs = '';
	// go through array of desired forms and compile the qs
	for (var i = 0; i < myForms.length; i++) {
		qs = buildQueryForForm(myForms[i], qs, bSkipNonMod);
	}
	return qs;
}


/**
 * Take a single form object and a string (blank or in process) and
 * add all desired parameters to the string.
 * Note: Unlike a normal post, we will include the name of checkboxes that are
 * NOT checked, but will give them a blank value.
 *
 * Parameters: Form object and string to which to append the query values.
 */
function buildQueryForForm(oForm, qs, bSkipNonMod) {
	// iterate over each element in the form. For each form field (indicated by
	// an element with a name) add the escaped value to the query string
	for (e = 0; e < oForm.elements.length; e++) {
		if (oForm.elements[e].name != '') {
			var elem =oForm.elements[e];
			if (!bSkipNonMod) {
				// append every element regardless of changed status
				//qs += (qs == '')  ?  '?'  :  '&';
				// this won't be first param in dnc project so no '?'
				qs += (qs == '')  ?  ''  :  '&';
				qs += oForm.name + '.' + elem.name + '=';
				// unchecked checkboxes will be included with a blank value
				qs += (elem.type.toUpperCase() == 'CHECKBOX'  &&  elem.checked == false) ? '' : escape(elem.value);
			} else {
				// test for changed, and if so then append to qs
				try {
					if (canonicalElementValue(oForm.id, getButtonIdPrefixForElement(oForm.id, elem.id), elem, false)) {
						qs += (qs == '')  ?  ''  :  '&';
						qs += oForm.name + '.' + elem.name + '=';
						qs += (elem.type.toUpperCase() == 'CHECKBOX'  &&  elem.checked == false) ? '' : escape(elem.value);
					}
				} catch (exception) {
					alert('buildQueryForForm failed for form name=' + oForm.name + ', name=' + elem.name);
				}
			}
		}
	}
	return qs;
}


// Helper function for submitSubmitButton.
// Inits all the submit buttons on a form. These submit buttons may monitor different fields,
// distinguished by the id pattern saved in mIdPattern[formId][btnIdPrefix];
function initAllSubmitButtons(formId) {
	var buttonIdPrefix;

	for (var i=0; i < mButtonIdPrefixes[formId].length; i++) {
		buttonIdPrefix = mButtonIdPrefixes[formId][i];
		initSubmitButton(formId, buttonIdPrefix, mIdPattern[formId][buttonIdPrefix]);
	}
}

// Helper function
// Puts all of the *names* of the modified fields since the last initSubmitButton into a
// form-field id'ed <formId>_modified_fields_notsaved.  
//
// If a modified field has a "columnname" attribute, adds that to the list as well. (This would 
// mean the field is part of a multi-field widget that gets recombined into a single database value.)
// 
function buildModifiedFields(formId, modifiedFieldsHidden) {
	if (modifiedFieldsHidden) {
		var frmMain = document.getElementById(formId);
		var numElements = frmMain.length
		for(var i = 0; i<numElements; i++) {
			var item = frmMain.elements.item(i);
			// use this helper function to check the current value against the values array
			try {
				if (canonicalElementValue(formId, getButtonIdPrefixForElement(formId, item.id), item, false)) {
					// if changed, then append to modified fields.
					modifiedFieldsHidden.value += (',' + item.name);
					if (item.getAttribute('columnname')) {
					    modifiedFieldsHidden.value += (',' + item.getAttribute('columnname'));
					}
				} else {
					// JCR added clause
					if (0) item.removeAttribute('name');
				}
			} catch (exception) {
//				alert('Canonical failed for: formId=' + formId + ', itemId=' + item.id);
			}
		}
//JCR		alert('ModifiedFields=' + modifiedFieldsHidden.value);
		return true
	} else {
//JCR		alert('No modified fields hidden defined.');
		return false
	}
}

// Helper function:
//  Given a form element's id, this function returns the buttonIdPrefix that monitors this element.
//  Assumes that the idPatterns are mutually exclusive.
//  Also assumes that if any buttonIdPrefix does not define an idPattern, there is only one set of monitoring
//     buttons.
function getButtonIdPrefixForElement(formId, elementId) {
	//alert('mButtonIdPrefixes['+formId + '].length=' + mButtonIdPrefixes[formId].length);
	for (var i=0; i < mButtonIdPrefixes[formId].length; i++) {
		buttonIdPrefix = mButtonIdPrefixes[formId][i];
		//alert('buttonIdPrefix=' + buttonIdPrefix);
		if (mIdPattern[formId][buttonIdPrefix]==null || mIdPattern[formId][buttonIdPrefix].test(elementId))
			return buttonIdPrefix;
	}
	return null;
}

// Helper function:
//  Given a form element's id, this function returns the buttonIdPrefix that monitors this element.
//  Assumes that the idPatterns are mutually exclusive.
//  Also assumes that if any buttonIdPrefix does not define an idPattern, there is only one set of monitoring
//     buttons.
//  CMC: 2/26/04 Added test for elementId starts with buttonIdPrefix, for the case where the
//    element in question *is* a monitoring button. This adds an additional requirement, however,
//    that the set of buttonIdPrefix's need to be mutually exclusive with the set of idPatterns.
//    If not, then a field that happens to be named <buttonIdPrefix1>somethingelse will
//    automatically return the buttonIdPrefix of buttonIdPrefix1 rather than the "correct" value
//    which was registered with initSubmitButton.
function getButtonIdPrefixForElementAlt(formId, elementId) {
    //alert('mButtonIdPrefixes['+formId + '].length=' + mButtonIdPrefixes[formId].length);
    for (var i=0; i < mButtonIdPrefixes[formId].length; i++) {
        buttonIdPrefix = mButtonIdPrefixes[formId][i];
        //alert('buttonIdPrefix=' + buttonIdPrefix);
        if (elementId.indexOf(buttonIdPrefix) == 0)
            return buttonIdPrefix;
        if (mIdPattern[formId][buttonIdPrefix]==null || mIdPattern[formId][buttonIdPrefix].test(elementId))
            return buttonIdPrefix;
    }
    return null;
}

// Helper function:
//  Given a form element's id and a button id, return the regex
//  that specified the element ids that button monitors.
// JCR added 3-02
function getElementIdPatternForButton(formId, buttonId) {
	for (var i=0; i < mButtonIdPrefixes[formId].length; i++) {
		buttonIdPrefix = mButtonIdPrefixes[formId][i];
		if (buttonId.indexOf(buttonIdPrefix) == 0) {
		    return mIdPattern[formId][buttonIdPrefix];
		}
	}
	return null;
}

function debugResetValues(formId) {
	var arr = resetValues[formId][buttonIdPrefix];
	var sBuf='Reset values for ' + formId;
	sBuf += arr.join('\n');

	alert (sBuf);
}


// --------------------------------------------------------------
//          PAGE-MONITORED EVENT DETECTION
// --------------------------------------------------------------


/**
 * Determine which browser capabilities to use.
 * (This browser detection taken from treeview.)
 */
var ns6 = document.getElementById&&!document.all
var ie4 = document.all&&navigator.userAgent.indexOf("Opera")==-1


/**
 * Register event handling for the page in a browser-dependent way.
 *     IE attaches an event handler to each form input.
 *     Netscape sets the event handler for the entire page.
 * The form id is taken as a parameter, but this is needed for IE only since
 * Netscape's page-wide registration is form-independent.
 */
function registerRefreshTrigger(formId) {
	if (ie4){
		var myForm = document.getElementById(formId);

		// For IE cycle through form fields and attach desired event trigger to each
		for (i=0; i < myForm.elements.length ; i++) {
			var elem = myForm.elements[i];

			if (elem.type.toUpperCase() == 'TEXT'  ||  elem.type.toUpperCase() == 'TEXTAREA') {
				elem.attachEvent("onkeyup", triggerRefresh);

			} else if (elem.type.toUpperCase() == 'CHECKBOX') {
				elem.attachEvent("onclick", triggerRefresh);

			} else {
				elem.attachEvent("onchange", triggerRefresh);
			}
		}
	}
	else if (ns6) {
		// For Netscape we register page-wide event handlers, which is broader
		// than we'd like, but we'll filter at least some of the key presses
		// in the triggerRefresh function. We have to do keyup to get instant
		// recognition while typing in the field that onchange wouldn't get.
		document.captureEvents(Event.CHANGE);
		document.onkeyup=triggerRefresh;
		document.onchange=triggerRefresh;
	}
}


/**
 * Event handler for refreshing the submit buttons (enable/disable).
 */
function triggerRefresh(e) {
	// browser-safe get event (window.event = IE)
	e = (e)  ?  e : ((window.event) ? window.event : "");
	if (e) {
		// browser-safe get target element (srcElement = IE)
		var cur = (e.target) ? e.target : e.srcElement;
		if (ns6) {
			// Only refresh submit if the keyup/change was on a form field
			// as indicated by the presence of a name.
			if (cur.name)
				refreshSubmitButton(cur);
		} else {
			// IE already triggers events only on form fields so just refresh the buttons.
			refreshSubmitButton(cur);
		}
	}
}

