﻿// ADDED by CS 20090107, to have debugging capability by developer
window.debugmode = true;

if (typeof window.cs=="undefined")
    window.cs = {};

if (typeof window.cs.forms=="undefined")
    window.cs.forms = {};
    
if (typeof window.cs.forms.common=="undefined")
    window.cs.forms.common={};

cs.debugalert = function(str){
    if (window.debugmode!=true)
        alert("The system is unable to process your request at the moment. Please try again.");
    else
        alert(str);
};

cs.forms.common.getNextElementCount = function(){
    if (window.elementcounter==undefined)
        window.elementcounter = 0;
    window.elementcounter += 1;
    return ("_u" + window.elementcounter);
};

cs.forms.common.insertElementCache = function(strEleID, ele)
{
    if (window.elementcache==undefined)
        window.elementcache={};
    window.elementcache[strEleID] = ele;
};

cs.forms.common.lookElementByID = function(strEleID)
{
    if (window.elementcache==undefined)
        window.elementcache={};
    var obj = window.elementcache[strEleID];
    if (obj==undefined)
    {
        obj = document.getElementById(strEleID);
        cs.forms.common.insertElementCache(strEleID, obj);
    }
    return obj;
};

cs.forms.common.getElementValue = function(strEleID)
{
    var ele = document.getElementById(strEleID);
    return cs.forms.common.getElementValueByElement(ele);
};

cs.forms.common.getElementValueByElement = function(ele) {
    if (ele == undefined)
        return null;
    else {
        switch (ele.tagName.toUpperCase()) {
            case "INPUT":
                switch (ele.type.toLowerCase()) {
                    case "text":
                    case "hidden":
                        return ele.value;
                        break;

                    case "radio":
                        // check if the radio group is selected
                        var aRadioGroup = document.getElementsByName(ele.name);
                        for (var i = 0; i < aRadioGroup.length; i++) {
                            var itemRadioGroup = aRadioGroup[i];
                            if (itemRadioGroup.checked) {
                                return itemRadioGroup.value;
                            }
                        }
                        return null;
                        break;

                    case "checkbox":
                        // check if the checkbox is checked
                        if (ele.checked)
                            return ele.value;
                        else
                            return "";
                        break;

                }
                break;

            case "SELECT":
                // Do not cater for multiple selection
                //var aVar = new Array();
                for (var i = 0; i < ele.options.length; i++) {
                    var itemOption = ele.options[i];
                    if (itemOption.selected) {
                        //aVar.push(ele.options[i].value);
                        return itemOption.value;
                    }
                }
                //return aVar;
                return null;
                break;

            case "TEXTAREA":
                return ele.value;
                break;

            default:
                cs.debugalert("Unsupported TagName:" + ele.tagName + " [" + strEleID + "]");
                break;
        }
    }
    return null;
};

cs.forms.common.setElementValue = function(strEleID, value, bdisabled)
{
    var ele = document.getElementById(strEleID);
    return cs.forms.common.setElementValueByElement(ele, value, bdisabled);
};

cs.forms.common.setElementValueByElement = function(ele, value, bdisabled) {
    if (ele == undefined)
        return false;
    else {
        switch (ele.tagName.toUpperCase()) {
            case "A":
                ele.innerHTML = value;
                break;

            case "SPAN":
                ele.innerHTML = value;
                break;

            case "INPUT":
                switch (ele.type.toLowerCase()) {
                    case "text":
                    case "hidden":
                    case "button":
                        ele.value = value;
                        if (bdisabled != undefined)
                            ele.disabled = bdisabled;
                        return true;
                        break;

                    case "checkbox":
                        if (bdisabled != undefined)
                            ele.disabled = bdisabled;
                        if (ele.value == value)
                            ele.checked = true;
                        ele.value = value;
                        return true;
                        break;

                    case "radio":
                        if (ele.value == value)
                            ele.checked = true;
                        else {
                            // Note, the radio button might be yet to added into the form, thus this will fail.
                            cs.forms.common.setRadioButtonByGroupName(ele.name, value);
                        }
                        return true;
                        break;
                }
                break;

            case "SELECT":
                for (var i = 0; i < ele.options.length; i++) {
                    var itemOption = ele.options[i];
                    if (itemOption.value == value) {
                        itemOption.selected = true;
                        return true;
                    }
                }
                // Added by CS on 20090528, if there is nothing in the select
                if (ele.options.length > 0)
                    ele.options[0].selected = true;
                break;

            case "TEXTAREA":
                ele.value = value;
                break;
        }
    }
    return false;
};

cs.forms.common.isValueEmpty = function(strEleID)
{
    var ele = document.getElementById(strEleID);
    if (ele == undefined)
    {
        //cs.debugalert(strEleID + " element not found.");
        return true;
    }
    else
    {
        return cs.string.isEmptyString(cs.forms.common.getElementValue(strEleID));
    }
};

cs.forms.common.isElementExist = function(strEleID)
{
    var ele = document.getElementById(strEleID);
    if (ele == undefined)
        return false;
    else
        return true;
};

cs.forms.common.isRadioButtonSelected = function(strRadioGroupName)
{
    var aEle = document.getElementsByName(strRadioGroupName);
    if (aEle.length == 0)
    {
        return false;
    }
    else
    {
        for (var i=0; i<aEle.length; i++)
        {
            if (aEle[i].checked == true)
                return true;
        }
        return false;
    }
};

cs.forms.common.setRadioButtonByGroupName = function(strRadioGroupName, value)
{
    var aEle = document.getElementsByName(strRadioGroupName);
    for (var i=0; i<aEle.length; i++)
    {
        var itemEle = aEle[i];
        if (itemEle.value == value)
        {
            itemEle.checked = true;
        }
        else
        {
            itemEle.checked = false;
        }
    }
};

