//  FormHandler Class

function FormHandler(container,formDefs){
    //  Establish superclass and inherit methods, StageElement contains 
    //  helper methods for objects that work with the screen
    this.inheritFrom = StageElement;
    this.inheritFrom(container);
    
    this.id="FormHandler";
    
    this.formObjs=new Object();
    this.loaded=false;
    
    //  Interval object for monitoring keypress events
    //  and delaying validation and calculation until
    //  typing is complete
    this.keyupInt=null;
    this.keyupValidateDelay=700;
    this.fieldChangeValDelay=500;
    this.keyupCalculateDelay=800;
    
    this.validator=new FormValidator(this);
    
    this.defaultResultsContent = "<p>Please select the type of calculator from the left menu, fill out the form, and the results will show here once the form is complete.";
    this.defaultResultsInvalidContent = "<p>There are incomplete or incorrect field values. Please correct to and the results will be calculated.<br>Incorrect fields are indicated with the red question mark &#160; <img src='webwork/tooltip/scndLevForm_red.gif' width='12' height='12'>. Place your mouse over the red question mark next to the field to see the reason the field is incomplete or incorrect.";
    this.defaultResultsError1 = "<p>There was a problem obtaining the results from the server.  This might be caused by a problem in the browser or on the server.  Please click the button below to retry the submittal.  If the problem persists, reload the page and try again, or try again later.<br><input name='Retry' value='Retry' type='button' style='background-color:#ffffff;' onclick='handler.resubmitAjax(\"";
    this.defaultResultsError2 = "\");' />";
    
    this.tooltipHandler=null;
    
    this.tttitle="field help";
    this.ttTextColor="#000066";
    this.ttTextErrorColor="#330000";
    this.errorTitleBg="#660000";
    this.errorTitle="field error";
    
    
    //  Methods
    
    this.addFormsConfig=formHandler_addFormsConfig;
    this.addForm=formHandler_addForm;
    
    this.showGroup=formHandler_showGroup;
    this.toggleGroup=formHandler_toggleGroup;
    
    this.setField=formHandler_setField;
    this.showField=formHandler_showField;
    this.hideField=formHandler_hideField;
    this.setFieldError=formHandler_setFieldError;
    this.clearFieldError=formHandler_clearFieldError;
    this.setFieldValue=formHandler_setFieldValue;
    this.focus=formHandler_focus;
    this.blur=formHandler_blur;
    this.change=formHandler_change;
    this.keyup=formHandler_keyup;
    this.checkFieldChanged=formHandler_checkFieldChanged;
    
    this.validateField=formHandler_validateField;
    this.isComplete=formHandler_isComplete;
    this.runFieldAction=formHandler_runFieldAction;
    this.performAction=formHandler_performAction;
    
    this.calculate=formHandler_calculate;
    this.showResultsInWin=formHandler_showResultsInWin;
    this.setCalculator=formHandler_setCalculator;
    this.setTooltipHandler=formHandler_setTooltipHandler;
    
    this.getField=formHandler_getField;
    this.findField=formHandler_findField;
    this.findForm=formHandler_findForm;
    this.findFieldGroup=formHandler_findFieldGroup;
    this.getFieldValue=formHandler_getFieldValue;
    this.setFieldValue=formHandler_setFieldValue;
    this.getFieldValues=formHandler_getFieldValues;
    
    this.submit=formHandler_submit;
    this.submitAjax=formHandler_submitAjax;
    this.onload=formHandler_onload;
    this.onerror=formHandler_onerror;
    //this.setLogLayer("calcResult");
    
    //  If passed the forms config data, call method to add forms to this object
    if(formDefs){this.addFormsConfig(formDefs);}
}   //  FormHandlerClass


/**
*   addFormsConfig
*/
function formHandler_addFormsConfig(config){
    if(typeof config!="object"){return;}
    //  Step through forms and add them to this object
    for(formName in config){this.addForm(formName,config[formName]);}
}   //  addFormsConfig


