    if (typeof caliper == 'undefined')
        var caliper = {};

    if ( typeof caliper.select == "undefined" ) 
        caliper.select = {};    

    if ( typeof caliper.select.edit == "undefined" ) 
        caliper.select.edit = {};    

    caliper.select.editableFieldPresent = false; //by default, no field is editable
    caliper.select.edit.recordsChecked = false;  //no record marked for edit
    caliper.select.edit.dirtyFlag = false;       //no field touched
    
    caliper.select.edit.editableFieldDefs = null;
    caliper.select.edit.selecttionTableFieldNames = null;

    /**
    * check if any of the fields is marked editable in the sub-config of the SelectControl, and get
    * field type if editable
    **/
    caliper.select.edit.checkEditable = function(arg) {
        caliper.select.edit.recordsChecked = false;
        caliper.select.editableFieldPresent = false;
        caliper.select.edit.selecttionTableFieldNames = new Array();
        caliper.select.edit.editableFieldDefs = null;

        if (!arg.Layers || !arg.LayerFields) return;

        var fieldTypes = arg.LayerFields["DataList Parameters"].FieldType;
        var editableFieldDefs = new Array();
        
        if (typeof(fieldTypes) == 'undefined') return; //no field type defined
        
        
        editableFieldDefs[0] = null;  //first field is always ID and it not editable
        for (i=0; i<fieldTypes.length; i++) {
            editableFieldDefs[i+1] = null;
            if (fieldTypes[i] != null && fieldTypes[i] != "" && fieldTypes[i] != "Not Editable") {
                //make sure for the field type of this editable field, a validating function is defined.
                //there are pre-defined generic validating functions for common field types - caliper.select.edit.validateString(),
                //"caliper.select.edit.validateInteger(), etc., if the user defines his own field type, i.e. Custom1, he will need
                //to have "caliper.select.edit.validateCustom1() ready in SelectContro.Edit.js
                var validatingFunction;
                
                eval("var validatingFunction = caliper.select.edit.validate"+fieldTypes[i]);
                if (typeof(validatingFunction) != 'undefined') {
                    editableFieldDefs[i+1] = fieldTypes[i];
                    caliper.select.editableFieldPresent = true;
                    }
                }
            }
        if (!caliper.select.editableFieldPresent) return;
        

        var layerInfo = arg.Layers[0];
        var fieldNameTypes = new Array();
        
        for (var j=0; j<layerInfo.Columns.length; j++) {
            field = layerInfo.ColumnDisplay[j];
            
            if (field == null || field.trim() == "") field = layerInfo.Columns[j];
            
            caliper.select.edit.selecttionTableFieldNames[j] = field;
             
            fieldNameTypes[j] = {"fieldName" : field, "fieldType": editableFieldDefs[j]};
            }

// caliper.select.edit.editableFieldDefs:
// 0    {fieldName:"ID",         fieldType:null},
// 1    {fieldName:"Area",       fieldType:null},
// 2    {fieldName:"Name",       fieldType:"String"},
// 3    {fieldName:"Population", fieldType:"Integer"},
// 4    {fieldName:"Category",   fieldType:"Custom1"},
//
// Edit button field was inserted between ID and 2nd field in Selection table, so actual field defs start from index 2
        caliper.select.edit.editableFieldDefs = fieldNameTypes;
        
        return editableFieldDefs;
        };
        
    /**
    * highlight the row of editable fields if the "edit" checkbox is checked,
    * clear the highlight otherwise
    **/
    caliper.select.edit.onCheckRecord = function(item) {
        var selectionTable = jQuery("#selected_records");
        var i=0;
        var classNames = new Array();
        var row_index=0;
        var rowHtml, search_pos = -1;

        //locate the check box in the table with the input item.id
        //the order of the row may be different than the original because of sorting
        jQuery("#selected_records tr").each(function() {
            if (search_pos < 0) {
                row_index += 1;
                rowHtml = jQuery(this.cells[caliper.select.CHECK_EDIT_COLUMN]).html();
                eval("search_pos = rowHtml.search(/"+item.id +"/);");
                }
            });    
        
        row_index = row_index - 1; //first row is the title
        if (typeof(item) == "object") {
            if (item.checked) 
                classNames = ["editable_field", "editable_field_checked"];
            else
                classNames = ["editable_field_checked", "editable_field"];
            }

        if (selectionTable[0]) {
            var cells = selectionTable[0].rows[row_index].cells;

            for (i=0; i<cells.length; i++) {
                if (cells[i].className == classNames[0]) 
                    cells[i].className = classNames[1];
                }    
            }
        };
    
    /**  
    * check or uncheck all records
    **/
    caliper.select.edit.markUnmarkAll = function() {
        var i=0;
        
        caliper.select.edit.recordsChecked = !caliper.select.edit.recordsChecked;
        
        //switch the check button between marked and unmarked
        if (caliper.select.edit.recordsChecked) {
            jQuery("#SelectControl_markUnmark").attr("src", "graphics/aaeditcheck2.gif");
            jQuery("#SelectControl_markUnmark").attr("alt", "Unmark all records");
            }
        else {
            jQuery("#SelectControl_markUnmark").attr("src", "graphics/aaeditcheck1.gif");
            jQuery("#SelectControl_markUnmark").attr("alt", "Mark all records");
            }
            
        jQuery("#selected_records input[type='checkbox']").each(function() {
            this.checked = caliper.select.edit.recordsChecked;
            caliper.select.edit.onCheckRecord(this);
            i += 1;
            });    
        };
          
    /**
    * create the edit table with editable fields and value input boxes
    **/
    caliper.select.edit.doEdit = function() {
        var editContainer = jQuery("#Edit_contents");
        var selectionTable = jQuery("#selected_records");
        var checkBox, cells, cellValue;
        var i, j;
        var editLines = 0;
        var editTableHTML = "";
        var browserSize;

        if (!selectionTable[0] || !editContainer[0]) return;
        
        jQuery("#selected_records input[type='checkbox']").each(function() {
            if (this.checked) {
                //this row is checked
                editLines++;

                var parentTR = dojo.isIE ? this.parentElement.parentElement : this.parentNode.parentNode ;
                if (parentTR && parentTR.tagName && parentTR.tagName.toUpperCase() == "TR") {
                    i = parentTR.rowIndex;
                cells = selectionTable[0].rows[i].cells;
                
                //0 - ID, 1 - checkbox, thus j starts from 2, corresponding to 1 in caliper.select.edit.editableFieldDefs
                for (j=2; j<cells.length; j++) {
                    if (caliper.select.edit.editableFieldDefs[j-1].fieldType != null) {
                        //this field is editable
                        cellValue = cells[j].firstChild.data;
                        caliper.select.edit.editableFieldDefs[j-1].fieldValue = cellValue; //single value for all checked rows
                        }
                    }
                    } //if parentTR.tagName.upperCase() == "TR"
                } //if this.checked
            });    
            
        if (editLines == 0) {
            alert("Please select at least 1 record to edit.");
            return;
            }
            
//        if (editLines > 1) {
//            var confirm = window.confirm("Multi-records confirm");
//            if (!confirm) return;
//            }
        
        caliper.select.edit.dirtyFlag = false; //no field touched

        editTableHTML = "<table width='100%' border=0>";    
        for (i=0; i< caliper.select.edit.editableFieldDefs.length; i++) {
            if (caliper.select.edit.editableFieldDefs[i].fieldType == null) continue;
            
            var fieldValue = "";
            if (editLines == 1)
                fieldValue = caliper.select.edit.editableFieldDefs[i].fieldValue;
                
            var fieldName = caliper.select.edit.editableFieldDefs[i].fieldName;
            fieldName = fieldName.replace(/\[/, "_").replace(/\]/, "_").replace(/ /, "_");
            editTableHTML = editTableHTML + "<tr><td class='info_field' width='50%' align='right'>"+caliper.select.edit.editableFieldDefs[i].fieldName+"</td>";
            editTableHTML = editTableHTML + "<td class='info_field' width='50%'><input id='edit_"+fieldName+"' type='text' onChange='javascript:caliper.select.edit.dirtyFlag = true;' style='width:200px;' class='info_value' value='"+fieldValue+"'/></td></tr>";
            }

        editTableHTML = editTableHTML + "<tr height='10px'><td colspan='2'></td></tr>";    
        editTableHTML = editTableHTML + "</table>";    
        
        editContainer.html(editTableHTML);
        
        var editWindowWidth  = editContainer[0].offsetWidth + 30;

        
        //editWindowHeight = Math.min(editWindowHeight, caliper.select.SELECT_WINDOW_HEIGHT);
        
        if ( editWindowWidth > caliper.select.SELECT_WINDOW_WIDTH + 30) {
            editWindowWidth = caliper.select.SELECT_WINDOW_WIDTH;
            }

        jQuery('#EditWindow').css("width",editWindowWidth);
        jQuery('#EditInnerWrapper').css("width",editWindowWidth);
        
        browserSize = CCUtil.getBrowserSize();
        
        jQuery("#EditWindow").css("visibility", "visible");
        jQuery("#EditWindow").css("z-index", "49999");
        
        jQuery("#selectControl_block").css("visibility", "visible");
        
        };
        
    /**
    * do value validation for each field, displays message if errors occur, otherwise apply new values 
    * back to server
    **/    
    caliper.select.edit.applyEdit_general = function() {
        var currentLayer = jQuery("#SelectControl_LayerList").attr("value");
        var i;
        var fieldType, fieldName, fieldValue, inputboxId, inputValue;
        var validatingReturn, validatingErrorMessages = "";

        //unlikely to happen
        if (currentLayer.search(/.config/) < 0) {
            //layer config file must be like "select_streets.config" (sub-config file)
            alert("The layer is not configurated to edit.")
            return;
            }
        
        //validate each input, show error message if returned by validating functions.
        //apply changes to not-null fields only
        for (i=0; i< caliper.select.edit.editableFieldDefs.length; i++) {
            caliper.select.edit.editableFieldDefs[i].fieldValue = null;
            
            if (caliper.select.edit.editableFieldDefs[i].fieldType == null) continue;
            
            fieldType = caliper.select.edit.editableFieldDefs[i].fieldType;
            fieldName = caliper.select.edit.editableFieldDefs[i].fieldName;

            inputboxId = "#edit_" + fieldName.replace(/\[/, "_").replace(/\]/, "_").replace(/ /, "_");
            inputValue = jQuery(inputboxId).attr("value").Trim();  
            
            //validate input values
            validatingReturn = null;
            function_to_call = eval("caliper.select.edit.validate" + fieldType);
            if (function_to_call) {
                try {
                   validatingReturn = function_to_call.apply(this, new Array(inputValue));
                } catch (err) {
                   alert('Error running: caliper.select.edit.validate' + fieldType + " " + inputValue);
                   return;
                }
            }
            else
            {
                alert('Cannot find javascript function: caliper.select.edit.validate' + fieldType);
                return;
            }
            
            
            
            if (validatingReturn.errorMessage != null)
                validatingErrorMessages = validatingErrorMessages + fieldName + ": " + validatingReturn.errorMessage;
            else if (validatingReturn.apply) 
                caliper.select.edit.editableFieldDefs[i].fieldValue = inputValue;
            }

        if (validatingErrorMessages != "") {
            alert(validatingErrorMessages);
            return;
            }
            
        var recordIds = new Array();
        var recordIndex = 0;
        
        jQuery("#selected_records input[type='checkbox']").each(function() {
            if (this.checked) {
                var recordId = jQuery(this).attr("gisdk_id");
                recordIds[recordIndex++] = recordId;
                }
            });    
        
        //need to convert caliper.select.edit.editableFieldDefs to collection type, so that it can be passed to 
        //DoMethod()
        var fieldNames = new Array(), fieldValues = new Array();
        recordIndex = 0;

        for (i=0; i<caliper.select.edit.editableFieldDefs.length; i++) {
            fieldType  = caliper.select.edit.editableFieldDefs[i].fieldType;
            fieldName  = caliper.select.edit.editableFieldDefs[i].fieldName;
            fieldValue = caliper.select.edit.editableFieldDefs[i].fieldValue;
            
            if (fieldType != null && fieldValue != null) {
                fieldNames[recordIndex]    = fieldName;
                fieldValues[recordIndex++] = fieldValue;
                }
            }

        var request = CCUtil.getGisdkProperties();
        request.currentLayerConfig = currentLayer;
        request.RecordIDs   = recordIds;
        request.fieldNames  = fieldNames;
        request.fieldValues = fieldValues;
        request.RedrawMap   = true;
            
        if (fieldNames.length > 0) 
            caliper.mapService.DoMethod("EditingTools.SetRecordsValues", request, "MapControl.config", "SelectControl", caliper.select.edit.onCompleteEdit);
        
        };
            
            
    /**
    * abort editing. display confirming message if field values had been changed.
    **/    
    caliper.select.edit.abortEdit_general = function() {
        if (caliper.select.edit.dirtyFlag) {
            var confirm = window.confirm("Discard the edit?");
            if (!confirm) return;
            }

        jQuery("#selectControl_block").css("visibility", "hidden");
        jQuery("#EditWindow").css("visibility", "hidden");
        };
    
            
      
    /**
    * update values in selection window to reflect editing changes. close edit window.
    **/    
    caliper.select.edit.onCompleteEdit = function(arg) {
        if (!arg) return;
        if (arg.Message != "OK") {
            alert(arg.Message);
            return;
            }
        
        var selectionTable = jQuery("#selected_records");
        var i, j;
        var cellCol, cellRow, cells;    
        var recordIds = arg.recordIds;
        if (recordIds) {
            for (i=0; i<recordIds[i]; i++) 
                jQuery("#selected_records input[gisdk_id='" + recordIds[i] + "']").each(function() {
                    if (this.checked) {
                    
                        var parentTR = dojo.isIE ? this.parentElement.parentElement : this.parentNode.parentNode ;
                        if (parentTR && parentTR.tagName && parentTR.tagName.toUpperCase() == "TR") {
                            cellRow = parentTR.rowIndex;
                        
                        if (typeof(cellRow) != 'NaN') {
                                cells = selectionTable[0].rows[cellRow].cells;
   
                            for (j=0; j<arg.fieldNames.length; j++) {
                                //get position of field in selection table
                                cellCol = Array.indexOf(caliper.select.edit.selecttionTableFieldNames, arg.fieldNames[j]);
                                if (cellCol < 0) continue;
                                
                                cells[cellCol+1].firstChild.data = arg.fieldValues[j]; //check box inserted as additional cell 
                                }
                            } //if (typeof(cellRow) != 'NaN')
                            } // if parentTR.tagName.toUpperCase() == "TR"
                        } //if (this.checked)
                    });    
                    
            jQuery("#selectControl_block").css("visibility", "hidden");

            jQuery("#EditWindow").css("visibility", "hidden");

            //disable Apply and Reset buttons
            jQuery("#edit_apply").attr("disabled",true);        
            jQuery("#edit_reset").attr("disabled",true);        

            var arguments = {};
            arguments.Command =  "update_map";
            jQuery("div.listener").trigger("MSG_updateMap", new Array(arguments));
            
            }
        };
        
      
    caliper.select.edit.startWindowDragging = function() {
        if (CCUtil) CCUtil.startToolboxDragging("EditWindow");
        };
      
      
      
    caliper.select.edit.switchToEditable = function() {
        var html, html_others;
        var item = this;
        
        //enable Apply and Reset buttons
        jQuery("#edit_apply").attr("disabled",false);        
        jQuery("#edit_reset").attr("disabled",false);        
                
        //change other editable-fields to text
        jQuery("#selected_records .editable_field").each(function() {
            html_others = jQuery(this).html();
            var pos = html_others.search(/input/i);
            if (pos >= 0 && this != item) {
                var value = this.firstChild.value;
                
                jQuery(this).html(value);
                }
            });    
        
        html = jQuery(item).html();
        if (html && html.search(/input/i) < 0 && item.firstChild) {
            var value = item.firstChild.data;
            
            jQuery(item).html("<input type=\"text\" style='width:50px;' value=\""+value+"\"/><img src='graphics/aaedit1.png' border='0' alt='Copy to all records' onclick='javascript:caliper.select.edit.copyRecords(this)' style='cursor:pointer;'/>");
            }
        };      
     
    caliper.select.edit.copyRecords = function(item) {
        var value = "";
        var col, thisTD;
        var errorMessage = null;
        
        if (!item.previousSibling) return;
        
        value = item.previousSibling.value;
//        if (typeof(value) == "undefined" || !value) return;
        value = value.Trim();
        
        thisTD = dojo.isIE ? item.parentElement : item.parentNode;
        if (!thisTD) return;
        
        //gets the field type of this column and do a value validation
        col = thisTD.cellIndex;
        errorMessage = caliper.select.edit.validateField(col, value);
        if (errorMessage != null) {
            alert(errorMessage);
            return;
            }
        //copy this value to other records of this field
        var html_others,pos, colIdx; 
        jQuery("#selected_records .editable_field").each(function() {
            html_others = jQuery(this).html();
            pos = html_others.search(/input/i);
            
            if (pos < 0) {
                colIdx = this.cellIndex;
                
                if (colIdx == col) {
                    //other records of this field
                    jQuery(this).html(value);
                    }
                }
            });    
        
            
        };
        
    caliper.select.edit.switchToText = function(item) {
        var html = jQuery(item).html();
        
        if (html.search(/input/i) >= 0) {
            var value = item.firstChild.value;
            
            jQuery(item).html(value);
            }
        };
        
    caliper.select.edit.applyEdit = function() {
    
        var selectionTable = jQuery("#selected_records");
        var i,j,k, item, parentTD, nextTD;
        var ID, html, value, fieldIdx, tempValue;
        var fieldType, fieldName,validatingReturn;
        var recordIDs = new Array(), fieldNames = new Array(), fieldValues = new Array();
        var fieldNameValueArray = {};
        var errorMessage = null;
        
        if (!selectionTable[0]) return;
        
        //validate each editable value, if valid, store in a value array
        jQuery("#selected_records img").each(function() {
            ID = this.id;
            if (parseInt(ID) > 0) {
                recordIDs.push(ID);
            
                //search for every editable field values in this row
                //img -> a -> TD
                parentTD = dojo.isIE ? this.parentElement.parentElement : this.parentNode.parentNode;
                nextTD = parentTD;
                fieldIdx = 0;
                while (nextTD != null) {
                    if (nextTD.className == "editable_field") {
                        html = jQuery(nextTD).html();
                        if (html && html.search(/input/i) >= 0) //text input bar
                            value = nextTD.firstChild.value.Trim();
                        else if (html.Trim() == "")
                            value = "";
                        else //text
                            value = nextTD.firstChild.data.Trim();
                            
                        fieldName = caliper.select.edit.editableFieldDefs[fieldIdx].fieldName;
                        validatingReturn = caliper.select.edit.validateField(fieldIdx, value);
                        if (validatingReturn != null)    
                            errorMessage = errorMessage + fieldName + "\n";  // removed + error because it's not defined!
                        else {
                            tempValue = fieldNameValueArray[fieldName];
                            if (typeof(tempValue) == 'undefined')
                                fieldNameValueArray[fieldName] = [value];
                            else {
                                tempValue.push(value);
                                fieldNameValueArray[fieldName] = tempValue;
                                }
                            } //if (validatingReturn != null)
                        } //if (nextTD.className == "editable_field") 
                        
                    fieldIdx ++;
                    nextTD = nextTD.nextSibling;
                    } //while
                } //if (typeof(parseInt(ID) != "NaN")
            });

        if (errorMessage) {
            alert(errorMessage);
            return;
            }
           
        //fieldNameValueArray = [
        // {field1: [value11, value12, value13]},
        // {field2: [value21, value22, value23]}
        // ]
        //
        //needs to be converted to an array of
        // {[field1, value11], [field2, value21]},
        // {[field1, value12], [field2, value22]},
        // {[field1, value13], [field2, value23]}
        
        for (item in fieldNameValueArray) {
            fieldNames.push(item);
            fieldValues.push(fieldNameValueArray[item]);
            }
            
        if (fieldValues.length == 0) return;
        if (recordIDs.length != fieldValues[0].length) return;
        
        var fieldValueArray = new Array();
        
        for (j=0; j<fieldValues[0].length; j++)  {
            tempValue = new Array();
            for (i=0; i< fieldNames.length; i++) { 
                tempValue.push([fieldNames[i], fieldValues[i][j]]);
                }
                
            fieldValueArray.push(tempValue);
            }
                
        var request = CCUtil.getGisdkProperties();
        request.currentLayerConfig = caliper.select.currentLayer;
        request.RecordIDs   = recordIDs;
        request.fieldValueGroups = fieldValueArray; 
        request.RedrawMap   = true;
            
        if (fieldNames.length > 0) 
            caliper.mapService.DoMethod("EditingTools.SetRecordsValues", request, "MapControl.config", "SelectControl", caliper.select.edit.onCompleteEdit);
  
        };
    
    caliper.select.edit.resetEdit = function() {
        //caliper.select.recordsValues
        var item, parentTD, nextTD;
        var ID, html, value, fieldIdx, tempValue;
        
        jQuery("#selected_records img").each(function() {
            ID = this.id;
            if (parseInt(ID) > 0) {
            
                //search for every editable field values in this row
                //img -> a -> TD
                parentTD = dojo.isIE ? this.parentElement.parentElement : this.parentNode.parentNode;
                nextTD = parentTD;
                fieldIdx = 0;
                while (nextTD != null) {
                    if (nextTD.className == "editable_field") {
                            
                        fieldName = caliper.select.edit.editableFieldDefs[fieldIdx].fieldName;
                        if (fieldName) {
                            item = caliper.select.recordsValues[ID][fieldName];
                            jQuery(nextTD).html(item);
                            }
                        } //if (nextTD.className == "editable_field") 
                        
                    fieldIdx ++;
                    nextTD = nextTD.nextSibling;
                    } //while
                } //if (typeof(parseInt(ID) != "NaN")
            });
    
        //disable Apply and Reset buttons
        jQuery("#edit_apply").attr("disabled",true);        
        jQuery("#edit_reset").attr("disabled",true);        
    
        };
            
         
    caliper.select.edit.validateField = function(field_idx, value) {
        var validatingErrorMessage = null;
        
        if (typeof(field_idx) == "number" && field_idx <= caliper.select.edit.editableFieldDefs.length) {
    
            var fieldName = caliper.select.edit.editableFieldDefs[field_idx].fieldName;
            var fieldType = caliper.select.edit.editableFieldDefs[field_idx].fieldType;

            var validatingReturn = null;
            var function_to_call = eval("caliper.select.edit.validate" + fieldType);
            if (function_to_call) {
                try {
                    validatingReturn = function_to_call.apply(this, new Array(value));
                    } catch (err) {
                    validatingErrorMessage = 'Error running: caliper.select.edit.validate' + fieldType + " " + value;
                    return validatingErrorMessage;
                    }
                }
            else {
                validatingErrorMessage = 'Cannot find javascript function: caliper.select.edit.validate' + fieldType;
                return validatingErrorMessage;
                }

            if (validatingReturn.errorMessage != null)
                validatingErrorMessage = fieldName + ": " + validatingReturn.errorMessage;
            else if (validatingReturn.apply) 
                caliper.select.edit.editableFieldDefs[field_idx].fieldValue = value;
            } //if (typeof(field_idx) == "number" && field_idx <= caliper.select.edit.editableFieldDefs.length)
        else {
            validatingErrorMessage = "Field "+ field_idx + " is not editable";
            }
            
        return validatingErrorMessage;    
        };
        
    /**
    * validate input value of certain type.
    * return:
    *   ret.apply:         false - field value should be left untouched
    *   ret.errorMessage:  not null - validation not passed for the reason in the message
    **/      
    caliper.select.edit.validateString = function(input) {
        var ret = {apply: true};
        
        if (input == null || input == "") ret.apply = false;
        
        return ret;
        };
        
    caliper.select.edit.validateInteger = function(input) {
        var value, ret = {apply: true};
        
        if (input == null || input == "") {
            ret.apply = false;
            return ret;
            }
            
        value = parseInt(input);
        if (typeof(value) == 'NaN' || value.toString() != input) {
            ret.errorMessage = "Field value is not a valid Integer number.\n";
            return ret;
            }
            
        return ret;    
        };        
        
    caliper.select.edit.validateReal = function(input) {
        var value, ret = {apply: true};
        
        if (input == null || input == "") {
            ret.apply = false;
            return ret;
            }
            
        value = parseInt(input);
        if (typeof(value) == 'NaN' || value.toString() != input) {
            ret.errorMessage = "Field value is not a valid Real number.\n";
            return ret;
            }
            
        return ret;    
        };        
        
    caliper.select.edit.validateCustom1 = function(input) {
        var value, ret = {apply: true};
        
        if (input == null || input == "") {
            ret.apply = false;
            return ret;
            }
            
        value = parseInt(input);
        if (typeof(value) == 'NaN' || value.toString() != input) {
            ret.errorMessage = "Field value must be an integer between 0 and 23.\n";
            return ret;
            }
            
        if (value < 0 || value > 23) {
            ret.errorMessage = "Field value must be an integer between 0 and 23.\n";
            return ret;
            }

        return ret;
        };        
        
        
        
        
        