// AX-4481.2         Oct 28, 2020   arr   Changes to an iForm can be lost with no warning message - Back button
// AX-28884          Jul 26, 2023   ST    Added newValidateNavMenu for PA 2.0

var g_prevSelectedId = null;
const generalModal = new GeneralModal();
const showErrorIcon = true;
const errorMessage = "One or more questions on this page have invalid answers. " +
    "Please ensure questions are completed correctly before navigating to a different page.";

$(document).ready(function () {
    // Custom messages and labels for iform

    $("body").on('click', function (e) {        // AX-24898.24919.2 .click(function (e) {
        validatePreviousInputs(e.target);
    });

    $("input").on('focus', function () {        // AX-24898.24919.2 .focus(function () {
        validatePreviousInputs(this);
    });

    $("textarea").on('focus', function () {     // AX-24898.24919.2 .focus(function () {
        validatePreviousInputs(this);
    });

    $("select").on('focus', function () {       // AX-24898.24919.2 .focus(function () {
        validatePreviousInputs(this);
    });

    $(".shortTextControl").each(function () {
        // var current = this;

        // Create maxlength condition
        $(this).maxlength({
            events: [], //Array of events to be triggered
            maxCharacters: 40,
            status: true, // True to show status indicator below the element
            statusClass: "remainingCount", // The class on the status div
            statusText: "/40", // The status text
            notificationClass: "", // the class added when maxlength is reached
            showAlert: false, // true to show a regular alert message
            alertText: "Cannot enter more than 40 characters", // text in alert message
            slider: true // true use counter slider
        });
        $(this).attr("maxlength", "40");
        $(this).attr("onblur", "validateShortText(this);");
        $(this).attr("onchange", "enableSubmitButton();");

        // Disable enter from submitting the form
        $(this).on('keypress', function (e) {		// AX-24898.24919.2 .keypress(function (e) {
            enableSubmitButton();
            disableEnter(e);
        });

        // there are cases where backspace is not registered as part of "on input" event in IE
        // this keyup handles the specific case to trigger validation
        $(this).on('keyup', function (e) {          // AX-24898.24919.2 .keyup(function (e) {
            // backspace
            if (e.which == 8) {
                validateShortText(this);
            }
        });

        // Create validation and update of submit button
        $(this).on("input", function () {
            validateShortText(this);
        });
    });

    $(".longTextControl").each(function () {
        //var current = this;

        // Create maxlength condition
        $(this).maxlength({
            events: [], //Array of events to be triggered
            maxCharacters: 32000,
            status: true, // True to show status indicator below the element
            statusClass: "remainingCount", // The class on the status div
            statusText: "/32000", // The status text
            notificationClass: "", // the class added when maxlength is reached
            showAlert: false, // true to show a regular alert message
            alertText: "Cannot enter more than 32000 characters", // text in alert message
            slider: true // true use counter slider
        });
        $(this).attr("maxlength", "32000");
        $(this).attr("rows", "4");

        // Disable enter from submitting the form
        $(this).on('keypress', function (e) {             // AX-24898.24919.2 .keypress(function (e) {
            enableSubmitButton();

            var max = 32000;
            var strokeValue = 0;

            // current selection
            var value = $(this).val();
            var currentSelection = getSelection(this);

            var range = currentSelection.end - currentSelection.start;

            // current expected length
            var lineBreakCount = (value.match(/\n/g) || []).length;
            var expectedLength = value.length + lineBreakCount - range;

            // onKeyDown and onKeyPress has different keyCode
            // this case, we are using keypress, which listens to all keys that outputs data
            // we only need to listen if the key stroke is an enter or backspace (FireFox behaviour),
            // to assign extra value to it

            if (e.which > 46)       // Regular keys
                strokeValue = 1;
            else if (e.which == 13) // Enter
                strokeValue = 2;
            else if (e.which == 32) // Spacebar
                strokeValue = 1;


            if (strokeValue == 0 || expectedLength + strokeValue <= max)
                return true;

            e.preventDefault();

            if (strokeValue == 2 && expectedLength + 1 <= max) {

                // replace enter with a space
                var currentPos = $(this).scrollTop();

                var formatText = value.substr(0, currentSelection.start) + " " + value.substr(currentSelection.end);
                $(this).val(formatText);
                $(this).scrollTop(currentPos);

                setSelection(this, currentSelection.start + 1, 0);
            }
            return false;
        });

        // there are cases where backspace is not registered as part of "on input" event in IE
        // this keyup handles the specific case to trigger validation
        $(this).on('keyup', function (e) {          // AX-24898.24919.2 .keyup(function (e) {    
            // backspace
            if (e.which == 8) {
                validateLongText(this);
            }
        });

        // Create validation and update of submit button
        $(this).on("input", function () {
            validateLongText(this);
        });
    });

    $(".numberControl").each(function () {
        var current = this;

        // Create maxlength condition
        $(this).maxlength({
            events: [], //Array of events to be triggered
            maxCharacters: 10,
            status: true, // True to show status indicator below the element
            statusClass: "remainingCount", // The class on the status div
            statusText: "/10", // The status text
            notificationClass: "", // the class added when maxlength is reached
            showAlert: false, // true to show a regular alert message
            alertText: "Cannot enter more than 10 characters", // text in alert message
            slider: true // true use counter slider
        });
        $(this).attr("maxlength", "10");
        $(this).attr("onblur", "validateNumber(this);");
        // $(this).numeric();       // AX-24898.24918
        $(this).on("numeric");      // AX-24898.24918


        // Disable enter from submitting the form
        $(this).on('keypress', function (e) {		// AX-24898.24919.2 .keypress(function (e) {
            enableSubmitButton();
            updateErrorIcon(this);
            disableEnter(e);
        });

        // there are cases where backspace is not registered as part of "on input" event in IE
        // this keyup handles the specific case to trigger validation
        $(this).on('keyup', function (e) {          // AX-24898.24919.2 .keyup(function (e) {
            // backspace
            if (e.which == 8) {
                enableSubmitButton();
                updateErrorIcon(this);
            }
        });

        // Disable tab when number validation fails
        $(this).on('keydown', function (e) {      // AX-24898.24919.2 use keydown() instead of live()
            var keyCode = e.keyCode || e.which;
            if (keyCode == 9) {
                var result = validateNumber(current);
                if (result.error > 0) {
                    e.preventDefault();
                }
            }
        });

        // Create validation and update of submit button
        $(this).on("input", function () {
            enableSubmitButton();
            updateErrorIcon(this);
        });
    });

    $(".listControl").each(function () {
        var current = this;
        $(current).on('change', function () {     // AX-24898.24919.2 .change(function () {
            validateList(this);
            enableSubmitButton();
        });
    });

    $(".yesNoControl").each(function () {
        var current = this;
        $(current).on('change', function () {     // AX-24898.24919.2 .change(function () {
            validateYesNo(current);
            enableSubmitButton();
        });
    });
    
    $(".dateControlNew").each(function () {
        const current = this;
        function handleDateUpdateAndValidation() {
            validateDateNew(current, true);
            enableSubmitButton();
        }
        
        $(current).on("blur", function () {
            handleDateUpdateAndValidation();
        });

        // Create validation and update of submit button
        $(current).on('keypress change input', function (e) {
            enableSubmitButton();
            updateErrorIcon(this);
            
            if (e.type === "keypress") {
                disableEnter(e);
            }
        });
    });

    $(".dateControl").each(function () {
        const current = this;
        // $(current).find('.yearControl').numeric(false);          // AX-24898.24918
        $(current).find('.yearControl').on("numeric", false);       // AX-24898.24918
        $(current).find('.yearControl').attr("maxlength", "4");
        $(current).find('.yearControl').attr("onblur", "validateYear(this);");
        $(current).find('.yearControl').on('keypress', function (e) {		// AX-24898.24919.2 .keypress(function (e) {
            updateDate(current);
            enableSubmitButton();
            updateErrorIcon(current);
            disableEnter(e);
        });

        // there are cases where backspace is not registered as part of "on input" event in IE
        // this keyup handles the specific case to trigger "Enable Submit Button"
        $(this).on('keyup', function (e) {          // AX-24898.24919.2 .keyup(function (e) {
            // backspace
            if (e.which === 8) {
                updateDate(current);
                updateErrorIcon(current);
                enableSubmitButton();
            }
        });

        $(current).find('input[type=hidden]').datepicker({
            minDate: new Date(1900, 0, 1),
            maxDate: new Date(2100, 11, 31),
            beforeShow: function (input, inst) {
                inst.dpDiv.css({
                    //marginTop: (-90 - input.offsetHeight) + 'px',
                    marginLeft: input.offsetWidth + 'px'
                });
            },
            onSelect: function () {
                updateYearMonthDay(current);
                enableSubmitButton();
            },
            dateFormat: 'yy-mm-dd',
            showOn: "button", // since the field is a hidden field, it should show only when button is clicked
            buttonImage: "/PatientAccess/Includes/Images/datepicker/calendar.png",
            buttonImageOnly: true,
            yearRange: '1900, 2100',
            onClose: function () {
            }
        });

        $(current).find('.monthControl').on('change', function () {     	// AX-24898.24919.2 .change(function () {
            updateDate(current);
            validateDate(current, true);
            enableSubmitButton();
        });

        $(current).find('.dayControl').on('change', function () {     	// AX-24898.24919.2 .change(function () {
            updateDate(current);
            validateDate(current, true);
            enableSubmitButton();
        });

        // May 17, 2013 - This process is not needed because each year control have onblur event handler
        // onblur event handler is to prevent older versions of IE not triggering event
        /*
        $(current).find('.yearControl').change(function () {
        updateDate(current);
        enableSubmitButton();
        });
        */

        // Create validation and update of submit button
        $(current).find('.yearControl').on("input", function () {
            updateDate(current);
            enableSubmitButton();
            updateErrorIcon(current);
        });

        // there are cases where backspace is not registered as part of "on input" event in IE
        // this keyup handles the specific case to trigger validation
        $(current).find('.yearControl').on('keyup', function (e) {          // AX-24898.24919.2 .keyup(function (e) {
            // backspace
            if (e.which == 8) {
                updateDate(current);
                enableSubmitButton();
                updateErrorIcon(current);
            }
        });

        $(current).find('.clearButton').click(function () {
            clearDate(current);
            validateDate(current, true);
            enableSubmitButton();
        });

        // Initialize date
        updateDate(current);
    });

    $(".checkListControl input[type=checkbox]").each(function () {
        var current = this;
        $(current).on('change', function () {     	// AX-24898.24919.2 .change(function () {
            enableSubmitButton();
        });
    });

    // Navigation buttons
    $(".nextButton").on("click", function () {
        performPageNavigation(NavigationDirection.Next);
        
        return false;
    });

    $(".prevButton").on("click", function () {
        performPageNavigation(NavigationDirection.Previous);

        return false;
    });
});