cs.forms.common.validateForm = function(aFormConfig)
{
    //aEmptyChecker.push({elementid:"txtphoneno", checktype:"compulsory", errormessage:"Plase enter your phone no"});
    var bFormValid = true;
    var aErrorMessage = new Array();
    
    // Loop thru each of the form config entry
    for (var i=0; i<aFormConfig.length; i++)
    {
        var bEntryValid = true;
        var itemFormConfig = aFormConfig[i];
        
        switch (aFormConfig[i].checktype)
        {
            case "compulsory":
                if (cs.forms.common.isValueEmpty(itemFormConfig.elementid))
                {
                    bEntryValid = false;
                    aErrorMessage.push(itemFormConfig.errormessage);
                }
                break;
                
            case "none":
                break;
                
            default:
                cs.debugalert("unknown checktype:" + itemFormConfig.checktype);
        }
        
        // Check only if there is data in there
        if (bEntryValid)
        {
            // Check for datatype
            switch (aFormConfig[i].datatype)
            {
                case "email":
                    if (!cs.string.isPatternMatch(document.getElementById(aFormConfig[i].elementid).value, "email"))
                    {
                        bEntryValid = false;
                        aErrorMessage.push(aFormConfig[i].errormessage);
                    }
                    break;
            }        
        }
        
        bFormValid &= bEntryValid;
    }
    
    return {valid:bFormValid, listoferrormessage:aErrorMessage};
};

cs.forms.common.collectFormData = function(aFormConfig)
{
    // aEmptyChecker.push({elementid:"txtphoneno", fieldid:"phoneno", checktype:"compulsory", errormessage:"Plase enter your phone no"});
    var dt = {};
    
    for (var i=0; i<aFormConfig.length; i++)
    {
        var itemFormConfig = aFormConfig[i];
        var ele = document.getElementById(itemFormConfig.elementid);
        
        // Check the element type
        dt[itemFormConfig.fieldid] = cs.forms.common.getElementValueByElement(ele);
    }
    
    return dt;
};

cs.forms.common.bindFormData = function(aFormConfig, dt)
{
    cs.debugalert("function bindFormData obsolete");
};

// Experimental framework by CS
cs.formconfig = function()
{
};

cs.selectionformconfig = function(opt)
{
    var plist = ["elementid", "labelid", "fieldid",
            "skipduplicatecheck",
            "checktype", "datatype",
            "errormessage",
            "templateid", "holderid",
            "value",
            "listofinstancename", "fieldmapper"];
    cs.utils.copyParam({source:opt, target:this, list:plist});
        
    this._listproperties = new Array();
    this._properties = new Array();
};

cs.selectionformconfig.prototype.addListProperty = function(opt)
{
    // Allow properties only if the datatype is multirow or group
    switch (this.datatype)
    {
        case "group":
            break;
            
        default:
            cs.debugalert("Fail to add listproperty: " + opt.elementid + " to non-group selectionformconfig");
            return false;
            break;
    }
    
    switch (opt.datatype)
    {
        case "multirow":
            // Check for duplicates
            for (var iprop=0; iprop<this._properties.length; iprop++)
            {
                if (this._properties[iprop].fieldid==opt.fieldid)
                {
                    cs.debugalert("Fail to add duplicate list property: " + opt.elementid + "(" + opt.fieldid + ") of " + opt.datatype + " to selectionformconfig");
                    return false;
                }
            }
            this._listproperties.push(opt);
            break;
            
        default:
            cs.debugalert("Fail to add non-multirow listproperty: " + opt.elementid + " to selectionformconfig");
            break;
    }
    
    return true;
};

cs.selectionformconfig.prototype.clearListProperties = function()
{
    this._listproperties.length = 0;
};

cs.selectionformconfig.prototype.removePropertyByFieldID = function(strfieldid)
{
    for (var i=0; i<this._properties.length; i++)
    {
        if (this._properties[i].fieldid==strfieldid)
        {
            this._properties.splice(i, 1);
            return true;
        }
    }
    return false;
};

cs.selectionformconfig.prototype.addProperty = function(opt)
{
    // Allow properties only if the datatype is multirow or group
    switch (this.datatype)
    {
        case "list":
            // must ensure that the adding item have the same fieldid as mine
            if (this.fieldid!=opt.fieldid)
            {
                cs.debugalert("Fail to add list item of different fieldid: " + opt.elementid + "(" + opt.fieldid + ") of " + opt.datatype + " to selectionformconfig");
                return false;
            }
            break;
            
        case "group":
            break;
            
        default:
            // all others should not be allowed to add properties
            cs.debugalert("Fail to add property to " + opt.elementid + "(" + opt.fieldid + ") of " + opt.datatype + " to non-group/list selectionformconfig");
            return false;
            break;
    }
    
    switch (opt.datatype)
    {
        case "string":
        case "boolean":
        case "date":
        case "email":
        case "static":
        case "group":
        case "list":
            switch (this.datatype)
            {
                case "list":
                    this._properties.push(opt);
                    break;
                    
                default:
                    // Check for duplicates
                    for (var iprop=0; iprop<this._properties.length; iprop++)
                    {
                        if (this._properties[iprop].fieldid==opt.fieldid)
                        {
                            cs.debugalert("Fail to add duplicate property: " + opt.elementid + "(" + opt.fieldid + ") of " + opt.datatype + " to selectionformconfig");
                            return false;
                        }
                    }
                    this._properties.push(opt);
                    break;
            }
            break;
            
        default:
            cs.debugalert("Fail to add property: " + opt.elementid + "(" + opt.fieldid + ") of " + opt.datatype + " to selectionformconfig");
            break;
    }
    
    return true;
    
};

cs.selectionformconfig.prototype.clearProperties = function()
{
    this._properties.length = 0;
};

cs.selectionformconfig.prototype.diagnose = function()
{
    // This function is meant to diagnose the selectionformconfig
    for (var i=0; i<this._properties.length; i++)
    {
        switch (this._properties[i].datatype)
        {
            case "static":
                // no need to check static
                break;
                
            case "group":
                // no need to check group
                break;
            
            default:
                // Check if the elementid is referring to a valid element in the document
                var ele = document.getElementById(this._properties[i].elementid);
                if (ele==undefined)
                    cs.debugalert("Missing element id in form config: " + this._properties[i].elementid);
                break;
        }
    }
    
    for (var i=0; i<this._listproperties.length; i++)
    {
        switch (this._listproperties[i].datatype)
        {
            case "multirow":
                var mlist = window.listofinstance[this._listproperties[i].listofinstancename];
                if (mlist==undefined)
                    cs.debugalert("Missing listofinstance in form config: " + this._listproperties[i].listofinstancename + "\nelementid:" + this._listproperties[i].elementid);
                break;
        }
    }
};