/**
*   addForm
*/
function formHandler_addForm(formName,config){
    var validation=(config.validation?config.validation:{});
    var fieldConfig=config.fieldConfig;
    var postConf=(config.postConfig?config.postConfig:{});
    var fieldActions=(config.fieldActions?config.fieldActions:{});
    
    this.log.debug("Adding form " + formName + "...");
    
    if(!validation){this.log.error("addForm: No validation data");return;}
    if(!fieldConfig){this.log.error("addForm: No form-field config data");return;}
    
    var form=this.container.forms[formName];
    if(!form){return;}
    
    var formId=form.id;
    
    var formEle=null;
    var fields=new Object();
    var groups=new Object();
    var dependFields=new Object();
    
    
    //  Cycle through the field objects in the form passed and save
    //  reference in fields collection
    for(var i=0; i<form.elements.length; i++){
        var formEle=form.elements[i];
        if(formEle.type=="submit"){continue;}
        var fldName=form.elements[i].name;//form.id+"_"+form.elements[i].name;
        var dotPos=fldName.indexOf(".");
        //  Replace any dots in field name with underscore
        if(dotPos>0){
            fldName=fldName.substring(0,dotPos)+"_"+fldName.substring(dotPos+1);
            }
        else if(dotPos===0){
            fldName="_"+fldName.substring(1);
            }
        var value=form.elements[i].value;
        if(formEle.type=="hidden"){
            var label=null;
            var div=null;
            var ttimgDefState=null;
            var ttName=null;
            var defaultTitle=null;
            var required=null;
            var requiredTag=null;
            var valid=null;
            var isValid=true;
            var fldActions=null;
            }
        else{
            var div=this.findObj("wwgrp_"+form.id+"_"+fldName);
            //  Get label text for field
            var label=this.findObj(form.id+"_"+fldName+"_label");
            label=(label?label.innerHTML:null);
            
            ttName="ttimg_" + form.id+"_"+fldName;
            var ttimgDefState="normal";
            var titleObj=this.findObj(ttName+"_tttitle");
            var defaultTitle=(titleObj?titleObj.innerHtml:"field help");
            
            if(fieldActions[fldName]){
                //this.log.warn("Have field action for field "  + fldName + ", type=" + typeof fieldActions[fldName] + " = " + fieldActions[fldName]);
                fldActions=fieldActions[fldName];
                }
            else {fldActions=null;}
            
            var requiredTag="optional";
            var required=false;
            //  Get validation data, and set default image based on data
            if(validation&&validation[fldName]){
                valid=validation[fldName];
                for(exp in valid){
                    if(exp=="formName"){continue;}
                    var fv=valid[exp];
                    if(fv.validatorName.indexOf("required")>=0){
                        ttimgDefState="required";
                        required=true;
                        requiredTag="required";
                        }
                    else if(fv.validatorName=="fieldcompare"){
                        //  Add this field as a dependent field to the field to be compared to, 
                        //  so a change in that field causes this field to be re-validated
                        if(fv.params.minInclusiveField){dField=fv.params.minInclusiveField;}
                        else if(fv.params.minExclusiveField){dField=fv.params.minExclusiveField;}
                        else if(fv.params.maxInclusiveField){dField=fv.params.maxInclusiveField;}
                        else if(fv.params.maxExclusiveField){dField=fv.params.maxExclusiveField;}
                        if(dField){
                            if(!dependFields[dField]){dependFields[dField]=new Array();}
                            dependFields[dField].push(fldName);
                            }
                        }
                    }
                }
            else {valid=null;}
            //  Field is already valid if there is a value, or if not a value and required=false;
            var isValid=(value?true:(required?false:true)); 
            }
        //  Save field and form names in field object so object can be
        //  passed around to methods and it will contain all info needed to use it
        fields[fldName]={name:fldName,type:"FormHandlerFormField",label:label,value:value,formName:formName,ele:formEle,
                        div:div,ttimgDefState:ttimgDefState,ttName:ttName,defaultTitle:defaultTitle,required:required,requiredTag:requiredTag,
                        fldActions:fldActions,valid:valid,isValid:isValid};
        //this.log.debug("Adding field " + fldName + ", element=" + form.elements[i].tagName + ", type=" + form.elements[i].type);
        this.hideField(fields[fldName]);
    }
    
    //  Add dependent field data to field objects
    for(fld in dependFields){
        if(fields[fld]){
            fields[fld].depends=dependFields[fld];
            }
    }
    
    //  Now step through field config data, and create
    //  group collections with links to the field objects 
    //  created above, and that contain the tooltips to use 
    //  for each field when displayed in each group
    for(var fieldName in fieldConfig){
        
        var fld=fieldConfig[fieldName];
        this.log.warn("Creating group with field " + fieldName);
        
        var dotPos=fieldName.indexOf(".");
        //  Replace any dots in field name with underscore
        if(dotPos>0){
            fieldName=fieldName.substring(0,dotPos)+"_"+fieldName.substring(dotPos+1);
            }
        else if(dotPos===0){
            fieldName="_"+fieldName.substring(1);
            }
        
        var fldGrps=fld.groups;
        var ttips=fld.tooltip;
        
        //  get array of groups that this field belongs to
        
        //  If field groups variable is string equal to 'all', save
        if(typeof fldGrps=="string"){
            if(fldGrps=="all"){
                groups["all"].push(fieldName);
                }
            else {fldGrps=[fldGrps];}
            }
        
        //  else if it's an array, step through
        if(fldGrps.length){
            for(i=0;i<fldGrps.length;i++){
                //  if there is a tooltip defined, use it, otherwise use empty string
                if(ttips){tooltip=((i<ttips.length)?ttips[i]:tooltip);}
                if((!tooltip||(tooltip===""))&&fields[fieldName]){fields[fieldName].ttimgDefState="hidden";}
                //  If field group object hasn't been created, create it
                if(!groups[fldGrps[i]]){groups[fldGrps[i]]={name:fldGrps[i],fields:{},shown:false,type:"FormHandlerGroup"};}
                this.log.warn("Adding field " + fieldName + " to group " + fldGrps[i] + " with tooltip " + tooltip);
                groups[fldGrps[i]].fields[fieldName]={tooltip:tooltip};
                }
            }
    }
    this.formObjs[formName]={name:formName,type:"FormHandlerForm",form:form,fields:fields,groups:groups,postConf:postConf,curGroup:null};
}   //  addForm



