$(document).ready(function () {
    $.ajaxSetup({ cache: false });

    //set locale of moment js
    moment.locale(AppLanugage.locale);
    moment.fn.customFormat = function (format) {
        const year = this.year();
        const month = String(this.month() + 1).padStart(2, '0'); // Months are 0-indexed
        const day = String(this.date()).padStart(2, '0');

        return format
            .replace('YYYY', year)
            .replace('MM', month)
            .replace('DD', day);
    };

    //set locale for datepicker
    (function ($) {
        $.fn.datepicker.dates['custom'] = {
            days: AppLanugage.days,
            daysShort: AppLanugage.daysShort,
            daysMin: AppLanugage.daysMin,
            months: AppLanugage.months,
            monthsShort: AppLanugage.monthsShort,
            today: AppLanugage.today
        };
    }(jQuery));

    //set datepicker language

    $('body').on('click', '[data-act=ajax-modal]', function (e) {

        if ($(this).closest("td.all").length > 0) {
            $(this).closest("td.all").trigger("click");
        }

        var data = { ajaxModal: 1 },
            url = $(this).attr('data-action-url'),
            isLargeModal = $(this).attr('data-modal-lg'),
            isFullscreenModal = $(this).attr('data-modal-fullscreen'),
            isCustomBgModal = $(this).attr('data-modal-custom-bg'),
            isCloseModal = $(this).attr('data-modal-close'),
            title = $(this).attr('data-title'),
            modalClass = $(this).attr('data-modal-class');

        if (!url) {
            console.log('Ajax Modal: Set data-action-url!');
            return false;
        }
        if (title) {
            $("#ajaxModalTitle").html(title);
        } else {
            $("#ajaxModalTitle").html($("#ajaxModalTitle").attr('data-title'));
        }

        if ($(this).attr("data-post-hide-header")) {
            $("#ajaxModal .modal-header").addClass("hide");
            $("#ajaxModal .modal-footer").addClass("hide");
        } else {
            $("#ajaxModal .modal-header").removeClass("hide");
            $("#ajaxModal .modal-footer").removeClass("hide");
        }

        $("#ajaxModalContent").html($("#ajaxModalOriginalContent").html());
        $("#ajaxModalContent").find(".original-modal-body").removeClass("original-modal-body").addClass("modal-body");
        $("#ajaxModal").modal('show');
        $("#ajaxModal").find(".modal-dialog").removeClass("custom-modal-lg");
        $("#ajaxModal").find(".modal-dialog").removeClass("modal-fullscreen");
        $("#ajaxModal").find(".modal-dialog").removeClass("custom-bg-modal");
        $("#ajaxModal").removeClass("global-search-modal");

        var existingModalClass = $("#ajaxModal").find(".modal-dialog").attr("data-modal-class");
        if (existingModalClass) {
            $("#ajaxModal").find(".modal-dialog").removeClass(existingModalClass);
        }

        $(this).each(function () {
            $.each(this.attributes, function () {
                if (this.specified && this.name.match("^data-post-")) {
                    var dataName = this.name.replace("data-post-", "");
                    data[dataName] = this.value;
                }
            });
        });
        ajaxModalXhr = $.ajax({
            url: url,
            data: data,
            cache: false,
            type: 'POST',
            success: function (response) {
                $("#ajaxModal").find(".modal-dialog").removeClass("mini-modal");
                if (isLargeModal === "1") {
                    $("#ajaxModal").find(".modal-dialog").addClass("custom-modal-lg");
                } else if (isFullscreenModal === "1") {
                    $("#ajaxModal").find(".modal-dialog").addClass("modal-fullscreen");
                }

                if (isCloseModal === "1") {
                    $("#ajaxModal").addClass("global-search-modal");
                }

                if (isCustomBgModal === "1") {
                    $("#ajaxModal").find(".modal-dialog").addClass("custom-bg-modal");
                }

                if (modalClass) {
                    $("#ajaxModal").find(".modal-dialog").addClass(modalClass).attr("data-modal-class", modalClass);
                }

                $("#ajaxModalContent").html(response);

                initAllNotEmptyWYSIWYGEditors(true, $("#ajaxModalContent"));
                setModalScrollbar();

                for (let i = 0; i < 5; i++) {
                    setTimeout(function () {
                        $modalBody = $("#ajaxModalContent").find(".modal-body");
                        if ($modalBody.length && $modalBody.data("scrollbar-added") != "1") {
                            setModalScrollbar();
                        }
                    }, 100 * (i + 1));
                }

                feather.replace();
            },
            statusCode: {
                403: function () {
                    console.log("403: Session expired.");
                    location.reload();
                },
                404: function () {
                    $("#ajaxModalContent").find('.modal-body').html("");
                    appAlert.error("404: Page not found.", { container: '.modal-body', animate: false });
                }
            },
            error: function () {
                $("#ajaxModalContent").find('.modal-body').html("");
                appAlert.error(AppLanugage.somethingWentWrong, { container: '.modal-body', animate: false });
            }
        });
        return false;
    });

    //abort ajax request on modal close.
    $('#ajaxModal').on('hidden.bs.modal', function (e) {
        if (e.target && e.target.id === 'ajaxModal') {
            ajaxModalXhr.abort();
            $("#ajaxModal").find(".modal-dialog").removeClass("modal-lg");
            $("#ajaxModal").find(".modal-dialog").addClass("modal-lg");

            $("#ajaxModalContent").html("");
        }
    });

    //common ajax request
    $('body').on('click show.bs.dropdown', '[data-act=ajax-request]', function () {

        if ($(this).closest("td.all").length > 0) {
            $(this).closest("td.all").trigger("click");
        }

        var data = {},
            $selector = $(this),
            url = $selector.attr('data-action-url'),
            removeOnSuccess = $selector.attr('data-remove-on-success'),
            removeOnClick = $selector.attr('data-remove-on-click'),
            fadeOutOnSuccess = $selector.attr('data-fade-out-on-success'),
            fadeOutOnClick = $selector.attr('data-fade-out-on-click'),
            inlineLoader = $selector.attr('data-inline-loader'),
            targetLoader = $selector.attr('data-target-loader'),
            reloadOnSuccess = $selector.attr('data-reload-on-success'),
            showResponse = $selector.attr('data-show-response'),
            successCallbackFunction = $selector.attr("data-success-callback"),
            contentType = $selector.attr("data-content-type");


        var $target = "";
        if ($selector.attr('data-real-target')) {
            $target = $($selector.attr('data-real-target'));
        } else if ($selector.attr('data-closest-target')) {
            $target = $selector.closest($selector.attr('data-closest-target'));
        }

        if (!url) {
            console.log('Ajax Request: Set data-action-url!');
            return false;
        }

        //remove the target element
        if (removeOnClick && $(removeOnClick).length) {
            $(removeOnClick).remove();
        }

        //remove the target element with fade out effect
        if (fadeOutOnClick && $(fadeOutOnClick).length) {
            $(fadeOutOnClick).fadeOut(function () {
                $(this).remove();
            });
        }

        var headers = {};

        $selector.each(function () {
            $.each(this.attributes, function () {
                if (this.specified && this.name.match("^data-post-")) {
                    var dataName = this.name.replace("data-post-", "");
                    data[dataName] = this.value;
                }

                if (this.specified && this.name.match("^data-header-")) {
                    var headerName = this.name.replace("data-header-", "");
                    headers[headerName] = this.value;
                }
            });
        });


        if (inlineLoader === "1") {
            $selector.addClass("spinning");
        } else if (targetLoader === "1") {
            appLoader.show({ container: $target });
        } else {
            appLoader.show();
        }

        var ajaxOptions = {
            url: url,
            data: data,
            cache: false,
            type: 'POST',
            headers: headers,
            success: function (response) {
                if (inlineLoader === "1") {
                    $selector.removeClass("spinning");
                }

                if (successCallbackFunction && typeof window[successCallbackFunction] != 'undefined') {
                    window[successCallbackFunction](response, $selector);
                }

                if (showResponse && response) {
                    if (response.success) {
                        if (response.message) {
                            appAlert.success(response.message, { duration: 10000 });
                        }

                        if (reloadOnSuccess) {
                            location.reload();
                        }

                    } else {
                        appAlert.error(response.message);
                    }
                } else if (reloadOnSuccess) {
                    location.reload();
                }

                //remove the target element
                if (removeOnSuccess && $(removeOnSuccess).length) {
                    $(removeOnSuccess).remove();
                }

                //remove the target element with fade out effect
                if (fadeOutOnSuccess && $(fadeOutOnSuccess).length) {
                    $(fadeOutOnSuccess).fadeOut(function () {
                        $(this).remove();
                    });
                }

                //trigger ajaxRequestHooks
                var group = $selector.attr("data-request-group");
                if (group && window.ajaxRequestHooks && window.ajaxRequestHooks[group]) {

                    window.ajaxRequestHooks[group].forEach(function (hook) {
                        if (typeof hook.onSuccess === 'function') {
                            hook.onSuccess(data);
                        }
                    });
                }

                appLoader.hide();
                if ($target.length) {
                    if ($selector.attr("data-append")) {
                        $selector.remove();
                        $target.append(response);
                    } else {
                        $target.html(response);
                    }
                }
            },
            statusCode: {
                404: function () {
                    appLoader.hide();
                    appAlert.error("404: Page not found.");
                }
            },
            error: function () {
                appLoader.hide();
                appAlert.error(AppLanugage.somethingWentWrong);
            }
        };

        if (showResponse) {
            ajaxOptions.dataType = 'json';
        }

        if (contentType == "application/json") {
            ajaxOptions.contentType = contentType;
            ajaxOptions.data = JSON.stringify(ajaxOptions.data);
        }

        ajaxRequestXhr = $.ajax(ajaxOptions);

    });

    //bind ajax tab
    $('body').on('click', '[data-bs-toggle="ajax-tab"] a', function () {
        var $this = $(this),
            loadurl = $this.attr('href'),
            target = $this.attr('data-bs-target');
        if (!target)
            return false;

        if ($this.attr("data-reload")) {
            //remove data first if it's need to reload everytime
            $(target).html("");
        }

        if ($(target).html() === "" || $this.attr("data-reload")) {
            appLoader.show({ container: target, css: "right:50%; bottom:auto;" });

            appAjaxRequest({
                url: loadurl,
                cache: false,
                type: 'GET',
                success: function (response) {
                    $(target).html(response);
                    feather.replace();
                    selectLastlySelectedTab(target);
                },
                statusCode: {
                    403: function () {
                        console.log("403: Session expired.");
                        location.reload();
                    },
                    404: function () {
                        appLoader.hide();
                        appAlert.error("404: Page not found.");
                    }
                },
                error: function () {
                    appLoader.hide();
                    appAlert.error(AppLanugage.somethingWentWrong);
                }
            });

            //            $.get(loadurl, function (data, test, test2) {
            //                $(target).html(data);
            //                feather.replace();
            //                selectLastlySelectedTab(target);
            //            });
        }
        $this.tab('show');
        return false;
    });

    selectLastlySelectedTab();

    $('body').on('click', '[data-toggle="app-modal"]', function () {
        var sidebar = true;

        if ($(this).attr("data-sidebar") === "0") {
            sidebar = false;
        }
        appContentModal.init({ url: $(this).attr("data-url"), content_url: $(this).attr("data-content_url"), sidebar: sidebar, sourceElement: $(this) });
        return false;
    });




    //prepare common delete confimation
    var recordDeleteHandler = function (result, $target) {
        var callbackFunction = $target.attr("data-success-callback");

        if (callbackFunction && typeof window[callbackFunction] != 'undefined') {
            window[callbackFunction](result, $target);

            if (result.message) {
                appAlert.warning(result.message, { duration: 20000 });
            }

        }
    };

    var linkDeleteConfirmationHandler = function (e) {
        deleteConfirmationHandler(e, recordDeleteHandler);
    };


    //bind the delete confimation modal which links are not in tables. because there is an another logic for datatable.
    $('body').on('click', 'a[data-action=delete-confirmation]:not(table a)', linkDeleteConfirmationHandler);

    mapBodyColor();

});


function delayAction(callback, ms) {
    var timer = 0;
    return function () {
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
            callback.apply(context, args);
        }, ms || 0);
    };
}



//select the lastly selected ajax-tab automatically if it exists user-wise
function selectLastlySelectedTab(target) {
    if (!target) {
        target = "";
    }

    $(target + " [data-bs-toggle='ajax-tab']").each(function () {
        var $this = $(this),
            tabList = $this.attr("id"),
            lastTab = getCookie("user_" + AppHelper.userId + "_" + tabList),
            $specificTab = $this.find("[data-bs-target='" + lastTab + "']");

        if ($this.attr("data-do-not-save-state") == "1") {
            lastTab = null;
        }

        if (lastTab && $specificTab.attr("data-bs-target")) {
            setTimeout(function () {
                $specificTab.trigger("click");
            }, 50);
        } else {
            //load first tab
            $this.find("a").first().trigger("click");
        }
    });
}

var registerNewHook = function (referenceId, onSuccess, hookVarName, hookType, contextId) {
    if (!referenceId || typeof onSuccess !== 'function') {
        return false;
    }

    if (!window[hookVarName]) {
        window[hookVarName] = {};
    }

    if (!window[hookVarName][referenceId]) {
        window[hookVarName][referenceId] = [];
    }

    // Remove the existing hook if it matches hookType and contextId
    if (hookType && contextId) {
        window[hookVarName][referenceId] = window[hookVarName][referenceId].filter(function (hook) {
            return !(hook.hookType === hookType && hook.contextId === contextId);
        });
    }

    window[hookVarName][referenceId].push({
        onSuccess: onSuccess,
        hookType: hookType,
        contextId: contextId
    });
}

var registerAppFormHook = function (formId, onSuccess, hookType, contextId) {
    registerNewHook(formId, onSuccess, "appFormHooks", hookType, contextId);
};

var registerAjaxRequestHook = function (groupId, onSuccess, hookType, contextId) {
    registerNewHook(groupId, onSuccess, "ajaxRequestHooks", hookType, contextId);
};

var registerAppModifierHook = function (groupId, onSuccess, hookType, contextId) {
    registerNewHook(groupId, onSuccess, "appModifierHooks", hookType, contextId);
};

var registerAppTableRowUpdateHook = function (tableId, onSuccess, hookType, contextId) {
    registerNewHook(tableId, onSuccess, "appTableRowUpdateHook", hookType, contextId);
};
var registerAppTableRowDeleteHook = function (tableId, onSuccess, hookType, contextId) {
    registerNewHook(tableId, onSuccess, "appTableRowDeleteHook", hookType, contextId);
};


//custom app form controller
(function ($) {
    $.fn.appForm = function (options) {

        var defaults = {
            ajaxSubmit: true,
            isModal: true,
            closeModalOnSuccess: true,
            dataType: "json",
            showLoader: true,
            onModalClose: function () {
            },
            onSuccess: function () {
            },
            onError: function () {
                return true;
            },
            onSubmit: function () {
            },
            onAjaxSuccess: function () {
            },
            beforeAjaxSubmit: function (data, self, options) {
            }
        };
        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }
        this.each(function () {
            if (settings.ajaxSubmit) {
                validateForm($(this), function (form) {
                    settings.onSubmit();


                    if (settings.isModal) {
                        maskModal($("#ajaxModalContent").find(".modal-body"));
                    } else {
                        $(form).find('[type="submit"]').attr('disabled', 'disabled');
                    }

                    //set empty value to all textarea, if they are empty
                    if (AppHelper.settings.enableRichTextEditor === "1") {
                        $("textarea").each(function () {
                            var $instance = $(this);
                            if ($instance.attr("data-rich-text-editor")) {
                                if ($instance.val() === '<p><br></p>') {
                                    $instance.val('');
                                }
                            }
                        });
                    }

                    $(form).ajaxSubmit({
                        dataType: settings.dataType,
                        beforeSubmit: function (data, self, options) {

                            //Modified \assets\js\jquery-validation\jquery.form.js #1178.
                            //Added data  a.push({name: n, value: v, type: el.type, required: el.required, data: $(el).data()});

                            //to set the convertDateFormat with the input fields, we used the setDatePicker function.
                            //it is the easiest way to regognize the date fields.
                            var removeIndexes = [],
                                checkboxes = {};

                            $.each(data, function (index, obj) {

                                if (obj.data && obj.data.encode_ajax_post_data == "1") {
                                    //data[index]["value"] = encodeAjaxPostData(getWYSIWYGEditorHTML($(form).find('[name="'+obj.name+'"]')));
                                    data[index]["value"] = encodeAjaxPostData(obj.value);
                                }

                                if (obj.data && obj.data.convertDateFormat && obj.value) {
                                    data[index]["value"] = convertDateToYMD(obj.value);
                                }

                                // Replace the current value with the comma-separated values
                                if (obj.data && obj.data.prepare_checkboxes_data == "1") {

                                    if (!checkboxes[obj.name]) {
                                        checkboxes[obj.name] = obj;
                                    } else {
                                        checkboxes[obj.name].value += checkboxes[obj.name].value ? ", " + obj.value : obj.value ? obj.value : "";
                                    }

                                    removeIndexes.push(index);
                                }
                            });

                            Object.keys(checkboxes).forEach(checkoxKey => {
                                data.push(checkboxes[checkoxKey]);
                            });

                            if (removeIndexes.length > 0) {
                                data = data.filter(function (obj, index) {
                                    // Return true to keep the field, false to remove it
                                    if (removeIndexes.includes(index)) {
                                        return false;
                                    } else {
                                        return true;
                                    }
                                });
                            }

                            if (!settings.isModal && settings.showLoader) {
                                appLoader.show({ container: form, css: "top:2%; right:46%;" });
                            }

                            var callbackResult = settings.beforeAjaxSubmit(data, self, options);

                            if (callbackResult === false) {
                                unmaskModal();
                                return false;
                            }

                            self.data('app_post_data', data);

                        },
                        success: function (result, statusText, xhr, $form) {
                            settings.onAjaxSuccess(result);

                            if (result.success) {
                                settings.onSuccess(result);
                                if (settings.isModal && settings.closeModalOnSuccess) {
                                    closeAjaxModal(true);
                                }

                                if (!settings.isModal) {
                                    $(form).find("textarea").each(function () {
                                        if ($(this).attr("data-rich-text-editor") != undefined && $(this).attr("data-keep-rich-text-editor-after-submit") == undefined) {
                                            destroyWYSIWYGEditor($(this))
                                        }
                                    });
                                }

                                //trigger appFormHooks
                                if ($form && window.appFormHooks) {
                                    var formId = $(form).attr('id');
                                    if (formId && window.appFormHooks[formId]) {
                                        var formPostData = {};
                                        $.each($form.serializeArray(), function () {
                                            formPostData[this.name] = this.value;
                                        });

                                        window.appFormHooks[formId].forEach(function (hook) {
                                            if (typeof hook.onSuccess === 'function') {
                                                hook.onSuccess(formPostData, result);
                                            }
                                        });

                                    }
                                }

                                appLoader.hide();
                            } else {
                                if (settings.onError(result)) {
                                    if (settings.isModal) {
                                        unmaskModal();
                                        if (result.message) {
                                            appAlert.error(result.message, { container: '.modal-body', animate: false });
                                        }
                                    } else if (result.message) {
                                        appAlert.error(result.message);
                                    }
                                }
                            }

                            $(form).find('[type="submit"]').removeAttr('disabled');
                        }
                    });
                });
            } else {
                validateForm($(this));
            }
        });
        /*
         * @form : the form we want to validate;
         * @customSubmit : execute custom js function insted of form submission. 
         * don't pass the 2nd parameter for regular form submission
         */

        function convertDateToYMD(date) {
            if (date) {
                var dateFormat = AppHelper.settings.dateFormat || "Y.m.d",
                    dateFormat = dateFormat.toLowerCase(),
                    separator = dateFormat.charAt("1"),
                    dateFormatArray = dateFormat.split(separator),
                    yearIndex = 0,
                    monthIndex = 1,
                    dayIndex = 2;

                if (dateFormatArray[1] === "y") {
                    yearIndex = 1;
                } else if (dateFormatArray[2] === "y") {
                    yearIndex = 2;
                }

                if (dateFormatArray[0] === "m") {
                    monthIndex = 0;
                } else if (dateFormatArray[2] === "m") {
                    monthIndex = 2;
                }

                if (dateFormatArray[0] === "d") {
                    dayIndex = 0;
                } else if (dateFormatArray[1] === "d") {
                    dayIndex = 1;
                }

                var dateValue = date.split(separator);

                return dateValue[yearIndex] + "-" + dateValue[monthIndex] + "-" + dateValue[dayIndex];
            }

        }

        function validateForm(form, customSubmit) {
            //add custom method
            $.validator.addMethod("greaterThanOrEqual",
                function (value, element, params) {
                    var paramsVal = params;
                    if (params && (params.indexOf("#") === 0 || params.indexOf(".") === 0)) {
                        paramsVal = $(params).val();
                    }

                    if (typeof $(element).attr("data-rule-required") === 'undefined' && !value) {
                        return true;
                    }

                    if (!/Invalid|NaN/.test(new Date(convertDateToYMD(value)))) {
                        return !paramsVal || (new Date(convertDateToYMD(value)) >= new Date(convertDateToYMD(paramsVal)));
                    }
                    return isNaN(value) && isNaN(paramsVal)
                        || (Number(value) >= Number(paramsVal));
                }, 'Must be greater than {0}.');

            //add custom method
            $.validator.addMethod("greaterThan",
                function (value, element, params) {
                    var paramsVal = params;
                    if (params && (params.indexOf("#") === 0 || params.indexOf(".") === 0)) {
                        paramsVal = $(params).val();
                    }
                    if (!/Invalid|NaN/.test(new Number(value))) {
                        return new Number((value)) > new Number((paramsVal));
                    }
                    return isNaN(value) && isNaN(paramsVal)
                        || (Number(value) > Number(paramsVal));
                }, 'Must be greater than.');

            //add custom method
            $.validator.addMethod("mustBeSameYear",
                function (value, element, params) {
                    var paramsVal = params;
                    if (params && (params.indexOf("#") === 0 || params.indexOf(".") === 0)) {
                        paramsVal = $(params).val();
                    }
                    if (!/Invalid|NaN/.test(new Date(convertDateToYMD(value)))) {
                        var dateA = new Date(convertDateToYMD(value)), dateB = new Date(convertDateToYMD(paramsVal));
                        return (dateA && dateB && dateA.getFullYear() === dateB.getFullYear());
                    }
                }, 'The year must be same for both dates.');

            $(form).validate({
                submitHandler: function (form) {
                    if (customSubmit) {
                        customSubmit(form);
                    } else {
                        return true;
                    }
                },
                highlight: function (element) {
                    $(element).closest('.form-group').addClass('has-error');
                },
                unhighlight: function (element) {
                    $(element).closest('.form-group').removeClass('has-error');
                },
                errorElement: 'span',
                errorClass: 'help-block',
                ignore: ":hidden:not(.validate-hidden)",
                errorPlacement: function (error, element) {
                    if (element.parent('.input-group').length) {
                        error.insertAfter(element.parent());
                    } else {
                        error.insertAfter(element);
                    }
                }
            });
            //handeling the hidden field validation like select2
            $(".validate-hidden").click(function () {
                $(this).closest('.form-group').removeClass('has-error').find(".help-block").hide();
            });
        }

        //show loadig mask on modal before form submission;
        function maskModal($maskTarget) {
            var padding = $maskTarget.height() - 80;
            if (padding > 0) {
                padding = Math.floor(padding / 2);
            }
            $maskTarget.after("<div class='modal-mask'><div class='circle-loader'></div></div>");
            //check scrollbar
            var height = $maskTarget.outerHeight();
            $('.modal-mask').css({ "width": $maskTarget.width() + 22 + "px", "height": height + "px", "padding-top": padding + "px" });
            $maskTarget.closest('.modal-dialog').find('[type="submit"]').attr('disabled', 'disabled');
            $maskTarget.addClass("hide");
        }

        //remove loadig mask from modal
        function unmaskModal() {
            var $maskTarget = $(".modal-body").removeClass("hide");
            $maskTarget.closest('.modal-dialog').find('[type="submit"]').removeAttr('disabled');
            $maskTarget.removeClass("hide");
            $(".modal-mask").remove();
        }

        //colse ajax modal and show success check mark
        function closeAjaxModal(success) {
            if (success) {
                $(".modal-mask").html("<div class='circle-done'><i data-feather='check' stroke-width='5'></i></div>");
                setTimeout(function () {
                    $(".modal-mask").find('.circle-done').addClass('ok');
                }, 30);
            }
            setTimeout(function () {
                $(".modal-mask").remove();
                $("#ajaxModal").modal('toggle');
                settings.onModalClose();
            }, 1000);
        }


        this.closeModal = function () {
            closeAjaxModal(true);
        };

        return this;
    };
})(jQuery);