cs.selectionformconfig.prototype.validateForm = function()
{
    var bFormValid = true;
    var aErrorMessage = new Array();
    
    // Loop thru each of the form config entry
    for (var i=0; i<this._properties.length; i++)
    {
        var bEntryValid = true;
        
        // Get the value of the property
        var strPropertyValue = "";
        switch (this._properties[i].datatype)
        {
            case "static":
                strPropertyValue = this._properties[i].value;
                break;
                
            case "group":
                break;
                
            default:
                strPropertyValue = cs.forms.common.getElementValue(this._properties[i].elementid);
                break;
        }
        
        // Check according to the checktype   
        switch (this._properties[i].checktype)
        {
            case "compulsory":
                if (strPropertyValue==undefined || strPropertyValue.length==0)
                {
                    bEntryValid = false;
                    aErrorMessage.push(this._properties[i].errormessage);
                }
                break;
                
            case "none":
                break;
                
            default:
                cs.debugalert("unknown checktype:" + this._properties[i].checktype);
        }
        
        // Check only if there is valid input
        if (bEntryValid)
        {
            // Check only if there is input in the value
            if (strPropertyValue!=undefined && strPropertyValue.length>0)
            {
                switch (this._properties[i].datatype)
                {
                    case "email":
                        if (!cs.string.isPatternMatch(strPropertyValue, "email"))
                        {
                            bEntryValid = false;
                            aErrorMessage.push(this._properties[i].errormessage);
                        }
                        break;
                }   
            }
        }

        if (!bEntryValid)
        {
            // Make the label red if it is available
            if (this._properties[i].labelid!=undefined)
            {
                var eleLabel = document.getElementById(this._properties[i].labelid);
                if (eleLabel!=undefined)
                {
                    eleLabel.style.color = "red";
                }
            }
        }
        else
        {
            // Make the label red if it is available
            if (this._properties[i].labelid!=undefined)
            {
                var eleLabel = document.getElementById(this._properties[i].labelid);
                if (eleLabel!=undefined)
                {
                    eleLabel.style.color = "black";
                }
            }
        }
        
        bFormValid &= bEntryValid;
    }
    
    return {valid:bFormValid, listoferrormessage:aErrorMessage};
};

cs.selectionformconfig.prototype.checkChildrenDuplicate = function()
{
    // I can only validate the child for duplicates
    
    var aErrorMessage = new Array();
    var bValid = true;
    
    // Check the properties
    for (var iProp=0; iProp<this._properties.length; iProp++)
    {
        var checkResultProp = this._properties[iProp].checkChildrenDuplicate();
        bValid &= checkResultProp.valid;
    }
    
    // Check the list properties
    if (this._listproperties!=undefined)
    {
        var listofChildElementValue = [];
        
        for (var iChild=0; iChild<this._listproperties.length; iChild++)
        {
            if (this._listproperties[iChild].elementid!=undefined &&
                this._listproperties[iChild].skipduplicatecheck!=true)
            {
                var strValue = cs.forms.common.getElementValue(this._listproperties[iChild].elementid);
                listofChildElementValue.push({elementid:this._listproperties[iChild].elementid,
                                            value:strValue, duplicate:false});
            }
            else
            {
                var checkResult = this._listproperties[iChild].checkChildrenDuplicate();
                bValid &= checkResult.valid;
                aErrorMessage = aErrorMessage.concat(checkResult.listoferrormessage);
            }
        }
        
        if (listofChildElementValue.length>0)
        {
            var intFirstDuplicateChildIndex=-1;

            for (var iCheckChild=0; iCheckChild<listofChildElementValue.length-1; iCheckChild++)
            {
                var strCheckValue = listofChildElementValue[iCheckChild].value;
                for (var iSubCheckChild=iCheckChild+1; iSubCheckChild<listofChildElementValue.length; iSubCheckChild++)
                {
                    if (!listofChildElementValue[iSubCheckChild].duplicate)
                    {
                        if (listofChildElementValue[iSubCheckChild].value==strCheckValue)
                        {
                            listofChildElementValue[iSubCheckChild].duplicate = true;
                            listofChildElementValue[iCheckChild].duplicate = true;
                            
                            if (intFirstDuplicateChildIndex<0)
                                intFirstDuplicateChildIndex = iCheckChild;
                                
                            bValid = false;
                        }
                    }
                }
            }
            
            for (var iCheckChild=listofChildElementValue.length-1; iCheckChild>=0; iCheckChild--)
            {
                if (listofChildElementValue[iCheckChild].duplicate)
                {
                    // Highlight them
                    try
                    {
                    document.getElementById(listofChildElementValue[iCheckChild].elementid).style.backgroundColor = "red";
                    }
                    catch(e)
                    {
                        cs.debugalert("error in setting style for :" + listofChildElementValue[iCheckChild].elementid);
                    }
                }
                else
                {
                    // Undo Highlight
                    document.getElementById(listofChildElementValue[iCheckChild].elementid).style.backgroundColor = "";
                }
            }
            
//            if (intFirstDuplicateChildIndex>=0)
//                document.getElementById(listofChildElementValue[intFirstDuplicateChildIndex].elementid).focus();
        }
    }
    
    return {valid:bValid, listoferrormessage:aErrorMessage};
};