function validateYear(elmt) {
    var value = $(elmt).val();

    if (!validateInteger(value)) {
        var pattern = /[^0-9]/g;
        value = value.replace(pattern, "");

        $(elmt).val(value);
    }

    var current = $(elmt).parent();
    updateDate(current);
    validateDate(current, true);
    enableSubmitButton();
}

function updateYearMonthDay(container) {
    var date = $(container).find("input[type=hidden]").val();
    if (date != null) {
        var ymd = date.split("-");
        if (ymd.length == 3) {
            var year = parseInt(ymd[0]);
            var month = parseInt(ymd[1].replace(/\D/g, ''), 10); // parseInt(ymd[0]);
            var day = ymd[2];

            //Update month dropdown
            // AX-307
            var daysInMonth = 32 - new Date(year, month - 1, 32).getDate();

            $(container).find(".dayControl option").remove();
            var ddl = $(container).find(".dayControl");
            $(ddl).append('<option value="0"></option>');
            for (var i = 1; i <= daysInMonth; i++) {
                var d = (i < 10) ? "0" + i : i;
                $(ddl).append('<option value="' + d + '">' + d + '</option>');
            }

            if (day > daysInMonth) {
                day = daysInMonth;
            }

            $(container).find(".yearControl").val(year);
            $(container).find(".monthControl").val(month);
            $(container).find(".dayControl").val(day);
        }
    }
}