(function ($) {

    $.fn.appDropdown = function (options) {
        var defaults = {
            list_data: [],
            showListAfterSearch: false
        };

        if (options === 'destroy') {
            return this.each(function () {
                var $selector = $(this);
                if ($selector.data("select2")) {
                    $selector.select2("destroy");
                }
            });
        }

        var settings = appExtend({}, defaults, options);
        if (!settings) {
            return false;
        }

        return this.each(function () {
            var $selector = $(this);
            var select2Options = {
                placeholder: $selector.attr('placeholder')
            };

            if (settings.list_data && settings.list_data[0]) {
                var firstItem = settings.list_data[0];
                if (firstItem.dropdown_type == "MAX_DROPDOWN_ITEMS_REACHED" && firstItem.source_url) {
                    select2Options.minimumInputLength = 2;
                    select2Options.query = function (query) {
                        clearTimeout(this._t);
                        this._t = setTimeout(function () {
                            appAjaxRequest({
                                url: firstItem.source_url,
                                type: 'POST',
                                dataType: 'json',
                                data: { search: query.term, blank_option_text: firstItem.blank_option_text },
                                success: function (data) {
                                    query.callback({ results: data });
                                }
                            });
                        }, 350); // delay of 350ms
                    }

                    select2Options.initSelection = function (element, callback) {
                        //set the selected value in edit mode.
                        if ($selector.val()) {
                            appAjaxRequest({
                                url: firstItem.source_url,
                                type: 'POST',
                                dataType: 'json',
                                data: { id: $selector.val() },
                                success: function (response) {
                                    if (response && response[0] && response[0].id == $selector.val() && response[0].text) {
                                        callback({ id: response[0].id, text: response[0].text });
                                    }
                                }
                            });
                        } else if (firstItem && firstItem.blank_option_text) {
                            callback({ id: "", text: firstItem.blank_option_text });
                        }
                    }

                    select2Options.formatInputTooShort = function () { return AppLanugage.enterMinimum2characters }
                    // select2Options.formatNoMatches = function () { return "Nothing found"; }
                    // select2Options.formatSearching = function () { return "Looking..."; }
                    // select2Options.escapeMarkup = function (m) {
                    //     return m; // prevent escaping the returned strings
                    // }
                }
            }

            if ($selector.is("select")) {
                //in mobile, don't show the search option if options length is less than 20
                if (isMobile()) {
                    $selector.find("option").length < 20 ? select2Options.minimumResultsForSearch = -1 : "";
                }

                if (settings.onChangeCallback) {
                    $selector.select2(select2Options).on("change", function () {
                        var instance = $(this);
                        settings.onChangeCallback(instance.val(), instance);
                    });
                } else {
                    $selector.select2(select2Options);
                }

            } else if ($selector.is("input")) {


                if (settings.multiple) {
                    $selector.data("multiple", 1);
                    select2Options.multiple = true;
                } else if ($selector.data("multiple") == 1) {
                    select2Options.multiple = true;
                }

                if (settings.canCreateTags) {
                    select2Options.tags = settings.list_data;
                } else if ($selector.data("can-create-tags") == 1) {
                    select2Options.tags = settings.list_data;
                } else {
                    select2Options.data = settings.list_data;
                }

                if (settings.escapeMarkup) {
                    select2Options.escapeMarkup = settings.escapeMarkup;
                }

                //in mobile, don't show the search option if options length is less than 20
                if (isMobile() && select2Options.data) {
                    select2Options.data.length < 20 ? select2Options.minimumResultsForSearch = -1 : "";
                }

                var selectorData = $selector.data();

                $selector.select2(select2Options).on("change", function () {
                    // If there are dependent dropdowns to reload on change
                    if (selectorData.roload_dropdown_on_change) {
                        var elements = selectorData.roload_dropdown_on_change.split(",");

                        elements.forEach(function (element) {
                            var $element = $(element);

                            if ($element.length) {
                                var elementData = $element.data();

                                if (elementData.source_url) {
                                    var data = {};

                                    // If post_field_values_of is specified, prepare data for the AJAX request
                                    if (elementData.post_field_values_of) {
                                        var postFieldValuesOf = elementData.post_field_values_of.split(",");

                                        postFieldValuesOf.forEach(function (fieldName) {
                                            data[fieldName] = $("[name='" + fieldName + "']").val();
                                        });
                                    }

                                    // Destroy existing select2 if initialized and clear value
                                    if ($element.data("select2")) {
                                        $element.select2("destroy").val("");
                                    }

                                    appAjaxRequest({
                                        url: elementData.source_url,
                                        type: 'POST',
                                        data: data,
                                        dataType: 'json',
                                        success: function (newListData) {
                                            var elementOptions = {
                                                list_data: newListData
                                            };
                                            if ($element.data("multiple") == 1) {
                                                elementOptions.multiple = true;
                                            }

                                            if ($element.data("can-create-tags") == 1) {
                                                elementOptions.canCreateTags = true;
                                            }

                                            $element.appDropdown(elementOptions);
                                        }
                                    });
                                }
                            }
                        });
                    }

                    if (settings.onChangeCallback) {
                        var instance = $(this);
                        settings.onChangeCallback(instance.val(), instance);
                    }

                });
            }
        });
    };

})(jQuery);


function getWeekRange(date) {
    //set first and last day of week
    if (!date)
        date = moment().customFormat("YYYY-MM-DD");

    var dayOfWeek = moment(date).format("E"),
        diff = dayOfWeek - AppHelper.settings.firstDayOfWeek,
        range = {};

    if (diff < 7) {
        range.firstDateOfWeek = moment(date).subtract(diff, 'days').customFormat("YYYY-MM-DD");
    } else {
        range.firstDateOfWeek = moment(date).customFormat("YYYY-MM-DD");
    }

    if (diff < 0) {
        range.firstDateOfWeek = moment(range.firstDateOfWeek).subtract(7, 'days').customFormat("YYYY-MM-DD");
    }

    range.lastDateOfWeek = moment(range.firstDateOfWeek).add(6, 'days').customFormat("YYYY-MM-DD");
    return range;
};

//find saved filter
function getFilterInfo(filterId) {
    var filterInfo = null;
    $.each(AppHelper.settings.filters || [], function (index, filter) {
        if (filterId === filter.id) {
            filterInfo = filter;
        }
    });
    return filterInfo;
}

//always check the getContextFilterInfo to apply filter 
function getContextFilterInfo(filterId, settings) {
    var filterInfo = getFilterInfo(filterId),
        context = settings.smartFilterIdentity,
        context_id = settings.contextMeta ? settings.contextMeta.contextId : "";
    if ((filterInfo && context) && (filterInfo.context !== context && filterInfo.context !== context + "_" + context_id)) {
        filterInfo = null; // context doesn't matched 
    }
    return filterInfo;
}


function getDynamicDateRanges() {
    return {
        'today': [moment().customFormat("YYYY-MM-DD"), moment().customFormat("YYYY-MM-DD")],
        'yesterday': [moment().subtract(1, 'days').customFormat("YYYY-MM-DD"), moment().subtract(1, 'days').customFormat("YYYY-MM-DD")],
        'tomorrow': [moment().add(1, 'days').customFormat("YYYY-MM-DD"), moment().add(1, 'days').customFormat("YYYY-MM-DD")],
        'last_7_days': [moment().subtract(6, 'days').customFormat("YYYY-MM-DD"), moment().customFormat("YYYY-MM-DD")],
        'next_7_days': [moment().customFormat("YYYY-MM-DD"), moment().add(6, 'days').customFormat("YYYY-MM-DD")],
        'last_30_days': [moment().subtract(29, 'days').customFormat("YYYY-MM-DD"), moment().customFormat("YYYY-MM-DD")],
        'this_month': [moment().startOf('month').customFormat("YYYY-MM-DD"), moment().endOf('month').customFormat("YYYY-MM-DD")],
        'last_month': [moment().subtract(1, 'month').startOf('month').customFormat("YYYY-MM-DD"), moment().subtract(1, 'month').endOf('month').customFormat("YYYY-MM-DD")],
        'next_month': [moment().add(1, 'month').startOf('month').customFormat("YYYY-MM-DD"), moment().add(1, 'month').endOf('month').customFormat("YYYY-MM-DD")],
        'this_year': [moment().startOf('year').customFormat("YYYY-MM-DD"), moment().endOf('year').customFormat("YYYY-MM-DD")],
        'next_year': [moment().add(1, 'year').startOf('year').customFormat("YYYY-MM-DD"), moment().add(1, 'year').endOf('year').customFormat("YYYY-MM-DD")],
        'last_year': [moment().subtract(1, 'year').startOf('year').customFormat("YYYY-MM-DD"), moment().subtract(1, 'year').endOf('year').customFormat("YYYY-MM-DD")]
    };

}

function getDynamicDates() {
    return {
        'today': moment().customFormat("YYYY-MM-DD"),
        'yesterday': moment().subtract(1, 'days').customFormat("YYYY-MM-DD"),
        'tomorrow': moment().add(1, 'days').customFormat("YYYY-MM-DD"),
        'in_last_2_days': moment().subtract(2, 'days').customFormat("YYYY-MM-DD"),
        'in_last_7_days': moment().subtract(7, 'days').customFormat("YYYY-MM-DD"),
        'in_last_15_days': moment().subtract(15, 'days').customFormat("YYYY-MM-DD"),
        'in_next_7_days': moment().add(7, 'days').customFormat("YYYY-MM-DD"),
        'in_next_15_days': moment().add(15, 'days').customFormat("YYYY-MM-DD"),
        'in_last_30_days': moment().add(30, 'days').customFormat("YYYY-MM-DD"),
        'in_last_1_month': moment().subtract(1, 'months').customFormat("YYYY-MM-DD"),
        'in_last_3_months': moment().subtract(3, 'months').customFormat("YYYY-MM-DD"),
        'start_of_month': moment().startOf('month').customFormat("YYYY-MM-DD"),
        'end_of_month': moment().endOf('month').customFormat("YYYY-MM-DD"),
        'start_of_year': moment().startOf('year').customFormat("YYYY-MM-DD"),
        'end_of_year': moment().endOf('year').customFormat("YYYY-MM-DD")
    };

}


function getContextFilters(settings) {
    var filters = [],
        context = settings.smartFilterIdentity,
        context_id = settings.contextMeta ? settings.contextMeta.contextId : "";

    var context_with_id = "";
    if (context_id) {
        context_with_id = context + "_" + context_id;
    }

    if (context) {
        $.each(AppHelper.settings.filters || [], function (index, filter) {
            if (filter.context === context || filter.context === context_with_id) {
                filters.push(filter);
            }
        });
    }

    filters.sort(function (a, b) {
        var fa = a.title.toLowerCase(),
            fb = b.title.toLowerCase();

        if (fa < fb) {
            return -1;
        }
        if (fa > fb) {
            return 1;
        }
        return 0;
    });

    return filters;
}


class DefaultFilters {
    constructor(settings) {
        this.settings = settings;
        this.init();
        return this.settings;
    }
    init() {
        var filterId = getFilterIdFromCookie(this.settings);
        if (filterId && this.settings.stateSave && !this.settings.ignoreSavedFilter && getContextFilterInfo(filterId, this.settings)) {
            this.initSelectedFilter(filterId);

        } else {
            this.prepareDefaultDateRangeFilterParams();
            this.prepareDefaultCheckBoxFilterParams();
            this.prepareDefaultMultiSelectilterParams();
            this.prepareDefaultRadioFilterParams();
            this.prepareDefaultDropdownFilterParams();
            this.prepareDefaultrSingleDatepickerFilterParams();
            this.prepareDefaultrRngeDatepickerFilterParams();
            this.prepareDefaultRangeRadioButtonsFilterParams();
            this.prepareDefaultDynamicRangeFilterParams();
        }
    }
    initSelectedFilter(filterId) {
        if (filterId) {
            var filterParams = {};
            var filterInfo = getContextFilterInfo(filterId, this.settings);
            if (filterInfo) {
                filterParams = cloneDeep(filterInfo.params);
            }

            this.settings.filterParams = cloneDeep(filterParams);
            this.applyInitialFilterHook();

        }
    }
    applyInitialFilterHook() {
        //if you need to modify any selected filter, you can do that here
        var dynamicFilterName = "";

        $.each(this.settings.filterParams, function (paramName, value) {
            //for dynamic filter, we have to set the value at the end of this process.
            //otherwise the range value could be overwritten on the next loop. 
            if (value == "dynamic") {
                dynamicFilterName = paramName;
            }

        });

        if (dynamicFilterName && this.settings.filterParams && this.settings.filterParams[dynamicFilterName + "_dynamic"]) {

            var defaultRanges = getDynamicDateRanges();
            var dynamicRangeName = this.settings.filterParams[dynamicFilterName + "_dynamic"];

            if (defaultRanges && defaultRanges[dynamicRangeName]) {

                //todo: add a common fuction to set start_date and end_date
                this.settings.filterParams.start_date = defaultRanges[dynamicRangeName][0];
                this.settings.filterParams.end_date = defaultRanges[dynamicRangeName][1];
            }
        }

    }
    prepareDefaultDateRangeFilterParams(dateRangeType) {
        var settings = this.settings;
        if (!dateRangeType) {
            dateRangeType = settings.dateRangeType;
        }

        if (dateRangeType === "daily") {
            settings.filterParams.start_date = moment().customFormat(settings._inputDateFormat);
            settings.filterParams.end_date = settings.filterParams.start_date;
        } else if (dateRangeType === "monthly") {
            var daysInMonth = moment().daysInMonth(),
                yearMonth = moment().customFormat("YYYY-MM");
            settings.filterParams.start_date = yearMonth + "-01";
            settings.filterParams.end_date = yearMonth + "-" + daysInMonth;
        } else if (dateRangeType === "yearly") {
            var year = moment().customFormat("YYYY");
            settings.filterParams.start_date = year + "-01-01";
            settings.filterParams.end_date = year + "-12-31";
        } else if (dateRangeType === "weekly") {
            var range = getWeekRange();
            settings.filterParams.start_date = range.firstDateOfWeek;
            settings.filterParams.end_date = range.lastDateOfWeek;
        }
        this.settings = settings;
    }
    prepareDefaultCheckBoxFilterParams() {
        var settings = this.settings;
        var values = [],
            name = "";
        $.each(settings.checkBoxes, function (index, option) {
            name = option.name;
            if (option.isChecked) {
                values.push(option.value);
            }
        });
        settings.filterParams[name] = values;
        this.settings = settings;
    }
    prepareDefaultMultiSelectilterParams() {
        var settings = this.settings;
        $.each(settings.multiSelect, function (index, option) {
            var saveSelection = option.saveSelection,
                selections = getCookie(option.name);

            var values = [];

            if (saveSelection && selections) {
                selections = selections.split("-");
                values = selections;
            } else {
                $.each(option.options, function (index, listOption) {
                    if (listOption.isChecked) {
                        values.push(listOption.value);
                    }
                });
            }

            settings.filterParams[option.name] = values;
        });

        this.settings = settings;
    }
    prepareDefaultRadioFilterParams() {
        var settings = this.settings;
        $.each(settings.radioButtons, function (index, option) {
            if (option.isChecked) {
                settings.filterParams[option.name] = option.value;
            }
        });
        this.settings = settings;
    }
    prepareDefaultDropdownFilterParams() {
        var settings = this.settings;
        $.each(settings.filterDropdown || [], function (index, dropdown) {
            if (dropdown.value) {
                settings.filterParams[dropdown.name] = dropdown.value;
            } else {
                $.each(dropdown.options, function (index, option) {
                    if (option.isSelected) {
                        settings.filterParams[dropdown.name] = option.id;
                    }
                });
            }
        });
        this.settings = settings;
    }
    prepareDefaultrSingleDatepickerFilterParams() {
        var settings = this.settings;
        $.each(settings.singleDatepicker || [], function (index, datepicker) {
            $.each(datepicker.options || [], function (index, option) {
                if (option.isSelected) {
                    settings.filterParams[datepicker.name] = option.value;
                }
            });
        });
        this.settings = settings;
    }
    prepareDefaultrRngeDatepickerFilterParams() {

        var settings = this.settings;
        $.each(settings.rangeDatepicker || [], function (index, datepicker) {

            if (datepicker.startDate && datepicker.startDate.value) {
                settings.filterParams[datepicker.startDate.name] = datepicker.startDate.value;
            }

            if (datepicker.startDate && datepicker.endDate.value) {
                settings.filterParams[datepicker.endDate.name] = datepicker.endDate.value;
            }

        });
        this.settings = settings;
    }

    prepareDefaultRangeRadioButtonsFilterParams() {
        var settings = this.settings;
        var it = this;
        $.each(settings.rangeRadioButtons || [], function (index, option) {
            if (option.selectedOption) {
                //remove it.. settings.dateRangeType = option.selectedOption;
                settings.filterParams[option.name] = option.selectedOption;

                it.prepareDefaultDateRangeFilterParams(option.selectedOption);
            }
        });
        this.settings = settings;
    }

    prepareDefaultDynamicRangeFilterParams() {
        var settings = this.settings;
        $.each(settings.rangeRadioButtons || [], function (index, option) {
            if (option.dynamicRanges) {
                var filterName = option.name + "_dynamic";
                settings.filterParams[filterName] = option.selectedDynamicRange || option.dynamicRanges[0];
            }
        });
        this.settings = settings;
    }

}

var prepareDefaultFilters = function (settings) {
    var filters = new DefaultFilters(settings);
    return filters;
};

function cloneDeep(value) {
    if (typeof value !== 'object' || value === null) {
        return value;
    }

    let clone;

    if (Array.isArray(value)) {
        clone = [];
        for (let i = 0; i < value.length; i++) {
            clone[i] = cloneDeep(value[i]);
        }
    } else {
        clone = {};
        for (let key in value) {
            if (value.hasOwnProperty(key)) {
                clone[key] = cloneDeep(value[key]);
            }
        }
    }

    return clone;
}

function getFilterIdFromCookie(settings) {
    var userId = AppHelper.userId ? AppHelper.userId : "public";
    return getCookie("filter_" + settings.smartFilterIdentity + "_" + userId);
}


class BuildFilters {
    constructor(settings, $instanceWrapper, $instance) {
        this.leftFilterSectionClsss = ".filter-section-left";
        this.rightFilterSectionClsss = ".filter-section-right";
        this.filterFormClass = ".filter-form";
        this.settings = settings;
        this.$instanceWrapper = $instanceWrapper;
        this.$instance = $instance;
        this.randomId = getRandomAlphabet(5);
        this.filterElements = []; // [paramName] = {setValue: function()}
        this.activeFilterId = "";
        this.state = "new_filter"; //new_filter/change_filter
    }
    init() {
        this.prepareSurchOption();
        //this.prepareCollapsePannelButton();
        this.prepareReloadButton();
        this.prepareSmartFilterDropdown();
        this.prepareFilterFormShowButton();
        this.prepareBookmarkFilterButtons();
        this.hideFilterForm();

        this.prepareDropdownFilters();
        this.prepareDateRangePicker();
        this.prepareDatePickerFilter();
        this.prepareSingleDatePicker();
        this.prepareMultiselectFilter();
        this.prepareCheckboxFilter();
        this.prepareRadioFilter();

        this.prepareRangeRadioButtons();

        this.prepareSaveFilterButton();
        this.prepareCancelFilterFormButton();

        this.initActiveFilterFromCookie();

        this.prepareSelectionHandler();

        if (!window.Filters) {
            window.Filters = [];
        }

        window.Filters[this.settings.smartFilterIdentity] = this;
    }
    saveSelectedFilter() {
        var userId = AppHelper.userId ? AppHelper.userId : "public";
        setCookie("filter_" + this.settings.smartFilterIdentity + "_" + userId, this.activeFilterId);
    }
    initActiveFilterFromCookie() {
        if (this.settings.stateSave && !this.settings.ignoreSavedFilter) {
            var filterId = getFilterIdFromCookie(this.settings);

            if (filterId) {
                var filterInfo = getContextFilterInfo(filterId, this.settings);
                if (filterInfo) {
                    this.activeFilterId = filterId;
                    this.applySelectedFilter(filterId, false);
                }
            }
        }
    }
    reloadInstance() {
        if (this.$instance.is("table")) {
            this.$instance.appTable({ reload: true, filterParams: this.settings.filterParams });
        } else {
            this.$instance.appFilters({ reload: true, filterParams: this.settings.filterParams });

            //Reset selection after reload
            var targetSelector = this.settings.targetSelector;
            $(targetSelector).trigger("reset-selection-menu");
        }
    }

    prepareSelectionHandler() {

        var it = this;

        var selectionHandler = it.settings.selectionHandler;
        if (!selectionHandler) return false;

        var viewType = "kanban",
            $selectionMenuWrapper = null,
            $itemWrapper = null;

        if (it.$instanceWrapper.hasClass("dt-container")) {
            viewType = "table";
            $selectionMenuWrapper = it.$instanceWrapper;
            $itemWrapper = it.$instanceWrapper.find(".dataTable");
        }

        if (viewType == "kanban") {
            $selectionMenuWrapper = it.$instance;
            $itemWrapper = $(it.settings.targetSelector);
        }

        var hideButton = it.settings.selectionHandler.hideButton;
        var hideButtonClass = "";
        if (hideButton) {
            hideButtonClass = "hide";
        }

        var dropdown = "<div class='dropdown btn-group mr5 hidden-xs'>"
            + "<button class='btn btn-default dropdown-toggle selection-handler-dropdown-btn " + hideButtonClass + "' type='button' data-bs-toggle='dropdown' aria-expanded='true' data-view_type='" + viewType + "'>"
            + "<i data-feather='crosshair' class='icon-16'></i>"
            + "</button>"
            + "<ul class='dropdown-menu' role='menu'>";

        var postData = selectionHandler.postData;
        var dataPost = '';
        if (postData) {
            $.each(postData, function (key, value) {
                dataPost = 'data-post-' + key + '="' + value + '" ';
            });
        }

        dropdown += "<li role='presentation'><a href='#' class='dropdown-item select-all-btn'>" + AppLanugage.selectAll + "</a></li>";
        dropdown += "<li role='presentation'><a href='#' class='dropdown-item select-specific-btn'>" + AppLanugage.selectSpecific + "</a></li>";

        if (it.settings.selectionHandler.batchUpdateUrl) {
            dropdown += "<li role='presentation'><a class='dropdown-item batch-update-btn hide' data-act='ajax-modal' data-action-url='' " + dataPost + " data-title='" + AppLanugage.batchUpdate + "' type='button'>" + AppLanugage.batchUpdate + "</a></li>";
        }

        if (it.settings.selectionHandler.batchDownloadUrl) {
            dropdown += "<li role='presentation'><a class='dropdown-item download-selected-btn hide' data-action-url='' data-title='" + AppLanugage.downloadSelectedItems + "' type='button'>" + AppLanugage.downloadSelectedItems + "</a></li>";
        }

        if (it.settings.selectionHandler.batchDeleteUrl) {
            dropdown += "<li role='presentation'><a class='dropdown-item delete-selected-btn hide' data-action-url='' data-title='" + AppLanugage.deleteSelectedItems + "' type='button' data-action = 'delete-confirmation' data-reload-on-success = 'true'>" + AppLanugage.deleteSelectedItems + "</a></li>";
        }

        dropdown += "<li role='presentation'><a href='#' class='dropdown-item clear-selection-btn hide'>" + AppLanugage.clearSelection + "</a></li>";

        dropdown += "</ul></div>";


        it.$instanceWrapper.find(".filter-section-right").prepend(dropdown);

        var batchIds = [];
        var isSelectionMode = false;

        $(".select-specific-btn").on("click", function () {
            toggleSelectionMode($itemWrapper);
        });

        var toggleSelectionMode = function ($container, enable = true) {
            if (enable) {
                $container.addClass("js-selection-mode");
                disableLinks($container);
                isSelectionMode = true;
            } else {
                $container.removeClass("js-selection-mode");
                enableLinks($container);
                isSelectionMode = false;
            }
        }

        var disableLinks = function ($container) {
            if ($container.hasClass('dataTable')) {
                $container.find("a").addClass("pe-none");
            } else {
                $container.find('[data-act="ajax-modal"]').attr('data-act', 'ajax-modal-disabled');
            }
            $container.find(".selection-pe-none").addClass("pe-none");
        }

        var enableLinks = function ($container) {
            if ($container.hasClass('dataTable')) {
                $container.find("a").removeClass("pe-none");
            } else {
                $container.find('[data-act="ajax-modal-disabled"]').attr('data-act', 'ajax-modal');
            }
            $container.find(".selection-pe-none").removeClass("pe-none");
        }

        $selectionMenuWrapper.find(".select-all-btn").on("click", function () {
            toggleSelectionMode($itemWrapper);

            var $items;
            if ($itemWrapper.hasClass('dataTable')) {
                $items = $itemWrapper.find("tbody tr");
            } else {
                $items = $itemWrapper.find(".kanban-item");
            }

            $items.each(function () {
                $(this).addClass("batch-operation-selected");

                if ($itemWrapper.hasClass('dataTable')) {
                    var id = $(this).find(".js-selection-id").data("id");
                } else {
                    var id = $(this).data("id");
                }

                if (!batchIds.includes(id)) {
                    batchIds.push(id);
                }
            });

            it.updateSelection($selectionMenuWrapper, $itemWrapper, batchIds);
            $selectionMenuWrapper.find(".clear-selection-btn").removeClass("hide");
        });

        $itemWrapper.on('click', 'tbody tr', function () {
            var $item = $(this);
            var id = $item.find(".js-selection-id").data("id");

            handelSelectSpecific($item, id, $selectionMenuWrapper, $itemWrapper);
        });

        $itemWrapper.on('click', '.kanban-item', function () {
            var $item = $(this);
            var id = $item.data("id");

            handelSelectSpecific($item, id, $selectionMenuWrapper, $itemWrapper);
        });

        var handelSelectSpecific = function ($item, id, $selectionMenuWrapper, $itemWrapper) {
            if (!isSelectionMode) return false;

            if ($.inArray(id, batchIds) !== -1) {
                var index = batchIds.indexOf(id);
                batchIds.splice(index, 1);
                $item.removeClass("batch-operation-selected");
            } else {
                batchIds.push(id);
                $item.addClass("batch-operation-selected");
            }

            it.updateSelection($selectionMenuWrapper, $itemWrapper, batchIds);
            $selectionMenuWrapper.find(".clear-selection-btn").removeClass("hide");
        }

        $selectionMenuWrapper.find(".clear-selection-btn").on("click", function () {
            handelClearSelection();
        });

        $itemWrapper.on('reset-selection-menu', function () {
            handelClearSelection();
        });

        var handelClearSelection = function () {
            toggleSelectionMode($itemWrapper, false);

            var $items;
            if ($itemWrapper.hasClass('dataTable')) {
                $items = $itemWrapper.find("tbody tr");
            } else {
                $items = $itemWrapper.find(".kanban-item");
            }

            $items.each(function () {
                $(this).removeClass("batch-operation-selected");

                if ($itemWrapper.hasClass('dataTable')) {
                    var id = $(this).find(".js-selection-id").data("id");
                } else {
                    var id = $(this).data("id");
                }

                batchIds = batchIds.filter(batchId => batchId !== id);
            });

            $selectionMenuWrapper.find(".clear-selection-btn").addClass("hide");
            it.updateSelection($selectionMenuWrapper, $itemWrapper, batchIds);
        }

    }