cs.selectionformconfig.prototype.getValue = function(){
    
    var res = {};
    
    switch (this.datatype)
    {
        
        case "multirow":
            var opt = {listofinstancename:this.elementid};
            if (this.fieldmapper!=undefined)
                opt.fieldmapper = this.fieldmapper;
            res = cs.utils.collectInstanceData(opt);
            break;
        
        case "group":
            // Add the properties to the result object
            for (var iProperty=0; iProperty<this._properties.length; iProperty++)
            {
                res[this._properties[iProperty].fieldid] = this._properties[iProperty].getValue();
            }
            break;
        
        case "list":
            // Add the properties to the result array object
            res = [];
            for (var iProperty=0; iProperty<this._properties.length; iProperty++)
            {
                res[iProperty] = this._properties[iProperty].getValue();
            }
            break;
        
        case "static":
            res = this.value;
            break;
            
        case "string":
        case "date":
        case "boolean":
        case "email":
            res = cs.forms.common.getElementValue(this.elementid);
            break;
            
        default:
            cs.debugalert("unable to collect unknown selectionformconfig datatype:" + this.datatype + ".");
            
    }
    
    
    // Add the list properties to the result object
    for (var iPropList=0; iPropList<this._listproperties.length; iPropList++)
    {
        var strFieldID = this._listproperties[iPropList].fieldid;
        res[strFieldID] = this._listproperties[iPropList].getValue();
    }
    
    return res;
};

cs.selectionformconfig.prototype.bindFormData = function(dt, bAtLeastOne)
{
    if (bAtLeastOne==undefined)
        bAtLeastOne = true;
        
    for (var i=0; i<this._properties.length; i++)
    {
        var cprop = this._properties[i];
        
        switch (cprop.datatype)
        {
                
            default:
                try
                {
                    if (dt[cprop.fieldid]!=undefined)
                        cs.forms.common.setElementValue(cprop.elementid, dt[cprop.fieldid]);
                }
                catch (e)
                {
                    cs.debugalert("error at:" + cprop.elementid);
                }
                break;
        }
        

    }
    
    for (var i=0; i<this._listproperties.length; i++)
    {
        var cprop = this._listproperties[i];
        
        switch (cprop.datatype)
        {
            case "multirow":
                // clear all the instances
                cs.utils.clearInstances({listofinstancename:cprop.elementid});
                // we are expecting to bind the data of Array type
                var aRowData = dt[cprop.fieldid];

                // we'll ignore it if the data is not provided for binding
                if (aRowData!=undefined)
                {
                    aRowData = cs.common.Array.createArrayFromEntry(aRowData);
                    for (var iRow=0; iRow<aRowData.length; iRow++)
                    {
                        cs.utils.addInstance({templateid:cprop.templateid, holderid:cprop.holderid, listofinstancename:cprop.elementid, data:aRowData[iRow]});
                    }
                }
                
                // Ensure that there is at least one row
                if (cs.utils.getInstanceCount({listofinstancename:cprop.elementid})==0)
                {
                    cs.utils.addInstance({templateid:cprop.templateid, holderid:cprop.holderid, listofinstancename:cprop.elementid});
                }
                
                break;
                
            default:
                cs.debugalert("Error in binding for ListProperty:" + cprop.elementid);
                break;
        }
        

    }
};

cs.utils = {};

// General utils that is used by most functions
cs.utils.checkParam = function(opt){
    
    var result = {};
    result.message = [];
    result.valid = true;
    
    if (opt.param == undefined)
    {
        result.valid = false;
        result.message.push("Error in passing parameter opt.param to cs.utils.checkParam for function " + opt.functionname);
        return result;
    }
    
    if (opt.checklist == undefined)
    {
        result.valid = false;
        result.message.push("Error in passing parameter opt.checklist to cs.utils.checkParam for function " + opt.functionname);
        return result;
    }
        
    for (var i=0; i<opt.checklist.length; i++)
    {
        if (opt.param[opt.checklist[i]]==undefined)
        {
            result.valid = false;
            result.message.push("Missing parameter:" + opt.checklist[i]);
        }
    }
    
    return result;
};

cs.utils.copyParam = function(opt){ 
    var cparam = cs.utils.checkParam({param:opt, checklist:["source","target","list"], functionname:"cs.utils.copyParam"});
    if (cparam.valid)
    {
        for (var i=0; i<opt.list.length; i++)
        {
            var strname = opt.list[i];
            opt.target[strname] = opt.source[strname];
        }
        return true;
    }
    else
    {
        cs.debugalert("cs.utils.copyParam received invalid parameters." + cparam.message.join("\n"));
        return false;
    }
};

cs.utils.collectInstanceData = function(opt) {

    var dt = [];
    var aListOfInstance = window.listofinstance[opt.listofinstancename];
    for (var iCfg = 0; iCfg < aListOfInstance.length; iCfg++) {
        var data = {};
        for (strField in aListOfInstance[iCfg]) {
            if (strField.charAt(0) != "_") {
                var strMappedField = strField;
                if (opt.fieldmapper != undefined) {
                    if (opt.fieldmapper[strField] != undefined)
                        strMappedField = opt.fieldmapper[strField];
                }
//                if (strField == "rbghodedit_institutiondepartment_ispd") {
//                    alert(aListOfInstance[iCfg][strField] + " -> " + strMappedField);
//                    alert(cs.Debug.inspect(opt.fieldmapper).join(""));
//                }
                data[strMappedField] = cs.forms.common.getElementValue(aListOfInstance[iCfg][strField]);
            }
        }
        dt.push(data);
    }
    return dt;
};

cs.utils.getInstanceCount = function(opt){
    var plist = ["listofinstancename"];
    // "referenceinstance" is not compulsory
    var cparam = cs.utils.checkParam({param:opt, checklist:plist, functionname:"cs.utils.getInstanceCount"});
    
    if (cparam.valid)
    {
        return window.listofinstance[opt.listofinstancename].length;
    }
    else
    {
        cs.debugalert("Missing parameter to getInstanceCount: " + cparam.message.join("\n"));
        return 0;
    }
};

cs.utils.bindInstance = function(opt){
    
    var instanceBind = {};
    instanceBind.name = opt.listofinstancename + " [DOM Clear Instance]";
    instanceBind.datalength = 0;
    instanceBind.starttime = new Date();
    
    cs.utils.clearInstances(opt);

    instanceBind.endtime = new Date();
    cs.utils.addPerformanceTrace(instanceBind);
    
    var optTmp = opt;
    var oldData = opt.data;
    
    if (oldData.length > 0)
    {

        instanceBind = {};
        instanceBind.name = opt.listofinstancename + " [DOM Bind Instance]";
        instanceBind.starttime = new Date();

        instanceBind.datalength = oldData.length;
        for (var i=0; i<oldData.length; i++)
        {

            optTmp.data = oldData[i];
            cs.utils.addInstance(optTmp);

        }

        instanceBind.endtime = new Date();
        cs.utils.addPerformanceTrace(instanceBind);
        
    }
    else
    {
        if (opt.atleastonerow==undefined || opt.atleastonerow==true)
        {
            optTmp.data = undefined;
            cs.utils.addInstance(optTmp);
        }
    }
    opt.data = oldData;
    
    
};