/**
*   showGroup
*   Shows all of the fields in the specified group, hides anything not in the group.
*/
function formHandler_showGroup(formName,groupName,dontHideCurrent){
    if(!this.formObjs[formName]||!this.formObjs[formName].groups||!this.formObjs[formName].groups[groupName]){
        this.log.error("showGroup() has no form '"+formName+"' or group '"+groupName+"'");return;
        }
    var form=this.formObjs[formName];
    var group=form.groups[groupName];
    
    /*
    if(typeof form.xmlReqObject=="object"){
        this.log.info("Showing group, canceling xml request.");
        form.xmlReqObject.cancel();
        }
    else this.log.info("Showing group, no xml request");
    */
    
    if(form.curGroup){
        if(form.curGroup==groupName){return;}
        else {form.groups[form.curGroup].shown=false;}
        }
    var showAll=((group == "all")?true:false);
    
    for(var fldName in form.fields){
        if(group.fields[fldName]){
            this.setField(form.fields[fldName],group.fields[fldName]);
            }
        else if(showAll){
            this.showField(form.fields[fldName]);
            }
        else if(!dontHideCurrent) {this.hideField(form.fields[fldName]);}
        }
    
    //  Check for field actions to show/hide fields
    for(fld in group.fields){
        if(form.fields[fld] && form.fields[fld].fldActions){
            this.log.info("Showing with field actions for " + form.fields[fld].name);
            for(var i=0; i<form.fields[fld].fldActions.length; i++){
                this.runFieldAction(form.fields[fld], form.fields[fld].fldActions[i]);
                }
            }
        }
    
    //  If title or desc div are defined, write text to 
    //  them from the calculator object
    if(form.calculator&&form.calculator.showGroup){form.calculator.showGroup(groupName);}
    if(form.calculator){form.calculator.clearResults(this.calcOutputDiv,this.defaultResultsContent);}
    
    form.curGroup=groupName;
    //this.log.debug("Set cur group to " + form.curGroup);
    group.shown=true;
    this.isComplete(form,groupName,true,false);
}   //  showGroup


/**
*   toggleGroup
*   Toggles the display of fields in the passed group, does not affect any fields 
*   not listed in the group. If fields are in toggled group, and also in the current
*   displayed group, they will not be toggled.
*       button defines a reference to a div that contains "show"/"hide" text, the text
*               will be toggled with the group
*/
function formHandler_toggleGroup(formName,groupName,button){
    if(!this.formObjs[formName]||!this.formObjs[formName].groups||!this.formObjs[formName].groups[groupName]){
        this.log.error("toggleGroup() No form named '"+formName+"' or group '"+groupName+"'");
        return;
        }
    var form=this.formObjs[formName];
    var group=form.groups[groupName];
    
    var hide=false;
    if(group.shown){
        hide=true;
        group.shown=false;
        }
    else {group.shown=true;}
    
    for(var fldName in form.fields){
        if(group.fields[fldName]){this.setField(form.fields[fldName],group.fields[fldName],hide);}
        }
    if(typeof button=="string"){button=this.findObj(button);}
    if(button&&button.innerHTML){
        if(!hide){button.innerHTML=button.innerHTML.replace("show","hide");}
        else{button.innerHTML=button.innerHTML.replace("hide","show");}
        }
}   //  toggleGroup


/**
*   setField
*   Shows and configures the passed field.
*/
function formHandler_setField(field,config,hide){
    
    //  Set tooltip message
    if(!hide){this.tooltipHandler.setToolTip(field.ttName,config.tooltip,null,null,null,field.requiredTag);}
    
    //  Show div for field ele
    if(hide){this.hideField(field);}
    else {this.showField(field);}
    
    //  If field has default value, set it
    //this.setFieldValue(field,config.defVal);
    
}   //  setField


/**
*   showField
*   Shows the div for the passed field name.
*/
function formHandler_showField(field){
    if(field.ele.type=="hidden"){return;}
    if(!field.div){
        this.log.error("showField() Can't show field " + field.name);
        return;
        }
    //  Use parent class function to show
    this.unCollapseObj(field.div);
    field.visible=true;

}   //  showField

/**
*   hideField
*   Hides the div for the passed field name.
*/
function formHandler_hideField(field,config){
    if(field.ele.type=="hidden"){return;}
    if(!field.div){
        this.log.error("hideField() Can't hide field " + field.name);
        return;
        }
    //  Use parent class function to hide
    this.collapseObj(field.div);
    field.visible=false;
}   //  hideField