    updateSelection($selectionMenuWrapper, $itemWrapper, batchIds) {
        var it = this;

        if (batchIds.length) {
            $selectionMenuWrapper.find(".batch-update-btn").removeClass("hide");
            $selectionMenuWrapper.find(".delete-selected-btn").removeClass("hide");
            $selectionMenuWrapper.find(".selection-handler-dropdown-btn").addClass("active");
        } else {
            $selectionMenuWrapper.find(".batch-update-btn").addClass("hide");
            $selectionMenuWrapper.find(".delete-selected-btn").addClass("hide");
            $selectionMenuWrapper.find(".selection-handler-dropdown-btn").removeClass("active");
        }

        var $items;
        if ($itemWrapper.hasClass('dataTable')) {
            $items = $itemWrapper.find("tbody tr");
        } else {
            $items = $itemWrapper.find(".kanban-item");
        }

        if ($items.length === batchIds.length && batchIds.length !== 0) {
            $selectionMenuWrapper.find(".select-all-btn").addClass("hide");
        } else {
            $selectionMenuWrapper.find(".select-all-btn").removeClass("hide");
        }

        if (batchIds.length === 0) {
            $selectionMenuWrapper.find(".select-specific-btn").removeClass("hide");
        } else {
            $selectionMenuWrapper.find(".select-specific-btn").addClass("hide");
        }

        var serializedIds = batchIds.join("-");

        var batchUpdateUrl = it.settings.selectionHandler.batchUpdateUrl;
        if (batchUpdateUrl) {
            $selectionMenuWrapper.find(".batch-update-btn").attr("data-action-url", batchUpdateUrl).attr("data-post-ids", serializedIds);
        }

        var batchDeleteUrl = it.settings.selectionHandler.batchDeleteUrl;
        if (batchDeleteUrl) {
            $selectionMenuWrapper.find(".delete-selected-btn").attr("data-action-url", batchDeleteUrl).attr("data-post-ids", serializedIds);
        }

    }

    prepareSmartFilterDropdown() {
        if (this.settings.smartFilterIdentity) {

            var it = this;


            var dataPostAttrs = " data-post-context='" + it.settings.smartFilterIdentity + "' "
                + " data-post-instance_id='" + it.getInstanceId() + "' ";

            if (it.getContextId()) {
                dataPostAttrs += " data-post-context_id= '" + it.getContextId() + "' ";
            }


            var actionUrl = AppHelper.baseUrl + "index.php/filters/manage_modal/" + it.settings.smartFilterIdentity;

            var dropdown = "<div class='dropdown-menu w300'>"
                + '<div class="pb10 pl10">'
                + '<a class="inline-block btn btn-default manage-filters-button" data-act="ajax-modal" data-title="' + AppLanugage.manageFilters + '" ' + dataPostAttrs + '  type="button" data-action-url="' + actionUrl + '" ><i data-feather="tool" class="icon-16 mr5"></i>' + AppLanugage.manageFilters + ' </a>'
                + '<a class="inline-block btn btn-default clear-filter-button ml10 hide" href="#"><i data-feather="delete" class="icon-16 mr5"></i>' + AppLanugage.clear + '</a></div>'
                + '<input type="text" class="form-control search-filter" placeholder="' + AppLanugage.search + '">'
                + '<div class="dropdown-divider"></div>'
                + "<ul class='list-group smart-filter-list-group'></ul>"
                + "</div>";

            var smartFilterDropdownDom = '<div class="filter-item-box smart-filter-dropdown-box">'
                + '<div class="dropdown smart-filter-dropdown-container">'
                + '<button class="btn btn-default smart-filter-dropdown dropdown-toggle caret" type="button" data-bs-toggle="dropdown" aria-expanded="true"></button>'
                + dropdown
                + '</div>'
                + '</div>';

            this.$instanceWrapper.find(it.leftFilterSectionClsss).append(smartFilterDropdownDom);
            this.refreshFilterDropdown();

            this.$instanceWrapper.find(".smart-filter-dropdown-container").on('click', '.smart-filter-item', function () {
                var data = $(this).data() || {},
                    filterId = data.id;
                it.state = "new_filter";
                it.applySelectedFilter(filterId);
            });

            var $dropdownSearch = this.$instanceWrapper.find(".search-filter");
            var $dropdown = this.$instanceWrapper.find(".smart-filter-dropdown-container");

            var addScrollOnDropdown = function () {
                var $listGroup = it.$instanceWrapper.find('.smart-filter-list-group');
                var $target = it.$instanceWrapper.find(".smart-filter-item.active");
                if (it.$instanceWrapper.find(".smart-filter-item:visible").length > 6) {
                    $listGroup.css({ "overflow-y": "scroll", "height": "270px" });
                    var targetTop = $target.offset() ? $target.offset().top : 0;
                    var listGroupTop = $listGroup.offset() ? $listGroup.offset().top : 0;

                    if ((targetTop - listGroupTop) > $listGroup.height()) {
                        $listGroup.scrollTop(targetTop - listGroupTop);
                    }
                } else {
                    $listGroup.css({ "overflow-y": "scroll", "height": "auto" });
                }
            };

            $dropdown.on("show.bs.dropdown", function () {
                setTimeout(function () {
                    addScrollOnDropdown();
                    $dropdownSearch.val("").focus();
                    if (!it.$instanceWrapper.find(".smart-filter-item.active").length) {
                        it.$instanceWrapper.find(".smart-filter-item").first().addClass("active");
                    }
                });

            });


            $dropdownSearch.on("input", function (e) {
                var $dropdownItems = it.$instanceWrapper.find(".smart-filter-item");
                var searchTerm = $(this).val().toLowerCase();
                var hasActive = false;
                $dropdownItems.each(function () {
                    var itemText = $(this).html().toLowerCase(),
                        removeActive = true;
                    if (itemText.includes(searchTerm)) {
                        $(this).parent().removeClass("hide");
                        if (!hasActive) {
                            $(this).addClass("active");
                            hasActive = true;
                            removeActive = false;
                        }
                    } else {
                        $(this).parent().addClass("hide");
                    }
                    if (removeActive) {
                        $(this).removeClass("active");
                    }

                });
                addScrollOnDropdown();
            });

            $dropdownSearch.on("keydown", function (e) {
                var $activeDropdown = it.$instanceWrapper.find(".smart-filter-item.active");

                if (e.keyCode === 40) { // Arrow Down
                    e.preventDefault();
                    if ($activeDropdown.parent().nextAll(":visible").length) {
                        $activeDropdown.removeClass("active");
                        $activeDropdown = $activeDropdown.parent().nextAll(":visible").first().find("a").addClass("active");
                    }
                } else if (e.keyCode === 38) { // Arrow Up
                    e.preventDefault();
                    if ($activeDropdown.parent().prevAll(":visible").length) {
                        $activeDropdown.removeClass("active");
                        $activeDropdown = $activeDropdown.parent().prevAll(":visible").first().find("a").addClass("active");
                    }

                } else if (e.keyCode === 13) { // Enter
                    e.preventDefault();
                    it.$instanceWrapper.find(".smart-filter-item.active").trigger("click");
                    $dropdown.dropdown("toggle");
                }

                var $listGroup = it.$instanceWrapper.find('.smart-filter-list-group');

                if ($activeDropdown.length && ($activeDropdown.offset().top + $activeDropdown.outerHeight() - $listGroup.offset().top) > $listGroup.height()) {
                    $listGroup.scrollTop($listGroup.scrollTop() + $activeDropdown.outerHeight());
                } else if ($activeDropdown.length && ($activeDropdown.offset().top - $listGroup.offset().top) < 0) {
                    $listGroup.scrollTop($listGroup.scrollTop() - $activeDropdown.outerHeight());
                }

            });

            this.$instanceWrapper.find(".clear-filter-button").click(function () {
                it.activeFilterId = "";
                it.clearAllFilters();
                it.refreshFilterDropdown();
                it.reloadInstance();
                it.saveSelectedFilter();
            });
        }
    }
    initChangeFilter(filterId) {
        this.activeFilterId = filterId;
        this.showFilterForm();
        this.state = "change_filter";
        this.applySelectedFilter(filterId);
    }
    applySelectedFilter(filterId, reload = true) {
        var it = this;


        if (filterId) {
            it.activeFilterId = filterId;

            var filterParams = [];

            var filterInfo = getContextFilterInfo(filterId, this.settings);
            it.settings.filterParams = cloneDeep(filterInfo.params);
            filterParams = cloneDeep(filterInfo.params);

            //set filter values
            var hasFilter = [];

            var shiftDynamicToEnd = function (obj) {
                var newObj = {};
                var dynamicKey = Object.keys(obj).find(key => obj[key] === "dynamic");
                var dynamicRangName = "";
                for (var key in obj) {
                    if (obj.hasOwnProperty(key) && key !== dynamicKey) {
                        newObj[key] = obj[key];
                    }

                    if (obj.hasOwnProperty(key) && key.endsWith('_dynamic')) {
                        dynamicRangName = key.slice(0, -8);
                    }

                }

                if (dynamicKey) {
                    newObj[dynamicKey] = obj[dynamicKey];
                }

                //remove the dynamic key if the value is not dynamic. 

                if (dynamicRangName && obj[dynamicRangName] != "dynamic") {
                    delete newObj[dynamicRangName + "_dynamic"];
                }

                return newObj;
            }

            //if there is any dynamic fiter, we should call that at the end
            filterParams = shiftDynamicToEnd(filterParams);


            $.each(filterParams, function (paramName, value) {
                hasFilter.push(paramName);
                var filterMap = it.filterElements[paramName];
                if (filterMap) {
                    filterMap.setValue(value, cloneDeep(filterParams));
                }

            });

            //reset other filters 

            for (var key in it.filterElements) {
                if (!hasFilter.includes(key)) {
                    var filterMap = it.filterElements[key];
                    if (filterMap) {
                        filterMap.setValue("");
                    }
                }
            }

            it.refreshFilterDropdown();
            if (reload !== false) {
                it.reloadInstance();
            }

            it.showHideClearFilterButton();
            it.updateFilterModalState(filterInfo);
            it.saveSelectedFilter();
        }

    }
    refreshFilterDropdown() {
        var options = "",
            it = this,
            filters = it.getFilters(),
            title = "";

        $.each(filters, function (index, filterItem) {

            var active = "";
            if (filterItem.id === it.activeFilterId) {
                active = "active";
                title = filterItem.title;
            }
            options += '<li><a href="#" class="dropdown-item smart-filter-item list-group-item clickable ' + active + ' "data-id="' + filterItem.id + '">';
            options += filterItem.title;
            options += '</a></li>';
        });

        this.$instanceWrapper.find(".smart-filter-list-group").html(options);

        if (!title) {
            title = AppLanugage.filters;
        }
        var smartFilterButtonText = '<i data-feather="filter" class="icon-16 mr5"></i>' + title;

        this.$instanceWrapper.find(".smart-filter-dropdown").html(smartFilterButtonText);

        if (filters.length) {
            this.$instanceWrapper.find(".smart-filter-dropdown-container").removeClass("hide").closest(".filter-item-box").css("position", "initial");
            this.$instanceWrapper.find(".show-filter-form-button").find(".add-filter-text").addClass("hide");
        } else {
            this.$instanceWrapper.find(".smart-filter-dropdown-container").addClass("hide").closest(".filter-item-box").css("position", "absolute");
            this.$instanceWrapper.find(".show-filter-form-button").find(".add-filter-text").removeClass("hide");
        }

        feather.replace();
    }
    getFilters() {
        return getContextFilters(this.settings);
    }
    prepareSurchOption() {
        var settings = this.settings,
            it = this;
        if (settings.search && settings.search.show !== false) {
            var searchDom = '<div class="filter-item-box">'
                + '<input type="search" class="custom-filter-search" name="' + settings.search.name + '" placeholder="' + settings.customLanguage.searchPlaceholder + '">'
                + '</div>';
            it.$instanceWrapper.find(it.rightFilterSectionClsss).append(searchDom);

            var wait;
            it.$instanceWrapper.find(".custom-filter-search").keyup(function () {
                appLoader.show();

                var $search = $(this);
                clearTimeout(wait);

                wait = setTimeout(function () {
                    it.settings.filterParams[settings.search.name] = $search.val();
                    it.reloadInstance();
                }, 700);

            });
        }
    }
    prepareCollapsePannelButton() {
        if (this.settings.isMobile && !this.settings.smartFilterIdentity) {

            if (this.settings.dateRangeType || typeof this.settings.checkBoxes[0] !== 'undefined' || typeof this.settings.multiSelect[0] !== 'undefined' || typeof this.settings.radioButtons[0] !== 'undefined' || typeof this.settings.singleDatepicker[0] !== 'undefined' || typeof this.settings.rangeDatepicker[0] !== 'undefined' || typeof this.settings.filterDropdown[0] !== 'undefined') {

                var collapsePanelDom = "<div class='float-end filter-collapse-button'>\
                        <button title='" + AppLanugage.filters + "' class='dropdown-toggle btn btn-default mt0' data-bs-toggle='collapse' data-bs-target='#table-collapse-filter-" + this.randomId + "' aria-expanded='false'><i data-feather='sliders' class='icon-18'></i></button>\
                    </div>\
                    <div id='table-collapse-filter-" + this.randomId + "' class='navbar-collapse collapse w100p'></div>";

                this.$instanceWrapper.find(this.leftFilterSectionClsss).append(collapsePanelDom);
            }
        }
    }
    prepareReloadButton() {
        var it = this;
        if (it.settings.reloadSelector) {
            if (!$(it.settings.reloadSelector).length) {
                var reloadDom = '<div class="filter-item-box reload-button-container">'
                    + '<button class="btn btn-default" id="' + it.settings.reloadSelector.slice(1) + '"><i data-feather="refresh-cw" class="icon-16"></i></button>'
                    + '</div>';
                this.$instanceWrapper.find(this.leftFilterSectionClsss).append(reloadDom);  //bind refresh icon
            }

            if ($(it.settings.reloadSelector).length) {
                $(it.settings.reloadSelector).click(function () {
                    appLoader.show();
                    it.reloadInstance();
                });
            }
        }
    }
    showHideClearFilterButton() {
        if (this.activeFilterId) {
            this.$instanceWrapper.find(".clear-filter-button").removeClass("hide");
        } else {
            this.$instanceWrapper.find(".clear-filter-button").addClass("hide");
        }
    }
    clearAllFilters() {
        var it = this;
        it.activeFilterId = "";
        for (var key in this.filterElements) {
            var filterMap = it.filterElements[key];
            it.settings.filterParams[key] = "";
            if (filterMap) {
                filterMap.setValue("");
            }
        }

        it.showHideClearFilterButton();
    }
    prepareFilterFormShowButton() {
        if (this.settings.smartFilterIdentity) {
            var filters = this.getFilters();
            var filterText = '<span class="add-filter-text ml5">' + AppLanugage.addNewFilter + '</span>';
            if (filters.length) {
                filterText = "";
            } else {
                this.$instanceWrapper.find(".smart-filter-dropdown-container").addClass("hide").closest(".filter-item-box").css("position", "absolute");
            }
            var smartFilterDropdownDom = '<div class="filter-item-box show-hide-filter-button-box">'
                + '<button class="btn btn-default show-filter-form-button" type="button"><i data-feather="plus" class="icon-16"></i>' + filterText + '</button>'
                + '</div>';

            var it = this;

            this.$instanceWrapper.find(this.leftFilterSectionClsss).append(smartFilterDropdownDom);

            this.$instanceWrapper.find(".show-filter-form-button").click(function () {
                //toggle
                if (it.$instanceWrapper.find(it.filterFormClass).hasClass("hide")) {
                    it.showFilterForm();
                } else {
                    it.hideFilterForm();
                }

            });

            //show filter bar based on settings
            var it = this;
            setTimeout(function () {
                var showFilterBar = false;
                var barType = AppHelper && AppHelper.settings && AppHelper.settings.filterBar ? AppHelper.settings.filterBar : "";
                if (barType === "always_expanded") {
                    showFilterBar = true;
                } else if (barType === "expanded_until_saved_filter_selected") {
                    var filterId = getFilterIdFromCookie(it.settings);
                    var filteInfo = getContextFilterInfo(filterId, it.settings);
                    if (!filteInfo) {
                        showFilterBar = true;
                    }
                }

                if (showFilterBar && !(it.settings.isMobile || it.settings.mobileMirror)) {
                    it.$instanceWrapper.find(".show-filter-form-button").trigger("click");
                }

            });

        }
    }
    prepareBookmarkFilterButtons() {
        if (this.settings.smartFilterIdentity) {

            var it = this;
            it.refreshBookmarkFilterButtons();

            it.$instanceWrapper.find(".filter-section-container").on('click', '.bookmarked-filter-button', function () {
                var data = $(this).data() || {},
                    filterId = data.id;
                it.state = "new_filter";
                it.applySelectedFilter(filterId);
            });
        }
    }
    refreshBookmarkFilterButtons() {
        if (this.settings.smartFilterIdentity) {
            var it = this,
                filters = it.getFilters();
            it.$instanceWrapper.find(".bookmarked-filter-button-wrapper").remove();

            var bookmarkContainer = it.$instanceWrapper.find(it.leftFilterSectionClsss).find(".bookmarked-filter-button-container");
            if (!bookmarkContainer.length) {
                it.$instanceWrapper.find(it.leftFilterSectionClsss).append("<div class='bookmarked-filter-button-container scrollable-container'></div>");
            }

            $.each(filters, function (index, filterItem) {
                if (filterItem.bookmark == "1") {
                    var bookmarkButtonContent = filterItem.title;
                    if (filterItem.icon) {
                        bookmarkButtonContent = '<i data-feather="' + filterItem.icon + '" class="icon-16"></i>';
                    }

                    var smartFilterDropdownDom = '<div class="filter-item-box bookmarked-filter-button-wrapper">'
                        + '<button class="btn btn-default bookmarked-filter-button round" type="button" data-id="' + filterItem.id + '"  >' + bookmarkButtonContent + '</button>'
                        + '</div>';

                    it.$instanceWrapper.find(it.leftFilterSectionClsss).find(".bookmarked-filter-button-container").append(smartFilterDropdownDom);
                }
            });

            feather.replace();
        }
    }

    hideFilterForm() {
        this.state = "new_filter";
        this.$instanceWrapper.find(this.filterFormClass).addClass("hide");
        this.showFilterFormButton();
    }
    showFilterForm() {
        this.$instanceWrapper.find(this.filterFormClass).removeClass("hide");
        this.hideFilterFormButton();
        this.showSaveFilterButton();
        this.updateFilterModalState();
    }
    hideFilterFormButton() {
        var $targetElement = this.$instanceWrapper.find(".show-filter-form-button").closest(".filter-item-box");
        if ($targetElement.find(".add-filter-text").html()) {
            $targetElement.addClass("hide");
        } else {
            $targetElement.find("button").find("svg").css({ "transform": "rotate(45deg)", "transition": "all 0.2s ease 0s" });
        }
    }
    showFilterFormButton() {

        var $targetElement = this.$instanceWrapper.find(".show-filter-form-button").closest(".filter-item-box");
        if ($targetElement.find(".add-filter-text").html()) {
            $targetElement.removeClass("hide");
        } else {
            $targetElement.find("button").find("svg").css("transform", "rotate(0deg)");
        }
    }
    updateFilterModalState(filterInfo) {
        var title = AppLanugage.newFilter;
        var $button = this.$instanceWrapper.find(".save-filter-button");

        if (this.state === "change_filter") {
            title = AppLanugage.updateFilter;
            if (filterInfo) {
                title += " (" + filterInfo.title + ")";
            }
            $button.attr("data-title", title);
            $button.attr("data-post-id", this.activeFilterId);
            $button.attr("data-post-change_filter", "1");
        } else {
            $button.attr("data-title", title);
            $button.attr("data-post-id", getRandomAlphabet(10));
            $button.attr("data-post-change_filter", "");
        }


    }
    showSaveFilterButton() {

        var filters = this.getFilters();

        if (filters.length) {
            this.$instanceWrapper.find(".save-filter-button").addClass("btn-default").removeClass("btn-success");
        } else {
            this.$instanceWrapper.find(".save-filter-button").addClass("btn-success").removeClass("btn-default");
        }

        this.$instanceWrapper.find(".save-filter-button").closest(".filter-item-box").removeClass("hide");
    }
    hideSaveSelectedFilterButton() {
        this.$instanceWrapper.find(".save-filter-button").closest(".filter-item-box").addClass("hide");
    }
    getInstanceId() {
        return this.$instance.attr("id");
    }
    getContextId() {
        if (this.settings.contextMeta && this.settings.contextMeta.contextId) {
            return this.settings.contextMeta.contextId;
        } else {
            return "";
        }
    }
    getContextDependencies() {
        if (this.settings.contextMeta && this.settings.contextMeta.dependencies) {
            return this.settings.contextMeta.dependencies;
        } else {
            return "";
        }
    }
    prepareSaveFilterButton() {
        if (this.settings.smartFilterIdentity) {
            var it = this;

            var dataPostAttrs = " data-post-context='" + it.settings.smartFilterIdentity + "' "
                + " data-post-instance_id='" + it.getInstanceId() + "' ";

            if (it.getContextId()) {
                dataPostAttrs += " data-post-context_id= '" + it.getContextId() + "' ";
            }


            var actionUrl = AppHelper.baseUrl + "index.php/filters/modal_form";
            var smartFilterCreateButon = '<div class="filter-item-box save-filter-box hide">'
                + '<button class="btn btn-default save-filter-button" data-act="ajax-modal" data-title="" ' + dataPostAttrs + '  type="button" data-action-url="' + actionUrl + '" ><i data-feather="check-circle" class="icon-16"></i></button>'
                + '</div>';

            this.$instanceWrapper.find(this.filterFormClass).append(smartFilterCreateButon);
        }
    }
    prepareCancelFilterFormButton() {
        if (this.settings.smartFilterIdentity) {
            var it = this;

            var smartFilterCancelButton = '<div class="filter-item-box filter-cancel-box">'
                + '<button class="btn btn-default cancel-filter-button" type="button" ><i data-feather="x-circle" class="icon-16"></i></button>'
                + '</div>';


            this.$instanceWrapper.find(this.filterFormClass).append(smartFilterCancelButton);

            this.$instanceWrapper.find(".cancel-filter-button").click(function () {
                it.hideFilterForm();
            });

        }
    }
    appendFilterDom(dom, rangeRadioButton = false) {
        if (this.settings.smartFilterIdentity) {
            if (rangeRadioButton) {
                this.$instanceWrapper.find(".range-radio-button").after(dom);
            } else {
                this.$instanceWrapper.find(this.filterFormClass).append(dom);
            }
        } else {
            this.$instanceWrapper.find(this.leftFilterSectionClsss).append(dom);
        }
    }
    prepareDateRangePicker(fromDateRangeFilter = false) {
        var it = this,
            settings = this.settings,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (settings.dateRangeType) {
            var dateRangeFilterDom = '<div class="filter-item-box btn-group">'
                + '<button data-act="prev" class="btn btn-default date-range-selector"><i data-feather="chevron-left" class="icon"></i></button>'
                + '<button data-act="datepicker" class="btn btn-default"></button>'
                + '<button data-act="next"  class="btn btn-default date-range-selector"><i data-feather="chevron-right" class="icon"></i></button>'
                + '</div>';

            if (fromDateRangeFilter) {
                this.appendFilterDom(dateRangeFilterDom, true);
            } else {
                this.appendFilterDom(dateRangeFilterDom);
            }

            var $datepicker = $instanceWrapper.find("[data-act='datepicker']"),
                $dateRangeSelector = $instanceWrapper.find(".date-range-selector");

            //init single day selector
            if (settings.dateRangeType === "daily") {
                var initSingleDaySelectorText = function ($elector) {
                    if (settings.filterParams.start_date === moment().customFormat(settings._inputDateFormat)) {
                        $elector.html(settings.customLanguage.today);
                    } else if (settings.filterParams.start_date === moment().subtract(1, 'days').customFormat(settings._inputDateFormat)) {
                        $elector.html(settings.customLanguage.yesterday);
                    } else if (settings.filterParams.start_date === moment().add(1, 'days').customFormat(settings._inputDateFormat)) {
                        $elector.html(settings.customLanguage.tomorrow);
                    } else {
                        $elector.html(moment(settings.filterParams.start_date).format("Do MMMM YYYY"));
                    }
                };
                // prepareDefaultDateRangeFilterParams();
                initSingleDaySelectorText($datepicker);

                //bind the click events
                $datepicker.datepicker({
                    format: settings._inputDateFormat,
                    autoclose: true,
                    todayHighlight: true,
                    language: "custom",
                    orientation: "bottom"
                }).on('changeDate', function (e) {
                    var date = moment(e.date).customFormat(settings._inputDateFormat);
                    settings.filterParams.start_date = date;
                    settings.filterParams.end_date = date;
                    initSingleDaySelectorText($datepicker);

                    it.reloadInstance();

                });

                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"), date = "";
                    if (type === "next") {
                        date = moment(settings.filterParams.start_date).add(1, 'days').customFormat(settings._inputDateFormat);
                    } else if (type === "prev") {
                        date = moment(settings.filterParams.start_date).subtract(1, 'days').customFormat(settings._inputDateFormat)
                    }
                    settings.filterParams.start_date = date;
                    settings.filterParams.end_date = date;
                    initSingleDaySelectorText($datepicker);
                    it.reloadInstance();
                });

                it.filterElements['start_date'] = {
                    setValue: function (value) {
                        $datepicker.datepicker('update', value);
                        initSingleDaySelectorText($datepicker);
                    }
                };

            }