cs.utils.clearInstances = function(opt){
    /* required
      - listofinstancename
    */
    if (window.listofinstance==undefined)
        window.listofinstance = {};
    if (window.listofinstance[opt.listofinstancename]==undefined)
        window.listofinstance[opt.listofinstancename] = new Array();
        
    var aListOfInstance = window.listofinstance[opt.listofinstancename];
    //cs.debugalert("No Of Instances to remove:" + aListOfInstance.length);
    for (var i=aListOfInstance.length-1; i>=0; i--)
    {
        //cs.debugalert("removing instance:" + i);
        cs.utils.deleteInstance({holderid:aListOfInstance[i].__holderid, referenceinstance:aListOfInstance[i], listofinstancename:opt.listofinstancename, forcedeleteall:true});
    }
    
};

cs.utils.deleteInstance = function(opt){
    
    // First my self in the array
    var eleHolder = document.getElementById(opt.holderid);
    var cfgRefInstance = opt.referenceinstance;    
    var aListOfInstance = window.listofinstance[opt.listofinstancename];
    
    if (aListOfInstance.length == 1)
    {
        if (opt.forcedeleteall==true)
        {
            // continue to delete
        }
        else
            return false;
    }
    
    for (var iCheckCfg=0; iCheckCfg<aListOfInstance.length; iCheckCfg++)
    {
        if (aListOfInstance[iCheckCfg]==cfgRefInstance)
        {
            break;
        }
    }
    
    // Remove from instance list
    aListOfInstance.splice(iCheckCfg, 1);
    window.listofinstance[opt.listofinstancename] = aListOfInstance;
    
    // Check if the eleHolder is TABLE
    if (eleHolder.tagName=="TABLE")
        eleHolder = eleHolder.tBodies[0];
    
    //cs.debugalert("cfgRefInstance.__startmarker:" + cfgRefInstance.__startmarker);
    // Remove from the HTML Element Tree
    var bRemove = false;
    for (var iChild=0; iChild < eleHolder.childNodes.length; iChild++)
    {
        var eleChild = eleHolder.childNodes[iChild];
            
        if (eleChild.id == cfgRefInstance.__startmarker)
            bRemove = true;
            
        if (eleChild.id == cfgRefInstance.__endmarker)
        {
            eleHolder.removeChild(eleChild);
            iChild -= 1;
            bRemove = false;
            break;
        }
        
        if (bRemove)
        {
            eleHolder.removeChild(eleChild);
            iChild -= 1;
        }
    }
    return true;
}

cs.utils.getElementArray = function(ele){
    
    var aCloneTree = new Array();
    
    if (ele!=undefined && ele.nodeType==1)
    {
        if (ele.id!="" || ele.name!="")
            aCloneTree.push(ele);
        
        if (ele.hasChildNodes())
        {
            for (var iChild=0; iChild < ele.childNodes.length; iChild++)
            {
                var eleChild = ele.childNodes[iChild];
                if (eleChild.nodeType==1)
                {
                    var aTMP = cs.utils.getElementArray(eleChild);
                    aCloneTree = aCloneTree.concat(aTMP);
                }
            }
        }
    }
    
    return aCloneTree;
};