/**
*   setFieldError
*   Flags a field to show an error, changes the tooltip icon
*   to red and sets the tooltip text to the error message.
*   Uses methods inherited from TooltipHandler class to control
*   the tooltips.
*/
function formHandler_setFieldError(field,msg){
    if(!field||!this.tooltipHandler){return;}
    
    //  Set tooltip, if default background color wasn't 
    //  set, don't set title background to error color
    this.tooltipHandler.setToolTip(field.ttName,msg,this.ttTextErrorColor,this.errorTitle,(this.errorTitleBg?this.errorTitleBg:null));
    
    //  Set image color
    this.tooltipHandler.setImageState(field.ttName,"error");
    
}   //  setFieldError


/**
*   clearFieldError
*   Clears a field error, returning the tooltip icon color to its 
*   standard color (based on whether its required or not), and the
*   tooltip message to the default help message.
*   Uses methods inherited from TooltipHandler class to control
*   the tooltips.
*/
function formHandler_clearFieldError(formName,field){
    if(!formName||!this.tooltipHandler||(!this.formObjs[formName])){this.log.error("clearFieldError() Invalid form passed.");return;}
    field=this.getField(formName,field);
    //this.log.debug("Clearing error for field "+field.name);
    var curGroup=this.findFieldGroup(formName,field.name);
    if(!curGroup){return;}
    
    //  Get default tooltip for this group from group object
    var msg=this.formObjs[field.formName].groups[curGroup].fields[field.name].tooltip;
    
    //  Set tooltip
    this.tooltipHandler.setToolTip(field.ttName,msg,this.ttTextColor,this.tttitle,null,field.requiredTag);
    
    //  Set image color
    this.tooltipHandler.setImageState(field.ttName,field.ttimgDefState);
    
}   //  clearFieldError


/**
*   focus
*   focus event handler callback function. Called by
*   form field focus events.
*/
function formHandler_focus(obj,evt){
    
    //this.log.debug(obj.name + " field fired focus event, event obj=" + evt +"<BR>");
    
}   //  focus

/**
*   blur
*   blur event handler callback function.Called by
*   form field blur events.
*/
function formHandler_blur(obj,evt){
    
    //this.log.debug(obj.name + " field fired blur event, event obj=" + evt +"<BR>");
    
}   //  blur

/**
*   change
*   onChange event handler callback function.Called by
*   form field onChange events.
*/
function formHandler_change(obj,evt){
    var target=obj;//(evt.target?evt.target:(evt.srcElement?evt.srcElement:null));
    if(!target){return;}
    //this.log.debug(obj.name + " = " + target.id + " field fired change event");
    //  verify that the field actually changed values
    this.checkFieldChanged(target);
}   //  change

/**
*   keyup
*   keyup event handler callback function.Called by
*   form field keyup events.
*/
function formHandler_keyup(obj,evt){
    var key=(evt.which?evt.which:evt.keyCode);
    var target=(evt.target?evt.target:(evt.srcElement?evt.srcElement:null));
    if(!target){return;}
    //this.log.debug(obj.name + " field fired keyup event");
    //  Ignore tabs and shift tab
    if((key==9)||(key==16)){return;}
    this.checkFieldChanged(target);
}   //  keyup


/**
*   checkFieldChanged
*   Checks whether a field's value has changed, and if so, calls 
*   validateField() after a slight delay, if this is called again 
*   before the delay expires, it cancels the previous call.
*/
function formHandler_checkFieldChanged(field){
    //this.log.debug("this.checkFieldChanged called with field = " + field);
    var field=this.getField(field.form.name,field);//findField(field);
    //this.log.debug("Change called on field " + field);
    if(field){
        var value=this.getFieldValue(field.formName,field.name);
        if(field.value != value){
            if(this.keyupInt){window.clearTimeout(this.keyupInt);}
            this.log.info("change() Field " + field.name + " value " + value + " has changed from " + field.value);
            field.value = value;
            
            if(field.fldActions){
                this.log.info("Showing with field actions for " + field.name);
                for(var i=0; i<field.fldActions.length; i++){
                    this.runFieldAction(field, field.fldActions[i]);
                    }
                }
            
            var timeout="handler.validateField('"+field.formName+"','"+field.name+"',true,true)";
            this.keyupInt=window.setTimeout(timeout,this.fieldChangeValDelay);
            }
        //this.log.debug("change() Field " + field.name + " value has not changed.");
        }
    else this.log.warn("change() Can't find field");
}   //  checkFieldChanged