            //init month selector
            if (settings.dateRangeType === "monthly") {
                var initMonthSelectorText = function ($elector) {
                    $elector.html(moment(settings.filterParams.start_date).format("MMMM YYYY"));
                };

                //prepareDefaultDateRangeFilterParams();
                initMonthSelectorText($datepicker);

                //bind the click events
                $datepicker.datepicker({
                    format: "YYYY-MM",
                    viewMode: "months",
                    minViewMode: "months",
                    autoclose: true,
                    language: "custom",
                    orientation: "bottom"
                }).on('changeDate', function (e) {
                    var date = moment(e.date).customFormat(settings._inputDateFormat);
                    var daysInMonth = moment(date).daysInMonth(),
                        yearMonth = moment(date).customFormat("YYYY-MM");
                    settings.filterParams.start_date = yearMonth + "-01";
                    settings.filterParams.end_date = yearMonth + "-" + daysInMonth;
                    initMonthSelectorText($datepicker);
                    it.reloadInstance();
                });

                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"),
                        startDate = moment(settings.filterParams.start_date),
                        endDate = moment(settings.filterParams.end_date);
                    if (type === "next") {
                        var nextMonth = startDate.add(1, 'months'),
                            daysInMonth = nextMonth.daysInMonth(),
                            yearMonth = nextMonth.customFormat("YYYY-MM");

                        startDate = yearMonth + "-01";
                        endDate = yearMonth + "-" + daysInMonth;

                    } else if (type === "prev") {
                        var lastMonth = startDate.subtract(1, 'months'),
                            daysInMonth = lastMonth.daysInMonth(),
                            yearMonth = lastMonth.customFormat("YYYY-MM");

                        startDate = yearMonth + "-01";
                        endDate = yearMonth + "-" + daysInMonth;
                    }

                    settings.filterParams.start_date = startDate;
                    settings.filterParams.end_date = endDate;

                    initMonthSelectorText($datepicker);
                    it.reloadInstance();
                });

                it.filterElements['start_date'] = {
                    setValue: function (value) {
                        $datepicker.datepicker('update', value);
                        initMonthSelectorText($datepicker);
                    }
                };
            }

            //init year selector
            if (settings.dateRangeType === "yearly") {
                var inityearSelectorText = function ($elector) {
                    $elector.html(moment(settings.filterParams.start_date).customFormat("YYYY"));
                };
                // prepareDefaultDateRangeFilterParams();
                inityearSelectorText($datepicker);

                //bind the click events
                $datepicker.datepicker({
                    format: "YYYY-MM",
                    viewMode: "years",
                    minViewMode: "years",
                    autoclose: true,
                    language: "custom",
                    orientation: "bottom"
                }).on('changeDate', function (e) {
                    var date = moment(e.date).customFormat(settings._inputDateFormat),
                        year = moment(date).customFormat("YYYY");
                    settings.filterParams.start_date = year + "-01-01";
                    settings.filterParams.end_date = year + "-12-31";
                    inityearSelectorText($datepicker);
                    it.reloadInstance();
                });

                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"),
                        startDate = moment(settings.filterParams.start_date),
                        endDate = moment(settings.filterParams.end_date);
                    if (type === "next") {
                        startDate = startDate.add(1, 'years').customFormat(settings._inputDateFormat);
                        endDate = endDate.add(1, 'years').customFormat(settings._inputDateFormat);
                    } else if (type === "prev") {
                        startDate = startDate.subtract(1, 'years').customFormat(settings._inputDateFormat);
                        endDate = endDate.subtract(1, 'years').customFormat(settings._inputDateFormat);
                    }
                    settings.filterParams.start_date = startDate;
                    settings.filterParams.end_date = endDate;
                    inityearSelectorText($datepicker);
                    it.reloadInstance();
                });


                it.filterElements['start_date'] = {
                    setValue: function (value) {
                        $datepicker.datepicker('update', value);
                        inityearSelectorText($datepicker);
                    }
                };
            }

            //init week selector
            if (settings.dateRangeType === "weekly") {
                var initWeekSelectorText = function ($elector) {
                    var from = moment(settings.filterParams.start_date).format("Do MMM"),
                        to = moment(settings.filterParams.end_date).format("Do MMM, YYYY");
                    $datepicker.datepicker({
                        format: "YYYY-MM-DD",
                        autoclose: true,
                        calendarWeeks: true,
                        language: "custom",
                        orientation: "bottom",
                        weekStart: AppHelper.settings.firstDayOfWeek
                    });
                    $elector.html(from + " - " + to);
                };

                //prepareDefaultDateRangeFilterParams();
                initWeekSelectorText($datepicker);

                //bind the click events
                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"),
                        startDate = moment(settings.filterParams.start_date),
                        endDate = moment(settings.filterParams.end_date);
                    if (type === "next") {
                        startDate = startDate.add(7, 'days').customFormat(settings._inputDateFormat);
                        endDate = endDate.add(7, 'days').customFormat(settings._inputDateFormat);
                    } else if (type === "prev") {
                        startDate = startDate.subtract(7, 'days').customFormat(settings._inputDateFormat);
                        endDate = endDate.subtract(7, 'days').customFormat(settings._inputDateFormat);
                    }
                    settings.filterParams.start_date = startDate;
                    settings.filterParams.end_date = endDate;
                    initWeekSelectorText($datepicker);
                    it.reloadInstance();
                });

                $datepicker.datepicker({
                    format: settings._inputDateFormat,
                    autoclose: true,
                    calendarWeeks: true,
                    language: "custom",
                    weekStart: AppHelper.settings.firstDayOfWeek
                }).on("show", function () {
                    $(".datepicker").addClass("week-view");
                    $(".datepicker-days").find(".active").siblings(".day").addClass("active");
                }).on('changeDate', function (e) {
                    var range = getWeekRange(e.date);
                    settings.filterParams.start_date = range.firstDateOfWeek;
                    settings.filterParams.end_date = range.lastDateOfWeek;
                    initWeekSelectorText($datepicker);
                    it.reloadInstance();
                });

                it.filterElements['start_date'] = {
                    setValue: function (value) {
                        $datepicker.datepicker('update', value);
                        initWeekSelectorText($datepicker);
                    }
                };
            }
        }
    }
    prepareDropdownFilters() {
        var settings = this.settings,
            it = this,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.filterDropdown[0] !== 'undefined') {

            $.each(settings.filterDropdown, function (index, dropdown) {

                var selectedValue = "",
                    placeholder = "";

                if (dropdown.value) {
                    selectedValue = dropdown.value;
                } else {
                    $.each(dropdown.options, function (index, option) {
                        if (option.isSelected) {
                            selectedValue = option.id;
                        }
                    });
                }

                if (dropdown.options && dropdown.options[0] && !dropdown.options[0].id && dropdown.options[0].text) {
                    placeholder = dropdown.options[0].text;
                }

                if (dropdown.name) {
                    settings.filterParams[dropdown.name] = selectedValue;
                }

                var selectDom = '<div class="filter-item-box">'
                    + '<input class="' + dropdown.class + '" name="' + dropdown.name + '" value="' + selectedValue + '" placeholder="' + placeholder + '" />'
                    + '</div>';

                it.appendFilterDom(selectDom);

                var $dropdown = $instanceWrapper.find("[name='" + dropdown.name + "']");
                var dropdownOnchangeCallback = function ($selector) {
                    var filterName = $selector.attr("name"),
                        value = $selector.val();

                    //set the new value to settings
                    settings.filterParams[filterName] = value;

                    //check if there any dependent files,
                    //reset the dependent fields if this value is empty
                    //re-load the dependent fields if this value is not empty

                    if (dropdown.dependent && dropdown.dependent.length) {
                        it.prepareDependentFilter(filterName, value, settings.filterDropdown, settings.filterParams);
                    }

                    //callback
                    if (dropdown.onChangeCallback) {
                        dropdown.onChangeCallback(value, settings.filterParams);
                    }

                    it.reloadInstance();
                }


                if (window.Select2 !== undefined) {
                    var appDropdownOptions = {
                        list_data: dropdown.options,
                        onChangeCallback: function (value, instance) {
                            dropdownOnchangeCallback(instance);
                        }
                    };

                    if (dropdown.showHtml) {
                        appDropdownOptions.escapeMarkup = function (markup) {
                            return markup;
                        };
                    }

                    $dropdown.appDropdown(appDropdownOptions);

                }

                // $dropdown.change(function () {
                //     var $selector = $(this),
                //         filterName = $selector.attr("name"),
                //         value = $selector.val();

                //     //set the new value to settings
                //     settings.filterParams[filterName] = value;

                //     //check if there any dependent files,
                //     //reset the dependent fields if this value is empty
                //     //re-load the dependent fields if this value is not empty

                //     if (dropdown.dependent && dropdown.dependent.length) {
                //         it.prepareDependentFilter(filterName, value, settings.filterDropdown, settings.filterParams);
                //     }

                //     //callback
                //     if (dropdown.onChangeCallback) {
                //         dropdown.onChangeCallback(value, settings.filterParams);
                //     }

                //     it.reloadInstance();
                // });

                it.filterElements[dropdown.name] = {
                    setValue: function (value, newFilterParams) {
                        $dropdown.select2("val", value);
                        if (dropdown.showHtml && !value) {
                            if (dropdown.options && dropdown.options[0] && !dropdown.options[0].id && dropdown.options[0].text) {
                                $dropdown.siblings(".select2-container").find(".select2-chosen").html(dropdown.options[0].text);
                            }
                        }

                        window[dropdown.name] = $dropdown;
                        if (dropdown.dependent && dropdown.dependent.length) {
                            it.prepareDependentFilter(dropdown.name, value, settings.filterDropdown, settings.filterParams, newFilterParams);
                        }

                        if (dropdown.onChangeCallback) {
                            dropdown.onChangeCallback(value, newFilterParams);
                        }
                    }
                };


            });
        }
    }

    getDynamicDateRanges() {
        return getDynamicDateRanges();
    }

    prepareDatePickerFilter(fromDateRangeFilter = false) {

        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.rangeDatepicker[0] !== 'undefined') {

            $.each(settings.rangeDatepicker, function (index, datePicker) {

                var startDate = datePicker.startDate || {},
                    endDate = datePicker.endDate || {},
                    showClearButton = datePicker.showClearButton ? true : false,
                    emptyText = '<i data-feather="calendar" class="icon-16"></i>',
                    startButtonText = startDate.value ? moment(startDate.value, settings._inputDateFormat).format("Do MMMM YYYY") : emptyText,
                    endButtonText = endDate.value ? moment(endDate.value, settings._inputDateFormat).format("Do MMMM YYYY") : emptyText;

                //set filter params
                settings.filterParams[startDate.name] = startDate.value;
                settings.filterParams[endDate.name] = endDate.value;

                var reloadDateRangeFilter = function (name, date) {
                    settings.filterParams[name] = date;
                    it.reloadInstance();
                };


                var defaultRanges = it.getDynamicDateRanges();


                var devider = '<span class="input-group-addon">-</span>';
                var showRange = false;

                if (datePicker.label) {
                    devider = '<span class="input-group-addon custom-date-range-lable">' + datePicker.label + '</span>';


                    if (datePicker.ranges) {

                        var options = "";
                        $.each(datePicker.ranges, function (index, range) {
                            if (defaultRanges[range]) {
                                options += '<li><a href="#" class="dropdown-item list-group-item clickable" data-range="' + range + '">';
                                options += AppLanugage[range];
                                options += '</a></li>';
                            }
                        });
                        if (options) {
                            showRange = true;
                            var rangeDropdownDom = ''
                                + '<div class="dropdown">'
                                + '<div class="dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="true">' + datePicker.label + '</div>'
                                + '<div class="dropdown-menu">'
                                + '<ul class="list-group">' + options + '</ul>'
                                + '</div>'
                                + '</div>'
                                ;

                            devider = '<span class="input-group-addon custom-date-range-dropdown clickable">' + rangeDropdownDom + '</span>';

                        }

                    }
                }


                var dateRangeClass = "daterange-" + getRandomAlphabet(5);

                //prepare DOM
                var selectDom = '<div class="filter-item-box">'
                    + '<div class="input-daterange input-group ' + dateRangeClass + '">'
                    + '<button class="btn btn-default form-control" name="' + startDate.name + '" data-date="' + startDate.value + '">' + startButtonText + '</button>'
                    + devider
                    + '<button class="btn btn-default form-control" name="' + endDate.name + '" data-date="' + endDate.value + '">' + endButtonText + ''
                    + '</div>'
                    + '</div>';

                if (fromDateRangeFilter) {
                    it.appendFilterDom(selectDom, true);
                } else {
                    it.appendFilterDom(selectDom);
                }

                var $datePicker = $instanceWrapper.find("." + dateRangeClass),
                    inputs = $datePicker.find('button').toArray();

                var showButtonText = function () {
                    var s_date = settings.filterParams[startDate.name],
                        e_date = settings.filterParams[endDate.name];
                    $(inputs[0]).html(s_date ? moment(s_date, settings._inputDateFormat).format("Do MMMM YYYY") : emptyText);
                    $(inputs[1]).html(e_date ? moment(e_date, settings._inputDateFormat).format("Do MMMM YYYY") : emptyText);
                };

                //init datepicker
                $datePicker.datepicker({
                    format: "yyyy-mm-dd",
                    autoclose: true,
                    todayHighlight: true,
                    language: "custom",
                    weekStart: AppHelper.settings.firstDayOfWeek,
                    orientation: "bottom",
                    inputs: inputs
                }).on('changeDate', function (e) {
                    var date = moment(e.date, settings._inputDateFormat).customFormat(settings._inputDateFormat);

                    //set save value if anyone is empty
                    if (!settings.filterParams[startDate.name]) {
                        settings.filterParams[startDate.name] = date;
                    }

                    if (!settings.filterParams[endDate.name]) {
                        settings.filterParams[endDate.name] = date;
                    }

                    reloadDateRangeFilter($(e.target).attr("name"), date);

                    //show button text
                    showButtonText();

                }).on("show", function () {

                    //show clear button
                    if (showClearButton) {
                        $(".datepicker-clear-selection").show();
                        if (!$(".datepicker-clear-selection").length) {
                            $(".datepicker").append("<div class='datepicker-clear-selection p5 clickable text-center'>" + AppLanugage.clear + "</div>");

                            //bind click event for clear button
                            $(".datepicker .datepicker-clear-selection").click(function () {
                                settings.filterParams[startDate.name] = "";
                                reloadDateRangeFilter(endDate.name, "");

                                $(inputs[0]).html(emptyText);
                                $(inputs[1]).html(emptyText);
                                $(".datepicker").hide();
                            });
                        }
                    }
                });


                if (showRange) {
                    it.$instanceWrapper.find("." + dateRangeClass).on('click', '.list-group-item', function () {
                        var data = $(this).data() || {};
                        var date = defaultRanges[data.range];
                        settings.filterParams[endDate.name] = date[1];

                        reloadDateRangeFilter(startDate.name, date[0]);

                        showButtonText();
                    });
                }


                it.filterElements[startDate.name] = {

                    setValue: function (value) {
                        settings.filterParams[startDate.name] = value;
                        $datePicker.datepicker('update', value);
                        showButtonText();
                    }
                };
                it.filterElements[endDate.name] = {
                    setValue: function (value) {

                        settings.filterParams[endDate.name] = value;
                        $datePicker.datepicker('update', value);
                        showButtonText();
                    }
                };

            });
        }
    }
    prepareSingleDatePicker() {
        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.singleDatepicker[0] !== 'undefined') {

            $.each(settings.singleDatepicker, function (index, datePicker) {

                var options = " ", value = "", selectedText = "";

                if (!datePicker.options)
                    datePicker.options = [];

                //add custom datepicker selector
                datePicker.options.push({ value: "show-date-picker", text: AppLanugage.custom });

                //prepare custom list
                $.each(datePicker.options, function (index, option) {
                    var isSelected = "";
                    if (option.isSelected) {
                        isSelected = "active";
                        value = option.value;
                        selectedText = option.text;
                    }

                    options += '<div class="list-group-item ' + isSelected + '" data-value="' + option.value + '">' + option.text + '</div>';
                });

                if (!selectedText) {
                    selectedText = "- " + datePicker.defaultText + " -";
                    options = '<div class="list-group-item active" data-value="">' + selectedText + '</div>' + options;
                }



                //set filter params
                if (datePicker.name) {
                    settings.filterParams[datePicker.name] = value;
                }

                var reloadDatePickerFilter = function (date) {
                    settings.filterParams[datePicker.name] = date;
                    it.reloadInstance();
                };

                var getDatePickerText = function (text) {
                    return text + "<span class='ml10 dropdown-toggle'></span>";
                };



                //prepare DOM
                var customList = '<div class="datepicker-custom-list list-group mb0">'
                    + options
                    + '</div>';

                var datePickerClass = "";
                if (datePicker.class) {
                    datePickerClass = datePicker.class;
                }


                var selectDom = '<div class="filter-item-box">'
                    + '<button name="' + datePicker.name + '" class="btn ' + datePickerClass + ' datepicker-custom-selector">'
                    + getDatePickerText(selectedText)
                    + '</button>'
                    + '</div>';

                it.appendFilterDom(selectDom);

                var $datePicker = $instanceWrapper.find("[name='" + datePicker.name + "']"),
                    showCustomRange = typeof datePicker.options[1] === 'undefined' ? false : true; //don't show custom range if options not > 1

                //init datepicker
                $datePicker.datepicker({
                    format: settings._inputDateFormat,
                    autoclose: true,
                    todayHighlight: true,
                    language: "custom",
                    weekStart: AppHelper.settings.firstDayOfWeek,
                    orientation: "bottom"
                }).on("show", function () {

                    //has custom dates, show them otherwise show the datepicker
                    if (showCustomRange) {
                        $(".datepicker-days, .datepicker-months, .datepicker-years, .datepicker-decades, .table-condensed").hide();
                        $(".datepicker-custom-list").show();
                        if (!$(".datepicker-custom-list").length) {
                            $(".datepicker").append(customList);

                            //bind click events
                            $(".datepicker .list-group-item").click(function () {
                                $(".datepicker .list-group-item").removeClass("active");
                                $(this).addClass("active");
                                var value = $(this).attr("data-value");
                                //show datepicker for custom date
                                if (value === "show-date-picker") {
                                    $(".datepicker-custom-list, .datepicker-months, .datepicker-years, .datepicker-decades, .table-condensed").hide();
                                    $(".datepicker-days, .table-condensed").show();
                                } else {
                                    $(".datepicker").hide();

                                    if (moment(value, settings._inputDateFormat).isValid()) {
                                        value = moment(value, settings._inputDateFormat).customFormat(settings._inputDateFormat);
                                    }

                                    $datePicker.html(getDatePickerText($(this).html()));
                                    reloadDatePickerFilter(value);
                                }
                            });
                        }
                    }
                }).on('changeDate', function (e) {
                    $datePicker.html(getDatePickerText(moment(e.date, settings._inputDateFormat).format("Do MMMM YYYY")));
                    reloadDatePickerFilter(moment(e.date, settings._inputDateFormat).customFormat(settings._inputDateFormat));
                });

                it.filterElements[datePicker.name] = {
                    setValue: function (value) {
                        $datePicker.datepicker('update', value);
                        //prepare custom list
                        var text = "";

                        $.each(datePicker.options, function (index, option) {
                            if (value === option.value) {
                                text = option.text;
                            }
                        });

                        if (value && text) {
                            $datePicker.html(getDatePickerText(text));
                        } else if (value) {
                            $datePicker.html(getDatePickerText(moment(value, settings._inputDateFormat).format("Do MMMM YYYY")));
                        } else if (datePicker.defaultText) {
                            $datePicker.html(getDatePickerText(datePicker.defaultText)); //set default text if option if don't have any value
                        }

                        $(".datepicker .list-group-item").removeClass("active");
                        $(".datepicker .list-group-item").each(function () {
                            if (value === $(this).attr("data-value")) {
                                $(this).addClass("active");
                            }
                        });
                    }
                };

            });
        }
    }
    prepareRadioFilter(rangeRadioButtonOptions = null) {
        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.radioButtons[0] !== 'undefined' || rangeRadioButtonOptions) {

            if (rangeRadioButtonOptions) {
                settings.radioButtons = rangeRadioButtonOptions.rangeOptions;
            }

            var radiobuttons = "",
                filterName = "",
                value = "";
            $.each(settings.radioButtons, function (index, option) {
                var checked = "", active = "";
                filterName = option.name;
                if (option.isChecked) {
                    checked = " checked";
                    active = " active";
                    settings.filterParams[option.name] = option.value;
                    value = option.value;
                }
                radiobuttons += '<label class="btn btn-default mb0 ' + active + '">';
                radiobuttons += '<input type="radio" name="' + option.name + '" value="' + option.value + '" autocomplete="off" ' + checked + '>' + option.text;
                radiobuttons += '</label>';
            });

            var rangeRadioButtonClass = "";
            if (rangeRadioButtonOptions) {
                rangeRadioButtonClass = "range-radio-button";
            }

            var radioDom = '<div class="filter-item-box ' + rangeRadioButtonClass + '">'
                + '<div class="btn-group filter" data-act="radio" data-toggle="buttons">'
                + radiobuttons
                + '</div>'
                + '</div>';

            it.appendFilterDom(radioDom);

            var $radioButtons = $instanceWrapper.find("[data-act='radio'] input[type=radio]");

            if (rangeRadioButtonOptions && rangeRadioButtonOptions.onInit) {
                rangeRadioButtonOptions.onInit(value);
            }


            $radioButtons.click(function () {
                setTimeout(function () {
                    var value = "";
                    $radioButtons.each(function () {
                        $(this).closest("label").removeClass("active");
                        if ($(this).is(":checked")) {
                            settings.filterParams[$(this).attr("name")] = $(this).val();
                            value = $(this).val();
                            $(this).closest("label").addClass("active");
                        }
                    });
                    if (rangeRadioButtonOptions && rangeRadioButtonOptions.onChange) {
                        rangeRadioButtonOptions.onChange(value);
                        it.reloadInstance();
                    } else {
                        it.reloadInstance();
                    }

                });
            });

            it.filterElements[filterName] = {

                setValue: function (value) {
                    $radioButtons.each(function () {
                        $(this).closest("label").removeClass("active");
                        if ($(this).val() == value) {
                            $(this).closest("label").addClass("active");
                            $(this).prop("checked", true);
                        } else {
                            $(this).prop("checked", false);
                        }
                    });


                    if (rangeRadioButtonOptions) {
                        rangeRadioButtonOptions.onInit(value);

                        if (filterName && settings.filterParams[filterName] == "dynamic" && settings.filterParams[filterName + "_dynamic"]) {
                            if (it.filterElements[filterName + "_dynamic"]) {
                                it.filterElements[filterName + "_dynamic"].setValue(settings.filterParams[filterName + "_dynamic"]);
                            }

                        }
                    }

                }
            };


        }
    }
    prepareMultiselectFilter() {
        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.multiSelect[0] !== 'undefined') {

            $.each(settings.multiSelect, function (index, select) {

                var multiSelect = "", values = [],
                    saveSelection = select.saveSelection,
                    selections = getCookie(select.name);

                if (selections) {
                    selections = selections.split("-");
                }

                $.each(select.options, function (index, listOption) {
                    var active = "";

                    if (
                        (saveSelection && selections && (selections.indexOf(listOption.value) > -1)) ||
                        (saveSelection && !selections && listOption.isChecked) ||
                        (!saveSelection && listOption.isChecked)
                    ) {
                        active = " active";
                        values.push(listOption.value);
                    }
                    //<li class=" list-group-item clickable toggle-table-column" data-column="1">ID</li>
                    multiSelect += '<li class="list-group-item clickable ' + active + '" data-name="' + select.name + '" data-value="' + listOption.value + '">';
                    multiSelect += listOption.text;
                    multiSelect += '</li>';
                });


                multiSelect = "<div class='dropdown-menu'><ul class='list-group' data-act='multiselect'>" + multiSelect + "</ul></div>";


                var multiSelectClass = "";
                if (select.class) {
                    multiSelectClass = select.class;
                }


                settings.filterParams[select.name] = values;
                var multiSelectDom = '<div class="filter-item-box">'
                    + '<span class="dropdown inline-block filter-multi-select">'
                    + '<button class="' + multiSelectClass + ' btn btn-default dropdown-toggle caret " type="button" data-bs-toggle="dropdown" aria-expanded="true">' + select.text + ' </button>'
                    + multiSelect
                    + '</span>'
                    + '</div>';

                it.appendFilterDom(multiSelectDom);

                var $multiSelect = $instanceWrapper.find("[data-name='" + select.name + "']");
                $multiSelect.click(function () {
                    var $selector = $(this);
                    $selector.toggleClass("active");
                    setTimeout(function () {
                        var values = [],
                            name = "";
                        $selector.parent().find("li").each(function () {
                            name = $(this).attr("data-name");
                            if ($(this).hasClass("active")) {
                                values.push($(this).attr("data-value"));
                            }
                        });

                        if (saveSelection) {
                            //save selected options to browser cookies
                            selections = values.join("-");
                            setCookie(select.name, selections);
                        }

                        settings.filterParams[name] = values;
                        it.reloadInstance();
                    });
                    return false;
                });


                it.filterElements[select.name] = {

                    setValue: function (values) {
                        if (!values) {
                            values = [];
                        }
                        $multiSelect.each(function () {

                            if (values.includes($(this).attr("data-value"))) {
                                $(this).addClass("active");
                            } else {
                                $(this).removeClass("active");
                            }

                        });
                    }
                };

            });


        }

    }
    prepareCheckboxFilter() {
        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.checkBoxes[0] !== 'undefined') {
            var checkboxes = "", values = [], name = "";
            $.each(settings.checkBoxes, function (index, option) {
                var checked = "", active = "";
                name = option.name;
                if (option.isChecked) {
                    checked = " checked";
                    active = " active";
                    values.push(option.value);
                }
                checkboxes += '<label class="btn btn-default mb0 ' + active + '">';
                checkboxes += '<input type="checkbox" name="' + option.name + '" value="' + option.value + '" autocomplete="off" ' + checked + '>' + option.text;
                checkboxes += '</label>';
            });
            settings.filterParams[name] = values;
            var checkboxDom = '<div class="filter-item-box">'
                + '<div class="btn-group filter" data-act="checkbox" data-toggle="buttons">'
                + checkboxes
                + '</div>'
                + '</div>';

            it.appendFilterDom(checkboxDom);

            var $checkbox = $instanceWrapper.find("[data-act='checkbox']");
            $checkbox.click(function () {
                var $selector = $(this);
                setTimeout(function () {
                    var values = [],
                        name = "";
                    $selector.parent().find("input:checkbox").each(function () {
                        name = $(this).attr("name");
                        if ($(this).is(":checked")) {
                            values.push($(this).val());
                            $(this).closest("label").addClass("active");
                        } else {
                            $(this).closest("label").removeClass("active");
                        }
                    });
                    settings.filterParams[name] = values;
                    it.reloadInstance();
                });
            });



            it.filterElements[name] = {
                setValue: function (values) {
                    if (!values) {
                        values = [];
                    }
                    $instanceWrapper.find("input:checkbox").each(function () {
                        //it'll find all checkboxes. Match with name
                        if (name === $(this).attr("name")) {
                            if (values.includes($(this).val())) {
                                $(this).closest("label").addClass("active");
                            } else {
                                $(this).closest("label").removeClass("active");
                            }
                        }
                    });
                }
            };


        }

    }
    prepareDependentFilter(filterName, filterValue, filterDropdown, filterParams, newFilterParams) {

        var it = this,
            $instanceWrapper = this.$instanceWrapper;

        //check all dropdowns and prepre the dependency dropdown list

        $.each(filterDropdown, function (index, option) {

            //is there any dependency for selected field (filterName)? Prepare the dropdown list 
            if (option.dependency && option.dependency.length && option.dependency.indexOf(filterName) !== -1) {

                var $dependencySelector = $instanceWrapper.find("[name='" + option.name + "']"); //select box
                var dependentFilterName = option.name;

                //we'll call ajax to get the data list
                if (((option.selfDependency && !filterValue) || filterValue) && option.dataSource) {

                    appAjaxRequest({
                        url: option.dataSource,
                        data: filterParams,
                        type: "POST",
                        dataType: 'json',
                        success: function (response) {
                            //if we found the dropdown list, we'll show the options in dropdown
                            if (response && response.length) {
                                $dependencySelector.select2("destroy").val("");
                                $dependencySelector.appDropdown({
                                    list_data: response,
                                });
                                //set the new dropdown list in select box
                                //  $dependencySelector.html(newOptions);

                                var selectedValue = "";

                                if (newFilterParams && newFilterParams[dependentFilterName]) {
                                    selectedValue = newFilterParams[dependentFilterName];
                                } else {
                                    if (response[0] && response[0].id) {
                                        selectedValue = response[0].id;
                                    }
                                }

                                $dependencySelector.select2("val", selectedValue);
                            }
                        }
                    });

                } else {
                    //no value selected in parent, reset the dropdown box

                    $dependencySelector.select2("destroy").val("");
                    var placeholder = $dependencySelector.attr("placeholder") ? $dependencySelector.attr("placeholder") : "-";

                    $dependencySelector.appDropdown({
                        list_data: [{ id: "", text: placeholder }],
                    });
                }

                //reset the filter param
                if (filterParams && newFilterParams && newFilterParams[dependentFilterName]) {
                    filterParams[dependentFilterName] = newFilterParams[dependentFilterName];
                } else if (filterParams) {
                    //check if there is any way to find the list.
                    var options = option.options || [];

                    var $firstOption = options[0] || {};
                    var firstValue = $firstOption.id || "";
                    filterParams[option.name] = firstValue;
                }

            }

        });

    }

    initDynamicFilter(rangeOptions, value, isOnChange = false) {
        var settings = this.settings,
            it = this,
            $instanceWrapper = it.$instanceWrapper;


        var $startDiv = $instanceWrapper.find('.range-radio-button');

        $startDiv.nextAll('.filter-item-box').find('[data-act="datepicker"]').closest('.filter-item-box').remove();
        $startDiv.nextAll('.filter-item-box').find('.input-daterange').closest('.filter-item-box').remove();
        $startDiv.nextAll('.filter-item-box').find('#dynamic-range-dropdown').closest('.filter-item-box').remove();


        // var $parentFilterBox = $instanceWrapper.find('[data-act="datepicker"]').closest('.filter-item-box');
        // $parentFilterBox.remove();

        // var $dateRangeParentFilterBox = $instanceWrapper.find('.input-daterange').closest('.filter-item-box');
        // $dateRangeParentFilterBox.remove();

        // var $dynamicDateRangeParentFilterBox = $instanceWrapper.find('#dynamic-range-dropdown').closest('.filter-item-box');
        // $dynamicDateRangeParentFilterBox.remove();

        if (value === "monthly") {
            settings.dateRangeType = "monthly";
            if (isOnChange || !settings.filterParams.start_date) {

                var daysInMonth = moment().daysInMonth(),
                    yearMonth = moment().customFormat("YYYY-MM");
                settings.filterParams.start_date = yearMonth + "-01";
                settings.filterParams.end_date = yearMonth + "-" + daysInMonth;
            }

            it.prepareDateRangePicker(true);

        } else if (value === "yearly") {
            settings.dateRangeType = "yearly";
            if (isOnChange) {
                var year = moment().customFormat("YYYY");
                settings.filterParams.start_date = year + "-01-01";
                settings.filterParams.end_date = year + "-12-31";
            }

            it.prepareDateRangePicker(true);
        } else if (value === "custom") {
            var datePickerOptions = [
                {
                    "startDate": {
                        "name": "start_date",
                        "value": isOnChange ? settings.filterParams.start_date : moment().customFormat("YYYY-MM-DD")
                    },
                    "endDate": {
                        "name": "end_date",
                        "value": isOnChange ? settings.filterParams.end_date : moment().customFormat("YYYY-MM-DD")
                    },
                    "showClearButton": true
                }
            ];

            settings.rangeDatepicker = datePickerOptions;

            it.prepareDatePickerFilter(true);
        } else if (value === "dynamic") {


            var dynamicOption = rangeOptions.find(function (option) {
                return option.value === "dynamic";
            });

            var dynamicRangeFilterName = dynamicOption.name + "_dynamic";

            settings.selectedDynamicRange = settings.filterParams[dynamicRangeFilterName];
            settings.dynamicRanges = dynamicOption.dynamicRanges;
            settings.dynamicRangeFilterName = dynamicRangeFilterName;

            it.prepareDynamicFilterDomAndEvents();

            if (isOnChange) {

                var filterMap = it.filterElements[dynamicRangeFilterName];
                if (filterMap) {
                    if (settings.dynamicRanges) {

                        if (!settings.selectedDynamicRange || !settings.dynamicRanges.includes(settings.selectedDynamicRange)) {
                            settings.selectedDynamicRange = settings.dynamicRanges[0]; //auto select the 1st object if there is no selected value for the dynamic range.
                        }
                    }
                    filterMap.setValue(settings.selectedDynamicRange);
                }
            }


        }

        setTimeout(function () {
            feather.replace();
        }, 1);

    }


    prepareRangeRadioButtons() {

        var settings = this.settings,
            it = this;

        if (settings.rangeRadioButtons && typeof settings.rangeRadioButtons[0] !== 'undefined') {
            var rangeRadioOptions = {};
            var rangeOptions = [];

            $.each(settings.rangeRadioButtons, function (index, optionObj) {

                $.each(optionObj.options, function (i, option) {
                    rangeOptions.push({
                        text: AppLanugage[option],
                        name: optionObj.name,
                        value: option,
                        isChecked: option === optionObj.selectedOption,
                        dynamicRanges: optionObj.dynamicRanges,
                        selectedDynamicRange: optionObj.selectedDynamicRange
                    });
                });

                rangeRadioOptions.rangeOptions = rangeOptions;

            });


            rangeRadioOptions.onInit = function (value) {
                it.initDynamicFilter(rangeOptions, value);
            }
            rangeRadioOptions.onChange = function (value) {
                it.initDynamicFilter(rangeOptions, value, true);
            };


            it.prepareRadioFilter(rangeRadioOptions);


        }
    }

    prepareDynamicFilterDomAndEvents() {
        var settings = this.settings,
            it = this,
            $instance = this.$instance,
            $instanceWrapper = this.$instanceWrapper;

        if (typeof settings.dynamicRanges !== 'undefined' && settings.dynamicRanges.length > 0) {
            var defaultRanges = it.getDynamicDateRanges();

            var buttonDom = '<div class="filter-item-box">' +
                '<div class="btn-group">';


            if (settings.selectedDynamicRange && settings.dynamicRanges) {
                if (!settings.dynamicRanges.includes(settings.selectedDynamicRange)) {
                    settings.selectedDynamicRange = settings.dynamicRanges[0]; //auto select the 1st object if there is no selected value for the dynamic range.
                }
            }


            var dropDownId = "dynamic-range-dropdown";

            buttonDom += '<div class="dropdown dynamic-range-filter">' +
                '<button class="btn btn-default dropdown-toggle caret" type="button" id="' + dropDownId + '" data-bs-toggle="dropdown">' +
                AppLanugage[settings.selectedDynamicRange] +
                '</button>' +
                '<div class="dropdown-menu"><ul class="list-group">';


            $.each(settings.dynamicRanges, function (index, range) {
                var activeClass = "";
                if (range == settings.selectedDynamicRange) {
                    activeClass = " active ";
                }
                buttonDom += '<li class="list-group-item clickable' + activeClass + '" data-range="' + range + '">' + AppLanugage[range] + '</li>';
            });

            buttonDom += '</ul></div>' +
                '</div>';

            buttonDom += '</div></div>';

            it.appendFilterDom(buttonDom, true);

            var filterName = settings.dynamicRangeFilterName;
            var $dynamicDropDown = $instanceWrapper.find('#' + dropDownId);

            var selectDynamicFilter = function (rangeName) {

                if (defaultRanges && defaultRanges[rangeName]) {
                    settings.filterParams.start_date = defaultRanges[rangeName][0];
                    settings.filterParams.end_date = defaultRanges[rangeName][1];
                    settings.filterParams[filterName] = rangeName;
                }

                $dynamicDropDown.html(AppLanugage[rangeName]);

                $dynamicDropDown.closest(".dropdown").find('.list-group-item.active').removeClass("active");
                $dynamicDropDown.closest(".dropdown").find('.list-group-item[data-range="' + rangeName + '"]').addClass("active");
            }


            it.filterElements[filterName] = {
                setValue: function (value) {
                    if (value) {
                        selectDynamicFilter(value);
                    }

                }
            };

            var $dropdownItem = $dynamicDropDown.closest(".dropdown").find('.dropdown-menu .list-group-item');
            $dropdownItem.on('click', function () {
                var range_name = $(this).data('range');

                if (range_name) {
                    selectDynamicFilter(range_name);
                    it.reloadInstance();
                }

            });



        }
    }

}