cs.utils.addInstance = function(opt){

    var plist = ["templateid", "holderid", "listofinstancename"];
    // "referenceinstance" is not compulsory
    var cparam = cs.utils.checkParam({param:opt, checklist:plist, functionname:"cs.utils.addInstance"});
    if (cparam.valid)
    {
        if (window.listofinstance==undefined)
            window.listofinstance = {};
        if (window.listofinstance[opt.listofinstancename]==undefined)
            window.listofinstance[opt.listofinstancename] = new Array();
            
        var eleTemplate = document.getElementById(opt.templateid);
        var eleHolder = document.getElementById(opt.holderid);
        var cfgRefInstance = opt.referenceinstance;
        var aListOfInstance = window.listofinstance[opt.listofinstancename];
        
        // Some error checking
        if (eleTemplate==undefined)
            cs.debugalert("Missing eleTemplate for addInstance with id: " + opt.templateid);
            
        // Create a new instance to track the new template
	    var cfgNewInstance = new Object();
	    cfgNewInstance.__startmarker = "smark" + cs.forms.common.getNextElementCount();
	    cfgNewInstance.__endmarker = "emark" + cs.forms.common.getNextElementCount();
    	cfgNewInstance.__holderid = opt.holderid;
    	
	    // Duplicate template into a element instance
	    var eleNewInstance = document.createElement(eleTemplate.tagName);
	    
	    // NEW Version
	    //eleNewInstance.innerHTML = eleTemplate.innerHTML;
	    
	    /*
	    eleNewInstance = eleTemplate.cloneNode(true);
	    eleNewInstance.id = "";
	    for (var strName in eleNewInstance.childNodes)
	    {
	        var ele = eleNewInstance.childNodes[strName];
	        switch(ele.nodeType)
	        {
	            case 1:
	                cs.utils.processInstance({element:ele, suffix:cs.forms.common.getNextElementCount(), templateid:opt.templateid, holderid:opt.holderid, instance:cfgNewInstance, listofinstancename:opt.listofinstancename, data:opt.data, fieldmapper:opt.fieldmapper});
	                break;
	        }
	    }
	    eleNewInstance.style.display = "";
	    */



        // CS Clone Node VERSION 1
/*        

        var eleCSClone = eleTemplate.cloneNode(true);
        var aCloneTree = cs.utils.getElementArray(eleCSClone);

	    // process the instance
	    for (var iCT=0; iCT<aCloneTree.length; iCT++)
	    {
	        cs.utils.processInstance({element:aCloneTree[iCT], suffix:cs.forms.common.getNextElementCount(),
			            templateid:opt.templateid,
			            holderid:opt.holderid,
			            instance:cfgNewInstance,
			            listofinstancename:opt.listofinstancename,
			            data:opt.data,
			            fieldmapper:opt.fieldmapper,
			            stylemapper:opt.stylemapper});
	    }

	    // iterate and add to the template
	    while (eleCSClone.childNodes.length>0)
	    {
            var itemTemp = eleCSClone.childNodes[0];
            eleNewInstance.appendChild(itemTemp);
	    }
*/	    
	    
	    // OLD VERSION

/*
            instanceItemBind = {};
            instanceItemBind.name = opt.listofinstancename + " [DOM cloneNode]";
            instanceItemBind.datalength = 0;
            instanceItemBind.starttime = new Date();
				            
        var aTemp = new Array();
	    for (var strName in eleTemplate.childNodes)
	    {
		    var eleTptChild = eleTemplate.childNodes[strName];
		    switch(eleTptChild.nodeType)
		    {
			    case 1:
				    var ele = eleTptChild.cloneNode(true);
				    aTemp.push(ele);
				    break;
				    
			    case 3:
			        if (eleTptChild.nodeValue!="" && eleTptChild.nodeValue!="\n")
				        aTemp.push(document.createTextNode(eleTptChild.nodeValue));	
				    break;
				    
			}
        }
            instanceItemBind.endtime = new Date();
            cs.utils.addPerformanceTrace(instanceItemBind);

            instanceItemBind = {};
            instanceItemBind.name = opt.listofinstancename + " [DOM processInstance]";
            instanceItemBind.datalength = 0;
            instanceItemBind.starttime = new Date();

        for (var intTemp=0; intTemp<aTemp.length; intTemp++)
        {
            var itemTemp = aTemp[intTemp];
            if (itemTemp.nodeType==1)
            {
			    cs.utils.processInstance({element:itemTemp, suffix:cs.forms.common.getNextElementCount(),
			            templateid:opt.templateid,
			            holderid:opt.holderid,
			            instance:cfgNewInstance,
			            listofinstancename:opt.listofinstancename,
			            data:opt.data,
			            fieldmapper:opt.fieldmapper,
			            stylemapper:opt.stylemapper});
            }
            else if (itemTemp.nodeType==2)
            {
            }
        }

            instanceItemBind.endtime = new Date();
            cs.utils.addPerformanceTrace(instanceItemBind);

            instanceItemBind = {};
            instanceItemBind.name = opt.listofinstancename + " [DOM AppendChild]";
            instanceItemBind.datalength = 0;
            instanceItemBind.starttime = new Date();

        for (var intTemp=0; intTemp<aTemp.length; intTemp++)
        {
            var itemTemp = aTemp[intTemp];
            if (itemTemp.nodeType==1)
            {
                eleNewInstance.appendChild(itemTemp);
            }
            else if (itemTemp.nodeType==2)
            {
                eleNewInstance.appendChild(itemTemp);
            }
        }

            instanceItemBind.endtime = new Date();
            cs.utils.addPerformanceTrace(instanceItemBind);
*/

	    for (var strName in eleTemplate.childNodes)
	    {
		    var eleTptChild = eleTemplate.childNodes[strName];
		    switch(eleTptChild.nodeType)
		    {
			    case 1:
				    var ele = eleTptChild.cloneNode(true);
				    cs.utils.processInstance({element:ele, suffix:cs.forms.common.getNextElementCount(),
				            templateid:opt.templateid,
				            holderid:opt.holderid,
				            instance:cfgNewInstance,
				            listofinstancename:opt.listofinstancename,
				            data:opt.data,
				            fieldmapper:opt.fieldmapper,
				            stylemapper:opt.stylemapper});
				            
				    eleNewInstance.appendChild(ele);
				    break;

			    case 3:
			        if (eleTptChild.nodeValue!="" && eleTptChild.nodeValue!="\n")
				        eleNewInstance.appendChild(document.createTextNode(eleTptChild.nodeValue));	
				    break;

		    }
	    }


    	
	    // Need to add market first
	    var eleStartMarker;
	    var eleEndMarker;
    	
	    switch (eleHolder.tagName)
	    {
	        case "TABLE":
	            eleStartMarker = document.createElement("TR");
	            eleEndMarker = document.createElement("TR");
	            break;
    	        
	        default:
	            eleStartMarker = document.createElement("A");
	            eleEndMarker = document.createElement("A");
	            break;
	    }
    	
	    eleStartMarker.id = cfgNewInstance.__startmarker;
	    eleEndMarker.id = cfgNewInstance.__endmarker;
    	
	    // Determine the position to insert into
    	var eleHolderActual = eleHolder;
    	
	    if (eleHolder.tagName == "TABLE")
	    {
		    if (eleHolder.tBodies.length==0)
			    eleHolder.appendChild(document.createElement("tbody"));
    	    eleHolderActual = eleHolder.tBodies[0];
	    }
	    
        // Have to insert before the starting marker of the next instance after reference instance
	    
	    // Algorithm changed by CS on 20081222, to find element from the tail
	    
        // Step 1: find the next child after the reference
        for (var iFindNextCfg=aListOfInstance.length-1; iFindNextCfg>=0; iFindNextCfg--)
        {
            if (aListOfInstance[iFindNextCfg]==cfgRefInstance)
            {
                break;
            }
        }

        if (iFindNextCfg < 0 || iFindNextCfg >= aListOfInstance.length - 1)
        {
            eleHolderActual.appendChild(eleStartMarker);
            eleHolderActual.appendChild(eleNewInstance);
            eleHolderActual.appendChild(eleEndMarker);
        }
        else
        {
            var cfgNextChildInstance = aListOfInstance[iFindNextCfg + 1];
            var eleRefNextChildStartMarker = document.getElementById(cfgNextChildInstance.__startmarker);
            eleHolderActual.insertBefore(eleStartMarker, eleRefNextChildStartMarker);
            eleHolderActual.insertBefore(eleNewInstance, eleRefNextChildStartMarker);
            eleHolderActual.insertBefore(eleEndMarker, eleRefNextChildStartMarker);
        }
    	
	    // Put cfgInstance after the reference instance in the list
	    for (var iFindCfg=0; iFindCfg<aListOfInstance.length; iFindCfg++)
	    {
	        if (aListOfInstance[iFindCfg] == cfgRefInstance)
	        {
	            break;
	        }
	    }
	    if (iFindCfg == aListOfInstance.length)
	    {
	        if (cfgRefInstance!=null)
	            cs.debugalert("Error!!");
	    }
	    if (iFindCfg == aListOfInstance.length - 1)
	    {
	        aListOfInstance.push(cfgNewInstance);
	    }
	    else
	    {
	        var aTempInstance = aListOfInstance.splice(iFindCfg + 1, aListOfInstance.length - (iFindCfg+1));
	        aListOfInstance.push(cfgNewInstance);
	        aListOfInstance = aListOfInstance.concat(aTempInstance);
	    }
    	
	    window.listofinstance[opt.listofinstancename] = aListOfInstance;
    }
    else
    {
        cs.debugalert("missing param for cs.utils.addInstance:" + cparam.message.join("\n"));
    }

};