/**
*   validateField
*   Sample Validation Data for field, uses validation object that contains one or more
*   expression tests, each one is tried, if one fails, method returns false.
*   Parameters:
*       markError  Decides whether to mark error after checking field for valid, if false,
*           only use validity to determine if form is valid. If true, also mark field 
*           valid/invalid and update tooltip. Default is true.
*       recheckForm Determines whether after checking field and finding validity changed, 
*           whether entire form is rechecked for completeness. Allows form to be calculated
*           after incorrect field is fixed. Default is false.
*   Sample Data:
*   <fieldname>:{
*       exp0:{name:"<fieldname>", validatorName:"required",error:"You must enter a value for field 2"},
*       exp1:{name:"<fieldname>", validatorName:"int",error:"Only people ages 13 to 19 may take this quiz",params:{min:13,max:19}},
*
*/
function formHandler_validateField(formName,field,markError,recheckForm){
    //this.log.debug("validate called for form "+formName+", field " + ((typeof field=="object")?field.name:field));
    var form=this.findForm(formName);
    //  Make sure we have an internal field object
    var fieldName = field;
    field=this.getField(formName,field);
    if(this.keyupInt){window.clearTimeout(this.keyupInt);}
    if(!field){this.log.error("validateField('"+fieldName+"') Can't find field, found " + field + " in form " + formName);return false;}
    var valid=true;
    //  Prefix for validate functions used in validator class,
    //  functions are prefix added to name of validator used
    var funPrefix="validate_";
    //  default error message, will be replaced if specific error
    //  message is present in validate settings
    var error="Field value is invalid, please correct.";
    
    if((markError===null)||(markError===undefined)){markError=true;}
    
    //  If validator for field exists, check tests
    if((field.valid) && (field.visible)){
        //  Cycle through tests in validator definition, executing each 
        //  by calling matching function (prefix+name) in validator class 
        for(var test in field.valid){
            var expr=field.valid[test];
            //  Validator object has functions named <prefix>XXX where XXX is the
            //  validatation type stored in expression
            //this.log.debug("Validating " + field.name + " with validator " + field.valid[test]);
            if(this.validator[funPrefix+expr.validatorName]){
                valid=this.validator[funPrefix+expr.validatorName](field.ele,expr.params);
                //this.log.debug("Field " + field.name + " After validator " +  expr.validatorName+ " " + (valid?"is":"is NOT")+" valid");
                if(!valid){
                    //  Break validation on first error
                    if(expr.error){error=expr.error;}
                    if(field.ele.value!=""){markError=true;}
                    break;
                    }
                else{field.error=null;}
                }
            }
    }
    else {valid = true;}
    
    if(field.depends){
        for(var i=0;i<field.depends.length;i++){
            
            if(this.getFieldValue(field.formName,field.depends[i])!==""){
                //this.log.debug("Checking dependent field " + field.depends[i]);
                this.validateField(field.formName,field.depends[i],true,false);
                }
            }
        }
    
    if(field.fldActions){
        for(var i=0; i<field.fldActions; i++){
            this.log.info("Calling field action");
            this.runFieldAction(field, field.fldActions[i]);
            }
        }
    
    if(!valid){
        //  Field is invalid
        field.isValid=false;
        this.log.debug("validateField('"+field.name+"') field not valid."); 
        if(markError || (field.value!="")){this.setFieldError(field,error);}
        if(form.calculator){form.calculator.clearResults((markError?this.defaultResultsInvalidContent:""));}
        }
    else {
        //  Field is valid
        var wasValid=field.isValid;
        field.isValid=true;
        //this.log.debug("validateField('"+field.name+"') field valid.");
        this.clearFieldError(form.name,field);
        if(markError || (!wasValid)){
            //  Check if form/group is complete
            //this.log.debug("returned group " + groupName);
            var groupName=this.findFieldGroup(form.name,field.name);
            if(recheckForm)this.isComplete(form,form.groups[groupName],true,false);
            }
        }
    
    return valid;
}   //  validateField

/**
*   isComplete
*   Checks the fields in the current group of the form to see if the form 
*   is complete and ready to be posted.
*       forceCheck Will cause the function to revalidate 
*           fields, not just use cached isValid value
*       markErrors
*/
function formHandler_isComplete(form,group,forceCheck,markErrors){
    if(this.keyupCalcInt){window.clearTimeout(this.keyupCalcInt);}
    
    form=this.findForm(form);
    
    //  force check validation on each field if flag is passed or if
    //  the form is set to validate each field at every check,
    //  the main reason to validate each field is if a field has a fieldCompare
    //  validator, and thus the validity of that field is dependent on others
    if(form.validateEachField){forceCheck=form.validateEachField;}
    
    if(!form||!form.curGroup){this.log.error("isComplete() No form ("+form+") or current group ("+group+") passed.");return false;}
    if(!group||!group.name){group=form.groups[form.curGroup];}
    
    var valid=true;
    for(var fld in group.fields){
        //this.log.debug("isComplete() calling validate on " + fld);
        if(!form.fields[fld]){this.log.error("isComplete() Can't find field " + fld + " in form config.");continue;}
        if((form.fields[fld].isValid==undefined)||forceCheck){this.validateField(form.name,form.fields[fld],markErrors,false);}
        if(form.fields[fld].isValid){
            /*this.log.debug("    field "+fld+" is already valid.");*/
            continue;
            }
        else{
            //this.log.warn("    field " + fld + " is not complete from group " + group.name);
            //this.setFieldError(form.fields[fld],form.fields[fld].error);
            valid=false;
            if(!forceCheck&&!markErrors){break;}
            }
        }
    
    this.log.info("isComplete() Group " + group.name + (valid?" is":" is not")+" complete.");
    
    //  If form group is valid, call calculate function,
    //  if there is a calculate class defined for group, it
    //  will perform calculation
    if(valid){
        //this.log.debug("isComplete() calculating...");
        if(form.calculator){form.calculator.clearResults("");}
        if(form.calculator){this.calculate(form,group.name);}
        if(form.postConf.ajaxAutoPost){
            if(!form.postConf.ajaxDefaultGroup||(form.postConf.ajaxDefaultGroup==group.name)){
                this.log.info("Submitting form with ajax...");
                this.submitAjax(form,group.name);
                }
            }
        }
    else if(form.calculator){
        this.log.debug("clearing results with markErrors = " + markErrors);
        form.calculator.clearResults((markErrors?this.defaultResultsInvalidContent:"Please enter the criteria into the fields to the left to see the results here."));
        }
    
    return valid;
}   //  isComplete