var buildFilterDom = function (settings, $instanceWrapper, $instance) {
    var filters = new BuildFilters(settings, $instanceWrapper, $instance);
    filters.init();
};

var $appFilterXhrRequest = 'new';

(function ($) {
    //appTable using datatable
    $.fn.appTable = function (options) {

        //set default display length
        var displayLength = AppHelper.settings.displayLength * 1;

        if (isNaN(displayLength) || !displayLength) {
            displayLength = 10;
        }

        var responsive = false;
        if (AppHelper.settings.disableResponsiveDataTable === "1") {
            responsive = false;
        } else if ((AppHelper.settings.disableResponsiveDataTableForMobile !== "1") && (window.outerWidth < 800)) {
            responsive = true;
        }

        var defaults = {
            source: "", //data url
            columns: [], //column title and options
            columnDefs: [],
            order: [[0, "asc"]], //default sort value
            serverSide: false,
            xlsColumns: [], // array of excel exportable column numbers
            printColumns: [], // array of printable column numbers
            pdfColumns: [], // array of pdf exportable column numbers
            hideTools: false, //show/hide tools section
            columnShowHideOption: true, //show a option to show/hide the columns,
            tableRefreshButton: false, //show a option to refresh the table
            displayLength: displayLength, //default rows per page
            filterParams: { datatable: true }, //will post this vales on source url
            smartFilterIdentity: null, //a to z and _ only. should be unique to avoid conflicts 
            ignoreSavedFilter: false, //sometimes, need to click on widget link to show specific filter. Enable for that. 
            dateRangeType: "", // type: daily, weekly, monthly, yearly. output params: start_date and end_date
            checkBoxes: [], // [{text: "Caption", name: "status", value: "in_progress", isChecked: true}] 
            multiSelect: [], // [{text: "Caption", name: "status", options:[{text: "Caption", value: "in_progress", isChecked: true}]}] 
            radioButtons: [], // [{text: "Caption", name: "status", value: "in_progress", isChecked: true}] 
            filterDropdown: [], // [{id: 10, text:'Caption', isSelected:true}] 
            singleDatepicker: [], // [{name: '', value:'', options:[]}] 
            rangeDatepicker: [], // [{startDate:{name:"", value:""},endDate:{name:"", value:""}}] 
            rangeRadioButtons: [], // [{options: ['monthly', 'yearly', 'custom', 'dynamic'], name: anything_range_radio_button, selectedOption: 'monthly', rangeFromName:"", rangeToName:""}]
            isMobile: window.matchMedia("(max-width: 800px)").matches,
            responsive: responsive, //by default, apply the responsive design only on the mobile view
            stateSave: true, //save user state
            stateDuration: 60 * 60 * 24 * 60, //remember for 60 days
            summation: "", /* {column: 5, dataType: 'currency'}  dataType:currency, time */
            onDeleteSuccess: function () { },
            onUndoSuccess: function () { },
            onInitComplete: function () { },
            footerCallback: function (row, data, start, end, display) { },
            rowCallback: function (nRow, aData, iDisplayIndex, iDisplayIndexFull) { },
            onRelaodCallback: function () { },
            reloadHooks: [],
            customLanguage: {
                noRecordFoundText: AppLanugage.noRecordFound,
                searchPlaceholder: AppLanugage.search,
                printButtonText: AppLanugage.print,
                excelButtonText: AppLanugage.excel,
                printButtonToolTip: AppLanugage.printButtonTooltip,
                today: AppLanugage.today,
                yesterday: AppLanugage.yesterday,
                tomorrow: AppLanugage.tomorrow
            }
        };

        var $instance = $(this);

        //check if this binding with a table or not
        if (!$instance.is("table")) {
            console.log("appTable: Element must have to be a table", this);
            return false;
        }

        $instance.on('length.dt page.dt order.dt search.dt', function () {
            setTimeout(function () {
                feather.replace();
            }, 1);
        });

        var settings = appExtend({}, defaults, options);
        if (!settings) {
            return false;
        }

        //map the columns class property to className 
        settings.columns.forEach(function (column) {
            if (column.class) {
                column.className = column.class;
                delete column.class;
            }
        });

        // reload
        var instanceId = $(this).attr("id");

        if (settings.reload) {
            var table = $(this).dataTable();

            var instanceSettings = {};

            if (window.InstanceCollection) {
                instanceSettings = window.InstanceCollection[instanceId];
            }

            if (!instanceSettings) {
                instanceSettings = settings;
            }
            var tableId = table.get(0).id;

            if (instanceSettings.serverSide) {
                window.appTables[tableId].api().draw();
            } else if (table && table.api().fnReloadAjax) {
                table.api().fnReloadAjax(instanceSettings.filterParams);
            }

            if ($(this).data("onRelaodCallback")) {
                $(this).data("onRelaodCallback")(table, instanceSettings.filterParams);
            }

            //Reset selection after reload
            $("#" + table.attr("id")).trigger("reset-selection-menu");

            return false;
        }


        // add/edit row
        if (settings.newData) {

            var table = $(this).dataTable();

            if (settings.dataId) {
                //check for existing row; if found, delete the row; 

                var $row = $(this).find("[data-post-id='" + settings.dataId + "']");

                if (!$row.length) {
                    $row = $(this).find("[data-index-id='" + settings.dataId + "']");
                }

                if ($row.length) {
                    // .fnDeleteRow($row.closest('tr'));

                    table.api().row(table.api().row($row.closest('tr')).index()).data(settings.newData);

                    table.api().fnUpdateRow(null, table.api().page()); //update existing row
                } else {
                    table.api().fnUpdateRow(settings.newData); //add new row
                }


            } else if (settings.rowDeleted) {
                table.api().fnUpdateRow(settings.newData, table.api().page(), true); //refresh row after delete
            } else {
                table.api().fnUpdateRow(settings.newData); //add new row
            }

            return false;
        }


        var matchesQuery = function (data, query) {
            return Object.entries(query).every(([key, value]) => data[key] === value);
        }


        var matchesHookFilter = function (postData, filterData) {

            var filterArray = Object.entries(filterData).map(([key, value]) => ({
                name: key,
                value: value
            }));

            return filterArray.every(filterItem => {
                return postData.some(postItem =>
                    postItem.name == filterItem.name && postItem.value == filterItem.value
                );
            });
        }

        var updateSingleRow = function (settings, id) {
            var postData = {};
            postData.id = id;
            postData.server_side = 0; //disable server side for this request

            appAjaxRequest({
                url: settings.source,
                type: 'POST',
                dataType: 'json',
                data: postData,
                success: function (result) {
                    if (result.data && result.data[0]) {
                        $("#" + instanceId).appTable({
                            newData: result.data[0],
                            dataId: id
                        });
                    }
                }
            });

        }


        $.each(settings.reloadHooks || [], function (index, hook) {

            if (hook.type === "app_form" && hook.id) {
                registerAppFormHook(hook.id, function (appFormPostData, appFormSuccessResult) {

                    if (hook.filter && !matchesQuery(appFormPostData, hook.filter)) return "continue";

                    if (!appFormPostData) appFormPostData = {};

                    var idField = hook.mapPostData && hook.mapPostData.id ? hook.mapPostData.id : "id";

                    var listDataId = appFormPostData[idField];

                    if (!listDataId && appFormSuccessResult.id) {
                        listDataId = appFormSuccessResult.id;
                    }
                    updateSingleRow(settings, listDataId);
                }, "appTable", instanceId);

            } else if (hook.type === "ajax_request" && hook.group) {
                registerAjaxRequestHook(hook.group, function (ajaxRequestPostData) {

                    if (!ajaxRequestPostData) ajaxRequestPostData = {};

                    var idField = hook.mapPostData && hook.mapPostData.id ? hook.mapPostData.id : "id";

                    var listDataId = ajaxRequestPostData[idField];

                    if (!listDataId) {
                        console.log("The id data is missing on the ajaxRequestData");
                        return false;
                    }

                    updateSingleRow(settings, listDataId);

                }, "appTable", instanceId);
            } else if (hook.type === "app_modifier" && hook.group) {
                registerAppModifierHook(hook.group, function (appModifierData, result) {

                    if (!appModifierData) appModifierData = {};

                    var idField = hook.mapPostData && hook.mapPostData.id ? hook.mapPostData.id : "id";

                    var listDataId = appModifierData[idField];

                    if (!listDataId) {
                        console.log("The id data is missing on the appModifierData");
                        return false;
                    }

                    updateSingleRow(settings, listDataId);

                }, "appTable", instanceId);

            } else if (hook.type === "app_table_row_update" && hook.tableId) {
                registerAppTableRowUpdateHook(hook.tableId, function (appTableRowUpdateData) {

                    if (!appTableRowUpdateData) appTableRowUpdateData = {};

                    var idField = hook.mapPostData && hook.mapPostData.id ? hook.mapPostData.id : "id";

                    var listDataId = appTableRowUpdateData[idField];

                    if (!listDataId) {
                        console.log("The id data is missing on the appTableRowUpdateData");
                        return false;
                    }

                    updateSingleRow(settings, listDataId);

                }, "appTable", instanceId);

            }
            //add other hooks here.

        });

        //add nowrap class in responsive view
        if (settings.responsive) {
            $instance.addClass("nowrap");
        }


        var _prepareFooter = function (settings, page, lable) {
            var tr = "",
                trSection = '';

            if (page === "all") {
                trSection = 'data-section="all_pages"';
            }

            tr += "<tr " + trSection + ">";

            $.each(settings.columns, function (index, column) {

                var thAttr = "class = 'tf-blank' ",
                    thLable = " ";


                if (settings.summation[0] && settings.summation[0].column - 1 === index) {
                    thLable = lable;
                    thAttr = "class = 'tf-lable' ";
                }

                $.each(settings.summation, function (fIndex, sumColumn) {
                    if (sumColumn.column === index) {
                        thAttr = "class = 'tf-result text-right' ";
                        thAttr += 'data-' + page + '-page="' + sumColumn.column + '"';
                    }
                });

                tr += "<th " + thAttr + ">";
                tr += thLable;
                tr += "</th>";

            });
            tr += "</tr>";

            return tr;

        };

        //add summation footer 
        //don't add it on mobile view. We'll show another field in mobile view.

        if (settings.summation && settings.summation.length && !settings.isMobile) {
            var content = "<tfoot>";

            content += _prepareFooter(settings, 'current', AppLanugage.total);
            content += _prepareFooter(settings, 'all', AppLanugage.totalOfAllPages);

            content += "</tfoot>";

            $instance.html(content);
        }

        settings._visible_columns = [];

        //check if there is any 'all' class with any column
        //if so, add the 'desktop' class with other columns
        var hasAllClass = false;
        if (settings.columns.find(column => column.className && column.className.includes('all'))) {
            hasAllClass = true;
        }

        var columnDefs = [];

        $.each(settings.columns, function (index, column) {
            if (column.visible !== false) {
                settings._visible_columns.push(index);
            }

            //set orderable: false if serverSide:true and don't have order_by reference. 
            //also check if dependant sorting column has order_by reference
            var orderable = false;

            if (settings.serverSide) {
                if (column.order_by) {
                    orderable = true;
                } else if (!column.order_by && column.iDataSort && settings.columns[column.iDataSort].order_by) {
                    orderable = true;
                }
            } else {
                if (column.sortable !== false) {
                    orderable = true;
                }
            }

            settings.columns[index].orderable = orderable;
            if (hasAllClass && settings.isMobile) {
                if ((column.className && !column.className.includes("all")) || !column.className) {
                    settings.columns[index].className = settings.columns[index].className ? settings.columns[index].className + " desktop" : "desktop";
                }
            }

            if (!settings.isMobile && settings.mobileMirror) {
                if ((column.className && !column.className.includes("all")) || !column.className) {
                    settings.columns[index].className = settings.columns[index].className ? settings.columns[index].className + " mobile-only" : "mobile-only";
                }
            }

            //prepare the columnDefs from older version 
            if (column.iDataSort || column.iDataSort == "0") {
                columnDefs.push({ targets: index, orderData: [column.iDataSort] });

                //remove the iDataSort from the column
                //delete column.iDataSort;
            }

        });


        //don't set columnDefs if it's already set
        if (settings.columnDefs.length == 0) {
            settings.columnDefs = columnDefs;
        }


        settings._exportable = settings.xlsColumns.length + settings.pdfColumns.length + settings.printColumns.length;
        settings._firstDayOfWeek = AppHelper.settings.firstDayOfWeek || 0;
        settings._inputDateFormat = "YYYY-MM-DD";


        settings = prepareDefaultFilters(settings);

        var aLengthMenu = [[10, 25, 50, 100, -1], [10, 25, 50, 100, AppLanugage.all]];
        if (settings.serverSide) {
            var menuOptions = [10, 25, 50, 100];

            if (window.addAppTableDisplayOption && Number(window.addAppTableDisplayOption) > 0) {
                menuOptions.push(Number(window.addAppTableDisplayOption));
            }

            aLengthMenu = [menuOptions, menuOptions];
        }



        var responsive = settings.responsive,
            stateSave = cloneDeep(settings.stateSave),
            displayLength = cloneDeep(settings.displayLength);


        if (!settings.isMobile && settings.mobileMirror) {
            responsive = {
                breakpoints: [
                    { name: 'all', width: Infinity },
                    { name: 'mobile-only', width: 480 }
                ]
            };
            stateSave = false;
            displayLength = 25;
        }


        var datatableOptions = {

            // sAjaxSource: settings.source,

            ajax: {
                url: settings.source,
                type: "POST",
                data: function (postData) {

                    var order_by_index = (postData.order && postData.order[0]) ? postData.order[0].column : "",
                        order_dir = (postData.order && postData.order[0]) ? postData.order[0].dir : "",
                        search = postData.search ? postData.search['value'] : "";

                    if (order_dir) {
                        order_dir = order_dir.toUpperCase();
                    }


                    var server_side = 0;
                    if (settings.serverSide) {
                        server_side = 1;
                    }

                    return $.extend({
                        order_by: settings.columns[order_by_index] ? settings.columns[order_by_index].order_by : "",
                        order_dir: order_dir,
                        search_by: search,
                        skip: postData.start,
                        limit: postData.length,
                        draw: postData.draw,
                        server_side: server_side
                    }, settings.filterParams);
                },
                error: function (xhr, error, thrown) {
                    appAlert.error(AppLanugage.somethingWentWrong);
                },
                dataSrc: function (response) {
                    settings.summationInfo = response.summation;
                    return response.data;
                }
            },
            sServerMethod: "POST",
            columns: settings.columns,
            columnDefs: settings.columnDefs,
            processing: true,
            serverSide: settings.serverSide,
            iDisplayLength: displayLength,
            aLengthMenu: aLengthMenu,
            bAutoWidth: false,
            bSortClasses: false,
            order: settings.order,
            stateSave: stateSave,
            responsive: responsive,
            pagingType: "simple_numbers",
            fnStateLoadParams: function (oSettings, oData) {
                //if the stateSave is true, we'll remove the search value after next reload. 
                if (oData && oData.search) {
                    oData.search.search = "";
                }
            },
            stateDuration: settings.stateDuration,
            fnInitComplete: function () {
                settings.onInitComplete(this);
            },
            language: {
                "lengthMenu": "_MENU_",
                "emptyTable": settings.customLanguage.noRecordFoundText,
                "info": "_START_-_END_ / _TOTAL_",
                "sInfo": "_START_-_END_ / _TOTAL_",
                "infoFiltered": "(_MAX_)",
                "search": "",
                "searchPlaceholder": settings.customLanguage.searchPlaceholder,
                "infoEmpty": "0-0 / 0",
                "sInfoFiltered": "(_MAX_)",
                "infoPostFix": "",
                "paginate": {
                    "previous": "<i data-feather='chevron-left' class='icon-16'></i>",
                    "next": "<i data-feather='chevron-right' class='icon-16'></i>"
                }
            },
            sDom: "",
            footerCallback: function (row, data, start, end, display) {
                var instance = this;
                if (settings.summation) {

                    var pageInfo = instance.api().page.info(),
                        summationContent = "",
                        pageTotalContent = "",
                        allPageTotalContent = "";

                    if (pageInfo.recordsTotal) {
                        $(instance).find("tfoot").show();
                    } else {
                        $(instance).find("tfoot").hide();
                        return false;
                    }

                    $.each(settings.summation, function (index, option) {
                        // total value of current page
                        var pageTotal = calculateDatatableTotal(instance, option.column, function (currentValue) {

                            //if we get <b> tag, we'll assume that is a group total. ignore the value
                            if (currentValue && !currentValue.startsWith("<b>")) {
                                if (option.dataType === "currency") {
                                    if (option.dynamicSymbol) { //find out currency symbol 
                                        var x = currentValue;
                                        option.currencySymbol = x.replace(/[0-9.,-]/g, "");
                                    }

                                    return unformatCurrency(currentValue, option.conversionRate);
                                } else if (option.dataType === "time") {
                                    return moment.duration(currentValue).asSeconds();
                                } else if (option.dataType === "number") {
                                    return unformatCurrency(currentValue);
                                } else {
                                    return currentValue;
                                }
                            } else {
                                return 0;
                            }

                        }, true);

                        if (option.dataType === "currency") {
                            pageTotal = toCurrency(pageTotal, option.currencySymbol);
                        } else if (option.dataType === "time") {
                            pageTotal = secondsToTimeFormat(pageTotal);
                        } else if (option.dataType === "number") {
                            pageTotal = toCurrency(pageTotal, "none");
                        }

                        var pagTotalTitle = table.column(option.column).header();
                        if (pagTotalTitle) {
                            pageTotalContent += "<div class='box'><div class='box-content'>" + $(pagTotalTitle).html() + "</div><div class='box-content text-right'>" + pageTotal + "</div></div>";
                        }

                        $(instance).find("[data-current-page=" + option.column + "]").html(pageTotal);

                        // total value of all pages
                        if (pageInfo.pages > 1) {
                            $(instance).find("[data-section='all_pages']").show();

                            var total = 0;

                            if (settings.serverSide) {
                                var summationInfo = settings.summationInfo;

                                if (summationInfo && option.fieldName) {
                                    total = summationInfo[option.fieldName] ? summationInfo[option.fieldName] : 0;
                                }

                            } else {
                                total = calculateDatatableTotal(instance, option.column, function (currentValue) {
                                    //if we get <b> tag, we'll assume that is a group total. ignore the value
                                    if (currentValue && !currentValue.startsWith("<b>")) {
                                        if (option.dataType === "currency") {
                                            return unformatCurrency(currentValue, option.conversionRate);
                                        } else if (option.dataType === "time") {
                                            return moment.duration(currentValue).asSeconds();
                                        } else if (option.dataType === "number") {
                                            return unformatCurrency(currentValue);
                                        } else {
                                            return currentValue;
                                        }
                                    } else {
                                        return 0;
                                    }
                                });
                            }


                            if (option.dataType === "currency") {
                                total = toCurrency(total, option.currencySymbol);
                            } else if (option.dataType === "time") {
                                total = secondsToTimeFormat(total);
                            } else if (option.dataType === "number") {
                                total = toCurrency(total, "none");
                            }

                            var title = table.column(option.column).header();
                            if (title) {
                                allPageTotalContent += "<div class='box'><div class='box-content'>" + $(title).html() + "</div><div class='box-content text-right'>" + total + "</div></div>";
                            }

                            $(instance).find("[data-all-page=" + option.column + "]").html(total);
                        } else {
                            $(instance).find("[data-section='all_pages']").hide();
                        }
                    });

                    //add summation section for mobile view.
                    if (settings.isMobile || settings.mobileMirror) {
                        if (pageTotalContent) {
                            summationContent += "<div class='box'><div class='box-content strong'>" + AppLanugage.total + "</div></div>" + pageTotalContent;
                        }
                        if (allPageTotalContent) {
                            summationContent += "<div class='box'><div class='box-content strong'>" + AppLanugage.totalOfAllPages + "</div></div>" + allPageTotalContent;
                        }

                        $(".summation-section").html(summationContent);
                    }

                }

                settings.footerCallback(row, data, start, end, display, instance);
            },
            fnRowCallback: function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
                settings.rowCallback(nRow, aData, iDisplayIndex, iDisplayIndexFull);
            },
            preDrawCallback: function (settings) {
                if (settings.aoData.length > 0) {
                    $(".summation-section").removeClass("hide");
                } else {
                    $(".summation-section").addClass("hide");
                }

            }
        };



        //to save the datatatable state in cookie, we'll use the user's reference.
        //sometime the same user (most of the time the admin user) will login to different account to check. 
        //since the table columns are different for different users, 
        //we'll save the coockie based on table reference + user reference 

        if (AppHelper.userId) {

            datatableOptions.stateSaveParams = function (dataTableSettings, data) {
                if (dataTableSettings.sInstance.indexOf("-user-ref-") === -1) {
                    dataTableSettings.sInstance += "-user-ref-" + AppHelper.userId;
                }
            };

            datatableOptions.stateLoadCallback = function (dataTableSettings) {
                if (dataTableSettings.sInstance.indexOf("-user-ref-") === -1) {
                    dataTableSettings.sInstance += "-user-ref-" + AppHelper.userId;
                }
                try {

                    var pathname = location.pathname;
                    if (settings.mobileMirror) {
                        pathname = pathname.replace(/\/compact_view\/.*/, '');
                    }

                    return JSON.parse(
                        (dataTableSettings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
                            'DataTables_' + dataTableSettings.sInstance + '_' + pathname
                        )
                    );
                } catch (e) {
                }
            };
        }


        var sDomExport = "";

        if (settings._exportable) {
            var datatableButtons = [];

            if (settings.xlsColumns.length) {
                //add excel button

                datatableButtons.push({
                    extend: 'excelHtml5',
                    footer: true,
                    text: settings.customLanguage.excelButtonText,
                    className: 'btn btn-default buttons-excel buttons-html5',
                    exportOptions: {
                        columns: settings.xlsColumns
                    },
                    customize: function (xls) {
                        //append the total of all pages row, if it exists
                        if ($instance.find("[data-section='all_pages']") && $instance.find("[data-section='all_pages']").css('display') !== "none") {
                            var sheet = xls.xl.worksheets['sheet1.xml'];

                            var $sheetSelector = $(sheet.childNodes[0].childNodes[1]);
                            var thisRowNumber = parseInt($sheetSelector.find("row:last-child").attr("r")) + 1;

                            //here should define the actual position of the item using the abc character
                            var chars = 'abcdefghijklmnopqrstuvwxyz',
                                rowCounted = 0;

                            var rowDom = '<row r="' + thisRowNumber + '">';

                            $instance.find("[data-section='all_pages'] th").each(function () {
                                if ($(this).text()) {
                                    rowDom += '<c t="inlineStr" r="' + chars[rowCounted].toUpperCase() + thisRowNumber + '" s="2">';
                                    rowDom += '<is>';
                                    rowDom += '<t>' + $(this).text() + '</t>';
                                    rowDom += '</is>';
                                    rowDom += '</c>';
                                }

                                //increase the position variable
                                rowCounted = rowCounted + 1;
                            });

                            rowDom += '</row>';

                            //add the row finally
                            sheet.childNodes[0].childNodes[1].innerHTML = sheet.childNodes[0].childNodes[1].innerHTML + rowDom;
                        }
                    }
                });
            }

            if (settings.printColumns.length) {
                datatableButtons.push({
                    extend: 'print',
                    autoPrint: false,
                    text: settings.customLanguage.printButtonText,
                    className: 'btn btn-default buttons-print',
                    footer: true,
                    exportOptions: {
                        columns: settings.printColumns
                    },
                    customize: function (win) {
                        $(win.document.body).closest("html").addClass("dt-print-view");

                        //append the total of all pages row, if it exists
                        if ($instance.find("[data-section='all_pages']") && $instance.find("[data-section='all_pages']").css('display') !== "none") {
                            var totalOfAllPagesClone = $instance.find("[data-section='all_pages']").clone();
                            $(win.document.body).find("tfoot").append(totalOfAllPagesClone);
                        }
                    },
                    customizeData: function (a, b, c) {

                    }
                });
            }

            datatableOptions.buttons = datatableButtons;

            sDomExport = "<'datatable-export filter-item-box'B >";
        }

        var filterFormDom = "";
        if (settings.smartFilterIdentity) {
            filterFormDom = "<'filter-form'>";
        }

        var sDomExportMobile = "";
        var footerSection1Class = "col-md-3",
            footerSection2Class = "col-md-9";

        if (settings.isMobile || settings.mobileMirror) {
            sDomExportMobile = sDomExport;
            sDomExport = ""; //show the export button on the bottom for mobile devices
            footerSection1Class = "col-md-12";
            footerSection2Class = "col-md-12 mini-list-pagination-container";
        }

        //if hideTools is true, then hide the tools
        if (settings.hideTools) {
            datatableOptions.sDom = "t";
        }

        //set custom toolbar
        if (!settings.hideTools) {
            datatableOptions.sDom = "<'filter-section-container' <'filter-section-flex-row' <'filter-section-left'> <'filter-section-right' " + sDomExport + " <'filter-item-box' f> > > " + filterFormDom + " r>t<'datatable-tools clearfix row'<'" + footerSection1Class + " pl15'<'summation-section'> <'table-bottom-left' li ><'float-end'" + sDomExportMobile + ">><'" + footerSection2Class + " pr15'p>>";
        }

        datatableOptions.drawCallback = function () {
            if (settings.serverSide) {
                var $searchBox = $instance.closest(".dt-container").find("input[type=search]");
                if (!$searchBox.val() && settings.filterParams && settings.filterParams.search_by) {
                    $searchBox.val(settings.filterParams && settings.filterParams.search_by);
                }
            }

            var api = this.api();

            // Copy sort indicator classes from the actual sorted column (iDataSort target) 
            // to the visible column so that the sorting icon appears correctly 
            // when sorting is based on a hidden column.
            $.each(settings.columns, function (index, column) {
                if (column.iDataSort != null) {
                    var classes = api.column(column.iDataSort).header().className;
                    classes = classes.replace("dt-type-date", "");
                    classes = classes.replace("dt-type-numeric", "");
                    api.column(index).header().className = classes;
                }
            });
        };

        var oTable = $instance.dataTable(datatableOptions),
            $instanceWrapper = $instance.closest(".dt-container");

        $(".dt-processing").css("display", "none"); //don't show the table loader on initial load

        var tableId = $instance.get(0) ? $instance.get(0).id : "id_not_found";

        if (!window.appTables) {
            window.appTables = [];
        }
        window.appTables[tableId] = oTable;

        $instanceWrapper.find("select").select2({
            minimumResultsForSearch: -1
        });

        //add the column show/hide option
        if (settings.columnShowHideOption) {

            var tableId = $instance.attr("id");
            table = $instance.DataTable();

            //prepare a popover
            var popover = '<button class="btn btn-default column-show-hide-popover" data-container="body" data-bs-toggle="popover" data-placement="bottom"><i data-feather="columns" class="icon-16"></i></button>';

            if (settings.isMobile || settings.mobileMirror) {
                // $instanceWrapper.find(".table-bottom-left").prepend('<div class="float-start mr10">' + popover + '</div>');
            } else {
                $instanceWrapper.find(".filter-section-left").append('<div class="filter-item-box">' + popover + '</div>');
            }


            //prepare the list of columns when opening the popover
            $instanceWrapper.find(".column-show-hide-popover").popover({
                html: true,
                sanitize: false,
                content: function () {
                    var tableColumns = "";

                    $.each(settings.columns, function (index, column) {
                        //in coulmn list, show only the visible columns
                        if (column.visible !== false) {

                            var tableColumn = table.column(index),
                                columnHiddenClass = "",
                                eyeOnOffIcon = "";

                            if (!tableColumn.visible()) {
                                columnHiddenClass = "active";
                                eyeOnOffIcon = "<i data-feather='eye-off' class='icon-16 mr10'></i>";
                            }


                            //prepare a list of columns
                            tableColumns += "<li class='" + columnHiddenClass + " list-group-item clickable toggle-table-column' data-column='" + index + "'>" + eyeOnOffIcon + column.title + "</li>";
                        }
                    });

                    return "<ul class='list-group' data-table='" + tableId + "'>" + tableColumns + "</ul>";

                }
            });

            //show/hide column when clicking on the list items    

            $instanceWrapper.find(".column-show-hide-popover").on('shown.bs.popover', function () {
                feather.replace();

                $(".toggle-table-column").on('click', function () {

                    var instanceId = $(this).closest(".list-group").attr("data-table");

                    var column = $("#" + instanceId).DataTable().column($(this).attr('data-column'));


                    // check the actual status of the table column and toggle it
                    if (column) {
                        column.visible(!column.visible());

                        $(this).toggleClass("active");
                    }

                });
            });

        }


        if (settings.tableRefreshButton) {
            //prepare a refreshButton

            var refreshButton = '<div class="filter-item-box float-start "><button class="btn btn-default at-table-refresh-button ml15"><i data-feather="refresh-cw" class="icon-16"></i></button></div>';
            $instanceWrapper.find(".filter-section-left").append(refreshButton);

            $instanceWrapper.find(".at-table-refresh-button").on('click', function () {
                $instance.appTable({ reload: true, filterParams: settings.filterParams });
            });
        }

        //hide popover when clicks on outside of the popover
        if (!$('body').hasClass("destroy-popover")) {
            $('body').addClass("destroy-popover"); //don't initiate this multiple time

            $('.destroy-popover').on('click', function (e) {
                if ($(e.target).closest("button").attr("data-bs-toggle") !== "popover" && !$(e.target).closest(".popover").length && !$(e.target).hasClass("editable")) {
                    var visiblePopoverId = $(".popover.in").attr("id");
                    $("[aria-describedby=" + visiblePopoverId + "]").trigger("click");

                }
            });
        }

        //set onReloadCallback
        $instance.data("onRelaodCallback", settings.onRelaodCallback);

        // add delay in search when applied serverside
        if (settings.serverSide) {
            var $searchBox = $instanceWrapper.find("input[type=search]");

            $searchBox.unbind().bind('input', (delayAction(function (e) {
                settings.filterParams.search_by = $(this).val();
                $instance.appTable({ reload: true, filterParams: settings.filterParams });
                return;
            }, 1000)));

            //search datatable when clicks on the labels.
            $('body').on('click', "#" + $instance.get(0).id + ' .badge.clickable', function () {
                var isSelectionMode = $(this).closest(".js-selection-mode").length;
                if (isSelectionMode) return false;

                settings.filterParams.search_by = $(this).text();
                $instance.appTable({ reload: true, filterParams: settings.filterParams });
                return false;
            });

            //search datatable when clicks on filter sub task icon
            $('body').on('click', "#" + $instance.get(0).id + ' .filter-sub-task-button', function () {
                settings.filterParams.search_by = $(this).attr('main-task-id');
                $instance.appTable({ reload: true, filterParams: settings.filterParams });
                return false;
            });

            //remove sub tasks filter
            $('body').on('click', "#" + $instance.get(0).id + ' .remove-filter-button', function () {
                settings.filterParams.search_by = "";
                $instance.appTable({ reload: true, filterParams: settings.filterParams });
                return false;
            });


        } else {
            //if not serverSide, then just re-draw the table when clicks on the labels. 
            $('body').on('click', "#" + $instance.get(0).id + ' .badge.clickable', function () {
                var value = $(this).text();

                $(this).closest(".dataTable").DataTable().search(value).draw();
                $(this).closest(".dt-container").find("input[type=search]").val(value).focus().select();
                return false;
            });
        }


        buildFilterDom(settings, $instanceWrapper, $instance);
        var undoHandler = function (eventData) {
            $('<a class="undo-delete" href="javascript:;"><strong>' + AppLanugage.undo + '</strong></a>').insertAfter($(eventData.alertSelector).find(".app-alert-message"));
            $(eventData.alertSelector).find(".undo-delete").bind("click", function () {
                $(eventData.alertSelector).remove();
                appLoader.show();
                appAjaxRequest({
                    url: eventData.url,
                    type: 'POST',
                    dataType: 'json',
                    data: { id: eventData.id, undo: true },
                    success: function (result) {
                        appLoader.hide();
                        if (result.success) {
                            $instance.appTable({ newData: result.data, rowDeleted: true });
                            //fire success callback
                            settings.onUndoSuccess(result);

                            var tableId = $instance.attr("id");
                            if (tableId && window.appTableRowDeleteHook && window.appTableRowDeleteHook[tableId]) {

                                window.appTableRowDeleteHook[tableId].forEach(function (hook) {
                                    if (typeof hook.onSuccess === 'function') {
                                        hook.onSuccess({ id: eventData.id });
                                    }
                                });
                            }
                        }
                    }
                });
            });
        };


        var rowDeleteHandler = function (result, $target) {
            var tr = $target.closest('tr'),
                table = $instance.dataTable(),
                undo = $target.attr('data-undo'),
                url = $target.attr('data-action-url'),
                id = $target.attr('data-id');

            if (tr.hasClass("child")) {
                tr = tr.prev('.parent');
            }

            table.api().row(tr).remove().draw(false);

            // oTable.fnDeleteRow(table.row(tr).index(), function () {
            //     table.page(table.page()).draw('page');
            // }, false);

            var alertId = appAlert.warning(result.message, { duration: 20000 });

            //fire success callback
            settings.onDeleteSuccess(result);

            //bind undo selector
            if (undo !== "0") {
                undoHandler({
                    alertSelector: alertId,
                    url: url,
                    id: id
                });
            }
        };

        var appTableDeleteConfirmationHandler = function (e) {
            deleteConfirmationHandler(e, rowDeleteHandler);
        };

        var appTableSimpleDeleteHandler = function (e) {
            deleteHandler(e, rowDeleteHandler);
        };

        var updateHandler = function (e) {
            appLoader.show();
            var $target = $(e.currentTarget);

            if (e.data && e.data.target) {
                $target = e.data.target;
            }

            var url = $target.attr("data-action-url");
            var tableId = $target.closest("table").attr("id");

            appAjaxRequest({
                url: url,
                dataType: 'json',
                success: function (response) {
                    if (response.success) {
                        if (response.data) {
                            $("#" + tableId).appTable({ newData: response.data, dataId: response.id });
                        }

                        appAlert.success(response.message, { duration: 10000 });

                        if (tableId && window.appTableRowUpdateHook && window.appTableRowUpdateHook[tableId]) {

                            window.appTableRowUpdateHook[tableId].forEach(function (hook) {
                                if (typeof hook.onSuccess === 'function') {
                                    hook.onSuccess({ id: response.id });
                                }
                            });
                        }

                    } else {
                        appAlert.error(response.message);
                    }
                    appLoader.hide();
                }
            });
        };


        window.InstanceCollection = window.InstanceCollection || {};
        window.InstanceCollection[$(this).attr("id")] = settings;

        $('body').find($instance).on('click', 'a[data-action=delete]', appTableSimpleDeleteHandler);
        $('body').find($instance).on('click', 'a[data-action=delete-confirmation]', appTableDeleteConfirmationHandler);
        $('body').find($instance).on('click', '[data-action=update]', updateHandler);

        // $.fn.dataTableExt.oApi.getSettings = function (oSettings) {
        //     return oSettings;
        // };

        $.fn.dataTable.Api.register('getSettings', function (settings) {
            return settings;
        });


        $.fn.dataTable.Api.register('fnReloadAjax', function (filterParams) {
            var table = this;
            var settings = table.settings()[0];

            if ($appFilterXhrRequest !== 'new') {
                // Another xhr request is already running
                return;
            }

            table.clear(); // Clear existing data
            table.processing(true); // Show processing indicator

            $appFilterXhrRequest = $.ajax({
                url: settings.ajax.url,
                type: "POST",
                dataType: "json",
                data: filterParams,
                success: function (json) {
                    $appFilterXhrRequest = 'new';

                    table.rows.add(json.data); // Add new data
                    table.draw();              // Redraw the table
                    table.processing(false);   // Hide processing indicator
                }
            });
        });



        // $.fn.dataTableExt.oApi.fnReloadAjax = function (oSettings, filterParams) {
        //     this.fnClearTable(this);
        //     this.oApi._fnProcessingDisplay(oSettings, true);
        //     var that = this;

        //     if ($appFilterXhrRequest !== 'new') {
        //         //an another xhr request is already running
        //         return;
        //     }

        //     $appFilterXhrRequest = $.ajax({
        //         url: oSettings.ajax.url,
        //         type: "POST",
        //         dataType: "json",
        //         data: filterParams,
        //         success: function (json) {
        //             $appFilterXhrRequest = 'new';

        //             /* Got the data - add it to the table */
        //             for (var i = 0; i < json.data.length; i++) {
        //                 that.oApi._fnAddData(oSettings, json.data[i]);
        //             }

        //             oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
        //             that.fnDraw(that);
        //             that.oApi._fnProcessingDisplay(oSettings, false);
        //         }
        //     });
        // };
        // $.fn.dataTableExt.oApi.fnUpdateRow = function (oSettings, data, page, renderBeforePageChange) {
        //     //oSettings is not any parameter, we'll get it automatically.

        //     // var serverSideOrigninal = oSettings.oFeatures.bServerSide;
        //     // if (serverSideOrigninal) {
        //     //     oSettings.oFeatures.bServerSide = false; //disable serverside processing temporarily
        //     // }

        //     if (data) {
        //         this.oApi._fnAddData(oSettings, data);
        //     }

        //     if (renderBeforePageChange) {
        //         this.fnDraw(this);
        //     }

        //     if (page) {
        //         this.oApi._fnPageChange(oSettings, page, true);
        //     } else {
        //         this.fnDraw(this);
        //     }

        //     //oSettings.oFeatures.bServerSide = serverSideOrigninal; //revert to orignal value

        // };

        $.fn.dataTable.Api.register('fnUpdateRow', function (data, page, renderBeforePageChange) {
            var table = this;
            //  var settings = table.settings()[0];

            if (data) {
                table.row.add(data);
            }

            if (renderBeforePageChange) {
                table.draw(false); // false = retain paging
            }

            if (page !== undefined && page !== null) {
                table.page(page).draw(false); // Go to specific page and redraw
            } else {
                table.draw(false); // Just redraw current page
            }
        });


    };
})(jQuery);


