/*
Copyright (c) 2010, Kinetic Data Inc. All rights reserved.
http://www.kineticdata.com
$Rev: 617 $
$Date: 2010-06-10 09:34:34 -0500 (Thu, 10 Jun 2010) $
*/
/**
 * This file is to be used for adding client-side functionality to Kinetic Survey and Kinetic Request.
 * @module kd_action
 *
 */

// setup shortcuts
if (typeof Dom == "undefined") { Dom = YAHOO.util.Dom; }
if (typeof Connect == "undefined") { Connect = YAHOO.util.Connect; }


/**
 * The KD global namespace object
 * @class KD
 * @static
 */
if (typeof KD == "undefined") {
    KD = {};
}
if (typeof KD.utils == "undefined") {
    KD.utils = {};
}

if (! KD.utils.Action){


    /*
ACTION CLASS
*/
    KD.utils.Action= new function(){
        this.byPassValidation = false;
        this.readOnlyColor="#E0E0E0";
        /**
         * Holds SimpleDataRequest errors so they don't get shown again and again when a connection goes down or for authentication.
         * Gets cleared as soon as a successful SDR is processed.
         */
        this.sdrErrors;

        /**
         * Remove an element from the display of the page.  The space that this element occuppied on the page will also
         * be removed causing any lower elements to shift up.  Uses the CSS property display:none;
         * @param label : The instanceID of the element (GUID) or the label of the element.
         * @param keepValue : Whether or not to clear the value of the question when removed.  Defaults to false.
         */
        this.removeElement = function(label, keepValue){
            /* default the label as the first argument */
            var label_id = label;
            var isOptional = false;
            /* if the argument is an array, the label id is the first item,
             * and the second item indicates whether the element should also be
             * made optional
             */
            if (label.constructor == Array) {
                label_id = label[0];
                isOptional = label[1];
            }
            var qLayer=KD.utils.Util.getElementObject(label_id);
            if(qLayer){
                if(!keepValue){
                    var theEls=Dom.getElementsByClassName('answerValue','',qLayer);
                    for(var i=0;i<theEls.length;i++){
                        KD.utils.Action.setQuestionValue(KD.utils.Util.getIDPart(theEls[i].id),"$\NULL$");
                    }
                }
                qLayer.style.display="none";
                if (isOptional) {
                    KD.utils.Action.makeQuestionOptional(label_id);
                }
            }
        }

        /**
         * Insert an element into the display of the page.  Elements below this element will shift downward.
         * Uses the CSS property display:block;
         * @param label : The instanceID of the element (GUID) or the label of the element.
         */
        this.insertElement = function(label){
            /* default the label as the first argument */
            var label_id = label;
            var isRequired = false;
            /* if the argument is an array, the label id is the first item,
             * and the second item indicates whether the element should also be
             * made required
             */
            if (label.constructor == Array) {
                label_id = label[0];
                isRequired = label[1];
            }
            var qLayer=KD.utils.Util.getElementObject(label_id);
            if(qLayer){
                //If element is hidden, setting display to block still won't show element, need to set it.
                var visib=Dom.getStyle(qLayer, 'visibility');
                if(visib && visib.toUpperCase() == "HIDDEN"){
                    qLayer.style.visibility="visible";
                }
                //inserting only divs, so default to block. Will then override any style=none that may be in the css.
                qLayer.style.display="block";
                if (isRequired) {
                    KD.utils.Action.makeQuestionRequired(label_id);
                }
            }
        }

        /**
         * Hide an element on the page.  The element will still retain its position on the page (leaving a blank space).
         * Uses the CSS property visibility:hidden;
         * @param label : The instanceID of the element (GUID) or the label of the element.
         * @param keepValue: true to keep field values when hidden, false to clear field values when hidden.
         */
        this.hideElementInPlace = function(label, keepValue){
            /* default the label as the first argument */
            var label_id = label;
            var isOptional = false;
            /* if the argument is an array, the label id is the first item,
             * and the second item indicates whether the element should also be
             * made optional
             */
            if (label.constructor == Array) {
                label_id = label[0];
                isOptional = label[1];
            }
            var qLayer=KD.utils.Util.getElementObject(label_id);
            if(qLayer){
                if(!keepValue){
                    var theEls=Dom.getElementsByClassName('answerValue','',qLayer);
                    for (var i = 0; i < theEls.length; i++) {
                        KD.utils.Action.setQuestionValue(KD.utils.Util.getIDPart(theEls[i].id), "$\NULL$");
                    }
                }
                qLayer.style.visibility="hidden";
                if (isOptional) {
                    KD.utils.Action.makeQuestionOptional(label_id);
                }
            }
        }

        /**
         * Show an element on the page.  The element will still retain its position on the page.
         * Uses the CSS property visibility:inherit;
         * @param label : The instanceID of the element (GUID) or the label of the element.
         */
        this.showElementInPlace = function(label){
            /* default the label_id as the first argument */
            var label_id = label;
            var isRequired = false;
            /* if the argument is an array, the label id is the first item,
             * and the second item indicates whether the element should also be
             * made required
             */
            if (label.constructor == Array) {
                label_id = label[0];
                isRequired = label[1];
            }
            var qLayer=KD.utils.Util.getElementObject(label_id);
            if (qLayer) {
                qLayer.style.visibility="inherit";
                if (isRequired) {
                    KD.utils.Action.makeQuestionRequired(label_id);
                }
            }
        }


        /**
         * Cross-browser function to add an event listener to an element.
         * @param object : The HTML object to add the listener to.
         * @param triggerEvent : The event to fire (blur, focus, load, etc.)
         * @param fnctn : The function to call when the event is fired
         */
        this.addListener = function(object, triggerEvent, fnctn){
            YAHOO.util.Event.addListener(object, triggerEvent, fnctn);
        }

        /**
         * Hightlight a question label. Typically used for a required field not filled in, or pattern not matching
         * @param answerField : The answer element
         */
        this.highlightField = function(answerField) {
            //Use label so it is safe for checkboxes
            var labelObj=KD.utils.Util.getQuestionLabel(KD.utils.Util.getLabelFromAnswerEl(answerField));
            if (labelObj) {
                var origColor=labelObj.getAttribute("origStyleColor");
                if(origColor==null || origColor == ""){
                    labelObj.setAttribute("origStyleColor", (labelObj.style.color)?labelObj.style.color:"defaultColor");
                    labelObj.style.color = "red";
                }
            }
        }
        /**
         * Removes the highlighting from a question label, usually done after a required field/pattern highlighting occurs.
         * @param answerField : The answer element
         */

        this.resetRequiredField = function(answerField) {
            var labelObj=KD.utils.Util.getQuestionLabel(KD.utils.Util.getLabelFromAnswerEl(answerField));
            if(labelObj){
                var origStyle = labelObj.getAttribute("origStyleColor");
                if (origStyle=="defaultColor"){
                    origStyle = "";
                }
                labelObj.style.color = origStyle;
                labelObj.removeAttribute("origStyleColor");
            }
        }

        /**
         * Retrieves the errorValue attribute of a question. Falls back to the question label if no required label is set.
         * This is the error message for a required field created for the question.
         * @param answerField : The answer element
         */

        this.getErrorValue = function(answerField){
            var errorVal = answerField.getAttribute("errorValue");
            if (errorVal == null || errorVal=="") {
                var labelObj=KD.utils.Util.getQuestionLabel(KD.utils.Util.getLabelFromAnswerEl(answerField));
                if (labelObj != null ){
                    errorVal= labelObj.getAttribute("label");
                }
            }
            return errorVal;
        }

        /**
         * Simply mimics the click event of a radio button question.
         * @param elementId : The survey question Id (Example 'SRVQSTN_KS234234234')
         * @param elIndex : The index (zero based) of the radio button to click.
         */
        this.mimicRadioClick = function(elementId, elIndex){
            var myRadioElements=document.getElementsByName(elementId);
            myRadioElements[elIndex].click();
        }

        /*
         * Watch the length of a textarea field and stop the typing if longer than desired length.
         * Automatically used for TEXTAREA fields which have a max char value.
         * @param el : The question element
         */
        this.watchFieldLength = function(el){
            var limit=el.getAttribute("maxlength");
            if(limit){
                limit=parseInt(limit);
            } else{
                return;
            }
            if(el.value && el.value.length>limit){
                el.value=el.value.substring(0,limit);
            }
        }

        /*
         * INTERNAL USE ONLY
         */
        this.loadPreviousPage = function() {
            var valid = true;

            // if authentication is required, check if session is still valid
            if (KD.utils.ClientManager.isAuthenticationRequired === "true") {
                KD.utils.Action.byPassValidation=true;

                // check if session is still valid
                KD.utils.ClientManager.checkSession();
                var response = KD.utils.ClientManager.sessionCheck;
                if (response.status !== 200) {
                    var message = response.statusText;
                    if (response.getResponseHeader != null && response.getResponseHeader['X-Error-Message'] != null){
                        message = response.getResponseHeader['X-Error-Message'];
                    }
                    KD.utils.ClientManager.registerInvalidSessionRequest("PREV_PAGE_REQUEST","", "", "");
                    KD.utils.Action.handleHttpErrorCode(response.status, message);
                    valid = false;
                }
            }

            /*Reset the buttons*/
            if(!valid){
                for (var i=0; i < KD.utils.ClientManager.submitButtons.length; i++) {
                    KD.utils.Action.enableButton(KD.utils.ClientManager.submitButtons[i]);
                }
                KD.utils.ClientManager.isSubmitting=false;
                return;
            }

            document.pageQuestionsForm.action = "PreviousPage";
            this.byPassValidation=true;
            document.pageQuestionsForm.submit();
        }

        /*
         * INTERNAL USE ONLY
         */
        this.validateForm = function(evt, form) {
            var trigger=YAHOO.util.Event.getTarget(evt);
            var pageId=Dom.get("pageID").value;
            var valid=true;

            //Only allow submit from buttons, not returns
            if(!trigger.type  || (trigger.type && trigger.type.toUpperCase()!="BUTTON" && trigger.type.toUpperCase()!="SUBMIT")){
                try {
                    YAHOO.util.Event.stopEvent(evt);
                } catch (e) {}
                
                KD.utils.ClientManager.isSubmitting=false;
                return false;
            }

            if (KD.utils.Action.byPassValidation) return true;

            //check beforeSubmit actions
            var actions=KD.utils.ClientManager.customEvents.getItem(pageId+"-beforeSubmit");
            if(actions && actions.length >0){
                for(var k=0; k < actions.length; k++){
                    var clientAction=actions[k];
                    var fn=clientAction.action;
                    valid=fn.call(evt, evt, clientAction);
                    if(valid == null){
                        valid=true;
                    }
                    if(valid==false){
                        try {
                            YAHOO.util.Event.stopEvent(evt);
                        } catch (e) {}

                        //re-enable submit button(s)
                        for(var i=0; i < KD.utils.ClientManager.submitButtons.length; i++){
                            KD.utils.Action.enableButton(KD.utils.ClientManager.submitButtons[i]);
                        }
                        KD.utils.ClientManager.isSubmitting=false;
                        return false;
                    }
                }
            }

            //for(var i=0; i < KD.utils.ClientManager.submitButtons.length; i++){
            //  KD.utils.Action.disableButton(KD.utils.ClientManager.submitButtons[i]);
            //}
            valid=KD.utils.Action.validateFields(form.elements);
            //check submit actions
            if(valid){
                var submitActions=KD.utils.ClientManager.customEvents.getItem(pageId+"-submit");
                if(submitActions && submitActions.length >0){
                    for(var m=0; m < submitActions.length; m++){
                        var clientAction=submitActions[m];
                        var fn=clientAction.action;
                        valid=fn.call(evt, evt, clientAction);
                        if(valid == null){
                            valid=true;
                        }
                        if (valid == false){
                            break;
                        }
                    }
                }
            }
            /*Do session check*/
            if (valid) {
                // if authentication is required, check if session is still valid
                if (KD.utils.ClientManager.isAuthenticationRequired === "true") {
                    // check if session is still valid
                    KD.utils.ClientManager.checkSession();
                    var response = KD.utils.ClientManager.sessionCheck;
                    if (response.status !== 200) {
                        var message = response.statusText;
                        if (response.getResponseHeader != null && response.getResponseHeader['X-Error-Message'] != null){
                            message = response.getResponseHeader['X-Error-Message'];
                        }
                        KD.utils.ClientManager.registerInvalidSessionRequest("NEXT_PAGE_REQUEST","", "", "");
                        KD.utils.Action.handleHttpErrorCode(response.status, message);
                        valid = false;
                    }
                }
            }
            /*Reset the buttons*/
            if(!valid){
                try {
                    YAHOO.util.Event.stopEvent(evt);
                } catch (e) {}

                for (var i=0; i < KD.utils.ClientManager.submitButtons.length; i++) {
                    KD.utils.Action.enableButton(KD.utils.ClientManager.submitButtons[i]);
                }
                KD.utils.ClientManager.isSubmitting=false;
            }
            //If submitted from a non-submit button, do the submit
            if (valid && trigger.type && trigger.type.toUpperCase()=="BUTTON") {
                form.submit();
            }
            return valid;
        }

        /*Validate an array of fields for required & pattern.  Will pop an alert and color the labels to red if not valid.
         * param fieldsArray : This should be an array of input/select/text/textarea elements, not layer elements.
         */
        this.validateFields = function(fieldsArray){
            var valid = true;
            var nextElement = null;
            var firstErrorElement = null;
            var requiredFieldList='';
            var formatFieldList='';
            var requiredWarning=KD.utils.ClientManager.requiredWarningStr+'\n';
            var formatWarning=KD.utils.ClientManager.validFormatWarningStr+'\n';
            var maxCharsOnSubmit=KD.utils.ClientManager.maxCharsOnSubmit;
            var characterCount=0;
            var disabledFields=new Array();
            var checkedFields=new Array();
            var checkboxFields={};

            // Process all required checkbox questions first, and store the parentId
            // to indicate if any of the children have been checked
            //
            for (var f=0; f < fieldsArray.length; f++) {
                var cb = fieldsArray[f].type && fieldsArray[f].type.toLowerCase()=="checkbox";
                if (cb) {
                    var cbChecked = fieldsArray[f].checked;
                    var reqAttr = fieldsArray[f].getAttribute("required");
                    if (reqAttr && reqAttr.toLowerCase()=="true") {
                        var cbParentId = KD.utils.Util.getCheckboxParentId(fieldsArray[f]);
                        if (cbParentId) {
                            var cbParentExists = typeof checkboxFields[cbParentId] !== "undefined"
                            // update if this is first child, or if parent isn't already checked
                            if (!cbParentExists || (cbParentExists && checkboxFields[cbParentId] == false)) {
                                checkboxFields[cbParentId] = fieldsArray[f].checked;
                            }
                        }
                    }
                }
            }

            for (var i = 0; i < fieldsArray.length; i++) {
                nextElement = fieldsArray[i];
                if(nextElement.disabled == true && nextElement.type && nextElement.type.toUpperCase() != "SUBMIT" && nextElement.type.toUpperCase()!= "BUTTON"){
                    disabledFields.push(nextElement);
                }

                var foundValue=false;

                if(nextElement.type=="radio" || nextElement.type=="checkbox"){
                    if (nextElement.checked){
                        characterCount += nextElement.value.length;
                        foundValue=true;
                    }
                } else if(nextElement.type!="button" && nextElement.value.length >0){
                    characterCount += nextElement.value.length;
                    foundValue=true;
                }

                if (foundValue) {
                    characterCount += nextElement.name.length+1;
                }

                if (nextElement.getAttribute("required") == 'true' && checkedFields[nextElement.name]!="true") {
                    checkedFields[nextElement.name]="true";

                    if (nextElement.tagName.toLowerCase() == 'select') {
                        if (nextElement.options.length == 0 || nextElement.selectedIndex == null || nextElement.value==""){

                            requiredFieldList=requiredFieldList+"  - "+KD.utils.Action.getErrorValue(nextElement)+"\n";
                            if (firstErrorElement==null){
                                firstErrorElement=nextElement;
                            }
                            KD.utils.Action.highlightField(nextElement);
                            valid=false;
                        } else {
                            KD.utils.Action.resetRequiredField(nextElement);
                        }
                    }
                    else if (nextElement.type.toLowerCase()=='radio'){
                        var radioGroup=document.getElementsByName(nextElement.name);
                        var isChecked = false;
                        for (var j=0;j<radioGroup.length;j++){
                            if(radioGroup[j].checked==true){
                                isChecked=true;
                            }
                        }
                        if (isChecked==false){
                            if (firstErrorElement==null){
                                firstErrorElement=radioGroup[0];
                            }
                            KD.utils.Action.highlightField(radioGroup[0]);
                            requiredFieldList=requiredFieldList+"  - "+KD.utils.Action.getErrorValue(radioGroup[0])+"\n";
                            valid=false;
                        } else {
                            KD.utils.Action.resetRequiredField(radioGroup[0]);
                        }

                    }
                    else if (nextElement.type.toLowerCase()=="checkbox"){
                        var cbParentId = KD.utils.Util.getCheckboxParentId(nextElement);
                        if (cbParentId && typeof checkedFields[cbParentId]=="undefined") {
                            if (checkboxFields[cbParentId] != true) {
                                if (firstErrorElement==null){
                                    firstErrorElement=nextElement;
                                }
                                KD.utils.Action.highlightField(nextElement);
                                requiredFieldList = requiredFieldList + "  - " + KD.utils.Action.getErrorValue(nextElement) + "\n";
                                valid=false;
                            }
                            checkedFields[cbParentId]="true";
                        }
                        if (cbParentId && checkboxFields[cbParentId] == true) {
                            KD.utils.Action.resetRequiredField(nextElement);
                        }
                    }
                    else if ((!nextElement.value || nextElement.value.length < 1) && nextElement.type.toLowerCase() != "checkbox") {
                        if (firstErrorElement==null){
                            firstErrorElement=nextElement;
                        }
                        KD.utils.Action.highlightField( nextElement);
                        requiredFieldList=requiredFieldList+"  - "+KD.utils.Action.getErrorValue(nextElement)+"\n";
                        valid=false;
                        continue;
                    } else {
                        KD.utils.Action.resetRequiredField(nextElement);
                    }
                }
                if (nextElement.value.length > 0 && nextElement.getAttribute("validationFormat")) {
                    var expStr = nextElement.getAttribute("validationFormat");
                    var exp = new RegExp(expStr);
                    if (exp.test(nextElement.value)==false) {
                        KD.utils.Action.highlightField(nextElement);
                        firstErrorElement=nextElement;
                        var validationText = nextElement.getAttribute("validationText");
                        if (validationText) {
                            formatFieldList = formatFieldList + "  - " + validationText+"\n";
                        }
                        valid=false;
                    } else {
                        KD.utils.Action.resetRequiredField(nextElement);
                    }
                } else if ((nextElement.value.length == 0 && nextElement.getAttribute("required") != 'true') && nextElement.getAttribute("validationFormat")) {
                    KD.utils.Action.resetRequiredField(nextElement);
                }

                // ensure date type questions contain a valid date
                if(nextElement.name && nextElement.name.indexOf('SRVQSTN_') != -1){
                    var isDateField = KD.utils.Util.isDate(nextElement);
                    if(isDateField){
                        var validDate = KD.utils.Action.validateDateField(nextElement);
                        if(!validDate){
                            if (firstErrorElement==null){
                                firstErrorElement=nextElement;
                            }
                            KD.utils.Action.highlightField( nextElement);

                            // get the date field label
                            var label = "";
                            var labelObj=KD.utils.Util.getQuestionLabel(
                                KD.utils.Util.getLabelFromAnswerEl(nextElement));
                            if (labelObj && labelObj.getAttribute('label')) {
                                label = labelObj.getAttribute('label');
                            }

                            // add the date field to the format field list string
                            formatFieldList = formatFieldList + "  - " + label + " is not a valid date\n";
                            valid=false;
                        } else {
                            KD.utils.Action.resetRequiredField(nextElement);
                        }
                    }
                }

            }
            if (!valid) {
                var errorStr = "";

                if (requiredFieldList)
                    errorStr = errorStr + requiredWarning + requiredFieldList;

                if (formatFieldList)
                    errorStr = errorStr + formatWarning+ formatFieldList;

                window.alert(errorStr);

                if (firstErrorElement.focus && firstErrorElement.style && firstErrorElement.style.visiblity != "hidden" && firstErrorElement.style.display != "none"){
                    try{
                        firstErrorElement.focus();
                    }catch(error){//continue
                    }
                }

                return false;
            }

            if (characterCount > maxCharsOnSubmit) {
                window.alert(KD.utils.ClientManager.tooManyCharactersForSubmitStr);
                return false;
            }
            /*Enable disabled radio buttons/checkboxes so they submit */
            for (var j=0; j < disabledFields.length; j++){
                disabledFields[j].disabled=false;
            }
            return true;
        }

        /**
         * Validates the element contains a valid date value
         * @param {Object} el question element to validate
         * @return boolean True if value date, otherwise false
         */
        this.validateDateField = function(el) {
            var elValue;

            // expected value format:  2008-04-24  --> March 24, 2008
            if (el && el.value && el.value != null && el.value != "") {
                elValue = el.value;

                var yr = parseInt(elValue.substr(0,4),10);
                var mn = parseInt(elValue.substr(5,2),10) - 1;
                var dy = parseInt(elValue.substr(8,2),10);
                var d1 = new Date(yr, mn, dy);

                if (d1.getFullYear() != yr || d1.getMonth() != mn || d1.getDate() != dy) {
                    return false;
                }
            }

            return true;
        }

        /**
         * Set the value of the Input element of the question
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         * @param value : The string value that is to be set
         * @return none
         */
        this.setQuestionValue = function(label_id, value){
            var nullThis=false;
            if(value == "$\NULL$" || value == "$NULL$"){
                nullThis=true;
            }
            var els = KD.utils.Util.getQuestionInput(label_id);

            if (els){
                // DATE FIELDS
                if (KD.utils.Util.isArray(els)){
                    var el = KD.utils.Util.getElementObject(label_id);
                    label_id = KD.utils.Util.getIDPart(el.id);
                    if (els[0].id && els[0].id.indexOf("year_"+label_id)==0) {
                        if (nullThis) {
                            KD.utils.Action.setDateFields(label_id, null, true);
                        } else {
                            var dateArr = [];
                            if (typeof value == "string") {
                                dateArr = value.split("-");
                            } else if (KD.utils.Util.isArray(value)) {
                                dateArr = value;
                            }
                            KD.utils.Action.setDateFields(label_id, dateArr);
                        }
                        return;
                    }
                }
                //RADIOS
                if (KD.utils.Util.isArray(els)){
                    for (var i=0; i<els.length; i++){
                        if (nullThis){
                            els[i].checked=false;
                            continue;
                        }
                        value = KD.utils.Util._escapeSpecialChars(value);
                        if (els[i].value == value){
                            els[i].checked=true;
                            return;
                        }
                    }
                //CHECKBOX OR SINGLE RADIO
                }else if (els.type && els.type.toUpperCase()=="CHECKBOX" || els.type.toUpperCase()=="RADIO"){
                    if (nullThis){
                        els.checked=false; return;
                    }
                    value = KD.utils.Util._escapeSpecialChars(value);
                    if (els.value == value){
                        els.checked=true;
                    }
                    return;
                //SELECT
                }else if (els.nodeName.toUpperCase()=="SELECT" && els.options.length>0){
                    if (nullThis){
                        els.selectedIndex=0; els.options[0].selected=true; return;
                    }
                    value = KD.utils.Util._escapeSpecialChars(value);
                    for(var i=0;i< els.options.length;i++){
                        if(els.options[i].value==value){
                            els.options[i].setAttribute("selected","true");
                            els.selectedIndex=i;
                            return;
                        }
                    }
                //TEXT
                } else {
                    if (nullThis){
                        value ="";
                    }else if (els.nodeName.toUpperCase()=="TEXTAREA"){
                        value = value.replace(/%0A/gi, "\n");
                        value = value.replace(/%0D/gi, "\n");
                    }
                    els.value = value;
                }
            } else {
                alert("function KD.utils.Action.setQuestionValue - Unable to locate the Object for: "+label_id);
                return false;
            }
            return true;
        }

        /**
         * Get the value of the Input element of the question
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         * @return A string value of the question
         */
        this.getQuestionValue = function(label_id){
            var els = KD.utils.Util.getQuestionInput(label_id);
            if (els){
                if (!KD.utils.Util.isArray(els)){
                    els=[els];  //If this is a single item, only need to run once, but treat as an array
                }
                var thisVal="";
                var qstnType="";
                for (var i=0; i<els.length; i++){
                    //Date Display Only
                    var cls=els[i].className;
                    var thisID=els[i].getAttribute("id");
                    if (cls && (cls.indexOf("dateYear") != -1 || cls.indexOf("dateMonth") != -1 || cls.indexOf("dateDay") != -1) && (thisID.indexOf("year_") == 0 || thisID.indexOf("month_") == 0 || thisID.indexOf("day_") == 0)){
                        qstnType="Date";
                        continue;
                    }
                    //Date Hidden
                    if(qstnType=="Date" && els[i].tagName.toUpperCase()=="INPUT" && els[i].type.toUpperCase() == "HIDDEN"){
                        thisVal= els[i].value;
                        break;
                    }
                    //Checkbox--Get all values
                    if (els[i].type && (els[i].type && els[i].type.toUpperCase() == "CHECKBOX") && els[i].checked == true){
                        qstnType="Checkbox";
                        thisVal+=els[i].value+", ";
                        thisVal=KD.utils.Util._unescapeSpecialChars(thisVal);
                        continue;
                    //Radio
                    }else if (els[i].type && els[i].type.toUpperCase() == "RADIO" && els[i].checked == true){
                        thisVal= els[i].value;
                        thisVal=KD.utils.Util._unescapeSpecialChars(thisVal);
                        break;
                    //Select
                    }else if (els[i].tagName.toUpperCase()=="SELECT") {
                        thisVal= els[i].value;
                        thisVal=KD.utils.Util._unescapeSpecialChars(thisVal);
                        break;
                    //Text/Textarea
                    }else if ((els[i].tagName.toUpperCase()=="INPUT" && els[i].type.toUpperCase() == "TEXT")|| els[i].tagName.toUpperCase()=="TEXTAREA") {
                        thisVal= els[i].value;
                        break;
                    }
                }
                //Remove last comma
                if (qstnType=="Checkbox" && thisVal.indexOf(",") != -1){
                    thisVal = KD.utils.Util.trimString(thisVal);
                    thisVal = thisVal.substring(0,thisVal.length-1);
                }
                return thisVal;
            }else {
                alert("function KD.utils.Action.getQuestionValue - Unable to locate the Object for :"+label_id);
                return "";
            }
        }

        /**
         * Change the appearance of the provided Question element to Read Only/disabled depending on question type
         * When the form is submitted, disabled objects will be re-enabled so they can be posted.
         * @param obj : Any valid HTML object within the page document
         * @return none
         */
        this.setReadOnly = function(label_id) {
            var els = KD.utils.Util.getQuestionInput(label_id);
            if (els){
                if (!KD.utils.Util.isArray(els)){
                    els=[els];  //If this is a single item, only need to run once, but treat as an array
                }
                //Handle Multiples for radio/checkboxes/dates
                for(var i=0;i<els.length;i++){
                    var qst=els[i];
                    if(qst.tagName && (qst.tagName.toUpperCase()=="INPUT" || qst.tagName.toUpperCase()=="TEXTAREA")){
                        if (qst.type && (qst.type.toUpperCase() == "RADIO" || qst.type.toUpperCase() == "CHECKBOX") || qst.type.toUpperCase() == "SUBMIT" || qst.type.toUpperCase() == "BUTTON"){
                            qst.disabled=true;
                        }else {
                            qst.setAttribute("readOnly", "readOnly");
                            qst.style.backgroundColor=KD.utils.Action.readOnlyColor;
                            // disable calendar picker if a date question
                            if (Dom.hasClass(qst, 'dateYear')) {
                                var calParent = qst.parentNode;
                                var calPicker = calParent.childNodes[5];
                                if (calPicker && calPicker.tagName.toUpperCase() == "IMG") {
                                    calPicker.style.display = "none";
                                }
                            }
                        }
                    //Handle selection boxes
                    }else if(qst.tagName && qst.tagName.toUpperCase()=="SELECT"){
                        qst.disabled=true;
                    //qst.style.backgroundColor=KD.utils.Action.readOnlyColor;
                    //Handle array of radio buttons or checkboxes
                    }
                }

            } else if ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_"))) {
                // maybe we are dealing with a page or section
                var nodeRef = ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_")));
                KD.utils.Action._iterActionCall.call(this, nodeRef, KD.utils.Action.setReadOnly);
            } else {
                alert("function setReadOnly - Unable to locate the Object for :"+label_id);
            }
        }

        /**
         * Change the appearance of the provided Question element to Read/Write or Enabled depending on question type.
         * Radio button/checkbox items will ALL be disabled for the id.
         * @param obj : Any valid HTML object within the page document
         * @return none
         */
        this.setReadWrite = function(label_id) {
            var els = KD.utils.Util.getQuestionInput(label_id);
            if (els){
                if (!KD.utils.Util.isArray(els)){
                    els=[els];  //If this is a single item, only need to run once, but treat as an array
                }
                //Handle Multiples for radio/checkboxes/dates
                for(var i=0;i<els.length;i++){
                    var qst=els[i];
                    if(qst.tagName && (qst.tagName.toUpperCase()=="INPUT" || qst.tagName.toUpperCase()=="TEXTAREA") && !qst.getAttribute('fileTypesAllowed')){
                        if (qst.type && (qst.type.toUpperCase() == "RADIO" || qst.type.toUpperCase() == "CHECKBOX"|| qst.type.toUpperCase() == "SUBMIT" || qst.type.toUpperCase() == "BUTTON")){
                            qst.disabled=false;
                        }else {
                            qst.removeAttribute('readOnly');
                            qst.style.backgroundColor='';
                            // enable calendar picker if a date question
                            if (Dom.hasClass(qst, 'dateYear')) {
                                var calParent = qst.parentNode;
                                var calPicker = calParent.childNodes[5];
                                if (calPicker && calPicker.tagName.toUpperCase() == "IMG") {
                                    calPicker.style.display = "";
                                }
                            }
                        }
                    //Handle selection boxes
                    }else if(qst.tagName && qst.tagName.toUpperCase()=="SELECT"){
                        qst.disabled=false;
                    //qst.style.backgroundColor=KD.utils.Action.readOnlyColor;
                    }
                }

            } else if ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_"))) {
                // maybe we are dealing with a page or section
                var nodeRef = ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_")));
                KD.utils.Action._iterActionCall.call(this, nodeRef, KD.utils.Action.setReadWrite);
            } else {
                alert("function setReadOnly - Unable to locate the Object for :"+label_id);
            }
        }
        /**
         * Set a button to being disabled
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         * @return none
         */
        this.disableButton = function(label_id) {
            var div = KD.utils.Util.getButtonObject(label_id);
            if (div && div != "") {
                input = div.getElementsByTagName("input");
                input[0].disabled = true;
            } else {
                alert("function ks_ - Unable to locate the Object for :"+label_id);
            }
        }

        /**
         * Set a button to being enabled
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         * @return none
         */
        this.enableButton = function(label_id) {
            var div = KD.utils.Util.getButtonObject(label_id);
            if (div && div != "") {
                input = div.getElementsByTagName("input");
                input[0].disabled = false;
            } else {
                alert("function ks_enableButton - Unable to locate the Object for :"+label_id);
            }
        }

        /**
         * Disables all submit buttons on the form
         * @param {Event} obj
         * @return null
         */
        this.disableSubmitButtons = function(obj){
            for (var i=0; i<KD.utils.ClientManager.submitButtons.length; i+=1) {
                KD.utils.Action.disableButton(KD.utils.ClientManager.submitButtons[i]);
            }
        };

        /**
         * Enables all submit buttons on the form
         * @param {Event} obj
         * @return null
         */
        this.enableSubmitButtons = function(obj){
            for (var i=0; i<KD.utils.ClientManager.submitButtons.length; i+=1) {
                KD.utils.Action.enableButton(KD.utils.ClientManager.submitButtons[i]);
            }
        };

        /**
         * Pass in an event or field and see whether this element has a pattern and it meets the pattern.
         * If the pattern isn't met, the field will turn red.
         * @param obj : Element or event
         * @return null
         */
        this.checkPattern = function(obj){
            //check whether this is an event or an element
            var qstn = KD.utils.Util.getElementFromObject(obj);
            var valFormat=qstn.getAttribute("validationFormat");
            if (valFormat && valFormat != "") {
                var expStr = qstn.getAttribute("validationFormat");
                var exp = new RegExp(expStr);
                var qstnVal = KD.utils.Util.trimString(qstn.value);
                if (qstnVal != null && qstnVal.length > 0 && exp.test(qstnVal) == false) {
                    KD.utils.Action.highlightField(qstn);
                    var validationText = qstn.getAttribute("validationText");
                    alert(validationText);
                    // re-attach submit button event handlers
                    KD.utils.Action._enableButtonEvents();
                    return;
                }
                if ((qstnVal == null || qstnVal == "") || exp.test(qstnVal) == true) {
                    /* Clean up if valid or null */
                    KD.utils.Action.resetRequiredField(qstn);

                    // re-attach submit button event handlers
                    KD.utils.Action._enableButtonEvents();

                    // clear the patternError attribute
                    var patternError = qstn.getAttribute("patternError");
                    if (patternError) {
                        qstn.removeAttribute("patternError");
                    }
                }
            }
        };

        /**
         * Make a question required (client-side).
         * If the question isn't filled out, the field label will turn red and pop an error message.
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         */
        this.makeQuestionRequired = function(label_id) {
            var els = KD.utils.Util.getQuestionInput(label_id);
            if (els){
                if (!KD.utils.Util.isArray(els)){
                    els=[els];  //If this is a single item, only need to run once, but treat as an array
                }
                //Handle Multiples for radio/checkboxes
                for(var i=0;i<els.length;i++){
                    var cls=els[i].className;
                    //Skip date display fields
                    if(cls != null && (cls.indexOf("dateYear") != -1 || cls.indexOf("dateMonth") != -1  || cls.indexOf("dateDay") != -1)){
                        continue;
                    }
                    els[i].setAttribute("required", "true");
                    var errorValue=els[i].getAttribute("errorValue");
                    if(!errorValue || errorValue==""){
                        //Use the label if there isn't a value set
                        els[i].setAttribute("errorValue", KD.utils.Util.getLabelFromAnswerEl(els[i]));
                    }
                }
            } else if ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_"))) {
                // maybe we are dealing with a page or section
                var nodeRef = ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_")));
                KD.utils.Action._iterActionCall.call(this, nodeRef, KD.utils.Action.makeQuestionRequired);
            }
        }

        /**
         * Make a question optional (client-side).  Note:  If a question is set up to be required server-side
         * (in the question definition), it cannot be made optional.  The server will check this on form submit and
         * display an error.
         * @param label_id : Either the Label name (Not the question name) OR the InstanceID for the Question
         */

        this.makeQuestionOptional = function(label_id) {
            var els = KD.utils.Util.getQuestionInput(label_id);
            if (els){
                if (!KD.utils.Util.isArray(els)){
                    els=[els];  //If this is a single item, only need to run once, but treat as an array
                }
                //Handle Multiples for radio/checkboxes
                for(var i=0;i<els.length;i++){
                    els[i].setAttribute("required", "");
                }
            } else if ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_"))) {
                // maybe we are dealing with a page or section
                var nodeRef = ((KD.utils.Util.getElementObject(label_id, "PAGE_")) || (KD.utils.Util.getElementObject(label_id, "SECTION_")));
                KD.utils.Action._iterActionCall.call(this, nodeRef, KD.utils.Action.makeQuestionOptional);
            }
        }

        /**
         * Typically used when a form is re-displayed to hide/show elements needed.
         * @param el : The HTML element (INPUT, DIV, etc)
         * @param clientAction : The ClientAction object defined for this event/action
         */

        this.applyValueActions = function(el,clientAction){
            if (!clientAction || !clientAction.action || !el || !el.type) {
                return;
            }
            fire=false;

            if(el.type.toUpperCase()=="RADIO" && el.checked){
                fire=true;
            }else if(el.type.toUpperCase()=="CHECKBOX" && el.checked){
                fire=true;
            }else if((el.type.toUpperCase()=="TEXT" || el.tagName.toUpperCase()=="SELECT" || el.tagName.toUpperCase()=="TEXTAREA") && el.value && el.value != ""){
                fire=true;
            }
            if(fire){
                clientAction.action.apply(KD.utils.Action,[{
                    target:el,
                    type:clientAction.jsEvent
                },clientAction]);
            }
        }

        /**
         * Takes in a Kinetic SR standard XML from a SimpleDataRequest, and outputs it as a table with no formatting/events.
         * Columns can be ordered, made visible/hidden, and column labels can be changed
         * @param recordXMLArray : The array of Record XML elements
         * @param questions : An array of question objects that are mapped with this table
         * @return a table element.  This table will have no rows if the data set is empty
         */
        this.showResultsAsAdvancedTable = function(recordXMLArray, questions) {
            var cols = [],
            visibleCols = [];

            // add the question ID as a property for easier access later
            for (var question in questions[0]) {
                questions[0][question]['qId'] = question;
                cols.push(questions[0][question]);
            }
            // columns should already be in the correct order,
            // but sort the table columns array by sort order attribute just in case
            cols.sort(function(a,b) {
                return a['sort_order']-b['sort_order'];
            });

            // we only want ot display the visible columns, or columns that haven't
            // been explicitly marked as not visible.
            for (var col in cols) {
                if (cols[col]['visible'] == "Yes" || cols[col]['visible'] == null ||
                    cols[col]['visible'] == "") {
                    visibleCols.push(cols[col]);
                }
            }

            // build the table
            var theTable=document.createElement('table');
            var theHead=document.createElement('thead');
            var theBody=document.createElement('tbody');
            var oddEven="odd";
            if (recordXMLArray && recordXMLArray.length > 0) {
                // Create Header
                var headerRow=document.createElement('tr');
                var headerFields = recordXMLArray[0].getElementsByTagName("field");
                for(var col in visibleCols) {
                    var qId = visibleCols[col]['qId'];
                    var thisCell=document.createElement('th');
                    var colLabel = KD.utils.Action._getAdvancedColumnLabel(visibleCols[col]);
                    thisCell.appendChild(document.createTextNode(colLabel));
                    headerRow.appendChild(thisCell);
                }
                headerRow.id="headerRow";
                theHead.appendChild(headerRow);
                // End Header

                // Create Rows
                for (var i =0; i < recordXMLArray.length; i++) {
                    var thisRow=document.createElement('tr');
                    thisRow.id="row_"+i;
                    thisRow.className="record row_"+oddEven;
                    var fields = recordXMLArray[i].getElementsByTagName("field");

                    for (col in visibleCols) {
                        var qId = visibleCols[col]['qId'];
                        var map = visibleCols[col][qId];
                        var value = KD.utils.Action._getAdvancedColumnValue(map, fields);

                        var thisCell=document.createElement('td');
                        thisCell.appendChild(document.createTextNode(value));
                        thisCell.id="r_"+i+"_c_"+col;
                        thisRow.appendChild(thisCell);
                    }
                    theBody.appendChild(thisRow);
                    if (oddEven=="odd") { 
                        oddEven="even";
                    } else {
                        oddEven="odd";
                    }
                }
            }
            theTable.appendChild(theHead);
            theTable.appendChild(theBody);
            return theTable;
        };


        this._getAdvancedColumnLabel = function(col) {
            // use the label specified unless blank
            var label = col['table_label'];
            if (label == null || KD.utils.Util.trimString(label) == '') {
                var questionId = col['qId'];
                var fieldMap = col[questionId];
                var flds = fieldMap.split("</FLD>");
                // just get the first field used, and set the label to the field name
                for (var i=0; i < flds.length; i++) {
                    if (flds[i].indexOf("<FLD>") > -1) {
                        label = (flds[i].split(";")[0]).substring(flds[i].indexOf("<FLD>")+5);
                        break;
                    }
                }
            }
            return label;
        };

        this._getAdvancedColumnValue = function(fieldMap, fields) {
            var fieldName = "",
            value = "",
            fieldValue = "",
            flds = fieldMap.split("</FLD>");

            for (var i = 0; i < flds.length; i++){
                //Just static text in this token
                if (flds[i].indexOf("<FLD>") == -1){
                    value += flds[i];
                } else {
                    //Get the static part out if there is some
                    var segs=flds[i].split("<FLD>");
                    if (segs.length>1) { 
                        value += segs[0];
                    }

                    // Get the next field name
                    fieldName = (flds[i].split(";")[0]).substring(flds[i].indexOf("<FLD>")+5);

                    // Get the value for this field name
                    for (var j = 0; j < fields.length; j++) {
                        if (fields[j].getAttribute("id") == fieldName) {
                            if (fields[j].childNodes.length > 0) {
                                fieldValue = fields[j].firstChild.nodeValue;
                            } else {
                                fieldValue = "";
                            }
                        }
                    }

                    value += fieldValue;
                }
            }
            return KD.utils.Util.trimString(value);
        };



        /**
         * Takes in a Kinetic SR standard XML from a SimpleDataRequest, and outputs it as a table with no formatting/events.
         * @param recordXMLArray : The array of Record XML elements
         * @return a table element.  This table will have no rows if the data set is empty
         */
        this.showResultsAsTable = function(recordXMLArray, tableId, tableClass){
            var theTable=document.createElement('table');
            if(tableId){ 
                theTable.setAttribute("id", tableId);
            }
            if(tableClass){
                theTable.className = tableClass;
            }
            var theBody=document.createElement('tbody');
            var oddEven="odd";
            if(recordXMLArray && recordXMLArray.length >0){
                /*Create Header*/
                var headerRow=document.createElement('tr');
                var headerFields = recordXMLArray[0].getElementsByTagName("field");
                for (var a = 0; a < headerFields.length; a++) {
                    var thisCell=document.createElement('th');
                    thisCell.appendChild(document.createTextNode(headerFields[a].getAttribute("id")));
                    headerRow.appendChild(thisCell);
                }
                headerRow.id="headerRow";
                theBody.appendChild(headerRow);
                /*End Header */
                /*Create Rows*/
                for (var i =0; i < recordXMLArray.length; i++){
                    var thisRow=document.createElement('tr');
                    thisRow.id="row_"+i;
                    thisRow.className="record row_"+oddEven;
                    var fields = recordXMLArray[i].getElementsByTagName("field");
                    for (var k = 0; k < fields.length; k++) {
                        var thisCell=document.createElement('td');
                        var fieldValue = "";
                        if (fields[k].firstChild){
                            fieldValue= fields[k].firstChild.nodeValue;
                        }
                        thisCell.appendChild(document.createTextNode(fieldValue));
                        thisCell.id="r_"+i+"_c_"+k;
                        thisRow.appendChild(thisCell);
                    }
                    theBody.appendChild(thisRow);
                    if(oddEven=="odd"){
                        oddEven="even";
                    }else{
                        oddEven="odd";
                    }
                }
            }
            theTable.appendChild(theBody);
            return theTable;
        }

        /**
         * Makes a call to SimpleDataRequest with the parameters provided.
         *
         * @param requestName : arbitrary name for your internal use
         * @param dataRequestId : The instanceId of the KS_SRV_SimpleDataRequest entry corresponding to this request.
         * @param connection : a KD.utils.ConResponse object that handles the response from the call.
         * The available formats are those jsp files in the applicationHome/resources/partials directory on the application server.
         * @param paramString :  the values that are to be substituted in the qualification of the KS_SRV_SimpleDataRequest.
         * @param format : optional String that corresponds to the format of the data returned.
         * An example would look like: "user=joe&type=red"
         * @param useGetList : By default the SDR will attempt to get a full entry object (all fields).
         * Specify false if you want to get only the fields specified.  This will be faster, but fields over 255 chars cannot be retrieved or special fields.
         * Also, a "Get" is not triggered for filters when using this.
         * @param synchronous : By default the request is asynchronous.  Set to true for a synchronous request.
         * @return null
         */
        this.makeAsyncRequest = function(requestName, dataRequestId, callback, paramString, format, useGetList, synchronous){
            if(KD.utils.Action.sdrErrors == null){
                KD.utils.Action.sdrErrors = new KD.utils.Hash();
            }
            //alert("Active: " + KD.utils.ClientManager.isActiveConnection(dataRequestId));
            if(paramString && paramString != "" && paramString.indexOf("&") != 0){
                paramString ="&"+paramString;
            }
            dataRequestId=encodeURIComponent(dataRequestId);
            requestName=encodeURIComponent(requestName);
            var sessionId=encodeURIComponent(KD.utils.ClientManager.sessionId);
            var now = new Date();
            var entryParam="";
            if(useGetList == true){
                entryParam="&useGetList=true"
            }
            var path = "SimpleDataRequest?requestName="+ requestName +"&dataRequestId="+ dataRequestId +"&sessionId="+sessionId +"&format="+ format + paramString+entryParam+"&noCache="+now.getTime();

            // register the parameters with the invalid session request manager
            KD.utils.ClientManager.registerInvalidSessionRequest(dataRequestId,'GET', path, callback);

            if (callback.includeLoadingImage) {
                var elId = callback.argument[0],
                el = null;

                if (elId) {
                    el = Dom.get(elId);
                }
                if (el) {
                    el.innerHTML='<img src="./resources/catalogIcons/ajax-loader.gif">';
                }
            }

            /*Determine whether to make the call.
             * If the same SDR is active and done with the same params, then don't make a call
             * If the SDR is active with "older" params, then abort that one and make a new call
             * If it's not active, then just make the call*/
            var isActive=KD.utils.ClientManager.isActiveConnection(dataRequestId);
            var isValid=true;
            if (isActive){
                var prevCall=KD.utils.ClientManager.getConnection(dataRequestId);
                if (prevCall[1]==paramString){
                    isValid=false;
                }else{
                    try{
                        prevCall[0].conn.abort(prevCall, callback, false);
                    }catch(error){
                    //just continue. issue in IE that won't allow aborts.  eventually output to a console if in debug mode
                    }
                }
            }
            if(isValid){
                //Wrap callback to handle session/other standard errors
                var fnctnError = callback.error;
                callback.failure = function(response){
                    var message = response.statusText;
                    if (response.getResponseHeader != null && response.getResponseHeader['X-Error-Message'] != null){
                        message = response.getResponseHeader['X-Error-Message'];
                    }

                    var connectionStatus = response.getResponseHeader['Connection'];
                    if (!KD.utils.Action.sdrErrors.getItem(response.status) || !connectionStatus || connectionStatus.indexOf("close")!=-1){
                        KD.utils.Action.sdrErrors.setItem(response.status, message);
                        if (!connectionStatus || connectionStatus.indexOf("close")!=-1){
                            // check is session is still valid
                            KD.utils.ClientManager.checkSession();
                            var sessionResponse = KD.utils.ClientManager.sessionCheck;
                            if (sessionResponse.status !== 200) {
                                if (callback.includeLoadingImage && el) {
                                    // clear the ajax-loader image if it is present
                                    el.innerHTML = "";
                                }
                                var sessionMessage = sessionResponse.statusText;
                                if (sessionResponse.getResponseHeader != null && sessionResponse.getResponseHeader['X-Error-Message'] != null){
                                    sessionMessage = sessionResponse.getResponseHeader['X-Error-Message'];
                                }
                                KD.utils.Action.handleHttpErrorCode(sessionResponse.status, sessionMessage);
                            }
                        } else {
                            KD.utils.Action.handleHttpErrorCode(response.status, sessionMessage);
                        }
                        return;
                    }

                    //Pass the error through to the operation-specific error
                    if(fnctnError){
                        fnctnError(response);
                    }
                }
                //Wrap the success callback to clear out errors
                var fnctnSuccess = callback.success;
                callback.success = function(response){
                    KD.utils.Action.sdrErrors = new KD.utils.Hash();
                    KD.utils.ClientManager.clearInvalidSessionRequest();
                    fnctnSuccess(response);
                }

                if (synchronous === true){
                    KD.utils.Action._makeSyncRequest(path, callback);
                } else {
                    // We can assume that the session has now been re-eastablished based on an SSO adapter
                    // As a result we want to recall the Data Request that initially failed.
                    var params = KD.utils.ClientManager.getInvalidSessionRequest();
                    // If recovered, run the last Datarequests again
                    if (params[1] !== null && params[2] !== null && params[3] !== null) {
                        Connect.asyncRequest(params[1],params[2],params[3]);
                    }            
                }
            }
        }

        /*
         * INTERNAL USE ONLY
         *
         * Makes a synchronous call to SimpleDataRequest.  To use, first call
         * makeAsyncRequest with the synchronous parameter set to true.
         *
         * @param path : The path with parameters built up in makeAsyncRequest
         * @param callback : The callback object handling the response
         * @return null
         */
        this._makeSyncRequest = function(path, callback) {
            var oXhr, // an object wrapper
            xhr, // the XMLHttpRequest object
            xhrTimeout, // a timeout function to abort hanged calls
            oResponse; // the server's response to the XMLHttpRequest call

            // create the XHR connection object
            oXhr = KD.utils.Action._createSyncXhr();

            if (oXhr) {
                xhr = oXhr.xhr;
                // open the request synchronously (last parameter)
                xhr.open('GET', path, false);

                // set a timer to handle timeout situations
                xhrTimeout = setTimeout(function() {
                    xhr.abort();
                }, callback.argument[0].timeout || 30000);

                // send the request to the server
                xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
                try {
                    xhr.send(null);
                } catch (e) {
                    /**Connection Failure*/
                    callback.failure({
                        status: 0,
                        statusText: "Communication Failure",
                        argument: callback.argument
                    });
                }

                // process response
                if (xhr.readyState == 4) {
                    // clear the timeout so the call doesn't get aborted
                    clearTimeout(xhrTimeout);
                    // build the response object
                    oResponse = KD.utils.Action._buildSyncResponse(xhr, callback.argument);

                    // handle errors
                    if (xhr.status != 200) {
                        callback.failure(oResponse);
                        return;
                    }

                    if (callback.success === KD.utils.Action._setFieldsCallback) {
                        // Need to block until everything is finished.  Currently only
                        // set fields actions that return a single value are working.
                        // The tricky part here is for set fields external requests that
                        // return a list of entries, of which the user must choose one
                        // from the list.  We have to wait until the user is done choosing
                        // before continuing.  What do we do if the user just closes the
                        // selection window without picking a value?
                        callback.success(oResponse);
                    } else {
                        callback.success(oResponse);
                    }
                }

            } else { // Synchronous AJAX request could not be created
                // probably want to replace this alert with something else
                alert('Your browser does not support AJAX requests.');
            }
        };

        /**
         * Get an array of request records as a result of calling the standard SimpleDataRequest
         *
         * @param: XMLHttp response object
         * @return An array of Records
         */
        this.getMultipleRequestRecords = function(o){
            try{
                return o.responseXML.getElementsByTagName("record");
            }catch(e){//continue
            }
        }

        /**
         * Get single request record as a result of calling the standard SimpleDataRequest
         *
         * @param: XMLHttp response object
         * @return A single Record
         */
        this.getSingleRequestRecord = function(o){
            try{
                return o.responseXML.getElementsByTagName("record")[0];
            }catch(e){//continue
            }
        }

        /**
         * Get the name of the current SimpleDataRequest.
         * @param: XMLHttp response object
         * @return request Name
         */
        this.getRequestName = function(o){
            try{
                return o.responseXML.getElementsByTagName("requestName")[0].firstChild.nodeValue;
            }catch(e){//continue
            }
        }

        /**
         * Opens the Date Picker.  Currently English-only.
         * @param id : The ID of the question element. (SRVQSTN_...).
         * @return request Name
         */
        this.openDatePicker = function(evt, id){
            var calEl=Dom.get("calendarPanel_"+id);
            var theQuestion=Dom.get("SRVQSTN_"+id);
            var topPos=0;
            var clickXY=Dom.getXY(YAHOO.util.Event.getTarget(evt));
            var questionLayer=theQuestion.parentNode.parentNode;
            if (clickXY[1]-150 > 0){
                topPos=clickXY[1]-150;
            }
            var leftPos=clickXY[0]+25;
            if (leftPos+150 > document.body.offsetWidth){
                leftPos=document.body.offsetWidth-150;
            }
            if(!calEl){
                calEl = document.createElement('div');
                calEl.id="calendarPanel_"+id;
                calEl.style.position="absolute";
                calEl.style.top=parseInt(topPos)+"px";
                calEl.style.left=parseInt(leftPos)+"px";
                //theQuestion.parentNode.parentNode.appendChild(calEl);
                document.body.appendChild(calEl);
            }
            var cal=new YAHOO.widget.Calendar("cal_"+id, "calendarPanel_"+id);
            cal.selectEvent.subscribe(KD.utils.Action.dateSelected, [id, cal]);

            var months=Dom.get("month_"+id).options;
            var months2= new Array();
            var offset = months.length - 12;
            for (var i=offset;i <months.length; i++){
                months2[i-offset]=months[i].text;
            }

            if(theQuestion.value && theQuestion.value != ""){
                var theDate=new Date();
                var dateParts = theQuestion.value.split("-");
                if (dateParts && dateParts.length == 3) {
                    theDate.setYear(dateParts[0]);
                    if(dateParts[1].indexOf("0")==0){
                        dateParts[1]=dateParts[1].replace("0","");
                    }
                    theDate.setMonth(parseInt(dateParts[1])-1);//months needed for cal are 0-11
                    theDate.setDate(dateParts[2]);
                }
                cal.setYear(theDate.getFullYear());
                cal.setMonth(theDate.getMonth());
                cal.select(theDate);
            }
            cal.cfg.setProperty("HIDE_BLANK_WEEKS", true);
            cal.cfg.setProperty("NAV_ARROW_LEFT", "resources/js/yui/assets/callt.gif");
            cal.cfg.setProperty("NAV_ARROW_RIGHT", "resources/js/yui/assets/calrt.gif");
            cal.cfg.setProperty("MONTHS_LONG", months2);
            cal.cfg.setProperty("close", true);
            cal.cfg.setProperty
            cal.render();
            cal.show();
        }

        this.dateSelected = function(evt, dateArr, params){
            var qstnId=params[0];
            var datePart=dateArr[0][0];
            KD.utils.Action.setDateFields(qstnId,datePart);
            var cal=params[1];
            cal.hide();
        }

        /**
         * Set the hidden date storage field when a date element changes.
         * @param el : The onchange event source that triggered this.
         */
        this.updateDate = function(el){
            var originalValue=el.value;
            var qstnId=KD.utils.Util.getIDPart(el.id);
            var qstn=Dom.get("SRVQSTN_" + qstnId);
            var yr=Dom.get("year_" + qstnId).value;
            var month=Dom.get("month_" + qstnId).value;
            var day=Dom.get("day_" + qstnId).value;

            if (el.value == null || el.value == '') {
                KD.utils.Action.setDateFields(qstnId, null, true);
                return;
            }

            if (yr && month && month != "" && day && day != ""){
                qstn.value=yr+"-"+month+"-"+day;
            } else {
                qstn.value="Invalid";
            }
        }

        /**
         * Set the Year, Day, Month fields with the date array indicated.
         *  If no array is included the value of the question will be used.  Otherwise, the current date.
         * @param labelId : Either the Label name (Not the question name) OR the InstanceID for the Date Question
         * @param dateArr : [YYYY,MM,DD] (optional)
         * @param nullThis : true to clear the date question
         */
        this.setDateFields = function(labelId, dateArr, nullThis){
            var qstn=KD.utils.Util.getElementObject(labelId, 'SRVQSTN_');
            if (!qstn) {
        		alert("function setDateFields - Unable to locate any input elements for :" + labelId);
                return;
            }
            var qstnId=qstn.id.slice("SRVQSTN_".length);
            var originalValue=qstn.value;
            var yr=Dom.get("year_" + qstnId);
            var month=Dom.get("month_" + qstnId);
            var day=Dom.get("day_" + qstnId);
            if(qstn){
                if(nullThis){
                    qstn.value="";
                    yr.value="";
                    month.selectedIndex=null;
                    day.selectedIndex=null;
                    if(originalValue && originalValue != ""){
                        KD.utils.Action._fireChange(qstn);
                    }
                    return;
                }
                if(!dateArr || dateArr.length<3){
                    if(qstn.value && qstn.value != "" && qstn.value.indexOf("-") != -1){
                        var dateArr=qstn.value.split("-");
                    }else{
                        var date=new Date();
                        var dateArr=new Array();
                        dateArr[0]=date.getFullYear();
                        dateArr[1]=date.getMonth()+1;
                        dateArr[2]=date.getDate();
                    }
                }
                yr.value=dateArr[0];
                month.value=KD.utils.Util.padZeros(dateArr[1], 2);
                day.value=KD.utils.Util.padZeros(dateArr[2], 2);
                qstn.value=yr.value+"-"+month.value+"-"+day.value;
                if(originalValue && originalValue != qstn.value && clientManager.isLoading==false){
                    KD.utils.Action._fireChange(qstn);
                }
            }
        }

        /**
         * Get a value of a field within a record based on the record item name
         * @param record : A record based on the result return from call the SimpleDataReqeuest
         * @param name : The name of the field in the record  (Such as "Last name")
         * @return A string value
         */
        this.getRequestValue = function(record, name){
            if (record) {
                var fields = record.getElementsByTagName("field");
                for (var i = 0; i < fields.length; i++) {
                    if (fields[i].getAttribute("id")==name) {
                        if (fields[i].childNodes.length>0) {
                            return fields[i].firstChild.nodeValue;
                        } else {
                            return "";
                        }
                    }
                }
            }
            return "";
        }


        /*
         * PRIVATE INTERNAL FUNCTIONS--CLIENT SIDE ACTIONS
         */

        /*
         * Used for standard "pair" actions like hide/show, read/write, required/optional
         */

        /*Used for actions applied to parent elements like pages or sections.
         */
        this._iterActionCall = function(nodeRef, fn) {
            for (var j = 0; j < nodeRef.childNodes.length; j++) {
                var thisNode=nodeRef.childNodes[j];
                if ((thisNode.id) && (KD.utils.Util.getElementType(thisNode.id)) == "Question") {
                    var questionId = KD.utils.Util.getIDPart(thisNode.id)
                    fn.call(KD.utils.Action,questionId)
                } else if (thisNode.nodeName=="DIV") {
                    KD.utils.Action._iterActionCall(thisNode, fn)
                }
            }
        }

        this._standardActionCall = function(evt, clientAction, fn){
            clientAction.currEvent=KD.utils.Util.cloneObj(evt);
            var obj=YAHOO.util.Event.getTarget(evt);
            var valid=true;
            if(clientAction.ifExpr){
                var expr=KD.utils.Util.trimString(clientAction.ifExpr);
                if(expr != ""){
                    valid=eval("("+expr+")");
                }
            }
            if(valid){
                var fields=clientAction.params;
                for (var i=0;i<fields.length; i++){
                    //eval("KD.utils.Action."+fn+"(fields[i]"));
                    fn.call(KD.utils.Action, fields[i]);
                }
            }
        }

        this._makeReadOnly = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.setReadOnly);
        }

        this._makeReadWrite = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.setReadWrite);
        }

        this._hideInPlace = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.hideElementInPlace);
        }

        this._showInPlace = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.showElementInPlace);
        }

        this._insert = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.insertElement);
        }

        this._remove = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.removeElement);
        }

        this._makeRequired = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.makeQuestionRequired);
        }

        this._makeOptional = function(evt, clientAction){
            KD.utils.Action._standardActionCall(evt, clientAction,KD.utils.Action.makeQuestionOptional);
        }

        /* For external set fields: the clientAction.params are [[qualParams],{SetFields-FieldMapping object}]
         */
        this._setFieldValues = function(evt, clientAction) {
            var obj, valid, expr, qualParams, rqtParamString, connection;

            clientAction.currEvent = KD.utils.Util.cloneObj(evt);
            // possibly used internally in the eval expression
            obj = YAHOO.util.Event.getTarget(evt);

            // skip the event if using Review Request, because all the
            // answers are already populated
            if (clientManager.submitType !== 'ReviewRequest') {
                clientAction.currEvent = KD.utils.Util.cloneObj(evt);
                valid = true;
                if (clientAction.ifExpr) {
                    expr = KD.utils.Util.trimString(clientAction.ifExpr);
                    if (expr != "") {
                        valid = eval("("+expr+")");
                    }
                }
                if (valid) {
                    //Qual Params is an array of zero or more fields/variables to pass in to the asynch request
                    qualParams = clientAction.params[0];
                    rqtParamString = KD.utils.Action._buildParamString(qualParams);
                    connection = new KD.utils.Callback(KD.utils.Action._setFieldsCallback, KD.utils.Action._setFieldsCallback, clientAction);
                    KD.utils.Action.makeAsyncRequest('setFieldValues', clientAction.actionId, connection, rqtParamString, '', !clientAction.useGetEntry, clientAction.synchronous);
                }
            }
        }

        /* Used for building up the request parameters (those passed in to the SDR qualification).
         *  @param qualParams: Takes an array of <FLD>BASE|ANSWER</FLD> strings and retrieves the values from variables/questions.
         */
        this._buildParamString = function(qualParams){
            var rqtParamString="";
            if(qualParams && qualParams.length && qualParams.length >0){
                for(var i=0; i<qualParams.length; i++){
                    var thisParam=qualParams[i];
                    var label=thisParam.split(";")[1];
                    var value="";
                    //BASE Params are window js variable
                    if(thisParam.indexOf(";BASE</FLD>") > 0){
                        value=eval(label);
                        rqtParamString += "&"+encodeURIComponent(label)+"="+encodeURIComponent(value);
                        continue;
                    } else if (thisParam.indexOf(";ANSWER</FLD>") >0){
                        value=KD.utils.Action.getQuestionValue(label);
                        rqtParamString += "&"+encodeURIComponent(label)+"="+encodeURIComponent(value);
                        continue;
                    }
                }
            }
            return rqtParamString;
        }

        this._setFieldsCallback = function(o, recordIndex){
            var recordXML;
            var clientAction=o.argument[0];
            var records=KD.utils.Action.getMultipleRequestRecords(o);
            var fMap, setFields, fireChange;

            if (records == null || records.length == 0){
                KD.utils.ClientManager.onSetFieldsReturn.fire(clientAction);
                return;
            }
            if (records.length > 1 && recordIndex == null) {
                recordXML = KD.utils.Action._openSelectionDialog(o);
                return;
            }
            if (records.length > 1 && recordIndex) {
                recordXML = records[recordIndex];
            } else {
                recordXML = records[0];
            }

            //The set fields list is the second clientAction parameter
            var action=eval('('+clientAction.params[1]+')');
            if (action){
                setFields=action.SetFields;
                var fireChangeArr = [];
                for (var i=0; i<setFields.length;i++){
                    for (var field in setFields[i]){
                        // if field is a string - simple mapping (pre ver 4.4)
                        if (typeof setFields[i][field] === "string") {
                            KD.utils.Action._handleSetReplace(field, setFields[i][field], recordXML);
                        } else {
                            // field is an array of field mapping objects (ver 4.4+)
                            fMap = setFields[i][field][field];
                            fireChange = setFields[i][field]["fire"];
                            KD.utils.Action._handleSetReplace(field, fMap, recordXML);
                            // keep track of what questions need to fire the change event
                            if (fireChange === "true") {
                                fireChangeArr.push(field);
                            }
                        }
                    }
                }
                // fire the change event on questions after all values have been
                // set in case the event uses the new question values.
                for (i=0; i<fireChangeArr.length;i++) {
                    var id = fireChangeArr[i];
                    var qstn = Dom.get("SRVQSTN_" + id);
                    if (qstn) {
                        KD.utils.Action._fireChange(qstn);
                    }
                }
            }
            KD.utils.ClientManager.onSetFieldsReturn.fire(clientAction);
        };

        /** Opens the dialog to display the results table.  Includes formatting of the table and dialog**/
        this._openSelectionDialog = function(o) {
            var recordXMLArray = KD.utils.Action.getMultipleRequestRecords(o),
            clientAction=o.argument[0],
            useAdvanced = true,
            classSuffix = '',
            resultsTable,
            action,
            setFields;

            // determine if displaying results as simple or advanced
            action=eval('('+clientAction.params[1]+')');
            if (action) {
                setFields=action.SetFields;
                if (setFields && setFields.length) {
                    try {
                        for (var field in setFields[0]) {
                            // if field is not a string - use advanced table (V4.4+)
                            if (typeof setFields[0][field] === "string") {
                                useAdvanced = false;
                            }
                            break;
                        }
                    } catch (e) {}
                }

                if (useAdvanced) {
                    classSuffix = 'Adv';
                    resultsTable = KD.utils.Action.showResultsAsAdvancedTable(recordXMLArray, setFields);
                } else {
                    resultsTable = KD.utils.Action.showResultsAsTable(recordXMLArray);
                }

                resultsTable.className += " resultsTable" + classSuffix;
                var elementType=KD.utils.Util.getElementType(clientAction.elObj.id);
                var cfg = {
                    modal:true,
                    draggable:false
                };

                if (elementType && elementType.toUpperCase() != "PAGE") {
                    cfg.context = [clientAction.elObj,"tl","bl"];
                }
                if (elementType && elementType.toUpperCase() == "PAGE") {
                    cfg.xy = [50,10];
                }

                var myDialogId = "selectionLayer_" + clientAction.elObj.id;
                var myDialog = new YAHOO.widget.Panel(myDialogId, cfg);

                /*Set Up Listeners */
                var recRows = Dom.getElementsByClassName('record', 'tr',resultsTable);
                for (var r=0; r<recRows.length; r++) {
                    YAHOO.util.Event.addListener(recRows[r],'click', KD.utils.Action._selectItem, [o,r,myDialog]);
                    YAHOO.util.Event.addListener(recRows[r],'mouseover', KD.utils.Action._mouseItem, [true]);
                    YAHOO.util.Event.addListener(recRows[r],'mouseout', KD.utils.Action._mouseItem, [false]);
                }
                /*End Listeners */

                clientAction.elObj.blur(); //Don't allow them to type anymore
                myDialog.setBody("<div class='multItemsHeader" + classSuffix + "'>Multiple Items Found (select one):</div>");
                myDialog.appendToBody(resultsTable);
                myDialog.render(document.body);

                // give focus to the panel
                try {
                    myDialog.focus();
                } catch (e) {}

                //If the table is too small (one or two columns), increase size to make it more visible.
                if (resultsTable.offsetWidth < 225) {
                    resultsTable.style.width="225px";
                }
                //If the dialog is over 60% of the screen show scroll bars
                var dlgWidth = (resultsTable.offsetWidth/Dom.getClientWidth());
                if (dlgWidth >0.60) {
                    myDialog.body.style.overflow="auto";
                    myDialog.body.style.width=String(Dom.getClientWidth()/2)+"px";
                }
            }
        }

        /** Highlight or remove highlight of a row **/
        this._mouseItem = function(evt, args) {
            var isMouseOver=args[0];
            var thisEl=YAHOO.util.Event.getTarget(evt);
            if(thisEl.nodeName=="TD"){
                thisEl=thisEl.parentNode;
            }
            if(thisEl.nodeName=="DIV"){
                thisEl=thisEl.parentNode.parentNode;
            }
            if (isMouseOver) {
                thisEl.setAttribute('previousClass',thisEl.className);
                thisEl.className="resultsTableHover";
            } else {
                var prevCls=thisEl.getAttribute('previousClass');
                if (prevCls != null) {
                    thisEl.className=prevCls;
                }
            }
        }

        /*Callback from clicking a row in the selection table or close button clicked
         * type is click event
         * args[0] is the original callback object including the XML of the request
         * args[1] is the row clicked
         * args[2] is the dialog
         */
        this._selectItem = function(type, args) {
            var myDialog=args[2];
            myDialog.hide();
            if (args[0] != null && args[1] != null) {
                KD.utils.Action._setFieldsCallback(args[0], args[1]);
            }
            myDialog.destroy();
        }



        this._setInternalFieldValues = function(evt, clientAction) {
            var obj, valid, expr, action, setFields, i, field;

            clientAction.currEvent = KD.utils.Util.cloneObj(evt);
            // possibly used internally in the eval expression
            obj = YAHOO.util.Event.getTarget(evt);

            // skip the event if using Review Request, because all the
            // answers are already populated
            if (clientManager.submitType !== 'ReviewRequest') {
                valid = true;
                clientAction.currEvent = KD.utils.Util.cloneObj(evt);
                if (clientAction.ifExpr) {
                    expr = KD.utils.Util.trimString(clientAction.ifExpr);
                    if (expr != "") {
                        valid = eval("(" + expr + ")");
                    }
                }
                if (valid) {
                    try {
                        action = eval('(' + clientAction.params[1] + ')');
                    } catch(err) {
                        alert(err.description);
                    }
                    if (action) {
                        setFields = action.SetFields;
                        var fireChangeArr = [];
                        for (i = 0; i < setFields.length; i += 1) {
                            for (field in setFields[i]) {
                                // if field is a string - simple mapping (pre ver 4.4)
                                if (typeof setFields[i][field] === "string") {
                                    KD.utils.Action._handleSetReplace(field, setFields[i][field]);
                                } else {
                                    // field is an array of field mapping objects (ver 4.4+)
                                    var fMap = setFields[i][field][field];
                                    var fireChange = setFields[i][field]["fire"];
                                    KD.utils.Action._handleSetReplace(field, fMap);
                                    // keep track of what questions need to fire the change event
                                    if (fireChange === "true") {
                                        fireChangeArr.push(field);
                                    }
                                }
                            }
                        }
                        // fire the change event on questions after all values have been
                        // set in case the event uses the new question values.
                        for (i=0; i<fireChangeArr.length;i++) {
                            var id = fireChangeArr[i];
                            var qstn = Dom.get("SRVQSTN_" + id);
                            if (qstn) {
                                KD.utils.Action._fireChange(qstn);
                            }
                        }
                    }
                }
            }
        }

        this._attachMenu = function (evt, clientAction) {
            var obj, valid, expr, action, attachMenu, selectObj, val, option,
                qualParams, rqtParamString, connection;

            clientAction.currEvent = KD.utils.Util.cloneObj(evt);
            // possibly used internally in the eval expression
            obj = YAHOO.util.Event.getTarget(evt);
            valid = true;
            if (clientAction.ifExpr) {
                expr = KD.utils.Util.trimString(clientAction.ifExpr);
                if (expr != "") {
                    valid = eval("(" + expr + ")");
                }
            }
            if (clientManager.submitType === 'ReviewRequest') {
                action = eval('(' + clientAction.params[1] + ')');
                if (action) {
                    attachMenu = action.AttachMenu[0];
                    selectObj = Dom.get('SRVQSTN_' + attachMenu.AttachTo);
                    if (selectObj) {
                        val = selectObj.getAttribute('originalValue');
                        if (val && val != 'null') {
                            // create a new option to display the selected value
                            option = document.createElement('option');
                            option.text = val;
                            // append to the list of existing options and select
                            try  {
                                selectObj.add(option, null); /* standards compliant */
                            } catch (ex)  {
                                selectObj.add(option); /* IE only */
                            }
                            // select the inserted option
                            try {
                                selectObj.options[selectObj.length - 1].selected = true;
                            } catch (ex1) {
                            /* ignore */
                            }
                        }
                    }
                }
                valid = false;
            }
            if (valid) {
                //Qual Params is an array of zero or more fields/variables to pass in to the asynch request
                qualParams = clientAction.params[0];
                rqtParamString = KD.utils.Action._buildParamString(qualParams);
                connection = new KD.utils.Callback(KD.utils.Action._attachMenuCallback, KD.utils.Action._attachMenuCallback, clientAction);
                clientAction.isOnLoad = clientManager.isLoading;
                // always make synchronous calls when attaching a menu (last param set to true)
                KD.utils.Action.makeAsyncRequest('attachMenu', clientAction.actionId, connection, rqtParamString, '', !clientAction.useGetEntry, true);
            }
        }

        this._attachMenuCallback = function(o) {
            var clientAction=o.argument[0];
            var recordsXML=KD.utils.Action.getMultipleRequestRecords(o);
            //Only populate the menu if we find some records
            if(recordsXML){
                //The second parameter is the attach menu object
                var action=eval('('+clientAction.params[1]+')');
                if (action) {
                    var attachMenu=action.AttachMenu[0];
                    var oOptionValue = attachMenu.ValueField.split(";");
                    var optionValueField = oOptionValue[1];
                    var oOptionLabel = attachMenu.LabelField.split(";");
                    var optionTextField = oOptionLabel[1];
                    var targetList = Dom.get("SRVQSTN_" + attachMenu.AttachTo);
                    // remove existing options
                    for (var h=targetList.options.length-1; h>=0; h--) {
                        targetList.remove(h);
                    }
                    // add a blank option
                    var blankOption = document.createElement("option");
                    blankOption.setAttribute("value", "");
                    targetList.appendChild(blankOption);
                    //Reset width to avoid IE bug
                    targetList.style.width = "1";
                    // add the unique option(s) found by the request
                    var uniqueValueHash=new KD.utils.Hash();
                    for (var i=0; i<recordsXML.length; i++) {
                        var optionValue = KD.utils.Action.getRequestValue(recordsXML[i], optionValueField);
                        var optionText = KD.utils.Action.getRequestValue(recordsXML[i], optionTextField);
                        var checkExists=uniqueValueHash.getItem(optionValue+"--"+optionText);
                        if(checkExists == null){
                            var newOption = document.createElement("option");
                            newOption.setAttribute("value", optionValue);
                            newOption.appendChild(document.createTextNode(optionText));
                            targetList.appendChild(newOption);
                            uniqueValueHash.setItem(optionValue+"--"+optionText, optionValue);
                        }
                    }
                    //Reset width back to avoid IE bug
                    targetList.style.width = "auto";
                    //fire an onchange for the field menu attached to
                    targetList.setAttribute('menuAttached','true');
                }
                //Set the attached menu to the original value if this is an onload event
                KD.utils.Action.setQuestionValue(attachMenu.AttachTo, targetList.getAttribute("originalValue"));
            }
            KD.utils.ClientManager.onAttachMenu.fire(clientAction);
        }

        /*Take an element obj (or array for radios), substitute and <FLD>items and set the field*/
        this._handleSetReplace = function(destElId, rawValue, recordXML){
            var value = "";
            var flds=rawValue.split("</FLD>");
            for (var i=0; i<flds.length; i++){
                //Just static text in this token
                if (flds[i].indexOf("<FLD>")== -1){
                    value += flds[i];
                } else{
                    //Get the static part out if there is some
                    var segs=flds[i].split("<FLD>");
                    if(segs.length>1){ 
                        value += segs[0];
                    }

                    //Get the instance ID out of the <FLD> area and get value
                    //EXTERNAL
                    if(recordXML){
                        var fieldName=(flds[i].split(";")[0]).substring(flds[i].indexOf("<FLD>")+5);
                        value += KD.utils.Action.getRequestValue(recordXML, fieldName);
                    //INTERNAL
                    }else{
                        var instId=flds[i].split(";")[1];
                        value += KD.utils.Action.getQuestionValue(instId);
                    }
                }
            }
            // Set the question value
            KD.utils.Action.setQuestionValue(destElId, KD.utils.Util.trimString(value));
        }

        /*Return of a YAHOO connection call*/
        this._addInnerHTML = function(o){
            var elId=o.argument[0];
            if(elId && elId.length){
                elId = elId[0];
            }
            var el = Dom.get(elId);
            if(el){
                if(o.responseText && o.responseText.indexOf("<errorMessage>") != -1  && o.responseText.indexOf("invalid session") != -1){
                    var yes=confirm("Your session has timed out.  Would you like to reload the page?");
                    if (yes) {
                        window.location.reload(true);
                    }
                }
                var htmlString=o.responseText;
                el.innerHTML=htmlString;
            }
        }

        this._fireChange = function(el){
            if(el.fireEvent){
                el.fireEvent('onchange');
            } else{
                var evt = document.createEvent('HTMLEvents');
                evt.initEvent('change', true, true);
                el.dispatchEvent(evt);
            }
        }

        this._customAction = function(evt, clientAction){
            clientAction.currEvent=KD.utils.Util.cloneObj(evt);
            if(evt){
                var obj=YAHOO.util.Event.getTarget(evt);
            }
            var valid=true;
            try{
                if(clientAction.ifExpr){
                    var expr=KD.utils.Util.trimString(clientAction.ifExpr);
                    if(expr != ""){
                        valid=eval("("+expr+")");
                    }
                }
                if(valid){
                    return eval(clientAction.params[0]);
                }
            } catch(error){
                alert("There was an error with the custom javascript:"+error);
            }
        }
        /*Toggle between a file link and the input value for attachment questions*/
        this._setFileLink =function(qstnEl){
            var link=Dom.get(qstnEl.getAttribute("id")+"_link");
            if(link){
                var qstn=link.parentNode;
                qstn.removeChild(link);
            }
            if(qstnEl.value && qstnEl.value != ""){
                //Build link and layer
                link = document.createElement('a');
                link.setAttribute('id', qstnEl.getAttribute("id")+"_link");
                link.setAttribute('target','_blank');
                var sessionId=encodeURIComponent(KD.utils.ClientManager.sessionId);
                var now = new Date();
                var questionId=encodeURIComponent(KD.utils.Util.getIDPart(qstnEl.getAttribute("id")));
                var path = "SimpleDataRequest?requestName=getFile&dataRequestId=attachment&sessionId="+sessionId+"&questionId="+questionId+"&noCache="+now.getTime()+"&fileName="+encodeURIComponent(qstnEl.value);
                link.setAttribute('href', path);
                link.className="fileLink";
                link.style.marginRight="20px";
                var fileName=document.createTextNode(qstnEl.value);
                link.appendChild(fileName);
                qstnEl.originalDisplay=qstnEl.style.display;
                qstnEl.style.display="none";
                var qstnLyr=qstnEl.parentNode;
                var first = qstnLyr.firstChild;
                qstnLyr.insertBefore(link,first);
            } else{
                if(!qstnEl.originalDisplay || !qstnEl.originalDisplay ==""){
                    qstnEl.originalDisplay="inline";
                }
                qstnEl.style.display=qstnEl.originalDisplay;
            }
        }

        /**
         * Remove the event listener on the submit button clicks
         */
        this._disableButtonEvents = function (e) {
            var buttons = KD.utils.ClientManager.submitButtons,
            buttonDiv,
            buttonEls,
            theButton;

            KD.utils.ClientManager.buttonListeners = {};
            for (var i = 0; i < buttons.length;  i += 1) {
                buttonDiv = Dom.get("BUTTON_" + buttons[i]);
                buttonEls = Dom.getElementsByClassName('templateButton', 'input', buttonDiv);
                if (buttonEls && buttonEls.length > 0) {
                    theButton = buttonEls[0];
                    YAHOO.util.Event.removeListener(theButton, "click");
                }
            }
        };

        /**
         * Add event listeners back to the submit buttons
         */
        this._enableButtonEvents = function (e) {
            var buttons = KD.utils.ClientManager.submitButtons,
            buttonDiv,
            buttonEls,
            theButton;

            for (var i = 0; i < buttons.length;  i += 1) {
                buttonDiv = Dom.get("BUTTON_" + buttons[i]);
                buttonEls = Dom.getElementsByClassName('templateButton', 'input', buttonDiv);
                if (buttonEls && buttonEls.length > 0) {
                    theButton = buttonEls[0];
                    YAHOO.util.Event.addListener(theButton, 'click', KD.utils.Action._submitButtonClick,
                        [ theButton.form, KD.utils.Util.getIDPart(buttonDiv.id)], true);
                }
            }
            KD.utils.ClientManager.buttonListeners = {};
        };

        /**
         * Handles form submit buttons
         */
        this._submitButtonClick = function (e, parms) {
            if (KD.utils.ClientManager.isSubmitting != true) {
                var theForm = parms[0];
                var id = parms[1];
                KD.utils.ClientManager.isSubmitting = true;
                KD.utils.Action.disableButton(id);
                KD.utils.Action.validateForm(e, theForm);
            }
        };


        /* CUSTOM EVENT HANDLERS */
        /*Handles the onSetFieldsReturn custom event*/
        this._setFieldsEventHandler = function(origAction, clientAction, origEvent){
            //Check if the event is tied to this element
            if(origAction.elementId==clientAction.elementId){
                clientAction.action.apply(KD.utils.Action,[origEvent,clientAction]);
            }else{
                return;
            }
        }

        /*Handles the onAttachMenu custom event*/
        this._attachMenuEventHandler = function(origAction, clientAction, origEvent){
            //Check if the event is tied to this element
            var theEl=eval('('+origAction.params[1]+')');
            if(theEl && theEl.AttachMenu && theEl.AttachMenu[0].AttachTo ==clientAction.elementId){
                clientAction.action.apply(KD.utils.Action,[origEvent,clientAction]);
            }
        }
        /*Custom event wrapper*/
        this._customEventHandler = function(type, origItems, clientAction){
            var origAction=origItems[0];
            if(!origAction){
                return;
            }
            var origEvent=origAction.currEvent;
            if(!origEvent){
                //Create a literal event object
                var theTarget=Dom.get("SRVQSTN_" + origAction.elementId);
                origEvent={
                    button: 0,
                    which:1,
                    target:theTarget,
                    type: origAction.jsEvent
                };
            }
            if(type=="onSetFieldsReturn"){
                KD.utils.Action._setFieldsEventHandler(origAction, clientAction, origEvent);
            }else if(type == "onAttachMenu"){
                KD.utils.Action._attachMenuEventHandler(origAction, clientAction, origEvent);
            }
        }


        /* SYNCHRONOUS XMLHTTPREQUEST FUNCTIONS */
        /* INTERNAL USE ONLY */
        /* Create and return an XmlHttpRequest object */
        this._createSyncXhr = function() {
            var obj = {},
            oXhr = null,
            oAX = ["MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];

            // make call Synchronously
            try {
                // try creating an XMLHttpRequest object
                oXhr = new XMLHttpRequest();
            } catch (e) {
                // didn't work, try with an ActiveXObject object
                for (var i=0; i < oAX.length; i+=1) {
                    try {
                        oXhr = new ActiveXObject(oAX[i]);
                        break;
                    } catch (e2) {}
                }
            }
            obj.xhr = oXhr;
            return obj;
        };

        /* INTERNAL USE ONLY */
        /*
         * Builds up the response object for synchronous xmlhttp requests
         * @param xhr : the xhrhttprequest or activeXObject
         * @param callbackArg : the callback argument from the callback object
         * @return responseObj : the response object with headers, status, response, and callback argument
         */
        this._buildSyncResponse = function(xhr, callbackArg) {
            var oRes = {},
            oHeader = {},
            headerStr,
            header,
            delimitPos;

            try
            {
                headerStr = xhr.getAllResponseHeaders();
                header = headerStr.split('\n');
                for(var i=0; i<header.length; i++){
                    delimitPos = header[i].indexOf(':');
                    if(delimitPos != -1){
                        oHeader[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
                    }
                }
            }
            catch(e){}

            oRes.status = xhr.status;
            oRes.statusText = xhr.statusText;
            oRes.getResponseHeader = oHeader;
            oRes.getAllResponseHeaders = headerStr;
            oRes.responseText = xhr.responseText;
            oRes.responseXML = xhr.responseXML;

            if (typeof callbackArg != "undefined") {
                oRes.argument = callbackArg;
            }
            return oRes;
        };

        this.handleHttpErrorCode = function(statusCode, message) {
            if (statusCode == 401) {
                KD.utils.ClientManager.renderSimpleLogin();
            } else if (statusCode == 0) {
                alert("The web server hasn't responded in a reasonable amount of time.\nPlease try the request again.");
            } else {
                alert('Error: ' + statusCode + '\n\n' + message);
            }
        };

    };  // KD.utils.Action


}  // KD.utils