/**
*   runFieldAction
*/
function formHandler_runFieldAction(field,action){
    
    var condition=action.condition;
    var value=action.value;
    if(condition=="="){
        if(field.ele.value.toLowerCase()==value.toLowerCase()){
            this.performAction(field.formName,action.action,action.fieldnames);
            }
        }
    
}   //  runFieldAction

function formHandler_performAction(formName,action,fields){
    this.log.info("type " + typeof fields);
    if(action == "show"){
        for(var f=0; f<fields.length; f++){
            var fld=this.getField(formName,fields[f]);
            this.log.info("Showing " + fld + " for " + fields[f]);
            this.showField(fld);
            }
        }
    else if(action == "hide"){
        for(var f=0; f<fields.length; f++){
            var fld=this.getField(formName,fields[f]);
            this.log.info("Hiding " + fld);
            this.hideField(fld);
            }
        }
}   //  



/**
*   calculate
*   Calls the calcualate function on a form, if one is defined.
*   Passed the form name, or a reference to one of this object's form objects.
*/
function formHandler_calculate(form,groupName){
    //  Make sure we have a reference to a form object
    form=this.findForm(form);
    if(!groupName){groupName=form.curGroup;}
    if(!form.groups[groupName]){this.log.error("calculate() Group " + groupName + " doesn't exist on config.");return;}
    
    if(form.calculator){
        var values=this.getFieldValues(form,groupName);
        form.calculator.calculateAndDisplay(form.name,groupName,values);
    }
}   //  calculate


/**
*   showResultsInWin
*   Opens a window and displays calculator results in window
*/
function formHandler_showResultsInWin(formName,groupName,container,div){
    form=this.findForm(formName);
    if(!groupName){groupName=form.curGroup;}
    
    var outLayer=new Layer(container,div);
    outLayer.build();
    
    if(form.calculator){
        var values=this.getFieldValues(form,groupName);
        form.calculator.showResultsInWin(form.name,groupName,values,outLayer);
    }
}   //  showResultsInWin


/**
*   setCalculator
*   Sets the calculator object to use to create custom calculations
*/
function formHandler_setCalculator(formName,calculator){
    form=this.findForm(formName);
    form.calculator = calculator;
}   //  setCalculator

/**
*   setTooltipHandler
*/
function formHandler_setTooltipHandler(ttHandler){
    this.tooltipHandler=ttHandler;
    //  Step through and set default states for tooltip icons and tooltips
    for(var frm in this.formObjs){
        var formObj=this.formObjs[frm];
        for(var fld in formObj.fields){
            var field=formObj.fields[fld];
            var ttname=field.ttName;
            //alert("setting tooltip for field " + fld + " state=" + field.ttimgDefState);
            this.tooltipHandler.setImageState(ttname,field.ttimgDefState);
            this.tooltipHandler.setToolTipTitle(ttname,field.defaultTitle,null,((field.ttimgDefState=="required"||field.ttimgDefState=="optional")?field.defState:null));
            }
        }
        
}   //  setTooltipHandler
    