deleteHandler = function (e, callback, postData = {}) {
    appLoader.show();
    var $target = $(e.currentTarget);

    if (e.data && e.data.target) {
        $target = e.data.target;
    }

    var url = $target.attr('data-action-url'),
        id = $target.attr('data-id'),
        reloadOnSuccess = $target.attr('data-reload-on-success'),
        tableId = $target.closest('table').attr('id');

    if (!postData) {
        postData = {};
    }

    postData.id = id;

    appAjaxRequest({
        url: url,
        type: 'POST',
        dataType: 'json',
        data: postData,
        success: function (result) {
            if (result.success) {

                if (callback) {
                    callback(result, $target);
                }

                if (reloadOnSuccess) {
                    location.reload();
                }

                if (tableId && window.appTableRowDeleteHook && window.appTableRowDeleteHook[tableId]) {

                    window.appTableRowDeleteHook[tableId].forEach(function (hook) {
                        if (typeof hook.onSuccess === 'function') {
                            hook.onSuccess({ id: id });
                        }
                    });
                }

            } else {
                appAlert.error(result.message);
            }
            appLoader.hide();
        }
    });
}


deleteConfirmationHandler = function (e, callback) {
    var $deleteButton = $("#confirmDeleteButton"),
        $target = $(e.currentTarget);
    //copy attributes

    var postData = {};
    $target.each(function () {
        $.each(this.attributes, function () {
            if (this.specified && this.name.match("^data-")) {
                $deleteButton.attr(this.name, this.value);
            }

            if (this.specified && this.name.match("^data-post-")) {
                var dataName = this.name.replace("data-post-", "");
                postData[dataName] = this.value;
            }

        });
    });

    $target.attr("data-undo", "0"); //don't show undo

    //bind click event
    $deleteButton.unbind("click");
    $deleteButton.on("click", { target: $target }, function (e) {
        deleteHandler(e, callback, postData);
    });

    $("#confirmationModal").modal('show');
};



