﻿YAHOO.namespace("MVC");
YAHOO.MVC.Form = function () {

    var m_oYEVNT = YAHOO.util.Event,
        m_oYDOM = YAHOO.util.Dom,
        m_oConnect = YAHOO.CSAjax.Connection,
        m_oUtils = YAHOO.CSUtils.Utils;

    /****************** Standard Validators ******************/
    var m_oVal = {
        1: { errmsg: 'A-Z, a-z, or space only', exp: /^[a-zA-Z\s]*$/ },
        28: { errmsg: 'A-Z, a-z, or space only, max: 10', exp: /^[a-zA-Z\s]*$/ },
        29: { errmsg: 'A-Z, a-z, or space only, max: 25', exp: /^[a-zA-Z\s]*$/ },
        30: { errmsg: 'A-Z, a-z, or space only, max: 50', exp: /^[a-zA-Z\s]*$/ },
        3: { errmsg: 'Alphanumeric or space', exp: /^[\w|\d]*$/ },
        31: { errmsg: 'Alphanumeric or space, max: 10', exp: /^[\w|\d]*$/ },
        32: { errmsg: 'Alphanumeric or space, max: 25', exp: /^[\w|\d]*$/ },
        33: { errmsg: 'Alphanumeric or space, max: 50', exp: /^[\w|\d]*$/ },
        4: { errmsg: 'Alphanumeric, space or \'-,.#/', exp: /^['\-,\.#\/\w\s\d\$]*$/ },
        34: { errmsg: 'Alphanumeric, space or \'-,.#/, max: 10', exp: /^['\-,\.#\/\w\s\d\$]*$/ },
        38: { errmsg: 'Alphanumeric, space or \'-,.#/, max: 100', exp: /^['\-,\.#\/\w\s\d\$]*$/ },
        35: { errmsg: 'Alphanumeric, space or \'-,.#/, max: 25', exp: /^['\-,\.#\/\w\s\d\$]*$/ },
        36: { errmsg: 'Alphanumeric, space or \'-,.#/, max: 50', exp: /^['\-,\.#\/\w\s\d\$]*$/ },
        7: { errmsg: 'Ex: john.doe@email.com, jane.doe@email.com', exp: /^[a-zA-Z0-9\-][a-zA-Z0-9\._%\-\+]*@[\-a-zA-Z0-9]+(\.[\-a-zA-Z0-9]+)*\.([cC][oO][mM]|[eE][dD][uU]|[iI][nN][fF][oO]|[gG][oO][vV]|[iI][nN][tT]|[mM][iI][lL]|[nN][eE][tT]|[oO][rR][gG]|[bB][iI][zZ]|[nN][aA][mM][eE]|[mM][uU][sS][eE][uU][mM]|[cC][oO]{2}[pP]|[aA][eE][rR][oO]|[pP][rR][oO]|[a-zA-Z]{2})(,[ ]?[a-zA-Z0-9\-][a-zA-Z0-9\._%\-\+]*@[\-a-zA-Z0-9]+(\.[\-a-zA-Z0-9]+)*\.([cC][oO][mM]|[eE][dD][uU]|[iI][nN][fF][oO]|[gG][oO][vV]|[iI][nN][tT]|[mM][iI][lL]|[nN][eE][tT]|[oO][rR][gG]|[bB][iI][zZ]|[nN][aA][mM][eE]|[mM][uU][sS][eE][uU][mM]|[cC][oO]{2}[pP]|[aA][eE][rR][oO]|[pP][rR][oO]|[a-zA-Z]{2}))*$/ },
        24: { errmsg: 'Free text', exp: /^[^\^]*$/ },
        39: { errmsg: 'Must be valid credit card number', exp: /^[3-6]{1}[\d\s]{13,19}$/ },
        13: { errmsg: 'Ex: mm/dd/yyyy', exp: /^(10|11|12|[0]?\d)\/(30|31|(0|1|2)?\d)\/(\d{2}|\d{4})$/ },
        6: { errmsg: 'Ex: john.doe@email.com', exp: /^[a-zA-Z0-9\-][a-zA-Z0-9\._%\-\+]*@[\-a-zA-Z0-9]+(\.[\-a-zA-Z0-9]+)*\.([cC][oO][mM]|[eE][dD][uU]|[iI][nN][fF][oO]|[gG][oO][vV]|[iI][nN][tT]|[mM][iI][lL]|[nN][eE][tT]|[oO][rR][gG]|[bB][iI][zZ]|[nN][aA][mM][eE]|[mM][uU][sS][eE][uU][mM]|[cC][oO]{2}[pP]|[aA][eE][rR][oO]|[pP][rR][oO]|[a-zA-Z]{2})$/ },
        22: { errmsg: 'Ex: mm/yy', exp: /^(10|11|12|[0]{0,1}\d)\/([19|20]?\d{2})$/ },
        11: { errmsg: '0 to 999,999,999.00', exp: /^\d{1,3}([,]?\d{3}){1,2}(\.00)?$/ },
        12: { errmsg: '0 to 999,999,999,999,999', exp: /^\d{1,3}([,]?\d{3}){1,4}?$/ },
        2: { errmsg: 'Numbers only', exp: /^\d*$/ },
        27: { errmsg: 'Numbers only: Max 10 digits', exp: /^\d*$/ },
        37: { errmsg: '0-99', exp: /^\d*$/ },
        26: { errmsg: '0-9999', exp: /^\d*$/ },
        53: { errmsg: '0-99999', exp: /^\d*$/ },
        5: { errmsg: 'Ex: (281) 321-1234 <i>ext 1234</i>', exp: /^(\(\d{3}\)|\d{3})[- ]?\d{3}[- ]?\d{4}([\s]?(x{1}|ext[.]?)[\s]?\d{2,6})?$/ },
        9: { errmsg: 'Ex: 123-45-5678', exp: /^(\d{9}|\d{3}\-\d{2}\-\d{4})$/ },
        10: { errmsg: 'Ex: 1,000 or $999,999', exp: /^(\$)?(\d{3,6}|\d{1,3},\d{3})(\.00)?$/ },
        14: { errmsg: 'Ex. 11:11PM', exp: /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))?$/ },
        51: { errmsg: 'US CA Postal Code', exp: /(^(\d{5})([-]{0,1}\d{4})?$)|(^[ABCEGHJKLMNPRSTVXYabceghjklmnprstvxy]{1}\d{1}[A-Z]{1} *\d{1}[A-Za-z]{1}\d{1}$)/ },
        8: { errmsg: 'Ex: 77069 <i>or 77069-1234</i>', exp: /^(\d{5})([- ]{0,1}\d{4})?$/ },
        50: { errmsg: 'Valid Canadian Zip', exp: /^[ABCEGHJKLMNPRSTVXYabceghjklmnprstvxy]{1}\d{1}[A-Z]{1}\s*\d{1}[A-Za-z]{1}\d{1}$/ },
        54: { errmsg: "Numeric: 0 to 9999.00", exp: /^[0-9]{1,4}(\.[0-9]{1,2})?$/ }
    },
    m_oTD = {},
    m_fType = {
        "Images": { description: "Images", extensions: "*.jpg;*.png;*.gif;*.jpeg;" },
        "Flash": { description: "Flash", extensions: "*.swf;*.flv;" },
        "Docs": { description: "Docs", extensions: "*.doc;*.pdf;*.docx;*.csv;*.xml;*.txt;*.rtf;*.xls;*.xlsx;*.ods;*.odt;*.html;*.ppt;*.pps;" },
        "MixMedia": { description: "MixMedia", extensions: "*.jpg;*.png;*.gif;*.jpeg;*.doc;*.pdf;*.docx;*.csv;*.xml;*.txt;*.rtf;*.xls;*.xlsx;*.ods;*.odt;*.html;*.ppt;*.pps;" },
        "Templates": { description: "Templates" }
    },
    m_iResubmitCnt = 0;

    var GetContainerDiv = function (element) {
        var oParent = element;
        if (m_oYDOM.hasClass(oParent, "csfld") == true)
            return oParent;
        else if (element.parentNode)
            oParent = GetContainerDiv(element.parentNode);
        else
        //form[ul-ID] returns all the child radio btns without parentNode property
            oParent = GetContainerDiv(element[0].parentNode);
        return oParent;
    },

    SafeFormObjID = function (element) {
        //Radios and Drops have a length
        //  Drops: options list BUT have an id
        //  Rads: input list BUT no id
        var iRadLen = element.length;
        if (element.options || !iRadLen || iRadLen <= 1) return element.id;

        //All inputs share name property to group themselves, though IDs are [Name/ID].1, ..., [Name/ID].N
        return element[0].name;
    },

    GetSelValue = function (element) {
        //For hidden fields we return null
        if (IsHiddenFld(element)) return null;

        //DropDown
        if (element.options) {
            try {
                return element.options[element.selectedIndex].innerHTML;
            } catch (e) { return element.options[0].innerHTML }
        }

        //CheckBox
        if (element.type == "checkbox") {
            if (element.checked) return "Yes";
            return "No";
        }

        //Radio buttons
        var iRadLen = element.length;
        if (iRadLen && iRadLen > 1)
            for (var i = 0; i < iRadLen; i++)
                if (element[i].checked)
                    return element[i].value;

        //Radio and TextBox
        return element.value;
    },

    GetJSFld = function (frmID, eleID) {
        var oFld = m_oTD[frmID].fields[eleID];
        if (oFld != null) return oFld;
        return m_oTD[frmID].fields[eleID.split(".")[0]];
    },

    GetLblName = function (contDiv) {
        var sLbl = null;

        try {
            sLbl = m_oYDOM.getElementsByClassName("cslt", "label", contDiv);
            if (sLbl != null && sLbl.length > 0) sLbl = sLbl[0].innerHTML.replace(/^\s+|\s+$|:*/g, "");
        } catch (e) { }

        if (sLbl == null || sLbl == "") {
            try {
                sLbl = m_oYDOM.getElementsByClassName("csrt", "label", contDiv);
                if (sLbl != null && sLbl.length > 0) sLbl = sLbl[0].innerHTML.replace(/^\s+|\s+$|:*/g, "");
            } catch (e) { }
        }
        return sLbl;
    },

    GetValidatorExp = function (frmID, element) {
        var sVal = m_oTD[frmID].fields[SafeFormObjID(element)].exp;
        if (sVal == null || sVal.length == 0) return;

        //Try to find an id match in the m_oVal object       
        if (m_oVal[sVal] != null) return m_oVal[sVal].exp;

        //When there is no match in the m_oVal object then sVal itself must be the expression
        return sVal;
    },

    IsTextType = function (type) {
        if (type == "text" || type == "textarea" || type == "email" || type == "tel" || type == "number" || type == "date")
            return true;
        return false;
    },

    GetValidTxt = function (frmID, element) {
        var sID = SafeFormObjID(element);

        //Try to use the errmsg in JSON if passed from server
        try { var sTxt = m_oTD[frmID].fields[sID].errmsg; } catch (e) { }
        if (sTxt != null && sTxt.length > 0) return sTxt;

        var sVal = m_oTD[frmID].fields[sID].exp;
        if (m_oVal[sVal] != null) return m_oVal[sVal].errmsg;

        return null;
    },

    GetTooltipHTML = function (lblText, validTxt, atTop) {
        if (validTxt && validTxt.length > 34) validTxt = validTxt.substring(0, 32) + " ...";
        else if (!validTxt) validTxt = "";

        //rendering below screen coords requires different classes
        if (atTop) return "<div class=\"ttbelow\"><div class=\"tttop\"></div><div class=\"ttmid\">" + lblText + "<br />" + validTxt + "</div><div class=\"ttbtm\"></div></div>";

        //Must be rendering above screen coords
        return "<div class=\"ttabove\"><div class=\"tttop\"></div><div class=\"ttmid\">" + lblText + "<br />" + validTxt + "</div><div class=\"ttbtm\"></div></div>";
    },

    /*         
    *  Adds a UL element inside validation summary div.
    *       Its id is elementId prefixed with 'ul'(uld9000)
    *
    *  @param string id        - validation summary div id.    
    */
    AddValSumUL = function (id) {
        var oValSumEle = m_oYDOM.get(id);
        var oUL = document.createElement("ul");
        m_oYDOM.setAttribute(oUL, "id", "ul" + id);
        oValSumEle.appendChild(oUL);
    },

    AddListeners = function (formID) {
        AddSubmitListener(formID);

        AddInputListeners(formID);
    },

    AddSubmitListener = function (formID) {
        //Get the submit button inside the form with id htmlID
        var oBtn = m_oYDOM.getElementBy(function (obj) { if (obj.type == "submit") return true; else return false; }, "input", formID);

        //Add listnener
        m_oYEVNT.addListener(oBtn, "click", hndlrSubmit, formID, false);
    },

    AddInputListeners = function (formID) {
        var oFields = m_oTD[formID].fields,
            oForm = document.forms[formID],
            oReqFlds = null, oUnhideFlds = null, oField = null, sEvent = null, sDispType = null, sEleType = null,
            IsAuthRequired = m_oTD[formID].authredir != null ? true : false,
            NoPrettyLabels = m_oTD[formID].noprettylabels;

        for (var iDx in oFields) {
            sEleType = oForm[iDx].type;

            //Redirect to login if authentication is required
            if (!NoPrettyLabels && IsAuthRequired) m_oYEVNT.addListener(oForm[iDx], "focus", hndlrRedirect2Login, m_oTD[formID].authredir, false);

            //blur event for TextType, change event for dropdowns and radio buttons and click event for checkbox
            if (IsTextType(sEleType)) {
                sEvent = "blur";
                if (!NoPrettyLabels)
                    m_oYEVNT.addListener(oForm[iDx], "focus", hndlrTxtFocus, { frmID: formID, element: oForm[iDx] }, true);
            }
            else sEvent = sEleType == "checkbox" ? "click" : "change";

            // For validation
            m_oYEVNT.addListener(oForm[iDx], sEvent, hndlrValidate, { frmID: formID, element: oForm[iDx] }, true);

            // hide/unhide  
            oUnhideFlds = oFields[iDx].unhide;
            if (oUnhideFlds && oUnhideFlds.length > 0)
                m_oYEVNT.addListener(oForm[iDx], sEvent, hndlrToggleHide, { frmID: formID, element: oForm[iDx] }, true);

            //Required if
            oReqFlds = oFields[iDx].reqif;
            if (oReqFlds && oReqFlds.length > 0)
                m_oYEVNT.addListener(oForm[iDx], sEvent, hndlrToggleReq, { frmID: formID, element: oForm[iDx] }, true);

            //Calenders
            sDispType = oFields[iDx].disptype;
            if (sDispType != null && sDispType == "cscal") InitCal(formID, iDx);
        }
    },

    AddCalListeners = function (inputEle, calender) {
        //Add listner to textbox click event to show the calender
        m_oYEVNT.addListener(inputEle, "focus", function () { this.show(); }, calender, true);

        //Add listner to textbox change event to hide the calender
        m_oYEVNT.addListener(inputEle, "change", function () { this.hide(); }, calender, true);

        //Subscribe to the select event on calender to get selected date
        calender.selectEvent.subscribe(hndlrCalDateSelected, calender, true);
    },

    ClearValCSS = function (conDiv) {
        m_oYDOM.removeClass(conDiv, "csinvalid");
        m_oYDOM.removeClass(conDiv, "csvalid");
    },

    InitCal = function (formID, dateTxtBoxID) {
        var oDateTxtBox = m_oYDOM.get(dateTxtBoxID),
            oConDiv = GetContainerDiv(oDateTxtBox),
            sCalDivID = "cal" + dateTxtBoxID, sCalHdr = "Choose a date:";

        m_oYDOM.addClass(oConDiv, "yui-skin-sam");

        //Insert a Container Div in the document
        var oCalDiv = document.createElement('div');
        oCalDiv.setAttribute('id', sCalDivID);
        m_oYDOM.insertAfter(oCalDiv, oDateTxtBox);
        var oCal = new YAHOO.widget.Calendar(sCalDivID, { title: sCalHdr, close: true, navigator: true, mindate: new Date(), frmID: formID });
        oCal.render();
        oCal.hide();

        AddCalListeners(oDateTxtBox, oCal);
    },

    /*         
    *  Called by ValidateFields. Returns true if the field container is hidden or
    *     when field's header section is hidden.  
    *
    *  @param object element        - HTML input element    
    */
    IsHiddenFld = function (element) {
        var oFldCont = GetContainerDiv(element);
        if (m_oYDOM.hasClass(oFldCont, "DfltHidden")) return true;

        var oHdrEle = oFldCont;
        while (!m_oYDOM.hasClass(oHdrEle, "cshdr")) {
            oHdrEle = oHdrEle.parentNode;
        }
        if (m_oYDOM.hasClass(oHdrEle, "DfltHidden")) return true;
        return false;
    },

    /*         
    *  Called by Toggle. Hides input element with id eleID when unHide is false else unhides it.  
    *
    *  @param object frmID        - Form ID   
    *  @param string eleID      - ID of element being validated
    *  @param boolean unHide       - When true, sets bad value else resets it
    */
    HideUnHide = function (form, eleID, unHide) {
        //Get the parent div with class "fld"
        var oContainerDiv = eleID.indexOf("d") > -1 ? GetContainerDiv(form[eleID]) : eleID;

        //Remove the class if it already exists
        m_oYDOM.removeClass(oContainerDiv, "DfltHidden");

        if (unHide)
            m_oYDOM.removeClass(oContainerDiv, "DfltHidden");
        else
            m_oYDOM.addClass(oContainerDiv, "DfltHidden");
    },

    /*         
    *  Called by Toggle. Sets or resets bad value in JSON.    
    *
    *  @param object frmID        - Form ID   
    *  @param string eleID        - ID of element being validated
    *  @param boolean isReq       - When true, sets bad value else resets it
    */
    SetResetBadVal = function (form, eleID, isReq) {
        var oEle = form[eleID], oConDiv = null;

        if (oEle == null || oEle.type == "radio") return;  //n/a for radio btns      

        if (isReq) {
            if (oEle.options && oEle.options[0].value == "0") //DropDown
                m_oTD[form.id].fields[eleID].bad = oEle.options[0].innerHTML;
            else if (oEle.type == "checkbox")  //CheckBox 
                m_oTD[form.id].fields[eleID].bad = "No";
            else m_oTD[form.id].fields[eleID].bad = "";  //TextBox       
        }
        else {
            m_oTD[form.id].fields[eleID].bad = null;

            //Clear all CSS and Validation summary if the element is validated before
            oConDiv = GetContainerDiv(oEle);
            UpdateUIVal(form.id, eleID, oConDiv, true); //Updates Val sum 
            ClearValCSS(oConDiv);
        }
    },

    /*         
    *  Called by IsValidFormat and Look4BadVal. This function is called when the validation fails.
    *         Adds a proper error message in JSON.
    *
    *  @param object frmID        - Form ID   
    *  @param object element      - Element being validated
    *  @param object conDiv       - Parent div of element with class "csfld"
    *  @param boolean isBad       - When true, it is an required cond. failure else a format validation failure
    */
    SetJSErrMsg = function (frmID, element, contDiv, isBad) {
        var sLbl = GetLblName(contDiv),
            sID = SafeFormObjID(element),
            oJSFld = m_oTD[frmID].fields[sID],
            sErMsg = null,
            iCnt = 0;

        while ((sLbl == null || sLbl.length == 0) && iCnt < 5) {
            contDiv = m_oYDOM.getPreviousSibling(contDiv);
            sLbl = GetLblName(contDiv);
            iCnt++; //To be safe from falling in an infinite loop
        }
        if (sLbl == null || sLbl.length == 0) return;

        if (isBad)
            sErMsg = "is required.";
        else {
            sErMsg = oJSFld.errmsg;
            if (sErMsg == null || sErMsg.length == 0)
                sErMsg = m_oVal[oJSFld.exp] != null ? "is invalid:<br />" + m_oVal[oJSFld.exp].errmsg : "is invalid.";
        }
        m_oTD[frmID].fields[sID].uierrmsg = sLbl + " " + sErMsg;  //Update JSON
    },

    /*         
    *  Called by UpdateUIVal. Creates an UL element if it doesn't exist.
    *        Adds Li element when isAdd is true else removes it. The id
    *        of each Li("li"+eleID) corresponds to eleID(Input element being validated)
    *
    *  @param object frmID        - Form ID   
    *  @param string eleID        - Id of input element being validated
    *  @param boolean isAdd       - When true, adds li element with id "li"+eleID else removes li corresponding to same id
    */
    UpdateValSum = function (frmID, eleID, isAdd) {
        var oValSumUL = m_oYDOM.get("ul" + m_oTD[frmID].valsumid),
            sErrMsg = null,
            oLI = null;

        //May need to create the validation summary UL element
        if (!oValSumUL) {
            if (!isAdd) return;

            AddValSumUL(m_oTD[frmID].valsumid);
            oValSumUL = m_oYDOM.get("ul" + m_oTD[frmID].valsumid);
        }

        //isAdd is true when the validation fails
        if (isAdd) {
            oLI = m_oYDOM.get("li" + eleID);
            if (!oLI) {
                oLI = document.createElement("li");
                m_oYDOM.setAttribute(oLI, "id", "li" + eleID);
                oValSumUL.appendChild(oLI);
            }
            oLI.innerHTML = m_oTD[frmID].fields[eleID].uierrmsg;
        }
        else {
            oLI = m_oYDOM.get("li" + eleID);
            if (oLI) {
                oValSumUL.removeChild(oLI);

                //If oValSumUL has no children, remove the val summary UL
                if (m_oYDOM.getChildren(oValSumUL).length == 0)
                    m_oYDOM.get(m_oTD[frmID].valsumid).innerHTML = "";
            }
        }
    },

    /*         
    *  Called by ValidateSingleElement and SetResetBadVal. Adds proper validation classes 
    *       and updates the validation summary. 
    *       
    *
    *  @param object frmID        - Form ID   
    *  @param string eleID        - Id of input element being validated
    *  @param object conDiv       - Parent div with class "csfld"
    *  @param boolean isValid     - True, if validated successfully
    */
    UpdateUIVal = function (frmID, eleID, conDiv, isValid) {
        //Validatation UI
        if (isValid) {
            m_oYDOM.addClass(conDiv, "csvalid");
            UpdateValSum(frmID, eleID, false);
            m_oTD[frmID].fields[eleID].uierrmsg = null; // Update JSON
        }
        else {
            m_oYDOM.addClass(conDiv, "csinvalid");
            UpdateValSum(frmID, eleID, true);
        }
    },

    /*         
    *  Called by ValidateSingleElement. Compares the format of input value against the regular expression
    *         and sets the error message in JSON if the validation failed.
    *       
    *
    *  @param object frmID        - Form ID   
    *  @param object element      - The element on which the change event has fired 
    *  @param object contDiv      - Parent div with class "csfld"
    */
    IsValidFormat = function (frmID, element, contDiv) {
        var IsValid = true;

        if (!element.value || element.value == "") return true;

        //Get the validation expression
        var sExp = GetValidatorExp(frmID, element)
        if (sExp == null) return true;

        //Update JSON with error message
        IsValid = element.value.match(sExp) != null;
        if (!IsValid)
            SetJSErrMsg(frmID, element, contDiv, false);

        return IsValid;
    },

    /*         
    *  Called by ValidateSingleElement. Compares the input value to bad value stored in JSON
    *        and sets the error message in JSON. Requiredif conditions are handled here.
    *
    *  @param object frmID        - Form ID   
    *  @param object element      - The element on which the change event has fired 
    *  @param object conDiv       - Parent div with class "csfld" 
    *  @param string inputVal     - User entered value
    */
    Look4BadVal = function (frmID, element, conDiv, inputVal) {
        var sBadVal = null, sID = SafeFormObjID(element);

        //Get bad value from JS object
        sBadVal = m_oTD[frmID].fields[sID].bad;

        //A null value implies the input field accepts any input
        if (sBadVal == null) return true;

        //Update JSON with error msg
        if (inputVal == sBadVal) {
            SetJSErrMsg(frmID, element, conDiv, true);
            return false;
        }
        return true;
    },

    /*         
    *  Called by hndlrValidate. Validates the element and updates UI
    *        to show success or failure.
    *
    *  @param object frmID        - Form ID   
    *  @param object element      - The element on which the change event has fired 
    */
    ValidateSingleElement = function (frmID, element, isSubmit) {
        var oConDiv = GetContainerDiv(element),
            IsValid = true,
            sSelVal = GetSelValue(element),
            sLbl = GetLblName(oConDiv),
            sID = SafeFormObjID(element);

        m_oTD[frmID].fields[sID].resp = sSelVal; //Update JSON resp    
        ClearValCSS(oConDiv); //Clear previous Validation CSS on contDiv
        UpdateValSum(frmID, sID, false); //Clear prev validation summary of this element

        //Validate all types against bad values
        //  Required fields check needs to happen first
        IsValid = Look4BadVal(frmID, element, oConDiv, sSelVal);

        //For dropdowns if the selected value is default label, set the response to null.
        //    Avoids saving labels for dropdowns
        if (isSubmit && IsValid && element.options && element.selectedIndex == 0 && element.options[0].value == "0")
            m_oTD[frmID].fields[sID].resp = null;

        //skip UI changes in case of required val failure and non submisssion        
        if (!isSubmit && !IsValid) return false;

        //Regex validation only for text boxes, and textarea
        if (IsTextType(element.type) && IsValid) {
            IsValid = IsValidFormat(frmID, element, oConDiv);
        }

        //Avoid showing success or failure on fields with empty values that are not required
        if (IsValid && (!sSelVal || sSelVal == "") && m_oTD[frmID].fields[sID].bad == null) return IsValid;

        UpdateUIVal(frmID, sID, oConDiv, IsValid); //Update validation results on UI

        return IsValid;
    },

    /*         
    *  Called by hndlrSubmit. Validates all the fields and builds the validation
    *        summary, if there are any errors. Returns true when all the fields are validated successfully.
    *
    *  @param object frmID        - Form ID     
    */
    ValidateFields = function (frmID) {
        var oFlds = m_oTD[frmID].fields,
            IsValid = true;

        for (var id in oFlds) {
            oEle = document.forms[frmID][id];

            if (IsHiddenFld(oEle)) continue; //If the fld is hidden or its header is hidden then skip validation

            IsValid = ValidateSingleElement(frmID, oEle, true) && IsValid;
        }
        return IsValid;
    },

    /*         
    *  Toggles the conditional fields(hide/Unhide or req based on the value of isUnHide)
    *
    *  @param object form         - Html Element   
    *  @param Array fldIDs        - Array of ids that are to be toggled   
    *  @param boolean isUnHide    - If true hides/unhides else toggles req fields
    *  @param boolean isSet       - If true unhides or sets required, else hides or resets required
    */
    Toggle = function (form, fldIDs, isUnHide, isSet) {
        for (var iDx = 0; iDx < fldIDs.length; iDx++) {
            if (isUnHide) HideUnHide(form, fldIDs[iDx], isSet);
            else SetResetBadVal(form, fldIDs[iDx], isSet);
        }
    },

    /*         
    *  Sets conditional fields for toggling.
    *
    *  @param object frmID        - Form ID   
    *  @param object element      - The element on which the change event has fired   
    *  @param boolean isUnHide    - If true hides/unhides else toggles req fields   
    */
    SetCondFields = function (frmID, element, isUnHide) {
        var sID = SafeFormObjID(element),
            oJsFld = GetJSFld(frmID, sID), //Corresponding JSON field to element 
            sSelVal = null,
            oForm = document.forms[frmID],
            oToggleIds = isUnHide ? oJsFld.unhide : oJsFld.reqif,
            sCondVal = isUnHide ? oJsFld.unhideval : oJsFld.reqval,
            NoPrettyLabels = m_oTD[frmID].noprettylabels;

        //For radio btns ignore when there is no conditional value
        if ((sCondVal == null || sCondVal.length == 0) && element.type == "radio") return;

        //Clear pretty label out so that we know if they actually put something
        if (!NoPrettyLabels)
            SetResetLbl(element, true);     

        //Get selected value
        sSelVal = GetSelValue(element);

        // Unhide/Set-req when Conditional value exists and matches to selected value 
        //  or conditional value is null and the element has some value
        if (sCondVal == null
            || (sCondVal == "" && sSelVal && sSelVal != "") 
            || (sCondVal != "" && sCondVal == sSelVal))
            Toggle(oForm, oToggleIds, isUnHide, true);
        else
            Toggle(oForm, oToggleIds, isUnHide, false);

        //Reset pretty label
        if (!NoPrettyLabels)
            SetResetLbl(element, false); 
    },

    DisplayLabelsInInputFields = function (frmID) {
        var oFlds = m_oTD[frmID].fields,
            oInputEle = null, sLabel = null, sDfltVal = null, sResp = null, oContDiv = null;

        for (iDx in oFlds) {
            oInputEle = m_oYDOM.get(iDx);
            oContDiv = GetContainerDiv(oInputEle);
            sLabel = GetLblName(oContDiv);
            sDfltVal = oFlds[iDx].dflt;
            sResp = oFlds[iDx].resp;

            //Set the text only when the control doesn't have default value and user response is null
            if (sLabel == null
             || oInputEle == null
             || (sDfltVal != null && sDfltVal.length > 0) //C# code pre-populates default values
             || (sResp != null && sResp.length > 0)
             || !IsTextType(oInputEle.type))
                continue;

            oInputEle.value = sLabel;
        }
    },

    SetResetLbl = function (element, isClear) {
        var sVal = element.value,
            sLbl = GetLblName(GetContainerDiv(element));

        if (!IsTextType(element.type)) return;

        if (!isClear &&
             (!sVal || sVal == "")) {
            element.value = sLbl;
        }
        else if (isClear && sVal == sLbl) {
            element.value = "";
        }
    },

    TruncateResp = function (formID) {
        var oTruncForm = {},
            oOutFields = {},
            oFlds = m_oTD[formID].fields,
            sResp = null;

        //Clone the form settings
        oTruncForm.id = m_oTD[formID].id;
        oTruncForm.qid = m_oTD[formID].qid;
        oTruncForm.lrid = m_oTD[formID].lrid;
        oTruncForm.s2sid = m_oTD[formID].s2sid;
        oTruncForm.dpt = m_oTD[formID].dpt;
        oTruncForm.vin = m_oTD[formID].vin;
        oTruncForm.redir = m_oTD[formID].redir;
        oTruncForm.valsumid = m_oTD[formID].valsumid;
        oTruncForm.sbmtid = m_oTD[formID].sbmtid;
        oTruncForm.authredir = m_oTD[formID].authredir;
        oTruncForm.errmsg = m_oTD[formID].errmsg;
        oTruncForm.noprettylabels = m_oTD[formID].noprettylabels;
        oTruncForm.isbb = m_oTD[formID].isbb;
        oTruncForm.bbdone = m_oTD[formID].bbdone;
        oTruncForm.flashht = m_oTD[formID].flashht;
        oTruncForm.flashwd = m_oTD[formID].flashwd;
        oTruncForm.swf = m_oTD[formID].swf;
        oTruncForm.swfid = m_oTD[formID].swfid;
        oTruncForm.png = m_oTD[formID].png;
        oTruncForm.offid = m_oTD[formID].offid;
        oTruncForm.emlid = m_oTD[formID].emlid;

        //Build the response field list
        for (iDx in oFlds) {
            sResp = oFlds[iDx].resp;
            if (!sResp || sResp.length == 0) continue;
            oOutFields[iDx] = { id: SafeFormObjID(oFlds[iDx]), resp: m_oUtils.EncodeString(sResp.replace(/"/g, "")) };
        }

        //Set it to the truncated object
        oTruncForm.fields = oOutFields;
        return oTruncForm;
    },

    Submit = function (formID, callBack) {
        //Default to what we sort of know the submission URL will be
        var sHref = "/forms/submit";

        //Supa Forms need to toggle the isbb flag based on whether or not an offer
        //  has been submitted
        try {
            if (m_oTD[formID].offid && m_oTD[formID].offid != "") {
                var sOff = GetSelValue(m_oYDOM.get(m_oTD[formID].offid));

                //For some reason, setting this directly doesn't work, so we use the if condition
                if (sOff && sOff != "") m_oTD[formID].isbb = true;
                else m_oTD[formID].isbb = false;
            }
        }
        catch (e) { m_oTD[formID].isbb = false; }

        //Try to use the action
        try {
            sHref = m_oYDOM.getAttribute(formID, "action");
        } catch (e) { }

        m_oConnect.Post(sHref, callBack, TruncateResp(formID), true);
    },

    LogError = function (formID, redirURL) {
        var sHref = "/forms/error";
        var oCallBack = { success: function (o) { return; }, failure: function (o) { return; } }
        m_oConnect.Post(sHref, oCallBack, TruncateResp(formID), true);

        //Redirect the user
        if (!redirURL || redirURL == "") redirURL = m_oTD[formID].redir;
        window.location = redirURL.replace("~", "");
    },

    AdWordsInit = function (frmID) {
        //Avoids tracking duplicate submisssions
        var iframe = m_oYDOM.get("ifAdwords");
        if (iframe) return;

        var id = m_oTD[frmID].adconid,
            lbl = m_oTD[frmID].adconlbl,
            val = m_oTD[frmID].adconval;

        //Convertion id and convertion label are required fields
        if (!id || id.length == 0 || !lbl || lbl.length == 0) return;

        if (!val || val.length == 0) val = 0; //Default the conv value to 0 if it is not passed

        iframe = m_oUtils.CreateIFrame("ifAdwords", null, "1px", "1px");
        document.body.appendChild(iframe);

        iframe = m_oYDOM.get("ifAdwords");
        m_oUtils.AddFrameCont(iframe, '<html><body><!-- Google Code for Test Conversion Page --><script type=\"text/javascript\">/* <![CDATA[ */var google_conversion_id =' + id + ';var google_conversion_language = \"en\";var google_conversion_format = \"2\";var google_conversion_color = \"ffffff\";var google_conversion_label = \"' + lbl + '\";var google_conversion_value =' + val + ';/* ]]> */<\/script><script type=\"text/javascript\" src=\"http://www.googleadservices.com/pagead/conversion.js\"><\/script><\/body><\/html>');
    },

    /*         
    *  Handles interaction with the forms contoller. See SimpleBBFormResp.cs
    * 
    *  @param {json} retData - return data from server AJAX call:
    *           id = form ID
    *           flashht = flash object height
    *           flashwd = flash object width
    *           swf = swf URL
    *           png = png URL
    *           swfid = ID of the swf object to embed in
    */
    InteractiveForm = function (retData, isCallback) {
        var sSwfId = retData.swfid;

        var divSwf = m_oYDOM.get(sSwfId),
            oFlshParams = { menu: "false", quality: "autohigh", wmode: "transparent", allowscriptaccess: "always" };
        if (!divSwf) return;

        var sFrmHtmlID = sSwfId.split("flsh")[0] + "f" + retData.id,
            oFlds = m_oTD[sFrmHtmlID].fields,
            sBBid = m_oTD[sFrmHtmlID].offid;

        //Disable all fields except bbOffer and launch adwords tracking if this is a callback
        if (isCallback) {
            //Initialize AdWords conversion tracking 
            AdWordsInit(sFrmHtmlID);

            for (iDx in oFlds) {
                if (iDx == sBBid && !retData.bbdone) continue; //Disable offer only when the offer is accepted.
                document.forms[sFrmHtmlID][iDx].disabled = true;
            }
        }

        //png default image
        try { divSwf.innerHTML = "<img src=\"" + retData.png + "\" alt=\"Congratulations\" \\>" }
        catch (e) { };

        //flash if it is supported
        swfobject.embedSWF(retData.swf, sSwfId, retData.flashwd, retData.flashht, "8.0.0", null, null, oFlshParams, null);
    },

    SetResetAllLbls = function (frmID, isClear) {
        var oFlds = m_oTD[frmID].fields;

        for (var id in oFlds) {
            SetResetLbl(document.forms[frmID][id], isClear);
        }
    },

    /****************** Event Handlers ******************/
    /*         
    *  this => document, use the formID passed in
    *  Event handler for form submit event
    * 
    *  @param {object} e        - event  
    *  @param {string} formID   - Html id of the form that initiated the submission   
    */
    hndlrSubmit = function (e, frmID) {
        m_oYEVNT.stopEvent(e);
        var NoPrettyLables = m_oTD[frmID].noprettylabels,
            IsValid = false;

        //Clear all lables in input fields
        if (!NoPrettyLables)
            SetResetAllLbls(frmID, true);

        //Clear validation summary, It will be rebuilt if there are any errors
        oValSum = m_oYDOM.get(m_oTD[frmID].valsumid)
        if (oValSum) oValSum.innerHTML = "";

        //Validate all fields
        IsValid = ValidateFields(frmID);
        if (IsValid)
            Submit(frmID, { success: CBSuccess, failure: CBFailure, argument: frmID });
        else if (!NoPrettyLables)
            SetResetAllLbls(frmID, false); //Set the lables back.

        //Scrolls the user to validation summary list when validation fails
        if (!IsValid) m_oYDOM.get("ul" + m_oTD[frmID].valsumid).scrollIntoView();
    },

    hndlrRedirect2Login = function (e, url) {
        var sRetUrl = window.location.href,
            oLocation = window.location;
        window.location = url + "?ReturnUrl=" + escape(sRetUrl.replace(oLocation.host, "").replace(oLocation.protocol, "").replace("//", ""));
    },

    /*         
    *  this => { frmID: formID, element: DomElement on which the event has fired }
    *  Event handler for click event on checkboxes and change event on other types.
    *      It validates the input value and updates UI with success or error messages.
    * 
    *  @param {object} e - event        
    */
    hndlrValidate = function (e) {
        YMVC.CleanToolTipMarkup();

        var NoPrettyLabels = m_oTD[this.frmID].noprettylabels;

        if (!NoPrettyLabels)
            SetResetLbl(this.element, true); //Clear Label     

        ValidateSingleElement(this.frmID, this.element, false);
        
        if (!NoPrettyLabels)
            SetResetLbl(this.element, false); //set Label
    },

    /*         
    *  this => { frmID: formID, element: DomElement on which the event has fired }
    *  Event handler for click event on checkboxes and change event on other types.
    *     Sets/Resets all conditional required fields.
    * 
    *  @param {object} e - event        
    */
    hndlrToggleReq = function (e) {
        SetCondFields(this.frmID, this.element, false);
    },

    /*         
    *  this => { frmID: formID, element: DomElement on which the event has fired }
    *  Event handler for click event on checkboxes and change event on other types.
    *      Sets/Resets all conditional unhide fields.
    * 
    *  @param {object} e - event        
    */
    hndlrToggleHide = function (e) {
        SetCondFields(this.frmID, this.element, true);
    },

    /*         
    * Event handler for the date selected event on calender. Enters the selected date in textbox, 
    *       validates the input and hides the calender
    *  
    *  @param string type         - event type   
    *  @param Array args          - Contains selected date   
    *  @param object calender     - Calender onject
    */
    hndlrCalDateSelected = function (type, args, calender) {
        //YUI appends '_t' at the end of calenderID
        var sDateTxtFldID = calender.id.replace("cal", "").slice(0, -2),
            oDateTxtFld = m_oYDOM.get(sDateTxtFldID),
            frmID = calender.cfg.initialConfig.frmid;

        oDateTxtFld.value = args[0][0][1] + "/" + args[0][0][2] + "/" + args[0][0][0];
        ValidateSingleElement(frmID, oDateTxtFld, false);
        SetResetLbl(oDateTxtFld, false);
        calender.hide();
    },

    /*         
    *  this => { frmID: formID, element: DomElement on which the event has fired }
    *  Event handler for focus event on textboxe and textarea elements.
    *      Displays tooltip.
    * 
    *  @param {object} e - event        
    */
    hndlrTxtFocus = function (e) {
        if (!YAHOO.env.ua.ie || YAHOO.env.ua.ie > 6) YMVC.DispToolTip(this.frmID, this.element);
        SetResetLbl(this.element, true); //Clear out the pretty lables from inputs that weren't used
    },

    /*
    *   this - oFormDef
    *  
    *   This function only really exists to prevent Chrome from allowing bad form submission on Enter Key
    */
    hndlrEnterKeyPress = function (type, args, obj) {
        m_oYEVNT.stopEvent(args[1]);
    },

    /*  Clears the saved/existing url
    *  
    *  @param {Object} event - Event that was fired.
    *  @param {string} formID - formID  
    */
    hndlrClear = function (event, formFldID) {
        var txtInput = m_oYDOM.get(formFldID),
            oConDiv = GetContainerDiv(txtInput);

        //Clears file path
        txtInput.value = "";

        //Clear all validation classes
        m_oYDOM.removeClass(oConDiv, "csvalid");

        return false;
    },

    /* 
    *  This function is triggered when the content of upload control is ready.
    *       Sets file type filters and disables selecting multiple files.
    *  
    *  @param {Object} event - Event that was fired.
    *  @param {Json Object} args - Collection of arguments sent when the event was fired.
    *                              Format - {uploader:"",fType:""}
    */
    hndlrUploadReady = function (event, args) {
        //Configure multi select
        this.uploader.setAllowMultipleFiles(this.isMultiUpload);

        // Apply new set of file filters to the uploader.
        this.uploader.setFileFilters(new Array(m_fType[this.fType]));
    },

    /*this => {uploader:"",frmID:""} 
    *
    *  This function is triggered when a file is selected to be uploaded. Displays
    *       status in progress and uploads the file
    *
    *  @param {Object} event - TEvent that was fired.    
    */
    hndlrUploadStart = function (event) {
        //Progress will be displayed only for single file uploads
        if (!this.isMultiUpload) {
            this.progressReport = m_oYDOM.get(this.fldID);
            this.progressReport.value = "Starting upload...";
        }

        //Multifile upload        
        if (event.fileList["file1"]) this.uploader.uploadAll(this.url, "POST");
        //Single file
        else this.uploader.upload("file0", this.url, "POST");
    },

    /* 
    *  This function is triggered when the file is uploading. It diaplays the percentage 
    *       of the file uploaded in the progress textbox.
    *  
    *  @param {Object} event - Event that was fired.    
    */
    hndlrUploadProgress = function (event) {
        iProg = Math.round(100 * (event["bytesLoaded"] / event["bytesTotal"]));
        this.progressReport.value = iProg + "% uploaded...";
    },


    /* 
    *  This function is triggered when the file is successfully uploaded.
    *  
    *  @param {Object} event - Event that was fired. 
    */
    hndlrUploadComplete = function (event) {
        //For some reason, this.progressReport can't be found in the DOM during this event
        m_oYDOM.get(this.fldID) = "Upload complete.";
    },

    /* 
    *  This function is triggered when the file upload is unsuccessful.
    *       Clears the existing value in progress textbox
    *  
    *  @param {Object} event - Event that was fired.   
    */
    hndlrUploadError = function (event) {
        var sFormID = this.frmID,
            sFormFldID = this.fldID,
            txtInput = m_oYDOM.get(sFormFldID);

        //Only format validation errors are shown dynamically.
        //Setting input value to "Upload Failed" will succeed required field validation
        // but will fail format validation 
        txtInput.value = "Upload Failed";
        ValidateSingleElement(sFormID, txtInput, false);

        m_oTD[sFormID].fields[sFormFldID].uierrmsg = "Error uploading the file";
        UpdateValSum(sFormID, sFormFldID, true);

        //Set the value to "" so that user cannot save
        txtInput.value = "";
    },

    /*****************************Callback functions ******************************/
    /* 
    *  This function is triggered after successful upload of file.
    *  
    *  @param {String} event - Event that was fired.
    *  @param {JSON} args - Collection of arguments sent when the event was fired.    
    *                       Format - {frmID: formID, fType: fileType, fldID: formFieldID}  
    */
    CBUploadResponse = function (event, args) {
        var sFormID = args.frmID,
            sFrmFldID = args.fldID,
            sFileType = args.fType,
            oRetInfo = YAHOO.lang.JSON.parse(event.data),
            IsMultiUpload = oRetInfo.isMultiUpload,
            sFileUrl = oRetInfo.url,
            sError = oRetInfo.error,
            txtInput = m_oYDOM.get(sFrmFldID);

        //Error
        if (sError && sError != "") {
            // In multi upload case we will not contain progress report
            if (!IsMultiUpload) {
                // We clear out the input field, so that nothing can be saved
                // To show the error we make the format validation fail by giving "Upload Failed" as value
                txtInput.value = "Upload Failed"; //Update progress field

                ValidateSingleElement(sFormID, txtInput, false);

                //Override JS error and update validation summary
                m_oTD[sFormID].fields[sFrmFldID].uierrmsg = sError;
                UpdateValSum(sFormID, sFrmFldID, true);

                //Reset the value to "" 
                txtInput.value = "";
            }
            return;
        }

        //In multiupload case, we fire an event with url infomation
        if (IsMultiUpload && m_oOnMultiFileUploaded) { m_oOnMultiFileUploaded.fire({ url: sFileUrl }); }

        //For some reason, this.progressReport can't be found in the DOM during this event
        //Update the file path
        txtInput.value = sFileUrl;
        ValidateSingleElement(sFormID, txtInput, false);
    },

    CBSuccess = function (o) {
        var oRetInfo = YAHOO.lang.JSON.parse(o.responseText);
        var frmID = o.argument;

        if (oRetInfo.srvervalid) {
            if (!m_oTD[frmID].isbb || oRetInfo.bbdone)
                window.location = oRetInfo.redir;  //confirmation page, usually
            else
                InteractiveForm(oRetInfo, true);  //Super Form or BB, usually
        }
        else {
            //Send Error Msg to server             
            m_oTD[frmID].errmsg = oRetInfo.errmsg;
            LogError(frmID, oRetInfo.redir);
        }
    },

    CBFailure = function (o) {
        //Send Error Msg to server 
        var frmID = o.argument,
            sStatusText = o.statusText;

        //If it is a communication failure or connection reset at peer, try to resubmit the response again. Maximum retries = 3
        if (m_iResubmitCnt < 3
           && sStatusText.toLowerCase().indexOf("communication failure") > -1) {
            m_iResubmitCnt = m_iResubmitCnt + 1; //To avoid infinite loop, we retry 3 times
            Submit(frmID, { success: CBSuccess, failure: CBFailure, argument: frmID });
        }
        else {
            m_iResubmitCnt = 0;
            m_oTD[frmID].errmsg = "Status: " + o.status + " StatusText: " + sStatusText;
            LogError(frmID);
        }
    };

    return {
        AddForm: function (htmlID, definition) {

            var oForm = m_oYDOM.get(htmlID);
            m_oTD[htmlID] = definition; //Add form definition to JSON

            //Prevent enter key from messing anything up
            var oKeyListener = new YAHOO.util.KeyListener(document, { keys: [13] }, { fn: hndlrEnterKeyPress, scope: oForm, correctScope: true });
            oKeyListener.enable();

            AddListeners(htmlID); //Add listeners

            if (!m_oTD[htmlID].noprettylabels)
                DisplayLabelsInInputFields(htmlID);

            //BB?
            if (m_oTD[htmlID].isbb && m_oTD[htmlID].swf && m_oTD[htmlID].swf != "") InteractiveForm(m_oTD[htmlID], false);
        },

        CleanToolTipMarkup: function () {
            var oToolTip = m_oYDOM.get("ToolTip");
            if (oToolTip) oToolTip.parentNode.removeChild(oToolTip);
        },

        DispToolTip: function (frmID, element, sLbl, sValidTxt) {
            //Extract the label (if it wasn't passed in)
            if (sLbl == null || sLbl.length == 0)
                sLbl = GetLblName(GetContainerDiv(element));
            if (sLbl == null || sLbl.length == 0) return;

            //Find out coords
            var AtTop = (m_oUtils.YFromViewPort(element) < 75 ? true : false);
            var iEleYOffset = m_oYDOM.getY(element), yCoord = AtTop ? iEleYOffset + 26 : iEleYOffset - 56;

            if (!sValidTxt) sValidTxt = GetValidTxt(frmID, element);
            var oToolTip = new YAHOO.widget.Tooltip("ToolTip", { context: element, text: GetTooltipHTML(sLbl, sValidTxt, AtTop), visible: true, width: "auto", y: yCoord, x: m_oYDOM.getX(element.id), hidedelay: 1000000, zIndex: 3000 });
            oToolTip.show();

            oToolTip.contextMouseOverEvent.subscribe(function (type, args) { return false; }); //Prevents default mouseover event
        },

        UploadInit: function (customerID, siteDefID, frmID, formFldID, uploaderID, lnkSelectID, lnkClearID, fileType, typeOfFileWebDir, isMultiUpload, sizeLimit) {
            var oSelLayer = m_oYDOM.getRegion(lnkSelectID),
                oOverlay = m_oYDOM.get(uploaderID),
                sUpUrl = "/files/upload/?customerID=" + customerID + "&fileType=" + fileType + "&typeOfFileWebDir=" + typeOfFileWebDir + "&sizeLimit=" + sizeLimit;
            if (!oOverlay || !oSelLayer) return;

            if (siteDefID) sUpUrl += "&siteDefID=" + siteDefID;
            if (isMultiUpload) sUpUrl += "&isMultiUpload=True";

            //Set size of clickable upload launching flash div area
            m_oYDOM.setStyle(oOverlay, "width", oSelLayer.right - oSelLayer.left + "px");
            m_oYDOM.setStyle(oOverlay, "height", oSelLayer.bottom - oSelLayer.top + "px");

            //Build actual YUI widget, register event handlers
            YAHOO.widget.Uploader.SWFURL = '/media/flash/uploader.swf';
            var oUploader = new YAHOO.widget.Uploader(uploaderID);
            oUploader.subscribe('contentReady', hndlrUploadReady, { uploader: oUploader, fType: fileType, isMultiUpload: isMultiUpload }, true);
            oUploader.subscribe('fileSelect', hndlrUploadStart, { uploader: oUploader, url: sUpUrl, fldID: formFldID, isMultiUpload: isMultiUpload }, true);
            oUploader.subscribe('uploadError', hndlrUploadError, { frmID: frmID, fldID: formFldID }, true);
            oUploader.subscribe('uploadCompleteData', CBUploadResponse, { frmID: frmID, fldID: formFldID, fType: fileType });
            m_oYEVNT.addListener(m_oYDOM.get(lnkClearID), "click", hndlrClear, formFldID); //Register clear event handler

            if (!isMultiUpload) return;

            oUploader.subscribe('uploadComplete', hndlrUploadComplete, { fldID: formFldID }, true);
            oUploader.subscribe('uploadProgress', hndlrUploadProgress);
        }
    }
} ();
var YMVC = YAHOO.MVC.Form;