/**
*   getField
*   Returns a reference to this objects field object, when passed the
*   form name and field name.
*/
function formHandler_getField(formName,fieldName){
    if(!fieldName){this.log.error("getField() No field name passed.");return null;}
    var type=typeof fieldName;
    if(type=="object"){
        //  If fieldName actually reference to field object, return it
        if(fieldName.formName){return fieldName;}
        //  fieldName references a html form field object, use name
        else if(fieldName.name) {
            fieldName=fieldName.name;
            }
        else{
            //this.log.debug("field is " + fieldName + " typeof " + typeof fieldName + " has id=" + fieldName.id);
            return null;}
        }
    else if(type!="string"){return null;}
    
    var dotPos=fieldName.indexOf(".");
    //  Replace any dots in field name with underscore
    if(dotPos>0){
        fieldName=fieldName.substring(0,dotPos)+"_"+fieldName.substring(dotPos+1);
        }
    else if(dotPos===0){
        fieldName="_"+fieldName.substring(1);
        }
    
    if(this.formObjs[formName]&&this.formObjs[formName].fields){
        if(!this.formObjs[formName].fields[fieldName]){
            fieldName=formName+"_"+fieldName;
            }
        //this.log.debug("Looking for field " + fieldName);
        return this.formObjs[formName].fields[fieldName];
        }
    return null;
}   //  getField

/**
*   findField
*   Searches forms registered for the first occurance of a field object 
*   with the specified name, and returns the reference to the field object.
*/
function formHandler_findField(fieldName){
    if(!fieldName){return null;}
    if(typeof fieldName=="object"){
        //  If fieldName actually reference to a field object, return it
        if(fieldName.type&&fieldName.type=="FormHandlerFormField"){return fieldName;}
        else if(fieldName.id){return this.findField(fieldName.name);}
        else {return null;}
        }
    for(var frm in this.formObjs){
        if(this.formObjs[frm].fields&&this.formObjs[frm].fields[fieldName]){
            return this.formObjs[frm].fields[fieldName];
            }
        }
    return null;
}   //  findField


/**
*   findForm
*   Searches forms registered and returns the internal form object for 
*   the first form with the specifed name.
*/
function formHandler_findForm(formName){
    //alert("Finding form " + formName);
    if(!formName){return null;}
    else if(typeof formName=="object"){
        //  If formName actually reference to a form object, return it
        if(formName.type&&formName.type=="FormHandlerForm"){return formName;}
        else {return null;}
        }
    //alert("Returning " + this.formObjs[formName]);
    return this.formObjs[formName];
}   //  findForm

/**
*   findFieldGroup
*   Finds the group that a displayed field is associated with, can be
*   current group for form, or can be subgroup.
*/
function formHandler_findFieldGroup(formName,fieldName){
    if((typeof fieldName!="string")&&fieldName.name){fieldName=fieldName.name;}
    if(!formName||!fieldName||!this.formObjs[formName]){this.log.error("findFieldGroup() Can't find form or no form or field name passed.");return;}
    var form=this.formObjs[formName];
    //  Check current group for field
    if(form.curGroup&&form.groups[form.curGroup].fields[fieldName]){return form.curGroup;}
    
    //  Not in current group, step through the groups, 
    //  and check each visible group to find field
    for(var group in form.groups){
        if(form.groups[group].shown&&form.groups[group].fields[fieldName]){return group;}
        }
    return null;
}   //  findFieldGroup

/**
*   getFieldValue
*   Returns the most recently saved value for a field.  Values are
*   cached in an object, that is used for validation and for calculations
*   these values are updated as field values change/and or fields are blurred.
*/
function formHandler_getFieldValue(formName,fieldName){
    if(!this.formObjs[formName]||!this.formObjs[formName].fields||!this.formObjs[formName].fields[fieldName]){
        return;
        }
    return this.formObjs[formName].fields[fieldName].ele.value;
}   //  getFieldValue


/**
*   setFieldValue
*   Sets the value in the passed field, if value is null it deletes the current value.
*   Field passed can be string field name, or field object.
*/
function formHandler_setFieldValue(formName,fieldName,value,suppressChangeEvent){
    //this.log.debug("setFieldValue() called with field = " + fieldName + ", and value= " + value);
    var field=this.getField(formName,fieldName);
    var ele=field.ele;
    if((ele.type=="select-one")||(ele.type=="select-multiple")){
        if(!value){value=0;}
        if(isNaN(parseInt(value))){
            //  If int is not passed as value, try to find value in selectlist
            for(var i=0;i<ele.options.length;i++){
                if((ele.options[i].value==value)||(ele.options[i].text==value)){value=i;break;}
            }
        ele.selectedIndex=value;
        }
    }
    else if(ele.value!==null){
        if(value === null){this.log.warn("setFieldValue() Null value passed.");value="";}
        ele.value=value;
        if(!suppressChangeEvent){ele.onchange(ele);}
        }
    else {this.error("setFieldValue() Error setting value.");}
}   //  setFieldValue


/**
*   getFieldValues
*   Returns an object that contains all of the field 
*   values for a form group in an associative array.
*/
function formHandler_getFieldValues(form,groupName){
    form=this.findForm(form);
    if(!groupName){groupName=form.curGroup;}
    if(!form||!form.groups[groupName]){this.log.error("getFieldValues() could not find form object or group");return;}
    //this.log.debug("---- Getting field values ------");
    var vals={};
    for(var fieldName in form.groups[groupName].fields){
        if(this.formObjs[form.name].fields[fieldName]){
            vals[fieldName]={label:this.formObjs[form.name].fields[fieldName].label,value:this.formObjs[form.name].fields[fieldName].ele.value};
            //this.log.debug("getFieldValues adding value from field " + fieldName + " with value " + vals[fieldName].value);
            }
        }
    return vals;
}   //  getFieldValues