// appAlert
(function (define) {
    define(['jquery'], function ($) {
        return (function () {
            var appAlert = {
                info: info,
                success: success,
                warning: warning,
                error: error,
                options: {
                    container: "body", // append alert on the selector
                    duration: 0, // don't close automatically,
                    showProgressBar: true, // duration must be set
                    clearAll: true, //clear all previous alerts
                    animate: true //show animation
                }
            };

            return appAlert;

            function info(message, options) {
                this._settings = _prepear_settings(options);
                this._settings.alertType = "info";
                _show(message);
                return "#" + this._settings.alertId;
            }

            function success(message, options) {
                this._settings = _prepear_settings(options);
                this._settings.alertType = "success";
                _show(message);
                return "#" + this._settings.alertId;
            }

            function warning(message, options) {
                this._settings = _prepear_settings(options);
                this._settings.alertType = "warning";
                _show(message);
                return "#" + this._settings.alertId;
            }

            function error(message, options) {
                this._settings = _prepear_settings(options);
                this._settings.alertType = "error";
                _show(message);
                return "#" + this._settings.alertId;
            }

            function _template(message) {
                var className = "info";
                if (this._settings.alertType === "error") {
                    className = "danger";
                } else if (this._settings.alertType === "success") {
                    className = "success";
                } else if (this._settings.alertType === "warning") {
                    className = "warning";
                }

                if (this._settings.animate) {
                    className += " animate";
                }

                return '<div id="' + this._settings.alertId + '" class="app-alert alert alert-' + className + ' alert-dismissible " role="alert">'
                    + '<button type="button" class="btn-close btn-close-white" data-bs-dismiss="alert" aria-label="Close"></button>'
                    + '<div class="app-alert-message">' + message + '</div>'
                    + '<div class="progress">'
                    + '<div class="progress-bar bg-' + className + ' hide" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 100%">'
                    + '</div>'
                    + '</div>'
                    + '</div>';
            }

            function _prepear_settings(options) {
                if (!options)
                    var options = {};
                options.alertId = "app-alert-" + _randomId();
                return this._settings = $.extend({}, appAlert.options, options);
            }

            function _randomId() {
                var id = "";
                var keys = "abcdefghijklmnopqrstuvwxyz0123456789";
                for (var i = 0; i < 5; i++)
                    id += keys.charAt(Math.floor(Math.random() * keys.length));
                return id;
            }

            function _clear() {
                if (this._settings.clearAll) {
                    $("[role='alert']").remove();
                }
            }

            function _show(message) {
                _clear();
                var container = $(this._settings.container);
                if (container.length) {
                    if (this._settings.animate) {
                        //show animation
                        setTimeout(function () {
                            $(".app-alert").animate({
                                opacity: 1,
                                right: "40px"
                            }, 500, function () {
                                $(".app-alert").animate({
                                    right: "15px"
                                }, 300);
                            });
                        }, 20);
                    }

                    $(this._settings.container).prepend(_template(message));
                    _progressBarHandler();
                } else {
                    console.log("appAlert: container must be an html selector!");
                }
            }

            function _progressBarHandler() {
                if (this._settings.duration && this._settings.showProgressBar) {
                    var alertId = "#" + this._settings.alertId;
                    var $progressBar = $(alertId).find('.progress-bar');

                    $progressBar.removeClass('hide').width(0);
                    var css = "width " + this._settings.duration + "ms ease";
                    $progressBar.css({
                        WebkitTransition: css,
                        MozTransition: css,
                        MsTransition: css,
                        OTransition: css,
                        transition: css
                    });

                    setTimeout(function () {
                        if ($(alertId).length > 0) {
                            $(alertId).remove();
                        }
                    }, this._settings.duration);
                }
            }
        })();
    });
}(function (d, f) {
    window['appAlert'] = f(window['jQuery']);
}));


(function (define) {
    define(['jquery'], function ($) {
        return (function () {
            var appLoader = {
                show: show,
                hide: hide,
                options: {
                    container: 'body',
                    zIndex: "auto",
                    css: "",
                }
            };

            return appLoader;

            function show(options) {
                var $template = $("#app-loader");
                this._settings = _prepear_settings(options);
                if (!$template.length) {
                    var $container = $(this._settings.container);
                    if ($container.length) {

                        if (!this._settings.css) {
                            this._settings.css = "top:25%; right:" + Math.round($container.outerWidth() / 2) + "px;";
                        }
                        $container.append('<div id="app-loader" class="app-loader" style="z-index:' + this._settings.zIndex + ';' + this._settings.css + '"><div class="loading"></div></div>');
                    } else {
                        console.log("appLoader: container must be an html selector!");
                    }

                }
            }

            function hide() {
                var $template = $("#app-loader");
                if ($template.length) {
                    $template.remove();
                }
            }

            function _prepear_settings(options) {
                if (!options)
                    var options = {};
                return this._settings = $.extend({}, appLoader.options, options);
            }
        })();
    });
}(function (d, f) {
    window['appLoader'] = f(window['jQuery']);
}));


(function (define) {
    define(['jquery'], function ($) {
        return (function () {
            var compactView = {
                init: init,
                setActiveRow: setActiveRow,
                _initListClickEvent: _initListClickEvent,
                _settings: {},
                options: {
                    dataSourceUrl: "",
                    backButtonUrl: "",
                    backButtonText: "",
                    compactViewBaseUrl: "",
                    compactViewId: null,

                    pageId: "#page-content",
                    pageWrapperClassName: "page-wrapper",
                    compactDetailsPageIdName: "compact-details-page",
                    appContentBuilderData: {
                        view_type: "compact_view"
                    },
                    appContentBuilderReloadHooks: [],
                }
            };

            function _initListClickEvent() {
                var _settings = this._settings;
                if (!_settings.pageId) return false;

                $(_settings.pageId).on('click', '[data-action=load_compact_view]', function () {

                    if ($(this).closest("td.all").length > 0) {
                        $(this).closest("td.all").trigger("click");
                    }



                    var $selector = $(this),
                        selectorData = $selector.data() || {},
                        $target = $("#" + _settings.compactDetailsPageIdName);

                    //change the browser url to match with compact view url
                    if (_settings.compactViewBaseUrl && selectorData.compact_view_id) {
                        var browserState = {
                            Url: _settings.compactViewBaseUrl + selectorData.compact_view_id
                        };
                        history.pushState(browserState, "", browserState.Url);

                        window.compact_view_id = selectorData.compact_view_id;

                        $selector.closest("table").find("tr.active").removeClass("active");
                        compactView.setActiveRow();
                    }

                    $target.children().fadeOut();

                    if (isMobile()) {
                        $selector.closest("table").find("tr.active .mini-list-item .spinning-btn").addClass("spinning");
                    } else {
                        appLoader.show({ container: $target });
                    }

                    var ajaxOptions = {
                        url: selectorData.actionUrl,
                        data: { view_type: "compact_view" },
                        cache: false,
                        type: 'POST',
                        dataType: 'json',
                        success: function (response) {
                            if (isMobile()) {
                                $selector.closest("table").find("tr.active .mini-list-item .spinning-btn").removeClass("spinning");
                            }

                            appLoader.hide();
                            $target.html(response.content);
                        },
                        statusCode: {
                            404: function () {
                                appLoader.hide();
                                appAlert.error("404: Page not found.");
                            }
                        },
                        error: function () {
                            appLoader.hide();
                            appAlert.error(AppLanugage.somethingWentWrong);
                        }
                    };

                    return $.ajax(ajaxOptions);

                });
            }


            function init(options) {
                if (!options) options = {};

                var _settings = $.extend({}, compactView.options, options);
                this._settings = _settings;

                var pageId = _settings.pageId;

                $(pageId).append('<div id="' + _settings.compactDetailsPageIdName + '" class="w-100"></div>');
                compactView._initListClickEvent();

                if (!_settings.compactViewId) return false;
                window.compact_view_id = _settings.compactViewId;

                compactView.setActiveRow();

                if (isMobile()) window.location.href = _settings.dataSourceUrl; //redirect to detils page in mobile

                //Re-structure the page
                $("." + _settings.pageWrapperClassName).removeClass(_settings.pageWrapperClassName);

                $('body').addClass('compact-view-active');

                $(pageId + ' div:first').addClass('mobile-mirror');
                $(pageId + ' div:first').addClass('compact-view-left-panel');
                $(pageId).wrapInner('<div class="d-flex"></div>');
                $(pageId).find('table.xs-hide-dtr-control').addClass('hide-dtr-control');
                if (_settings.backButtonUrl) {
                    // $(pageId).find("ul.nav-tabs").prepend('<a class="back-btn dark" href="' + _settings.backButtonUrl + '"><i data-feather="arrow-left" class="icon-16"></i> ' + _settings.backButtonText + '</a>');
                    $(pageId).find("ul.nav-tabs li a").first().prepend('<span class="compact-view-back-btn"><i data-feather="arrow-left" class="icon-16"></i> ' + _settings.backButtonText + '</span>');
                }
                $(pageId).find("ul.nav-tabs").append('<div id="mobile-function-button" class="more-options-btn"></div>');

                //Convert buttons to dropdown
                $(".title-button-group").removeClass("skip-dropdown-migration");
                convertTabButtonsToDropdownOnMobileView(".title-button-group", true);

                //init content refresher
                appContentBuilder.init(_settings.dataSourceUrl, {
                    data: _settings.appContentBuilderData,
                    reloadHooks: _settings.appContentBuilderReloadHooks,
                    reload: function (bind, response) {
                        bind("#" + _settings.compactDetailsPageIdName, response.content);
                    }
                }).reload();

                return compactView;
            }

            function setActiveRow() {
                var _settings = this._settings;
                $(_settings.pageId).find('[data-action=load_compact_view][data-compact_view_id=' + window.compact_view_id + ']').closest("tr").addClass("active");
            }

            return compactView;

        })();
    });
}(function (d, f) {
    window['appCompactView'] = f(window['jQuery']);
}));

(function (define) {
    define(['jquery'], function ($) {
        return (function () {

            var appContentBuilder = {
                id: null,
                ajaxConfig: {},
                reloadHooks: [],
                reloadCallback: null,
                init: initBuilder,
                attachHooks: attachReloadHooks,
                reload: reloadContent
            };

            return appContentBuilder;

            function initBuilder(url, options) {
                this.ajaxConfig.url = url;
                this.ajaxConfig.data = options.data || {};  // POST data
                this.reloadHooks = options.reloadHooks || [];  // Hooks to trigger reload
                this.reloadCallback = options.reload || null;  // Optional callback for reload
                this.id = options.id || getRandomAlphabet(5);
                this.attachHooks();  // Attach hooks to trigger reload
                return this;
            }

            function attachReloadHooks() {
                var self = this;

                if (!window.LinkHooks) {
                    window.LinkHooks = {};
                }

                $.each(self.reloadHooks, function (index, hook) {
                    if (hook.type === "app_form" && hook.id) {
                        registerAppFormHook(hook.id, function () {
                            self.reload();
                        }, "appContentBuilder", self.id);

                    } else if (hook.type === "ajax_request" && hook.group) {
                        registerAjaxRequestHook(hook.group, function () {
                            self.reload();
                        }, "appContentBuilder", self.id);
                    } else if (hook.type === "app_modifier" && hook.group) {
                        registerAppModifierHook(hook.group, function () {
                            self.reload();
                        }, "appContentBuilder", self.id);
                    } else if (hook.type === "app_table_row_update" && hook.tableId) {
                        registerAppTableRowUpdateHook(hook.tableId, function () {
                            self.reload();
                        }, "appContentBuilder", self.id);
                    } else if (hook.type === "app_table_row_delete" && hook.tableId) {
                        registerAppTableRowDeleteHook(hook.tableId, function () {
                            self.reload();
                        }, "appContentBuilder", self.id);
                    }
                    //add other hooks here.

                });
            }

            function reloadContent() {
                var self = this;
                appAjaxRequest({
                    url: self.ajaxConfig.url,
                    method: 'POST',
                    data: self.ajaxConfig.data,
                    dataType: 'json',
                    success: function (result) {
                        if (typeof self.reloadCallback === 'function') {
                            self.reloadCallback(bindContent, result);
                        }
                    },
                    error: function (xhr, status, error) {
                        console.error('Error reloading appContentBuilder content:', error);
                    }
                });
            }

            function bindContent(selector, content) {
                $(selector).html(content);
            }

        })();
    });
}(function (d, f) {
    window['appContentBuilder'] = f(window['jQuery']);
}));


/*prepare html form data for suitable ajax submit*/
function encodeAjaxPostData(html) {

    html = replaceAll("background-image", "00bg-img00", html);
    html = replaceAll("&quot;", "00quotation00", html);
    html = replaceAll("=", "00~00", html);
    html = replaceAll("&", "00^00", html);

    return html;
}

//replace all occurrences of a string
function replaceAll(find, replace, str) {
    return str.replace(new RegExp(find, 'g'), replace);
}

(function (define) {
    define(['jquery'], function ($) {
        return (function () {

            var appContentModal = {
                init: init,
                destroy: destroy,
                _settings: {},
                options: {
                    url: "",
                    css: "",
                    sidebar: false,
                    modalId: getRandomAlphabet(5)
                }
            };

            return appContentModal;

            function escKeyEvent(e) {
                if (e.keyCode === 27) {
                    destroy(e.data.settings);
                }
            }

            function init(options) {
                this._settings = _prepear_settings(this, options);
                destroy(this._settings);
                _load_template(this._settings);
            }

            function destroy(settings) {
                var newId = getRandomAlphabet(5);
                $("#" + settings.modalId).attr("id", newId).fadeOut(200); //update the id. Otherwise next modal will not work due to same id. 
                if ($("#" + newId).length) {
                    setTimeout(function () {
                        $("#" + newId).remove();
                    }, 200);
                }

                $(document).unbind("keyup", escKeyEvent);
                if (typeof appModalXhr !== 'undefined') {
                    appModalXhr.abort();
                }
                $("body").removeClass("app-modal-open");
            }

            function _prepear_settings(it, options) {
                if (!options)
                    options = {};

                return $.extend({}, it.options, options);
            }

            function _load_modal_content(settings, content) {
                var modalId = settings.modalId;
                var sourceData = settings.sourceElement.data();
                var windowHeight = $(window).height(),
                    windowWidth = $(window).width();

                if (content) {
                    var $content = $(content);

                    $("#" + modalId + " .app-modal-content-area").html($content.find(".app-modal-content").html());
                    var sidebarContent = $content.find(".app-modal-sidebar").html();
                    $("#" + modalId + " .app-modal-sidebar-area").html(sidebarContent);
                    $content.remove();
                } else if (sourceData.content_url) {
                    var contentHtml = '';
                    if (sourceData.type === "image") {
                        contentHtml = "<img id='img_" + modalId + "' src='" + sourceData.content_url + "'>";
                    } else if (sourceData.type === "iframe") {
                        contentHtml = "<div style='background:#fff;'><iframe id='iframe-file-viewer' src='" + sourceData.content_url + "' style='width:" + windowWidth + "px; margin:0; border:0; height:" + windowHeight + "px; '><div>";
                    } else if (sourceData.type === "txt") {
                        if (windowWidth > 1300) {
                            windowWidth = windowWidth - 200;
                        }
                        contentHtml = "<div style='background:#fff;'><iframe id='iframe-file-viewer' src='" + sourceData.content_url + "' style='width:" + windowWidth + "px; margin:0; border:0; height:" + windowHeight + "px; '><div>";
                    } else if (sourceData.type === "audio") {
                        contentHtml = "<audio src='" + sourceData.content_url + "' controls='' class='audio'></audio>";
                    } else if (sourceData.type === "not_viewable") {
                        contentHtml = "<div class='card'><div class='card-body'><h5 class='card-title'>" + sourceData.description + "</h5><p class='card-text'>" + sourceData.filename + "</p></div></div>";
                    }

                    $("#" + modalId + " .app-modal-content-area").html(contentHtml);

                }


                $("#" + modalId).removeClass("loading");

                if (sidebarContent) {
                    $("#" + modalId).addClass("has-sidebar");

                    setTimeout(function () {
                        if ($("#" + modalId + " #file-preview-comment-container").height() > ($(window).height() - 300)) {
                            initScrollbar("#" + modalId + " #file-preview-comment-container", {
                                setHeight: $(window).height() - 300
                            });
                        }
                    });

                } else {
                    $("#" + modalId).removeClass("has-sidebar");
                }

                appLoader.hide();

                var $viewableElements = $("[data-toggle='app-modal']");
                var preview_function = null;


                if (sourceData.target_group) {
                    $viewableElements = $("[data-group='" + sourceData.target_group + "']");
                } else if (sourceData.group) {
                    $viewableElements = $("[data-group='" + sourceData.group + "']");
                }

                if (sourceData.preview_function) {
                    preview_function = sourceData.preview_function;
                }

                var previousIndex = 0,
                    nextIndex = 0,
                    currentIndex = 0;

                if ($viewableElements.length > 1) {
                    $viewableElements.each(function (index) {
                        var data = $(this).data();
                        if (data && data.url && data.url === settings.url) {
                            currentIndex = index;
                        } else if (data && data.content_url && data.content_url === settings.content_url) {
                            currentIndex = index;
                        }
                    });

                    if ($viewableElements[currentIndex - 1]) {
                        previousIndex = currentIndex - 1;
                    } else {
                        previousIndex = $viewableElements.length - 1; //previous element doesn't exists. show last element (circular)
                    }


                    if ($viewableElements[currentIndex + 1]) {
                        nextIndex = currentIndex + 1;
                    } else {
                        nextIndex = 0; //next element doesn't exists. show first element (circular)
                    }


                    var _trigger_preview = function ($element, preview_function) {
                        if (preview_function) {

                            if (typeof window[preview_function] === "function") {
                                window[preview_function]($element);
                            } else {
                                console.log("Undefined preview_function", preview_function);
                            }
                        } else {
                            $element.trigger("click");
                        }
                    };

                    $("#" + modalId + " .app-modal-previous-button").click(function () {
                        $viewableElements.each(function (index) {
                            if (index === previousIndex) {
                                _trigger_preview($(this), preview_function);
                            }
                        });

                    });

                    $("#" + modalId + " .app-modal-next-button").click(function () {
                        $viewableElements.each(function (index) {
                            if (index === nextIndex) {
                                _trigger_preview($(this), preview_function);
                            }
                        });

                    });

                    feather.replace();
                    $("#" + modalId + " .app-modal-previous-button").removeClass("hide");
                    $("#" + modalId + " .app-modal-next-button").removeClass("hide");

                }

                $("#" + modalId + " .app-modal-zoom-in-button").click(function () {

                    if ($(this).hasClass("disabled")) {
                        return false;
                    }

                    $("#" + modalId + " .app-modal-content-area").addClass("scrollable");
                    $("#" + modalId + " .app-modal-content").removeClass("fit-window-height");

                    $(this).addClass("disabled");
                    $("#" + modalId + " .app-modal-zoom-out-button").removeClass("disabled");

                });

                $("#" + modalId + " .app-modal-zoom-out-button").click(function () {
                    if ($(this).hasClass("disabled")) {
                        return false;
                    }

                    $("#" + modalId + " .app-modal-content-area").removeClass("scrollable");
                    $("#" + modalId + " .app-modal-content").addClass("fit-window-height");

                    $(this).addClass("disabled");
                    $("#" + modalId + " .app-modal-zoom-in-button").removeClass("disabled");

                });


                var img = document.getElementById('img_' + modalId);
                if (img) {
                    img.addEventListener('load', function () {
                        var windowHeight = $(window).height();
                        var $img = $("#" + modalId + " .app-modal-content-area").find("img");
                        if ($img && $img[0] && $img[0].naturalHeight > windowHeight) {
                            $("#" + modalId + " .app-modal-zoom-in-button").removeClass("hide");
                            $("#" + modalId + " .app-modal-zoom-out-button").removeClass("hide").addClass("disabled");
                        }

                        if ($.fn.mCustomScrollbar) {
                            $("#" + modalId + " .app-moadl-sidebar-scrollbar").mCustomScrollbar({ setHeight: windowHeight, theme: "minimal-dark", autoExpandScrollbar: true });
                        }
                    });
                }

                setTimeout(function () {

                    var windowHeight = $(window).height();
                    var $img = $("#" + modalId + " .app-modal-content-area").find("img");
                    if ($img && $img[0] && $img[0].naturalHeight > windowHeight) {
                        $("#" + modalId + " .app-modal-zoom-in-button").removeClass("hide");
                        $("#" + modalId + " .app-modal-zoom-out-button").removeClass("hide").addClass("disabled");
                    }

                    if ($.fn.mCustomScrollbar) {
                        $("#" + modalId + " .app-moadl-sidebar-scrollbar").mCustomScrollbar({ setHeight: windowHeight, theme: "minimal-dark", autoExpandScrollbar: true });
                    }

                }, 300);


            }


            function _load_template(settings) {
                var modalId = settings.modalId;
                var sidebar = "<div class='app-modal-sidebar hidden-xs'>\
                                        <div class='app-modal-close'><span>&times;</span></div>\
                                        <div class='app-moadl-sidebar-scrollbar'>\
                                            <div class='app-modal-sidebar-area'>\
                                            </div>\
                                        </div>\
                                    </div>";
                var controlIcon = "<span class='expand round-button hidden-xs'><i data-feather='maximize-2' class='icon-16'></i></span>";

                if (settings.sidebar === false || isMobile()) {
                    sidebar = "";
                    controlIcon = "<div class='app-modal-close app-modal-fixed-close-button'><span>&times;</span></div>";
                }

                controlIcon += "<span class='app-modal-zoom-in-button hide round-button clickable'><i data-feather='zoom-in' class='icon-18'></i></span>";
                controlIcon += "<span class='app-modal-zoom-out-button hide round-button clickable'><i data-feather='zoom-out' class='icon-18'></i></span>";

                controlIcon += "<span class='app-modal-previous-button hide round-button clickable'><i data-feather='chevron-left' class='icon-18'></i></span>";
                controlIcon += "<span class='app-modal-next-button hide round-button clickable'><i data-feather='chevron-right' class='icon-18'></i></span>";


                var template = "<div class='app-modal loading' id='" + modalId + "'>\
                                <span class='compress round-button'><i data-feather='minimize-2' class='icon-16'></i></span>\
                                <div class='app-modal-body'>\
                                    <div class='app-modal-content fit-window-height'>" + controlIcon +
                    "<div class='hide app-modal-close'><span>&times;</span></div>\
                                        <div class='app-modal-content-area d-inline-block'>\
                                        </div>\
                        </div>" + sidebar + "</div>\
                            </div>";

                $("body").addClass("app-modal-open").prepend(template);

                $("#" + modalId + " .expand").click(function () {
                    $(".app-modal").addClass("full-content");
                });

                $("#" + modalId + " .compress").click(function () {
                    $(".app-modal").removeClass("full-content");
                });
                $("#" + modalId + " .app-modal-close").click(function () {
                    destroy(settings);
                });

                $(document).bind("keyup", { settings: settings }, escKeyEvent);

                if ($("#" + modalId + " .app-modal-sidebar").is(":visible")) {
                    appLoader.show({ container: '#' + modalId, css: "top:35%; right:55%;" });
                } else {
                    appLoader.show({ container: '#' + modalId, css: "top:35%; right:48%;" });
                }

                if (settings.url) {
                    appModalXhr = $.ajax({
                        url: settings.url || "",
                        data: {},
                        cache: false,
                        type: 'POST',
                        success: function (response) {
                            _load_modal_content(settings, response);
                        },
                        statusCode: {
                            404: function () {
                                appContentModal.destroy(settings);
                                appAlert.error("404: Page not found.");
                            }
                        },
                        error: function () {
                            appContentModal.destroy(settings);
                            appAlert.error(AppLanugage.somethingWentWrong);
                        }
                    });

                } else {
                    _load_modal_content(settings);
                }

            }
        })();
    });
}(function (d, f) {
    window['appContentModal'] = f(window['jQuery']);
}));

//custom daterange controller
(function ($) {
    $.fn.appDateRange = function (options) {
        var defaults = {
            dateRangeType: "yearly",
            filterParams: {},
            onChange: function (dateRange) {
            },
            onInit: function (dateRange) {
            }
        };
        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }
        settings._inputDateFormat = "YYYY-MM-DD";

        this.each(function () {

            var $instance = $(this);
            var dom = '<div class="ml15 btn-group">'
                + '<button data-act="prev" class="btn btn-default date-range-selector"><i data-feather="chevron-left" class="icon-16"></i></button>'
                + '<button data-act="datepicker" class="btn btn-default"></button>'
                + '<button data-act="next"  class="btn btn-default date-range-selector"><i data-feather="chevron-right" class="icon-16"></i></button>'
                + '</div>';
            $instance.append(dom);

            var $datepicker = $instance.find("[data-act='datepicker']"),
                $dateRangeSelector = $instance.find(".date-range-selector");

            if (settings.dateRangeType === "yearly") {
                var inityearSelectorText = function ($elector) {
                    $elector.html(moment(settings.filterParams.start_date).customFormat("YYYY"));
                };

                inityearSelectorText($datepicker);

                //bind the click events
                $datepicker.datepicker({
                    format: "YYYY-MM",
                    viewMode: "years",
                    minViewMode: "years",
                    autoclose: true,
                    language: "custom",
                    orientation: "bottom"
                }).on('changeDate', function (e) {
                    var date = moment(e.date).customFormat(settings._inputDateFormat),
                        year = moment(date).customFormat("YYYY");
                    settings.filterParams.start_date = year + "-01-01";
                    settings.filterParams.end_date = year + "-12-31";
                    settings.filterParams.year = year;
                    inityearSelectorText($datepicker);
                    settings.onChange(settings.filterParams);
                });

                //init default date
                var year = moment().customFormat("YYYY");
                settings.filterParams.start_date = year + "-01-01";
                settings.filterParams.end_date = year + "-12-31";
                settings.filterParams.year = year;
                settings.onInit(settings.filterParams);


                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"),
                        startDate = moment(settings.filterParams.start_date),
                        endDate = moment(settings.filterParams.end_date);
                    if (type === "next") {
                        startDate = startDate.add(1, 'years').customFormat(settings._inputDateFormat);
                        endDate = endDate.add(1, 'years').customFormat(settings._inputDateFormat);
                    } else if (type === "prev") {
                        startDate = startDate.subtract(1, 'years').customFormat(settings._inputDateFormat);
                        endDate = endDate.subtract(1, 'years').customFormat(settings._inputDateFormat);
                    }

                    settings.filterParams.start_date = startDate;
                    settings.filterParams.end_date = endDate;
                    settings.filterParams.year = moment(startDate).customFormat("YYYY");

                    inityearSelectorText($datepicker);
                    settings.onChange(settings.filterParams);
                });


            } else if (settings.dateRangeType === "monthly") {

                var initMonthSelectorText = function ($elector) {
                    $elector.html(moment(settings.filterParams.start_date).format("MMMM YYYY"));
                };

                //prepareDefaultDateRangeFilterParams();
                initMonthSelectorText($datepicker);

                //bind the click events
                $datepicker.datepicker({
                    format: "YYYY-MM",
                    viewMode: "months",
                    minViewMode: "months",
                    autoclose: true,
                    language: "custom",
                    orientation: "bottom"
                }).on('changeDate', function (e) {
                    var date = moment(e.date).customFormat(settings._inputDateFormat);
                    var daysInMonth = moment(date).daysInMonth(),
                        yearMonth = moment(date).customFormat("YYYY-MM");
                    settings.filterParams.start_date = yearMonth + "-01";
                    settings.filterParams.end_date = yearMonth + "-" + daysInMonth;
                    initMonthSelectorText($datepicker);
                    settings.onChange(settings.filterParams);
                });

                //init default date
                var year = moment().customFormat("YYYY");
                var yearMonth = moment().customFormat("YYYY-MM");
                var daysInMonth = moment().daysInMonth();

                settings.filterParams.start_date = yearMonth + "-01";
                settings.filterParams.end_date = yearMonth + "-" + daysInMonth;
                settings.filterParams.year = year;
                settings.onInit(settings.filterParams);

                $dateRangeSelector.click(function () {
                    var type = $(this).attr("data-act"),
                        startDate = moment(settings.filterParams.start_date),
                        endDate = moment(settings.filterParams.end_date);
                    if (type === "next") {
                        var nextMonth = startDate.add(1, 'months'),
                            daysInMonth = nextMonth.daysInMonth(),
                            yearMonth = nextMonth.customFormat("YYYY-MM");

                        startDate = yearMonth + "-01";
                        endDate = yearMonth + "-" + daysInMonth;

                    } else if (type === "prev") {
                        var lastMonth = startDate.subtract(1, 'months'),
                            daysInMonth = lastMonth.daysInMonth(),
                            yearMonth = lastMonth.customFormat("YYYY-MM");

                        startDate = yearMonth + "-01";
                        endDate = yearMonth + "-" + daysInMonth;
                    }

                    settings.filterParams.start_date = startDate;
                    settings.filterParams.end_date = endDate;
                    settings.filterParams.year = moment(startDate).customFormat("YYYY-MM");

                    initMonthSelectorText($datepicker);
                    settings.onChange(settings.filterParams);
                });
            }


        });
    };
})(jQuery);