cs.utils.processInstance = function(opt) {

    var plist = ["element", "suffix", "instance", "templateid", "holderid", "listofinstancename"];
    var cparam = cs.utils.checkParam({ param: opt, checklist: plist, functionname: "cs.utils.processInstance" });
    //cs.debugalert("data:" + cs.Debug.inspect(opt.data).join("") + "\nelement:" + opt.element.id);

    if (cparam.valid) {
        if (opt.listofbuttongroup == undefined)
            opt.listofbuttongroup = {};

        var ele = opt.element;
        var strSuffix = opt.suffix;
        var cfginstance = opt.instance;
        var listofinstance = window.listofinstance[opt.listofinstancename];

        // My self
        switch (ele.nodeType) {

            case 1:
                if (ele.id != "") {
                    //cs.debugalert("Processing instance id:" + ele.id);
                    // Try to get the onfocus event wired

                    var eleOrigin = document.getElementById(ele.id);
                    //				    if (eleOrigin==undefined)
                    //				    {
                    //				        alert("not found :" + ele.id);
                    //				        return false;
                    var strEleOriginID = eleOrigin.id;

                    var strFieldID = ele.name;
                    if (strFieldID == undefined || strFieldID == "")
                        strFieldID = ele.id;

                    cfginstance[strFieldID] = ele.id + strSuffix;

                    ele.id = ele.id + strSuffix;

                    // check if it is a radio button
                    // if it is a radio button, check if the name is in the listofradiogroup
                    if (ele.type == "radio") {
                        //cs.debugalert("Radio button!");
                        // TODO: This is useless, need to consider,
                        // because the processInstance function do not return the listofradiogroup
                        // back to the caller to cascade it over\
                        //cs.debugalert(eleOrigin.id);
                        strEleOriginID = ele.name;
                        //cs.debugalert(strEleOriginID);
                        if (opt.listofbuttongroup[ele.name] == undefined)
                            opt.listofbuttongroup[ele.name] = ele.name + strSuffix;

                        ele.name = opt.listofbuttongroup[ele.name];
                    }
                    else {
                        ele.name = ele.name + strSuffix;
                    }

                    if (opt.data != undefined) {
                        // Cater for element that do not have the name attribute
                        if (opt.fieldmapper != undefined && opt.fieldmapper[strEleOriginID] != undefined) {
                            var strmappedfield = opt.fieldmapper[strEleOriginID];
                            
                            if (strmappedfield != "") {
                                // cs.debugalert("strmappedfield:" + strmappedfield + " / " + strEleOriginID + " / " + cs.Debug.inspect(opt.fieldmapper).join(""));
                                var strDataValue = opt.data;

                                var aField = strmappedfield.split(".");
                                for (var iFieldCnt = 0; iFieldCnt < aField.length; iFieldCnt++) {
                                    if (strDataValue != undefined)
                                        strDataValue = strDataValue[aField[iFieldCnt]];
                                }
                                if (strDataValue == undefined) strDataValue = "";
                                cs.forms.common.setElementValueByElement(ele, strDataValue);
                            }

//                            if (eleOrigin.id != strEleOriginID)
//                                cs.debugalert(strmappedfield + "\n" + cs.Debug.inspect(opt.fieldmapper).join("") + "\n" + strDataValue);

                        }
                        else if (eleOrigin.name != undefined) {
                            var itemData = opt.data[eleOrigin.name];
                            if (itemData != undefined)
                                cs.forms.common.setElementValueByElement(ele, itemData);
                            else
                                cs.forms.common.setElementValueByElement(ele, "");
                        }

                        // See if there is stylemapper
                        if (opt.stylemapper != undefined && opt.stylemapper[strEleOriginID] != undefined) {
                            var strmappedstylefield = opt.stylemapper[strEleOriginID];
                            if (strmappedstylefield != "") {
                                var strStyleData = opt.data.style;
                                var aField = strmappedstylefield.split(".");
                                for (var iFieldCnt = 0; iFieldCnt < aField.length; iFieldCnt++) {
                                    if (strStyleData != undefined)
                                        strStyleData = strStyleData[aField[iFieldCnt]];
                                }
                                if (strStyleData == undefined) strStyleData = "";
                                pc.style.applyCSStoDOM(strStyleData, ele);
                            }
                        }

                    }

                    if (eleOrigin.onfocus != undefined)
                        ele.onfocus = eleOrigin.onfocus;

                }
                break;

            case 3:
                // Replace the content #RowNo# with strSuffix
                var re = new RegExp("#RowNo#", "gi");
                ele.nodeValue = ele.nodeValue.replace(re, strSuffix);
                break;
        }

        switch (ele.name) {
            case "adder":
                var func = function(obj) {
                    cs.utils.addInstance({ templateid: obj.templateid, holderid: obj.holderid, referenceinstance: obj.referenceinstance, listofinstancename: obj.listofinstancename, fieldmapper: opt.fieldmapper, listofbuttongroup: opt.listofbuttongroup });
                };
                ele.onclick = cs.CreateFunctionCallBack(func, { templateid: opt.templateid, holderid: opt.holderid, referenceinstance: cfginstance, listofinstancename: opt.listofinstancename, fieldmapper: opt.fieldmapper });
                break;

            case "deleter":
                var func = function(obj) {
                    cs.utils.deleteInstance({ templateid: obj.templateid, holderid: obj.holderid, referenceinstance: obj.referenceinstance, listofinstancename: obj.listofinstancename, fieldmapper: opt.fieldmapper });
                };
                ele.onclick = cs.CreateFunctionCallBack(func, { templateid: opt.templateid, holderid: opt.holderid, referenceinstance: cfginstance, listofinstancename: opt.listofinstancename, fieldmapper: opt.fieldmapper });
                break;
        }

        if (ele.hasChildNodes()) {
            for (var intChild = 0; intChild < ele.childNodes.length; intChild++) {
                var eleChild = ele.childNodes[intChild];
                cs.utils.processInstance({ templateid: opt.templateid, holderid: opt.holderid, element: eleChild, suffix: strSuffix, instance: cfginstance, listofinstancename: opt.listofinstancename, data: opt.data, fieldmapper: opt.fieldmapper, listofbuttongroup: opt.listofbuttongroup });
            }
        }

    }
    else {
        cs.debugalert("missing param for cs.utils.processInstance:\n" + cparam.message.join("\n"));
    }

};