if (! KD.utils.FileLoader){
    /**
FILE LOADER CLASS
     */
    KD.utils.FileLoader= new function(){
        this.questionObj;
        this.questionId;
        this.enforceFileTypes=false;
        this.fileTypesAllowed;
        this.sizeLimit;
        this.contextPath;
        this.loadFileWindow;

        this._getCenterWinFeatures = function(windowWidth, windowHeight){
            var screenWidth = window.screen.availWidth;
            var screenHeight = window.screen.availHeight;
            var left = parseInt(screenWidth /2 ) - (windowWidth / 2);
            var top = parseInt(screenHeight / 2) - (windowHeight / 2);
            var features = ",width=" + windowWidth + ",height=" + windowHeight;
            features += ",top=" + top + ",left=" + left;
            return features;
        }

        this.loadFile = function(attachQstn) {
            if(!attachQstn) {
                alert("Must include a question object");
                return false;
            }

            this.questionId = attachQstn.id.substring(attachQstn.id.indexOf("_")+1,attachQstn.id.length);
            this.questionObj = Dom.get("SRVQSTN_"+this.questionId);
            if (this.questionObj.getAttribute("enforceFileTypes").toUpperCase() == "ENFORCE") this.enforceFileTypes=true;
            this.fileTypesAllowed=this.questionObj.getAttribute("fileTypesAllowed");
            this.sizeLimit=this.questionObj.getAttribute("sizeLimit");
            this.contextPath=document.location.pathname.substring(0,document.location.pathname.lastIndexOf("/", document.location.pathname.length));

            var sFeatures=this._getCenterWinFeatures(500, 200);
            var path = this.contextPath + "/resources/uploadFile.html";
            if (this.loadFileWindow && !this.loadFileWindow.closed && this.loadFileWindow.location)
            {
                this.loadFileWindow.location.href = path;
            }
            else
            {
                this.loadFileWindow=window.open(path,'_blank',sFeatures);
                if (!this.loadFileWindow.opener) this.loadFileWindow.opener = self;
            }
            if (window.focus) {
                this.loadFileWindow.focus()
            }
        }

        this.clearFile = function(obj) {

            questionID = obj.id.substring(obj.id.indexOf("_")+1,obj.id.length);
            questionObj = Dom.get("SRVQSTN_"+questionID);
            questionObj.value = '';
            KD.utils.Action._setFileLink(questionObj);
        }
    };
}  // KD.utils.FileLoader

/**
 *
 * @constructor
 * YAHOO callback object
 */
KD.utils.Callback = function(successObj, errorObj, args, includeLoadingImage){
    this.success=successObj;
    /*This will be applied in addition to the default SDR failure handling.
    If no special handling is needed, leave as null */
    this.error=errorObj;
    this.argument=new Array(args);
    this.includeLoadingImage=false;
    if(includeLoadingImage){
        this.includeLoadingImage=includeLoadingImage;
    }
};  // KD.utils.Callback