var loadFilterView = function (settings) {
    if (settings.source && settings.targetSelector) {
        appAjaxRequest({
            url: settings.source,
            data: settings.filterParams,
            cache: false,
            type: 'POST',
            success: function (response) {
                $(settings.targetSelector).html(response);
                appLoader.hide();
            },
            statusCode: {
                404: function () {
                    appLoader.hide();
                    appAlert.error("404: Page not found.", { container: '.modal-body', animate: false });
                }
            },
            error: function () {
                appLoader.hide();
                appAlert.error(AppLanugage.somethingWentWrong, { container: '.modal-body', animate: false });
            }
        });
    }
};

//custom filters controller
(function ($) {

    $.fn.appFilters = function (options) {
        appLoader.show();

        var defaults = {
            source: "", //data url
            targetSelector: "",
            reloadSelector: "",
            dateRangeType: "", // type: daily, weekly, monthly, yearly. output params: start_date and end_date
            checkBoxes: [], // [{text: "Caption", name: "status", value: "in_progress", isChecked: true}] 
            multiSelect: [], // [{text: "Caption", name: "status", options:[{text: "Caption", value: "in_progress", isChecked: true}]}] 
            radioButtons: [], // [{text: "Caption", name: "status", value: "in_progress", isChecked: true}] 
            filterDropdown: [], // [{id: 10, text:'Caption', isSelected:true}] 
            singleDatepicker: [], // [{name: '', value:'', options:[]}] 
            rangeDatepicker: [], // [{startDate:{name:"", value:""},endDate:{name:"", value:""}}] 
            stateSave: true,
            ignoreSavedFilter: false, //sometimes, need to click on widget link to show specific filter. Enable for that. 
            isMobile: window.matchMedia("(max-width: 800px)").matches,
            filterParams: { customFilter: true }, //will post this vales on source url
            search: { show: false },
            customLanguage: {
                searchPlaceholder: AppLanugage.search,
                today: AppLanugage.today,
                yesterday: AppLanugage.yesterday,
                tomorrow: AppLanugage.tomorrow
            },
            beforeRelaodCallback: function () { },
            afterRelaodCallback: function () { },
            onInitComplete: function () { }
        };

        var $instance = $(this),
            $instanceWrapper = $instance; //$instanceWrapper is same as instance in this case

        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }

        if (settings.reload) {
            var instance = $(this);
            var instanceSettings = window.InstanceCollection[instance.attr("id")];


            if (instance.data("beforeRelaodCallback")) {
                instance.data("beforeRelaodCallback")(instance, instanceSettings.filterParams);
            }


            loadFilterView(instanceSettings);

            if (instance.data("afterRelaodCallback")) {
                instance.data("afterRelaodCallback")(instance, instanceSettings.filterParams);
            }


            return false;
        } else {

            var filterForm = "";
            if (settings.smartFilterIdentity) {
                filterForm = "<div class='filter-form'></div>";
            }

            $instanceWrapper.append("<div class='filter-section-container'>\n\
                    <div class='filter-section-flex-row'>\n\
                            <div class='filter-section-left'></div><div class='filter-section-right'></div>\n\
                    </div>" + filterForm + "</div>");
        }

        settings._firstDayOfWeek = AppHelper.settings.firstDayOfWeek || 0;
        settings._inputDateFormat = "YYYY-MM-DD";


        settings = prepareDefaultFilters(settings);

        buildFilterDom(settings, $instanceWrapper, $instance);
        window.InstanceCollection = window.InstanceCollection || {};
        window.InstanceCollection[$instance.attr("id")] = settings;
        if (settings.onInitComplete) {
            settings.onInitComplete($instance, settings.filterParams);
        }

        loadFilterView(settings);


        //bind calbacks
        $instance.data("beforeRelaodCallback", settings.beforeRelaodCallback);
        $instance.data("afterRelaodCallback", settings.afterRelaodCallback);

    };
})(jQuery);



//find and replace all search string
replaceAllString = function (string, find, replaceWith) {
    return string.split(find).join(replaceWith);
};

//convert a number to curency format
toCurrency = function (number, currencySymbol) {

    if (AppHelper.settings.noOfDecimals == "0") {
        number = Math.round(parseFloat(number)) + ".00"; //round it and the add static 2 decimals
    } else {
        number = parseFloat(number).toFixed(2);
    }

    if (!currencySymbol) {
        currencySymbol = AppHelper.settings.currencySymbol;
    }
    var result = number.replace(/(\d)(?=(\d{3})+\.)/g, "$1,");

    //remove (,) if thousand separator is (space)
    if (AppHelper.settings.thousandSeparator === " ") {
        result = result.replace(',', ' ');
    }
    if (AppHelper.settings.decimalSeparator === ",") {
        result = replaceAllString(result, ".", "_");
        result = replaceAllString(result, ",", ".");
        result = replaceAllString(result, "_", ",");
    }
    if (currencySymbol === "none") {
        currencySymbol = "";
    }
    if (AppHelper.settings.noOfDecimals == "0") {
        result = result.slice(0, -3); //remove decimals
    }

    if (AppHelper.settings.currencyPosition === "right") {
        return result + "" + currencySymbol;
    } else {
        if (result.indexOf("-") == "0") {
            result = result.replace('-', '');
            return "-" + currencySymbol + result;
        } else {
            return currencySymbol + "" + result;
        }
    }
};


calculateDatatableTotal = function (instance, columnNumber, valueModifier, currentPage) {
    var api = instance.api(),
        columnOption = {};
    if (currentPage) {
        columnOption = { page: 'current' };
    }

    return api.column(columnNumber, columnOption).data()
        .reduce(function (previousValue, currentValue, test, test2) {
            if (valueModifier) {
                return previousValue + valueModifier(currentValue);
            } else {
                return previousValue + currentValue;
            }
        }, 0);
};

// rmove the formatting to get integer data
unformatCurrency = function (currency, conversionRate) {
    currency = currency.toString();
    var mainAmount = currency, decimalSeparatorUnformatted = false;

    if (currency) {
        currency = currency.replace(/[^0-9.,-]/g, '');

        if (conversionRate) {
            //prepare converted amount
            var currencySymbol = mainAmount.replace(currency, '');
            if (conversionRate[currencySymbol]) {
                //conversion rate exists for this currency
                currency = unformatDecimalSeparator(currency);
                currency = ((1 / conversionRate[currencySymbol]) * 1) * currency;
                currency = currency.toString();
                decimalSeparatorUnformatted = true;
            }
        }

        if (currency.indexOf(".") == 0 || currency.indexOf(",") == 0) {
            currency = currency.slice(1);
        }

        if (!decimalSeparatorUnformatted) {
            currency = unformatDecimalSeparator(currency);
        }

        currency = currency * 1;
    }
    if (currency) {
        return currency;
    }
    return 0;
};

unformatDecimalSeparator = function (currency) {
    if (AppHelper.settings.decimalSeparator === ",") {
        currency = replaceAllString(currency, ".", "");
        currency = replaceAllString(currency, ",", ".");
    } else {
        currency = replaceAllString(currency, ",", "");
    }
    return currency;
};

// convert seconds to hours:minutes:seconds format
secondsToTimeFormat = function (sec) {
    var sec_num = parseInt(sec, 10),
        hours = Math.floor(sec_num / 3600),
        minutes = Math.floor((sec_num - (hours * 3600)) / 60),
        seconds = sec_num - (hours * 3600) - (minutes * 60);
    if (hours < 10) {
        hours = "0" + hours;
    }
    if (minutes < 10) {
        minutes = "0" + minutes;
    }
    if (seconds < 10) {
        seconds = "0" + seconds;
    }
    var time = hours + ':' + minutes + ':' + seconds;
    return time;
};

//clear datatable state
clearAppTableState = function (tableInstance) {
    if (tableInstance) {
        setTimeout(function () {
            tableInstance.api().state.clear();
        }, 200);
    }
};

//show/hide datatable column
showHideAppTableColumn = function (tableInstance, columnIndex, visible) {
    tableInstance.DataTable().column(columnIndex).visible(!!visible);
};

//appMention using at.js
(function ($) {

    $.fn.appMention = function (options) {
        var defaults = {
            at: "@",
            dataType: "json",
            source: "",
            data: {}
        };

        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }

        var selector = this;

        appAjaxRequest({
            url: settings.source,
            data: settings.data,
            dataType: settings.dataType,
            method: "POST",
            success: function (result) {
                if (result.success) {
                    $(selector).atwho({
                        at: settings.at,
                        data: result.data,
                        insertTpl: '${content}'
                    });
                }
            }
        });

    };
})(jQuery);

//custom multi-select controller
(function ($) {
    $.fn.appMultiSelect = function (options) {
        var defaults = {
            text: "",
            options: [],
            onChange: function (values) {
            },
            onInit: function (values) {
            }
        };
        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }

        this.each(function () {

            var $instance = $(this);
            var multiSelect = "", values = [];

            $.each(settings.options, function (index, listOption) {
                var active = "";

                if (listOption.isChecked) {
                    active = " active";
                    values.push(listOption.id);
                }
                //<li class=" list-group-item clickable toggle-table-column" data-column="1">ID</li>
                multiSelect += '<li class="list-group-item clickable ' + active + '" data-name="' + settings.name + '" data-value="' + listOption.id + '">';
                multiSelect += listOption.text;
                multiSelect += '</li>';
            });

            multiSelect = "<div class='dropdown-menu'><ul class='list-group' data-act='multiselect'>" + multiSelect + "</ul></div>";

            var dom = '<div class="">'
                + '<span class="dropdown inline-block filter-multi-select">'
                + '<button class="btn btn-default dropdown-toggle caret " type="button" data-bs-toggle="dropdown" aria-expanded="true">' + settings.text + ' </button>'
                + multiSelect + '</span>'
                + '</div>';

            $instance.append(dom);
            settings.onInit(values);

            var $multiselect = $instance.find("[data-name='" + settings.name + "']");
            $multiselect.click(function () {
                var $selector = $(this);
                $selector.toggleClass("active");
                setTimeout(function () {
                    var values = [];
                    $selector.parent().find("li").each(function () {
                        if ($(this).hasClass("active")) {
                            values.push($(this).attr("data-value"));
                        }
                    });
                    settings.onChange(values);
                });
                return false;
            });
        });
    };
})(jQuery);

//instant popover modifier
(function ($) {
    $.fn.appModifier = function (options) {
        var defaults = {
            actionUrl: "", //the url where the response will go after modification
            value: "", //existing value
            actionType: "dropdown", //action type
            showButtons: false, //show submit/cancel button
            datepicker: {}, //options for datepicker
            select2Option: {}, //options for select2
            timepickerOptions: {}, //options for timepicker
            dataType: 'json',
            postData: {},
            className: "",
            placeholder: "",
            ruleRequired: false,
            msgRequired: "",
            dropdownData: {},
            onSuccess: function () {
            }
        };

        var $instance = $(this);
        var instanceData = $instance.data() ? $instance.data() : {};

        var settings = appExtend({}, defaults, instanceData, options);
        if (!settings) {
            return false;
        }

        if (settings.actionType == "select2") {  //backward compatibility
            settings.actionType = "dropdown";
        }

        if (settings.actionType === "dropdown" && settings.field && settings.dropdownData[settings.field]) {
            settings.select2Option.data = settings.dropdownData[settings.field];
        }

        if ($instance.attr('data-value')) {
            settings.value = $instance.attr('data-value');
        }


        //prepare select2 tags
        if (settings.canCreateTags == "1") {
            settings.showButtons = true;

            settings.select2Option.tags = (settings.select2Option.data && settings.select2Option.data.length > 0) ? settings.select2Option.data : [];
            delete settings.select2Option.data;
        }

        if (settings.multipleTags == "1") {
            settings.showButtons = true;
            settings.select2Option.multiple = true;
        }



        //create popover content dom
        var tempId = getRandomAlphabet(5);

        //prepare submit or close buttons
        var buttonDom = "";
        if (settings.showButtons) {
            buttonDom = "<div class='custom-popover-button-area mt10 clearfix row'>\n\
                            <div id='custom-popover-submit-btn-" + tempId + "' class='col-md-6 pr5'><button class='btn btn-primary btn-sm w100p'><i data-feather='check' class='icon-16'></i></button></div>\n\
                            <div class='col-md-6 pl5 custom-popover-close-btn'><button class='btn btn-default btn-sm w100p'><i data-feather='x' class='icon-16'></i></button></div>\n\
                        </div>";
        }

        var required = "";
        if (settings.ruleRequired) {
            required = " data-rule-required=1 ";

            if (settings.msgRequired) {
                required += ' data-msg-required= "' + settings.msgRequired + '" ';
            }
        }


        //prepare container dom
        var containerDom = "";
        if (settings.actionType === "dropdown") {
            containerDom = '<input id="' + tempId + '" value="' + settings.value + '" placeholder="' + settings.placeholder + '" type="text" class="form-control popover-tempId ' + settings.className + '" ' + required + '/> ' + buttonDom;
        } else if (settings.actionType === "date") {
            var dateFormat = getJsDateFormat();
            var dateArray = settings.value.split("-"),
                year = dateArray[0],
                month = dateArray[1],
                day = dateArray[2];
            var dateValue = dateFormat.replace("yyyy", year).replace("mm", month).replace("dd", day);

            containerDom = '<div style="height: 240px;" id="' + tempId + '"  data-date="' + dateValue + '" data-date-format="' + dateFormat + '" class="popover-tempId" ' + required + '></div>'; //set height first for right popover position
        } else if (settings.actionType === "time") {
            containerDom = '<input class="form-control" type="text" id="' + tempId + '"  value="' + settings.value + '" /><div id="popover-timepicker-container-' + tempId + '" ' + required + '></div>' + buttonDom;
        } else if (settings.actionType === "text") {
            containerDom = '<input class="form-control" type="text" id="' + tempId + '"  value="' + settings.value + '" ' + required + ' /><div class="js-help-message"></div> ' + buttonDom;
        }



        //show popover
        var offset = $instance.offset();
        var top = offset.top;
        var leftOffset = offset.left;
        var topOffset = top + $instance.outerHeight() + 10; //10 for arrow

        //create popover dom
        var popoverDom = "<div class='app-popover' style='max-width: 350px; top: " + topOffset + "px; left: " + leftOffset + "px'>\n\
                                <span class='app-popover-arrow' ></span>\n\
                                <div class='app-popover-body'>\n\
                                    <div class='loader-container inline-loader hide'></div>\n\
                                    " + containerDom + " \n\
                                </div>\n\
                            </div>";

        $(".app-popover").remove();
        $("body").append(popoverDom);
        feather.replace();

        //apply select2/datepicker on popover content
        var $inputField = $("#" + tempId);
        var $timepickerContainer = $("#popover-timepicker-container-" + tempId);
        if (settings.actionType === "dropdown") {

            if (settings.showButtons) {
                //submit with buttons
                $("#" + tempId).select2(settings.select2Option);
            } else {
                if (settings.select2Option.data.length > 0 && settings.select2Option.data.length <= 7) {


                    window["initAjaxAction_" + tempId] = function (value, changedText) {
                        initAjaxAction($instance, value, settings, changedText);
                        if ($("#" + tempId).next()) {
                            $("#" + tempId).next().addClass("hide");
                        }
                    }
                    var dropdownList = "";
                    settings.select2Option.data.forEach(item => {
                        var activeClass = '';
                        if (settings.value == item.id) {
                            activeClass = 'active';
                        }
                        dropdownList += `<a href="#" class="dropdown-item list-group-item ${activeClass}" onclick='initAjaxAction_${tempId}("${item.id}", "${item.text}")'>${item.text}</a>`;
                    });
                    $("#" + tempId).closest(".app-popover-body").addClass("pl0 pr0").append("<div>" + dropdownList + "</div>");
                    $("#" + tempId).addClass("hide");
                } else {
                    $("#" + tempId).select2(settings.select2Option).change(function (action) {
                        initAjaxAction($instance, $(this).val(), settings, action["added"]["text"]);
                    });
                }


            }
        } else if (settings.actionType === "date") {
            settings.datepicker.onChangeDate = function (response) {
                initAjaxAction($instance, response, settings);
            };

            setDatePicker("#" + tempId, settings.datepicker);
        } else if (settings.actionType === "time") {

            var appendWidgetTo = "#popover-timepicker-container-" + tempId;
            var showMeridian = AppHelper.settings.timeFormat == "24_hours" ? false : true;

            var timepickerSettings = $.extend({}, {
                minuteStep: AppHelper.settings.timepickerMinutesInterval,
                defaultTime: "",
                appendWidgetTo: appendWidgetTo,
                showMeridian: showMeridian,
                isInline: true
            }, settings.timepickerOptions);

            $inputField.timepicker(timepickerSettings);

            $inputField.timepicker().on('show.timepicker', function (e) {
                feather.replace();
            });

            setTimeout(function () {
                $inputField.focus();
                setTimeout(function () {
                    $(".bootstrap-timepicker-widget").removeClass("dropdown-menu");
                });
            });
        } else if (settings.actionType === "text") {

            $inputField.on("keydown", function (e) {
                if (e.keyCode === 13) { // Enter
                    e.preventDefault();
                    $("div#custom-popover-submit-btn-" + tempId).trigger("click");
                }
            });

            setTimeout(function () {
                $inputField.focus().select();
            });
        }

        //check if the right side is overflowed
        $("body").find(".app-popover").each(function () {
            //position content
            var right = $(window).width() - ($(this).offset().left + $(this).outerWidth());
            if (right < 0) {
                //overflowed
                $(this).css({ "left": "unset", "right": "10px" });

                //position arrow
                var right = $(window).width() - ($instance.offset().left + (($instance.outerWidth() / 2) * 1));
                $(this).find(".app-popover-arrow").css({ "left": "unset", "right": right });
            }
        });

        //submit button
        $("div#custom-popover-submit-btn-" + tempId).click(function () {
            if (settings.ruleRequired && !$inputField.val()) {
                $inputField.parent().addClass("has-error").find(".js-help-message").html('<span class="help-block" style="">' + settings.msgRequired + '</span>');
                return false;
            }
            if ($inputField.val()) {
                $inputField.parent().removeClass("has-error").find(".js-help-message").html("");
            }

            initAjaxAction($instance, $inputField.val(), settings);
        });

        //close button
        $(".custom-popover-close-btn").click(function () {
            $(".app-popover").remove(); //hide popover
        });

        function initAjaxAction($instance, value, settings, changedText) {
            var popoverContentHeight = $inputField.closest(".app-popover-body").height();
            var popoverContentWidth = $inputField.closest(".app-popover-body").width();
            $inputField.closest(".app-popover-body").find(".loader-container").removeClass("hide").css({ "height": popoverContentHeight, "width": popoverContentWidth });
            $inputField.closest(".app-popover-body").find(".custom-popover-button-area").addClass("hide");
            $inputField.addClass("hide");
            $timepickerContainer.addClass("hide");

            var postData = $.extend({}, settings.postData, { value: value });

            appAjaxRequest({
                url: settings.actionUrl,
                type: 'POST',
                dataType: settings.dataType,
                data: postData,
                success: function (result) {
                    $(".app-popover").remove(); //hide popover
                    setTimeout(function () {
                        $inputField.closest(".app-popover-body").find(".loader-container").addClass("hide");
                        $inputField.closest(".app-popover-body").find(".custom-popover-button-area").removeClass("hide");
                        $inputField.removeClass("hide");
                        $timepickerContainer.removeClass("hide");
                    }, 200);

                    if (result.success) {
                        settings.onSuccess(result, value);

                        //update for dropdown
                        if (result.content) {
                            $instance.html(result.content);
                        } else if (changedText) {
                            $instance.text(changedText);
                        }

                        $instance.attr("data-value", value); //update value for instant future use
                        $(".app-popover").remove();

                        var group = $instance.attr("data-modifier-group");
                        if (group && window.appModifierHooks && window.appModifierHooks[group]) {

                            window.appModifierHooks[group].forEach(function (hook) {
                                if (typeof hook.onSuccess === 'function') {
                                    hook.onSuccess($instance.data(), result);
                                }
                            });
                        }

                    } else {
                        appAlert.error(result.message);
                    }
                }
            });
        }
    };
})(jQuery);

//instant popover modifier
(function ($) {
    $.fn.appConfirmation = function (options) {
        var defaults = {
            title: "",
            btnConfirmLabel: "",
            btnCancelLabel: "",
            onConfirm: function () {
            }
        };

        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }

        var $instance = $(this);
        //show popover
        var offset = $instance.offset();
        var top = offset.top;
        var leftOffset = offset.left;
        var bottomOffset = $(window).height() - (top - 10); //10 for arrow

        //create popover dom
        var popoverDom = "<div class='app-popover' style='bottom: " + bottomOffset + "px; left: " + leftOffset + "px'>\n\
                                <span class='app-popover-arrow bottom-arrow' ></span>\n\
                                <div class='loader-container inline-loader hide'></div>\n\
                                <div class='app-popover-content-container'>\n\
                                    <div class='confirmation-title'>" + settings.title + "</div>\n\
                                    <div class='app-popover-body pt0'>\n\
                                        <div class='custom-popover-button-area mt15 clearfix row'>\n\
                                            <div class='col-md-6 pr5'><button class='btn btn-danger w100p confirmation-confirm-button'><i data-feather='check' class='icon-16'></i> " + settings.btnConfirmLabel + "</button></div>\n\
                                            <div class='col-md-6 pl5'><button class='btn btn-default w100p confirmation-cancel-button'><i data-feather='x' class='icon-16'></i> " + settings.btnCancelLabel + "</button></div>\n\
                                        </div>\n\
                                    </div>\n\
                                </div>\n\
                            </div>";

        $(".app-popover").remove();
        $("body").append(popoverDom);
        feather.replace();

        //submit button
        $(".confirmation-confirm-button").click(function () {
            $(".app-popover").remove(); //hide popover
            settings.onConfirm();
        });

        //close button
        $(".confirmation-cancel-button").click(function () {
            $(".app-popover").remove(); //hide popover
        });
    };
})(jQuery);

// Make any items sortable
// The row should have attributes: data-sort-value, data-id
(function ($) {
    $.fn.appSortable = function (options) {
        var defaults = {
            actionUrl: "", // the url where the response will go after modification
            rowClass: "", // the class of the row to be sorted
            sortDirection: "asc", // the direction of the sort (asc or desc)
        };

        var $instance = $(this);
        var $selector = $instance;
        var settings = $.extend({}, defaults, options);
        if (!settings) {
            return false;
        }

        // if it's a table then apply the sortable on the tbody
        if ($instance.attr("id").endsWith("table")) {
            var randomId = getRandomAlphabet(5);
            $instance.find("tbody").attr("id", randomId);
            $selector = $("#" + randomId);
        }

        Sortable.create($selector[0], {
            animation: 150,
            handle: '.move-icon',
            chosenClass: "sortable-chosen",
            ghostClass: "sortable-ghost",
            onUpdate: function (e) {
                appLoader.show();

                // get the sort value based on the previous and next item
                if ($instance.attr("id").endsWith("table")) {
                    var $movedItem = $(e.item).find(settings.rowClass);
                    var $prevItem = $movedItem.closest('tr').prev('tr').find(settings.rowClass);
                    var $nextItem = $movedItem.closest('tr').next('tr').find(settings.rowClass);
                } else {
                    var $movedItem = $(e.item);
                    var $prevItem = $movedItem.prev(settings.rowClass);
                    var $nextItem = $movedItem.next(settings.rowClass);
                }

                var prevSortValue = $prevItem.attr('data-sort-value') || null;
                var nextSortValue = $nextItem.attr('data-sort-value') || null;
                prevSortValue = prevSortValue ? prevSortValue * 1 : null;
                nextSortValue = nextSortValue ? nextSortValue * 1 : null;

                let newSort;

                // prepare sort value for the moved item
                if (prevSortValue !== null && nextSortValue !== null) {
                    newSort = (prevSortValue + nextSortValue) / 2; // moved in the middle, calculate the middle value
                } else if (prevSortValue == null) {
                    newSort = ((settings.sortDirection === "asc") ? (nextSortValue - 0.1) : (nextSortValue + 1));
                } else if (nextSortValue == null) {
                    newSort = ((settings.sortDirection === "asc") ? (prevSortValue + 1) : (prevSortValue - 0.1));
                } else {
                    newSort = 1; // default value
                }

                newSort = Math.round(newSort * 100000) / 100000; // use upto 5 decimal places
                $movedItem.attr('data-sort-value', newSort);

                $.ajax({
                    url: settings.actionUrl,
                    type: "POST",
                    data: {
                        id: $movedItem.attr('data-id'),
                        sort: newSort
                    },
                    success: function () {
                        appLoader.hide();
                    }
                });
            }
        });
    };
})(jQuery);