function updateDate(container) {

    var year = $(container).find(".yearControl").val();
    var month = $(container).find(".monthControl").val();
    var day = $(container).find(".dayControl").val();

    if (month != "") {
        // AX-307
        var daysInMonth = 32 - new Date(year, month - 1, 32).getDate();

        $(container).find(".dayControl option").remove();
        var ddl = $(container).find(".dayControl");
        $(ddl).append('<option value="0"></option>');
        for (var i = 1; i <= daysInMonth; i++) {
            var d = (i < 10) ? "0" + i : i;
            $(ddl).append('<option value="' + d + '">' + d + '</option>');
        }

        if (day > daysInMonth) {
            day = daysInMonth;
        }

        $(ddl).val(day);
    }

    // If any of the field is empty, but not the others,
    // This means invalid date
    if (year == "" && month == "0" && day == "0") {

        $(container).find("input[type=hidden]").val("");

    } else if (
        (day == "0" && (year != "" || month != "0")) ||
        (year == "" && (month != "0" || day != "0")) ||
        (month == "0" && (year != "" || day != "0")) ||
        year < 1900 ||
        year > 2100) {

        $(container).find("input[type=hidden]").val("null");

    } else {

        var date = "";
        date += year + "-";
        date += (month < 10) ? "0" : "";
        date += month + "-";
        date += day;


        $(container).find("input[type=hidden]").val(date);
    }
}

function updateDateNew(container) {
    const val = $(container).val().split('-'); // 2023-12-28

    const year = val[0];
    const month = val[1] || "0";
    let day = val[2] || "0";
    
    const hiddenDate = $(container).parent().parent().find("input[type=hidden]");

    // If any of the field is empty, but not the others,
    // This means invalid date
    if (year === "" && month === "0" && day === "0") {
        $(hiddenDate).val("");
    } else if (
        (day === "0" && (year !== "" || month !== "0")) ||
        (year === "" && (month !== "0" || day !== "0")) ||
        (month === "0" && (year !== "" || day !== "0")) ||
        year < 1900 ||
        year > 2100) {
        $(hiddenDate).val("null");
    } else {

        let date = "";
        date += year + "-";
        date += (month < 10) ? "0" : "";
        date += month + "-";
        date += day;
        $(hiddenDate).val(date);
    }
}

function clearDate(container) {
    $(container).find(".yearControl").val("");
    $(container).find(".monthControl").val("0");
    $(container).find(".dayControl").val("0");
    $(container).find("input[type=hidden]").val("");
}

// Enable submit button if value of input field is different from original value
function enableSubmitButton(hideErrorIcon = true) {
    const unsavedItemCount = checkUnsavedItems();

    // if (unsavedItemCount === 0) {
    //     hideErrorIcon && $(".errorIcon").hide();
    //     $("#ContButton, #MarkButton").show();
    //     // AX-4481
    //     $('#SaveBtn').hide();
    // } else {
    //     $("#ContButton, #MarkButton").hide();
    //     // AX-4481
    //     $('#SaveBtn').show();
    // }
}

/**
 * Returns true if all date fields are valid
 * @returns {boolean}
 */
function checkDateFieldsValidity() {
    let dateFieldsAreValid = true;
    const dateFields = $(".dateControlNew.is-invalid");

    for (let i = 0; i < dateFields.length; i++) {
        if (!dateFields[i].checkValidity()) {
            dateFieldsAreValid = false;
            break;
        }
    }

    return dateFieldsAreValid;
}


const NavigationDirection = {
    Next: 'next',
    Previous: 'previous'
};

/**
 * Check for errors and navigate to the next or previous page
 * @param {NavigationDirection} direction - The direction for page navigation.
 */

function performPageNavigation(direction) {

    function setSaveFlagAndClearInvalidDates() {
        // If there are unsaved items, let the server know that it needs to save the form
        // If not, inform the server to just handle the page navigation without saving the form first
        const unsavedItemCount = checkUnsavedItems();

        $("#PreventSaveFlag").val(`${unsavedItemCount === 0}`);

        clearInvalidDateFields();
    }

    parent.openModalSpinner();

    const { error, empty } = validateInputs();

    if (error > 0) {
        showErrorMessage();
        
        parent.closeModalSpinner();

        return;
    }

    if (direction === NavigationDirection.Next) {
        if (empty > 0) {

            parent.closeModalSpinner();

            showConfirmationModal(function () {
                parent.openModalSpinner();

                setSaveFlagAndClearInvalidDates();

                $("#HiddenNextBtn")
                    .trigger('click');
            });

            return;
        }
    }

    setSaveFlagAndClearInvalidDates();

    if (direction === NavigationDirection.Next) {
        $("#HiddenNextBtn")
            .trigger('click');
    } else {
        $("#HiddenPrevBtn")
            .trigger('click');
    }
}

function showErrorMessage() {
    // Toast defined in ~/PatientAccess/Controls/Notification.ascx file, already loaded in FormDetails.aspx
    ShowCustomToast(errorMessage, false, ToastType.Error);
}