cs.utils.replaceElementID = function(opt){
    var plist = ["element", "prefix", "suffix"];
    
    if (opt.prefix==undefined) opt.prefix = "";
    if (opt.suffix==undefined) opt.suffix = "";
    
    var ele = opt.element;
    
    // Replace ID of the element itself
    if (ele.id!=undefined)
    {
        ele.id = opt.prefix + ele.id + opt.suffix;
        cs.forms.common.insertElementCache(ele.id, ele);
    }
    
    // Replace ID of the element's children
    for (strChild in ele.childNodes)
    {
        cs.utils.replaceElementID({element:ele.childNodes[strChild], prefix:opt.prefix, suffix:opt.suffix});
    }
};

cs.utils.addPerformanceTrace = function(tobj){
    if (window.domperformancetrace==undefined)
        window.domperformancetrace = new Array();
    window.domperformancetrace.push(tobj);
};

cs.utils.alertPerformanceTrace = function(){
    var dp = window.domperformancetrace;
    
    if (dp==undefined) return false;
    
    var aLog = new Array();
    aLog.push("<table>");
    aLog.push("<tr>");
    aLog.push("<th>Name</th>");
    aLog.push("<th>Data Length</th>");
    aLog.push("<th>Time</th>");
    aLog.push("<th>Start Time</th>");
    aLog.push("<th>End Time</th>");
    aLog.push("</tr>");
    
    for (var i=0; i<dp.length; i++)
    {
        aLog.push("<tr>");
        aLog.push("<td>" + dp[i].name + "</td>");
        aLog.push("<td>" + (dp[i].datalength?dp[i].datalength:"-") + "</td>");
        aLog.push("<td>" + ((dp[i].endtime.getTime() - dp[i].starttime.getTime()) / 1000)  + "</td>");
        aLog.push("<td>" + dp[i].starttime.getHours() + ":" + dp[i].starttime.getMinutes() + ":" + dp[i].starttime.getSeconds() + "</td>");
        aLog.push("<td>" + dp[i].starttime.getHours() + ":" + dp[i].endtime.getMinutes() + ":" + dp[i].endtime.getSeconds() + "</td>");
        aLog.push("</tr>");
    }
    aLog.push("</table>");
    var myWindow = window.open("", "_blank", 'scrollbars=yes,resizable=yes,width=600,height=600');
    myWindow.document.write(aLog.join(""));
    myWindow.document.bgColor="lightblue";
    myWindow.document.close();
    
    return false;
};


var pc = {
    version: 1.0,
    style: {
        applyCSStoDOM : function(strStyle, objDOM)
        {
            // break the style into each attributes and set
            var styleAttrib = strStyle.split(";");
            for (var si=0; si<styleAttrib.length; si++)
            {
                // it got to be length > 3, at least like a=1
                if (styleAttrib[si].length > 3)
                {
                    styleAttrib[si] = styleAttrib[si].replace(/^\s*/, "").replace(/\s*$/, "");
                    var stmp = styleAttrib[si].split(":");
                    if (stmp.length==2)
                    {
                        var styleAttb=stmp[0].toLowerCase().replace(/^\s*/, "").replace(/\s*$/, "");
                        styleAttb = pc.style.convertCSStoJS(styleAttb);
                        try
                        {
                            objDOM["style"][styleAttb]=stmp[1].replace(/^\s*/, "").replace(/\s*$/, "");
                        }
                        catch (e)
                        {
                            // Some error occured in the style, but let's ignore it first
                            //cs.debugalert(styleAttb + stmp[1].replace(/^\s*/, "").replace(/\s*$/, ""));
                        }
                    }
                    else
                    {
                        cs.Debug.addTrace("ERR", "Unrecognized style sheet:" + styleAttrib[si]);
                    }
                }
            }        
        },
        
        convertCSStoJS : function(str)
        {
            var styleAttb=str.toLowerCase();
            switch (styleAttb)
            {
                case "text-decoration":
                    styleAttb = "textDecoration";
                    break;
                    
                case "background-color":
                    styleAttb = "backgroundColor";
                    break;
                    
                case "color":
                    styleAttb = "color";
                    break;
                
                case "text-align":
                    styleAttb = "textAlign";
                    break;
                
                case "float":
                case "floating":
                    styleAttb = "cssFloat";
                    break;

                case "vertical-align":
                case "valign":
                    styleAttb = "verticalAlign";
                    break;
                    
                case "padding":
                    styleAttb = "padding";
                    break;
                    
                case "opacity":
                    styleAttb = "opacity";
                    break;
                    
                case "z-index":
                    styleAttb = "zIndex";
                    break;
            }
            return styleAttb;
        }
    }
}