/**
*   submit
*   submits the form according to the settings in the
*   config.postConfig properties, uses regular post to
*   a target or xmlHttpRequest if set to do so.
*/
function formHandler_submit(form,groupName,params,skipAjax){
    form=this.findForm(form);
    if(!groupName){groupName=form.curGroup;}
    if(!form){this.log.error("post() Could not find form object");return;}
    //  Make sure form is valid
    this.log.info("Checking if the form [" + form.id + "] " + form.name + " is complete before submitting...<BR>------------------------");
    if(!this.isComplete(form,groupName,true,true)){
        this.log.info("Form is not complete, canceling submittal.");
        
        //  Add call to function that will notify user that there are incomplete fields...
        
        return;
        }
    
    if(!skipAjax && form.postConf.useAjax){
        this.log.debug("Submitting form with ajax...");
        this.submitAjax(form,groupName,params);
        }
    //  Else add manual post...
    else{
        //this.log.debug("Submitting form manually...");
        //  Add any passed params to the query string of request
        var paramStr="";
        for(ele in params){
            if(paramStr==""){paramStr="?";}
            else {paramStr+="&";}
            paramStr+=ele+"="+escape(params[ele]);
            }
        var oldAction=form.form.action;
        form.form.action+=paramStr;
        form.form.submit();
        form.form.action=oldAction;
        }
}   //  submit

/**
*   submitAjax
*   Submits the passed form as an ajax XMLHttpRequest, setting
*   a simple xml file format for the form.
*/
var req;
var targetDiv;
function formHandler_submitAjax(form,groupName,params){
    form=this.findForm(form);
    
    if(!groupName){groupName=form.curGroup;}
    if(!form){this.log.error("post() Could not find form object");return;}
    
    //  If target is an object that we're inserting results into, and we're not
    //  using ajaxAutoPost, then clear target and put loading text into div
    if(form.postConf.target && !form.postConf.ajaxAutoPost
        && (typeof form.postConf.target=="object") && (form.postConf.target.innerHTML)){
        form.postConf.target.innerHTML=this.loadingAjaxMsg;
        }
    
    if(!form.xmlReqObject){
        form.xmlReqObject = new XMLRequest(document,form.postConf.url,true);
        form.xmlReqObject.setDebugResponse(true);
        if(form.postConf.ajaxDataType)form.xmlReqObject.setDataType(form.postConf.ajaxDataType);
        if(form.postConf.target)form.xmlReqObject.setTarget(form.postConf.target);
        if(form.postConf.ajaxOnload)form.xmlReqObject.addCallback("onload",form.postConf.ajaxOnload);
        form.xmlReqObject.addCallback("onload",this,{url:form.postConf.url,form:form.name});
        form.xmlReqObject.addCallback("onerror",this,{url:form.postConf.url,form:form.name});
        if(form.postConf.ajaxDataDefinition)form.xmlReqObject.setDataDefinition(form.postConf.ajaxDataDefinition);
        }
    
    if(!form.postConf.loadLayer && form.postConf.loadingGraphic){
            if(!form.postConf.loadingGraphicDiv){
                form.postConf.loadingGraphicDiv=null;
                }
            //  Create layer for loading graphic in target div
            form.postConf.loadLayer=new Layer(this.container,form.postConf.loadingGraphicDiv,{display:"hidden"});
            form.postConf.loadLayer.build();
            form.postConf.loadLayer.setContent("<p><img src=\""+form.postConf.loadingGraphic+"\" width=\"100\" height=\"100\"><p>");
            }
    if(form.postConf.loadLayer){
        form.postConf.loadLayer.show();
        this.loadingLayer=form.postConf.loadLayer;
        }
    
    form.xmlReqObject.send(params,true);
    
}   //  submitAjax

/**
*   Ajax event handler for onload.
*/
function formHandler_onload(xmlReq,params){
    this.log.info("---- ajax submittal to '" + params.url + "' is complete ----");
    if(this.loadingLayer){this.loadingLayer.hide();}
    if(params.form){
        var form=this.findForm(params.form);
        if(form && form.calculator && form.calculator.onDisplay){form.calculator.onDisplay();}
        }
}   //  ajaxSubmitted

/**
*   Ajax event handler for errors.
*/
function formHandler_onerror(xmlReq,params){
    this.log.error("---- ajax submittal to '" + params.url + "' failed ----");
    if(this.loadingLayer){this.loadingLayer.hide();}
    if(params.form){
        var form=this.findForm(params.form);
        if(form && form.calculator && form.calculator.clearResults){form.calculator.clearResults(this.defaultResultsError1+form.name+this.defaultResultsError2);}
        }
}   //  onerror

function doAction(){;}