function validateSave(ignoreEmpty, isFinishLater = false) {
    // Show spinner, defined in ModalSpinner.ascx file, already loaded in the Master page of PA 2.0 
    // ------------------------------------------------------
    parent.openModalSpinner();

    const result = validateInputs();
    let err = result.error;
    let emp = result.empty;

    const $HiddenSaveBtn = $("#HiddenSaveBtn");
    const $HiddenSaveAndFinishLaterBtn = $("#HiddenSaveAndFinishLaterBtn");
    
    // display error/empty confirmation windows
    if (err > 0) {
        parent.closeModalSpinner();
        
        showErrorMessage();
        
        return false;
        
    } else if (emp > 0 && !ignoreEmpty) {
        const confirmHeader = getSettingMessage("SettingEmptyFormSubmitHeader");
        const confirmMessage = getSettingMessage("SettingEmptyFormSubmitMessage");

        const $SaveButton = $("#SaveButton");
        const cancelButtonText = "Cancel";
        const okButtonText = ($SaveButton.length > 0) ? $SaveButton.attr("value") : "Continue";

        parent.closeModalSpinner();

        generalModal.createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText,
            function () {
                const $validButton = (isFinishLater)
                    ? $HiddenSaveAndFinishLaterBtn
                    : $HiddenSaveBtn;
                
                clearInvalidDateFields();
                
                if (!document.forms[0].checkValidity()) {
                    $validButton
                        .attr("saving", "0")
                        .trigger('click');
                } else {
                    parent.openModalSpinner();             
                    // AX-4481.4
                    $validButton
                        .attr("saving", "1")
                        .trigger('click');
                }
            });
        //return false;  AX-4481.4
    } else {
        clearInvalidDateFields();
        
        if (isFinishLater) {
            $HiddenSaveAndFinishLaterBtn
                .attr("saving", "1")
                .trigger('click');
        } else {
            $HiddenSaveBtn
                .attr("saving", "1")
                .trigger('click');
        }
    }
    
    return false;
}

function hideSpinnerAndRedirect(redirectTo) {
    parent.closeModalSpinner();

    // Check if success window is open and remove it
    const parentWindow = getParentWindow(window);
    
    if (parentWindow.isSuccessWindowOpened()) {
        parentWindow.removeSuccessWindow();
    }

    // Redirect to the specified URL
    window.location.replace(redirectTo);
}


function validateInputs() {
    // error and empty counter
    let err = 0;
    let emp = 0;
    
    //validate short text items
    $(".shortTextControl").each(function () {
        var result = validateShortText(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate long text items
    $(".longTextControl").each(function () {
        var result = validateLongText(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate number items
    $(".numberControl").each(function () {
        var result = validateNumber(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate dropdown list items
    $(".listControl").each(function () {
        var result = validateList(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate dropdown for yes/no items
    $(".yesNoControl").each(function () {
        var result = validateYesNo(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate date items
    $(".dateControl").each(function () {
        var result = validateDate(this, false);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    // validate check list items
    $(".checkListControl").each(function () {
        var result = validateCheckList(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    history.pushState(null, null, null);    // AX-4481.?

    $(".spreadsheetControl").each(function () {
        var result = validateSpreadsheet(this);
        emp = emp + result.empty;
    });
    
    $(".dateControlNew").each(function () {
        const result = validateDateNew(this);
        err = err + result.error;
        emp = emp + result.empty;
    });

    return {error: err, empty: emp};
}

function validatePageJump(id, page) {
    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened())
        return false;

    // error and empty counter
    var counter = checkUnsavedItems();

    if (counter > 0) {
        var confirmHeader = getSettingMessage("SettingLeaveUnsavedFormHeader");
        var confirmMessage = getSettingMessage("SettingLeaveUnsavedFormMessage");
        var cancelButtonText = "No";
        var okButtonText = "Yes";

        getParentWindow(window).createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText,
            function () {
                window.location.href = window.location.pathname + '?id=' + id + '&page=' + page;
            });

        return false;
    }

    window.location.href = window.location.pathname + '?id=' + id + '&page=' + page;

    return true;
}

function validateClick() {
    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened())
        return false;

    return true;
}

function validateMarkClick() {
    if (generalModal.isConfirmWindowOpened()) {
        return false;
    }

    clearInvalidDateFields();

    const { empty, error } = validateInputs();

    if (error > 0) {
        showErrorMessage();
        
        parent.closeModalSpinner();

        return false;
    }

    if (empty > 0) {
        const submitWithoutCheck = $("#MarkButton").data("submit-without-check");

        if (submitWithoutCheck) {
            parent.openModalSpinner();

            return true;
        }

        // Show confirm 
        showConfirmationModal(function () {
            parent.openModalSpinner();
            isSaving = true;
            $("#MarkButton")
                .data("submit-without-check", true)
                .trigger('click');
        });

        return false;
    }

    // AX-8776: Hide spinner before processing
    parent.openModalSpinner();

    return true;

}

function clearInvalidDateFields() {
    $(".dateControlNew.is-invalid")
        .each(function () {
            $(this)
                .removeClass("is-invalid")
                .val("");
        })
}

// validate if navigating away from the form is valid
function validateNav(ignoreButtonClass) {
    parent.openModalSpinner();

    // Check for unsaved items
    const unsavedItemCount = checkUnsavedItems();
    
    const areDateFieldsValid = checkDateFieldsValidity(); 

    if (unsavedItemCount > 0 || !areDateFieldsValid) {
        const confirmHeader = getSettingMessage("SettingLeaveUnsavedFormHeader");
        const confirmMessage = getSettingMessage("SettingLeaveUnsavedFormMessage");
        const cancelButtonText = "No";
        const okButtonText = "Yes";
        
        parent.closeModalSpinner();
        
        generalModal.createConfirmWindow(
            confirmHeader,
            confirmMessage,
            cancelButtonText,
            okButtonText,
            function () {
                parent.openModalSpinner();
                // If this code gets called, it means the user wants to submit without saving invalid data entered in date fields.
                // We just have validity for date fields, let's clean invalid date fields before submitting
                clearInvalidDateFields();

                $(`.${ignoreButtonClass}`).trigger('click');
            });

        return false;
    }

    return true;
}

function showConfirmationModal(callback) {
    const confirmHeader = getSettingMessage("SettingEmptyFormSubmitHeader");
    const confirmMessage = getSettingMessage("SettingEmptyFormSubmitMessage");
    const cancelButtonText = "Cancel";
    const okButtonText = "Continue";

    generalModal.createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText, callback);
}

var isSaving = false;

// AX-28884
function newValidateNavMenu(redirectUrl, redirectNode) {
    // check if current window has error window opened
    // this process will be terminated
    if (generalModal.isErrorWindowOpened() || generalModal.isConfirmWindowOpened()) {
        return false;
    }

    if (isSaving) {
        return true; // form has been submited
    }

    // error and empty counter
    const counter = checkUnsavedItems();
    
    if (counter > 0) {
        const confirmHeader = getSettingMessage("SettingLeaveUnsavedFormHeader");
        const confirmMessage = getSettingMessage("SettingLeaveUnsavedFormMessage");
        const cancelButtonText = "No";
        const okButtonText = "Yes";

        generalModal.createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText, function () {
            const parentWindow = getParentWindow(window); 
            if (redirectUrl) {
                parent.openModalSpinner();
                if (redirectNode != undefined) {
                    parent.window.toggleNavItem(redirectNode);
                }
                parentWindow.redirectIFrameTo(redirectUrl);
            } else {
                parentWindow.location.reload();
            }
        });
        return false;     // This is trigger first time before even show the confirmWindow, so need to return false.
    }
    return true;      // Nothing to save, then return true
}

// AX-4481.1 validate if navigate to menu option is possible (has not questions in iForm to be saved)
function validateNavMenu(redirectUrl, redirectId) {
    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened())
        return false;

    // error and empty counter
    var counter = checkUnsavedItems();

    if (counter > 0) {
        var confirmHeader = getSettingMessage("SettingLeaveUnsavedFormHeader");
        var confirmMessage = getSettingMessage("SettingLeaveUnsavedFormMessage");
        var cancelButtonText = "No";
        var okButtonText = "Yes";

        getParentWindow(window).createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText,
            function () {
                getParentWindow(window).location.href = redirectId !== '0' ? redirectUrl + redirectId : redirectUrl;    // When in confirm window select Ok continue and lose changes, then redirect
            });

        return false;     // This is trigger first time before even show the confirmWindow, so need to return false.
    }
    return true;      // Nothing to save, then return true

}

// AX-4481.1 validate if logout is possible (has not questions in iForm to be saved)
function validateLogoutOrAuthor(continueOption) {
    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened())
        return false;

    // error and empty counter
    var counter = checkUnsavedItems();

    if (counter > 0) {
        var confirmHeader = getSettingMessage("SettingLeaveUnsavedFormHeader");
        var confirmMessage = getSettingMessage("SettingLeaveUnsavedFormMessage");
        var cancelButtonText = "No";
        var okButtonText = "Yes";

        getParentWindow(window).createConfirmWindow(confirmHeader, confirmMessage, cancelButtonText, okButtonText,
            function () {
                var hdnBtn;
                if (continueOption === 1)
                    hdnBtn = jQuery('#logoutIfrmId', window.parent.document).contents().find("#HiddenApplyAuthorBtn");
                else {
                    hdnBtn = jQuery('#logoutIfrmId', window.parent.document).contents().find("#HiddenLogoutBtn");
                }
                hdnBtn.trigger('click');	        // AX-24898.24919.2 hdnBtn.click();
            });

        return false;     // This is trigger first time before even show the confirmWindow, so need to return false.
    }
    return true;      // Nothing to save, then return true

}

// AX-4481.2
function validateiFormSavingProcess(e) {
    //console.log("=== At top validateiFormSavingProcess() ====");

    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened() || getParentWindow(window).isSuccessWindowOpened()) {
        return false;
    }

    // the spinning waiting is showing. then ask if want to lost saving
    var isSpinning = getParentWindow(window).isSpinnerShowing();

    //console.log("=== At validateiFormSavingProcess() isSpinning: " + isSpinning);
    if (isSpinning) {
        var confirmHeaderSpin = getSettingMessage("SettingLeaveSavingFormHeader");
        var confirmMessageSpin = getSettingMessage("SettingLeaveSavingFormMessage");

        getParentWindow(window).createSuccessWindow(confirmHeaderSpin, confirmMessageSpin, null);
        //console.log("=== At valid.js validateiFormSavingProcess() return False ====");
        return false;     // This is trigger first time before even show the confirmWindow, so need to return false.
    }

    //console.log("=== At valid.js validateiFormSavingProcess() return true ====");
    return true;      // Nothing to save, then return true
}


// AX-4481.2
function backBtnValidateiFormNoSaved(e) {
    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened() || getParentWindow(window).isConfirmWindowOpened() || getParentWindow(window).isSuccessWindowOpened()) {
        return "";
    }

    // Check if there are unsaved items
    var counter = checkUnsavedItems();

    if (counter > 0) {
        var confirmMessage = getSettingMessage("SettingBackBtnLeaveUnsavedFormMessage");

        return confirmMessage;     // Message then something to save
    }

    /* AX-4481.5 Nothing to changed, no check if is saving
    var saveBackend = $("#HiddenSaveBtn").attr("saving");    // AX-4481
    if (saveBackend === '0') {
       var confirmMessageNoCalculate = getSettingMessage("SettingBackBtnLeaveUnsavedFormMessage");
 
       return "No calculate if there is mising" + confirmMessageNoCalculate;     // Message then no calculate yet
 
    }
    */
    return "";      // Nothing to save, then return empty
}

// function to check if there are any items that are not saved
function checkUnsavedItems() {
    // error and empty counter
    var counter = 0;

    //validate short text items
    $(".shortTextControl").each(function () {
        var originalValue = $(this).parent().children('input[name$="original"]').val();
        var currentValue = $(this).val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate long text items
    $(".longTextControl").each(function () {
        var originalValue = $(this).parent().children('input[name$="original"]').val();
        var currentValue = $(this).val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate number items
    $(".numberControl").each(function () {
        var originalValue = $(this).parent().parent().children('input[name$="original"]').val();
        var currentValue = $(this).val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate date items
    $(".dateControl").each(function () {
        var originalValue = $(this).parent().parent().children('input[name$="original"]').val();
        var currentValue = $(this).find('input[type=hidden]').val();
        if (currentValue == null)
            currentValue = $(this).find('input[type=text]').val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate list items
    $(".listControl").each(function () {
        var originalValue = $(this).parent().parent().children('input[name$="original"]').val();
        var currentValue = $(this).val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate yes/no items
    $(".yesNoControl").each(function () {
        var originalValue = $(this).parent().parent().children('input[name$="original"]').val();
        var currentValue = $(this).val();

        if (originalValue != currentValue)
            counter += 1;
    });

    // validate check list items
    $(".checkListControl").each(function () {
        var originalValue = $(this).children('.checkBoxControl').find('input[name$="original"]').val();
        var currentValue = $(this).children('.checkBoxControl').find('input[type=checkbox]').is(":checked") ? "1" : "0";

        if (originalValue != currentValue)
            counter += 1;
    });

    function normalizeDateString(dateStr) {
        // Replaces both - and / with an empty string
        return dateStr.replace(/[-/]/g, ''); 
    }
 
    // validate date items
    $(".dateControlNew").each(function () {
        const originalValue = $(this).parent().parent().children('input[name$="original"]').val();
        const currentValue = $(this).val();

        if (normalizeDateString(originalValue) !== normalizeDateString(currentValue))
            counter += 1;
    });

    return counter;
}

// validate element with short text rules
function validateShortText(elmt) {
    const $element = $(elmt);

    const value = $element.val();
    let empty = 0;
    let error = 0;

    if (!validateRequired(value)) {
        if ($element.is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                const isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    if (!validateLength(value, 40)) {
        error += 1;
    }

    // When length validation fails, it trims the width automatically, rendering error icon invalid
    // render the icon only when field is empty
    if (empty == 0) {
        showErrorIcon && $element.parent().parent().find(".errorIcon").first().hide();
        removeErrorFromElement($element);
    } else {
        showErrorIcon && $element.parent().parent().find(".errorIcon").first().show();
        addErrorToElement($element, false);
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error + empty > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: empty};
}

// validate element long text rules
function validateLongText(elmt) {
    const $element = $(elmt);
    
    const value = $element.val();
    let empty = 0;
    let error = 0;

    if (!validateRequired(value)) {
        if ($element.is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                const isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    if (!validateLength(value, 32000)) {
        error += 1;
    }

    // When length validation fails, it trims the width automatically, rendering error icon invalid
    // render the icon only when field is empty
    if (empty == 0) {
        showErrorIcon && $element.parent().parent().find(".errorIcon").first().hide();
        removeErrorFromElement($element);
    } else {
        showErrorIcon && $element.parent().parent().find(".errorIcon").first().show();
        addErrorToElement($element, false);
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error + empty > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: empty};
}

// validate element with number rules
function validateNumber(elmt) {
    // check if current window has error window opened
    // this process will be terminated
    // if (generalModal.isErrorWindowOpened())
    //     return {error: 1, empty: 0};

    const $element = $(elmt);
    
    let value = $element.val();
    const min = $element.parent().parent().find("input[name$=min]").val();
    const max = $element.parent().parent().find("input[name$=max]").val();
    const increment = $element.parent().parent().find("input[name$=inc]").val();

    let empty = 0;
    let error = 0;

    if (!validateRequired(value)) {
        if ($element.is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                var isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    } else if (!validateDecimal(value)) {
        const pattern = /[^\-\.0-9]/g;
        let output = "";

        // get rid of all un-allowed characters
        let str = value.replace(pattern, "");
        if (str === "")
            value = "";
        else {
            // get rid of negative symbol if it is not in correct place
            if (str.substr(0, 1) === "-") {
                output += "-";
                str = str.substr(1);
            }
            str = str.replace(/[\-]/g, "");

            // get rid of decimal if it is not in correct place
            if (str.substr(str.length - 1) === ".")
                str = str.substr(0, str.length - 1);

            var tokens = str.split(".");
            for (var i = 0; i < tokens.length; i++) {
                if (i === 0) {
                    var trimmed = tokens[i].replace(/^0+/, "");
                    output += (trimmed === "") ? "0" : trimmed;
                    if (tokens.length > 1)
                        output += ".";
                } else {
                    output += tokens[i];
                }
            }

            value = output;

            //check if value is 0
            const p = /[^\-\.0]/g;
            if (!p.test(value)) {
                value = "0";
            }
        }

        $element.val(value);
    } else {
        var isNegative = false;
        var o = "";
        var s = value;

        // save negative symbol
        if (s.substr(0, 1) === "-") {
            isNegative = true;
            s = s.substr(1, s.length - 1);
        }

        // remove last . if not followed by other numbers
        if (s.substr(s.length - 1) === ".")
            s = s.substr(0, s.length - 1);

        // right trim, if if leading with zero
        var t = s.split(".");
        for (var j = 0; j < t.length; j++) {
            if (j == 0) {
                var x = t[j].replace(/^0+/, "");
                o += (x == "") ? "0" : x;
                if (t.length > 1)
                    o += ".";
            } else {
                o += t[j];
            }
        }

        value = (isNegative) ? "-" + o : o;

        //check if value is 0
        var q = /[^\-\.0]/g;
        if (!q.test(value)) {
            value = "0";
        }

        $element.val(value);
    }


    if (!validateRange(value, min, max, increment)) {
        error += 1;
    }

    const $errorIcon = $element.parent().parent().parent().find(".errorIcon").first();
    
    if (empty === 0 && error === 0) {
        showErrorIcon && $errorIcon?.hide();
        removeErrorFromElement($element);
    } else {
        showErrorIcon && $errorIcon?.show();
        
        let errorMsg;
        
        if (error > 0) {
            // const invalidNumberHeader = $('input[id$="SettingInvalidNumberMessageHeader"]').val();
            const invalidNumberMsg = $('input[id$="SettingInvalidNumberMessage"]').val();
            errorMsg = invalidNumberMsg.replace("{0}", min).replace("{1}", max).replace("{2}", increment);
        }

        addErrorToElement($element, errorMsg);
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error + empty > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: empty};
}

//validate schemed answer list
function validateList(elmt) {
    const $element = $(elmt);

    const value = $element.val();
    let empty = 0;
    const error = 0;

    if (!validateRequired(value)) {
        if ($element.is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                const isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    // render the icon only when field is empty
    if (empty === 0) {
        removeErrorFromElement($element);
        showErrorIcon && $element.parent().parent().parent().find(".errorIcon").first().hide();
    } else {
        addErrorToElement($element);
        showErrorIcon && $element.parent().parent().parent().find(".errorIcon").first().show();
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error + empty > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: empty};
}

//validate yes no question
function validateYesNo(elmt) {
    const $element = $(elmt);

    const value = $element.val();
    let empty = 0;
    const error = 0;

    if (value === "-1") {
        if ($element.is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                const isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    // render the icon only when field is empty
    if (empty === 0) {        
        showErrorIcon && $element.parent().parent().parent().find(".errorIcon").first().hide();
        removeErrorFromElement($element);
    } else {
        showErrorIcon && $element.parent().parent().parent().find(".errorIcon").first().show();
        addErrorToElement($element);
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error + empty > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: empty};
}

//validate date
function validateDate(elmt, ignoreIncomplete) {

    // check if current window has error window opened
    // this process will be terminated
    if (getParentWindow(window).isErrorWindowOpened())
        return {error: 1, empty: 0};

    var year = $(elmt).find('.yearControl').val();
    // var month = $(elmt).find('.monthControl').val();
    // var day = $(elmt).find('.dayControl').val();
    var date = $(elmt).find("input[type=hidden]").val();

    var empty = 0;
    var rangeError = 0;
    var inputError = 0;

    if (date == "") {
        if ($(elmt).is(".additional")) {
            // Check if current item is a child of a "checkListControl"
            // The element can be invisible due to collapsed header
            // Need to validate based on checked status of its parent item
            if ($(elmt).closest('.childItems').parent().is(".checkListControl")) {
                var isChecked = $(elmt).closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            }
                // If current item is not a child of a "checkListControl", it means it is a child of a Yes/No question
                // In this case, if element is visible/invisible based on the availability for submition
            // Need to validate the control only if it is visible
            else if ($(elmt).is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    if (ignoreIncomplete == false && date == "null")
        inputError += 1;

    if (year != "" && (year < 1900 || year > 2100))
        rangeError += 1;

    if (empty == 0 && rangeError == 0 && inputError == 0) {
        $(elmt).parent().parent().find(".errorIcon").first().hide();
    } else {
        $(elmt).parent().parent().find(".errorIcon").first().show();
        if (rangeError > 0) {
            var invalidYearHeader = $('input[id$="SettingInvalidYearMessageHeader"]').val();
            var invalidYearMsg = $('input[id$="SettingInvalidYearMessage"]').val();

            getParentWindow(window).createErrorWindow(invalidYearHeader, invalidYearMsg, function () {
                $(elmt).find('.yearControl').val("");
            });
        }
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (rangeError + inputError + empty > 0)
        openItem(elmt);

    enableSubmitButton();
    return {error: (rangeError + inputError), empty: empty};
}

function validateDateNew(element, ignoreIncomplete) {
    const $element = $(element);
    
    const year = $element.val().split('-')[0]; // 2023-12-28

    // Extract year, month, day, and hidden date input
    const date = $element.val();

    let empty = 0;
    let rangeError = 0;
    let inputError = 0;

    // Check if the date is empty
    if (date === "") {
        if ($element.is(".additional") || $element.parent().is(".additional")) {
            // Check if the current item is a child of a "checkListControl"
            if ($element.closest('.childItems').parent().is(".checkListControl")) {
                const isChecked = $element.closest('.childItems').parent().children(".checkBoxControl").find("input:checkbox").is(":checked");
                if (isChecked) {
                    empty += 1;
                }
            } else if ($element.is(":visible")) {
                empty += 1;
            }
        } else {
            empty += 1;
        }
    }

    // Check for input error
    if (!ignoreIncomplete && date === "null") {
        inputError += 1;
    }

    // Check for range error in the year
    if (year !== "" && (year < 1900 || year > 2100)) {
        rangeError += 1;
    }

    // Handle display of error icons
    if (empty === 0 && rangeError === 0 && inputError === 0) {
        showErrorIcon && $(element).parent().parent().parent().find(".errorIcon").first().hide();
        removeErrorFromElement(element);
    } else {
        showErrorIcon && $element.parent().parent().parent().find(".errorIcon").first().show();
        
        let errorMsg = false;
        
        if (rangeError > 0) {
            errorMsg = $('input[id$="SettingInvalidYearMessage"]').val();
        }
        
        addErrorToElement(element, errorMsg);
    }

    // Open collapsed child item area containing the current item if it has an error or empty field
    if (rangeError + inputError + empty > 0) {
        openItem(element);
    }

    // Update the submit button based on validation results
    enableSubmitButton(false);

    return { error: rangeError + inputError, empty: empty };
}


// validate checklist
function validateCheckList(elmt) {
    var limit = $(elmt).parent().children('input[name$="limit"]').first().val();
    var i = $(elmt).parent().children('.checkListControl').children('.checkBoxControl').find('input[type=checkbox]:checked').length;
    var error = 0;

    if (limit > 0) {
        if (i > limit) {
            error += 1;

            //turn children text color
            $(elmt).parent().children('.checkListControl').children('.checkBoxControl').find('input[type=checkbox]').each(function () {
                if ($(this).attr("checked")) {
                    $(this).parent().children("label").addClass("checkListError");
                } else {
                    $(this).parent().children("label").removeClass("checkListError");
                }
            });
        }
    }

    // Open collapsed child item area containing current item if current item has error or empty field
    if (error > 0)
        openItem(elmt);

    enableSubmitButton(false);

    return {error: error, empty: 0};
}

function validateCheckListLimit(limit, id) {
    // Following is current implementation structure of checklist html:
    // <div class="checkListControl">
    //
    //   <div class="checkBoxControl">
    //     <input type="checkbox" id="xx" />
    //     <label for="xx" />
    //   </div> <!-- end of 1st level input and labels -->
    //
    //   <div class="childItems">
    //
    //     <div class="checkListControl">
    //       <div class="checkBoxControl">
    //         <input type="checkbox" id="yy" />
    //         <label for="yy" />
    //       </div>
    //       <div class="childItems">...</div>
    //     </div> <!-- end of sub checkListControl 1 -->
    //
    //     <div class="checkListControl">
    //       <div class="checkBoxControl">
    //         <input type="checkbox" id="zz" />
    //         <label for="zz" />
    //       </div>
    //       <div class="childItems">...</div>
    //     </div> <!-- end of a sub checkListControl 2 -->
    //
    //   </div> <!-- end of sub checkListControl -->
    // </div> <!-- end of 1st level checkListControl -->

    // Limit validation only applies to direct child elements
    // Gets a total number of checked checkListControls from immediate child list
    var checked = $("#childItems_" + id + " > .checkListControl > .checkBoxControl > input:checkbox:checked").length + +($("input[id$='" + id + "_disabledCheckedCount']").val());

    // if number of checked items exceeds maximum limit
    // add "error" class to limit label (displayed next to parent label in small grayed out text)
    // and (un)highlight each labels with the (in)valid selections
    if (limit < checked) {
        $("#limitLabel_" + id).addClass("checkListError");
        $("#childItems_" + id + " > .checkListControl > .checkBoxControl > label").each(function () {
            // checkbox is a sibling control to the label, and in order to access the selected status of the checkbox,
            // it is neccessary to navigate to parent node and find any checkbox node
            if ($(this).parent().find("input:checkbox").attr("checked"))
                $(this).addClass("checkListError");
            else
                $(this).removeClass("checkListError");
        });
        $("#childItems_" + id + " > .checkListControl > .checkBoxControl > span > label").each(function () {
            // checkbox is a sibling control to the label, and in order to access the selected status of the checkbox,
            // it is neccessary to navigate to parent node and find any checkbox node
            if ($(this).parent().find("input:checkbox").attr("checked"))
                $(this).addClass("checkListError");
            else
                $(this).removeClass("checkListError");
        });
    }
        // if number of checked items does not exceed the maximum limit,
        // remove "checkListError" class from the limit label (displayed next to parent label in small grayed out text)
    // and unhighlight each labels
    else {
        $("#limitLabel_" + id).removeClass("checkListError");
        $("#childItems_" + id + " > .checkListControl > .checkBoxControl > label").each(function () {
            $(this).removeClass("checkListError");
        });
        $("#childItems_" + id + " > .checkListControl > .checkBoxControl > span > label").each(function () {
            $(this).removeClass("checkListError");
        });
    }
}

function validateSpreadsheet(elmt) {

    var empty = 0;

    var value = $(elmt).find("input[type=hidden]").val();
    if (value == "")
        empty += 1;

    if (empty > 0)
        $(elmt).parent().find(".errorIcon").first().show();
    else
        $(elmt).parent().find(".errorIcon").first().hide();

    return {error: 0, empty: empty};
}

function openItem(elmt) {
    if (!$(elmt).closest('.childQuestions').parent().is(":visible")) {
        $(elmt).closest('.childQuestions').parent().slideDown(function () {
            resizeParentIframe('', window);
            $(elmt).closest('.childQuestions').parent().parent().find(".upDownArrow").addClass("active");
        });
    }
}

function validatePreviousInputs(current) {

    var currentId;
    if ($(current).is(".shortTextControl") ||
        $(current).is(".longTextControl") ||
        $(current).is(".numberControl") ||
        $(current).is(".listControl") ||
        $(current).is(".yesNoControl") ||
        $(current).is(".checkListControl input[type=checkbox]")) {
        currentId = $(current).attr("id");
    } else if (
        $(current).is(".monthControl") ||
        $(current).is(".dayControl") ||
        $(current).is(".yearControl")) {
        currentId = $(current).parent(".dateControl").find("input[type=hidden]").attr("id");
    }

    //validate previous element if is a date control
    if (g_prevSelectedId != null && $("#" + g_prevSelectedId).is(".hasDatepicker")) {

        if (g_prevSelectedId != currentId)
            validateDate($("#" + g_prevSelectedId).parent(".dateControl"), false);
    }

    if (currentId != null)
        g_prevSelectedId = currentId;
}

function updateErrorIcon(elmt) {
    if ($(elmt).is(".numberControl")) {

        // show error
        if ($(elmt).val() == "")
            $(elmt).parent().parent().find(".errorIcon").first().show();

        // hide error
        else
            $(elmt).parent().parent().find(".errorIcon").first().hide();

    } else if ($(elmt).is(".dateControl")) {

        // Get value of the date that would be submitted
        var date = $(elmt).find("input[type=hidden]").val();

        // show error
        if (date == "")
            $(elmt).parent().parent().find(".errorIcon").first().show();

        // hide error
        else
            $(elmt).parent().parent().find(".errorIcon").first().hide();

    }
}

/**
 * @param element
 * @param message {string|boolean}
 */
function addErrorToElement(element, message) {
    // Add .is-invalid class to the element
    // Add error message after the element with .invalid-feedback class
    const $element = $(element);
    $element.addClass("is-invalid");
    if (message) {
        $element.parent().find(".invalid-feedback").remove();
        $element.parent().append(`<div class="invalid-feedback">${message}</div>`);   
    }
}

function removeErrorFromElement(element) {
    // Remove .is-invalid class from the element
    // Remove error message after the element with .invalid-feedback class
    const $element = $(element);
    $element.removeClass("is-invalid");
    $element.parent().find(".invalid-feedback").remove();
}

function clearInvalidFields() {
    $(".is-invalid").val("");
}