| /**
 * Global library
 *
 * @author     Aby Dahana <[email protected] >
 * @copyright  (c) Aksara Laboratory <https://aksaracms.com>
 * @license    MIT License
 *
 * This source file is subject to the MIT license that is bundled with this
 * source code in the LICENSE.txt file.
 */
$(document).ready(function() {
    /**
     * Clear previous storage
     */
    sessionStorage.clear();
    /**
     * Reactivate in first load
     */
    reactivate();
    /**
     * Open child menu when current page is presents in navigation
     */
    let segmentation = config.current_slug,
        count = segmentation.split('/').length;
    if (count) {
        for (let i = 0; i < count; i++) {
            if (segmentation) {
                segmentation = segmentation.substr(0, segmentation.lastIndexOf('/'));
                if (segmentation && $('[data-segmentation=' + segmentation.replaceAll('/', '_') + ']').length) {
                    $('[data-segmentation=' + segmentation.replaceAll('/', '_') + ']').parents('li').addClass('active');
                    break;
                }
            }
        }
    }
    /**
     * Set the color scheme of browser status bar
     */
    let colorScheme = rgba2hex($(config.wrapper.header).css('backgroundColor') ?? '#3e67ba');
    document.querySelector('meta[name=msapplication-navbutton-color]').setAttribute('content', colorScheme);
    document.querySelector('meta[name=theme-color]').setAttribute('content', colorScheme);
    document.querySelector('meta[name=apple-mobile-web-app-status-bar-style]').setAttribute('content', colorScheme);
    /**
     * Store the request to browser history
     */
    history.replaceState({path: window.location.href}, '');
    /**
     * Override the jQuery error exception
     */
    $(window).on('error', function(response) {
        // Throw log to console
        return throw_exception(500, (typeof response.originalEvent.message !== 'undefined' ? response.originalEvent.message : response.originalEvent.srcElement));
    });
    /**
     * Describe the back button of browser to fix the SPA
     */
    $(window).bind("popstate", function(e) {
        if (! e.originalEvent.state) return;
        let oldTitle = $('[role=title]').text();
        let oldIcon = $('[role=icon]').attr('class');
        let oldIconBtn = $(this).find('i.mdi').attr('class');
        xhr = $.ajax({
            url: location.href,
            context: this,
            method: 'POST',
            data: {
                prefer: (UA === 'mobile' ? 'html' : null)
            },
            beforeSend: function(progress) {
                if (xhr) {
                    xhr.abort();
                }
                $('[role=title]').text(phrase('Loading...'));
                $('[role=icon]').removeAttr('class').addClass('mdi mdi-loading mdi-spin');
                $('[role=description]').removeClass('show');
                $('[role=reload]').attr('href', location.href);
                $('[data-bs-toggle=tooltip]').tooltip('dispose');
                $('[data-bs-toggle=popover]').popover('dispose');
                $('.modal.show').modal('hide');
            }
        })
        .done(function(response) {
            parser.render($(this), response, true);
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            return throw_exception(response.code, response?.message);
        });
    });
    /**
     * Overtake the keyboard shortcut
     */
    $('body').on('keydown', function(e) {
        let keycode = (e.which ? e.which : e.keyCode);
        if (keycode === 116) {
            e.preventDefault();
            // Overtake F5
            if ($('a[role=reload]').length) {
                $('a[role=reload]').trigger('click');
            } else {
                $('<a href="' + window.location.href + '" class="--xhr"></a>').appendTo('body').trigger('click').remove();
            }
        } else if ((keycode == 115 || keycode == 83) && ((e.ctrlKey || e.metaKey) || (keycode == 19)) && $('form.--validate-form:visible').length) {
            e.preventDefault();
            
            // Overtake CTRL + S
            $('form.--validate-form:visible').trigger('submit');
        } else if (e.ctrlKey && keycode === 70 && ! $('.modal:visible').length && $('input[name="q"]').length) {
            e.preventDefault();
            
            // Overtake CTRL + F
            $('input[name="q"]').focus();
        } else if (keycode === 27 && $('.modal:visible').length && $('.modal:visible').attr('data-bs-backdrop') != 'static') {
            e.preventDefault();
            
            // Overtake ESC
            $('.modal:visible').find('[data-bs-dismiss="modal"]').trigger('click');
        }
    });
    /**
     * Toggle full screen
     */
    $('body').on('click touch', '[data-toggle=fullscreen]', function(e)
    {
        e.preventDefault();
        
        if ((document.fullScreenElement && document.fullScreenElement !== null) || (!document.mozFullScreen && !document.webkitIsFullScreen)) {
            let success = false;
            
            if (document.documentElement.requestFullScreen) {
                success = true;
                
                document.documentElement.requestFullScreen()
            } else if (document.documentElement.mozRequestFullScreen) {
                success = true;
                
                document.documentElement.mozRequestFullScreen()
            } else if (document.documentElement.webkitRequestFullScreen) {
                success                             = true;
                
                document.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)
            }
            
            if (success)
            {
                $(this).find('.mdi').removeClass('mdi-fullscreen').addClass('mdi-fullscreen-exit')
            }
        } else {
            let success = false;
            
            if(document.cancelFullScreen) {
                success = true;
                
                document.cancelFullScreen()
            } else if (document.mozCancelFullScreen) {
                success = true;
                
                document.mozCancelFullScreen()
            } else if(document.webkitCancelFullScreen) {
                success = true;
                
                document.webkitCancelFullScreen()
            }
            
            if(success) {
                $(this).find('.mdi').removeClass('mdi-fullscreen-exit').addClass('mdi-fullscreen')
            }
        }
    });
    $('body').on('click touch', 'button, .btn, [data-ripple]', function(e) {
        if ($(this).is('.btn-disabled') || $(this).is('.no-ripple')) {
            return;
        }
        let position = $(this).css('position'),
            offset = $(this).offset(),
            xAxis = e.pageX - offset.left,
            yAxis = e.pageY - offset.top,
            diagonal = Math.min(this.offsetHeight, this.offsetWidth, 100),
            ripple = $('<div class="ripple"></div>').appendTo($(this));
        if (! position || position === 'static') {
            $(this).css({
                position: 'relative'
            });
        }
        $('<div class="rippleWave"></div>').css({
            background: $(this).data("ripple"),
            width: diagonal,
            height: diagonal,
            left: xAxis - (diagonal / 2),
            top: yAxis - (diagonal / 2)
        }).appendTo(ripple).on('animationend', function() {
            ripple.remove();
        });
    });
    /**
     * Submit and validate form
     */
    $('body:not(.no-ajax)').on('submit', 'form.--validate-form[method=POST]', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
        // Prevent form resubmission when previous request is on progress
        if ($(this).find('button[type=submit]').hasClass('disabled')) return false;
        let context = $(this);
        let formData = new FormData(this);
        let reset = (context.attr('data-reset') ? context.data('reset') : true);
        $('input[role=money]', $(this)).each(function() {
            // Rollback formatted money
            formData.append($(this).attr('name'), ($(this).val() ?? 0).replaceAll(',', ''));
        });
        if (! $(this).find('input[name=_token]').length) {
            formData.append('_token', (sessionStorage.getItem('_token') ? sessionStorage.getItem('_token') : $('meta[name=_token]').attr('content')));
        }
        if ($(this).find('button[type=submit]:focus').attr('name') && $(this).find('button[type=submit]:focus').attr('value')) {
            // Set new value
            formData.append($(this).find('button[type=submit]:focus').attr('name'), $(this).find('button[type=submit]:focus').attr('value'));
        }
        xhr = $.ajax({
            method: 'POST',
            context: this,
            url: context.attr('action'),
            data: formData,
            contentType: false,
            processData: false,
            beforeSend: function(progress) {
                if (xhr) {
                    xhr.abort();
                }
                // Focus to the submit button
                context.find('button[type=submit]').addClass('disabled').find('i.mdi').removeClass('mdi-check').addClass('mdi-loading mdi-spin').focus().blur();
                // Remove all previous field checker
                context.find('input, textarea, select').removeClass('is-invalid');
                context.find('.input-group-text, .select2-selection').removeClass('border-danger');
                // Remove all previous field error message
                context.find('[role=validation-callback]').removeAttr('class').html('');
                if (typeof $.fn.tooltip !== 'undefined') {
                    $('body').tooltip('dispose');
                }
                
                if (typeof $.fn.popover !== 'undefined') {
                    $('body').popover('dispose');
                }
            },
            statusCode: {
                301: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        successBuzzer.play();
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    // Validation success, hide form modal and redirect
                    context.closest('.modal').modal('hide');
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                400: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        warningBuzzer.play();
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    let margin = '3'; // 1rem
                    let rounded = '3'; // 1rem
                    let appendTo = context.children().last();
                    if (context.closest('.modal:visible').length) {
                        margin = '0';
                        if (context.hasClass('modal-content')) {
                            rounded = '0';
                        }
                    }
                    if (appendTo.hasClass('opt-btn')) {
                        appendTo = appendTo.prev();
                    }
                    
                    // Validation error
                    if (! context.find('[role=validation-callback]').length) {
                        $(`<div class="alert alert-warning py-1 rounded-${rounded} mb-${margin}" role="validation-callback"></div>`).insertBefore(appendTo);
                    } else {
                        context.find('[role=validation-callback]').addClass('alert alert-warning py-1 rounded-' + rounded + ' mb-' + margin)
                    }
                    
                    let index = 0;
    
                    $.each(response.message, function(key, val) {
                        context.find(':input[name=' + key + ']').addClass('is-invalid');
                        context.find(':input[name=' + key + ']').prev('.input-group-text').addClass('border-danger');
                        context.find(':input[name=' + key + ']').next('.input-group-text').addClass('border-danger');
                        context.find(':input[name=' + key + ']').next('.select2-container').find('.select2-selection').addClass('border-danger');
                        $('<p class="my-1' + (index ? ' border-top border-warning' : '') + '">' + val + '</p>').appendTo(context.find('[role=validation-callback]'));
                        
                        index++;
                    });
                    
                    var selector = 'html, body';
    
                    if (context.closest('.modal:visible').length) {
                        selector = '.modal';
                    }
    
                    $(selector).animate
                    ({
                        scrollTop: $('[role=validation-callback]').offset().top + 60
                    }, 200);
                },
                403: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        warningBuzzer.play();
                    }
                    
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                404: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        warningBuzzer.play();
                    }
                    
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                500: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        failBuzzer.play();
                    }
                    
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                }
            }
        })
        .done(function(response) {
            // Animate the submit button
            context.find('button[type=submit]').removeClass('disabled').find('i.mdi').removeClass('mdi-loading mdi-spin').addClass('mdi-check');
            // Remove element
            if (typeof response.remove_element !== 'undefined' && $(response.remove_element).length) {
                $(response.remove_element).remove()
            }
            // Append to element
            if (typeof response.prepend_to !== 'undefined' && typeof response.content !== 'undefined') {
                $(response.content).prependTo((typeof response.in_context !== 'undefined' && response.in_context ? context.closest(response.prepend_to) : response.prepend_to));
            }
            // Prepend to element
            if (typeof response.append_to !== 'undefined' && typeof response.content !== 'undefined') {
                $(response.content).appendTo((typeof response.in_context !== 'undefined' && response.in_context ? context.closest(response.append_to) : response.append_to));
            }
            // Insert before element
            if (typeof response.insert_before !== 'undefined' && typeof response.content !== 'undefined') {
                $(response.content).insertBefore((typeof response.in_context !== 'undefined' && response.in_context ? context.closest(response.insert_before) : response.insert_before));
            }
            // Insert after element
            if (typeof response.insert_after !== 'undefined' && typeof response.content !== 'undefined') {
                $(response.content).insertAfter((typeof response.in_context !== 'undefined' && response.in_context ? context.closest(response.insert_after) : response.insert_after));
            }
            if (typeof response.target !== 'undefined') {
                // Redirect into target
                if (typeof response.popup !== 'undefined' && response.popup) {
                    $('.modal').modal('hide'),
                    $('<a href="' + response.target + '" class="--modal"></a>').appendTo('body').trigger('click').remove();
                } else {
                    $('<a href="' + response.target + '" class="--xhr"></a>').appendTo('body').trigger('click').remove();
                }
                return;
            } else if (typeof response.element !== 'undefined' && typeof response.content !== 'undefined') {
                // Modify element
                $(response.element).html(response.content);
                context.closest('.modal').modal('hide');
            } else if (typeof response.meta !== 'undefined' && typeof response.meta.popup !== 'undefined' && response.meta.popup) {
                if (typeof response.meta.keep_modal === 'undefined') {
                    // Hide previous modal
                    $('.modal').modal('hide');
                }
                let template = parser.parse('core/modal.twig', response);
                $(template).appendTo('body').modal('show', {
                    backdrop: 'static',
                    keyboard: false
                });
                if (typeof response.reactivate !== 'undefined') {
                    reactivate(response.reactivate);
                }
            } else {
                // Play sound
                if (config.action_sound) {
                    successBuzzer.play();
                }
                // Validation success, hide form modal and follow response
                context.closest('.modal').modal('hide');
                // Throw messages
                return throw_exception(response.code, response.message, response.target, response.redirect);
            }
            if (reset) {
                context.trigger('reset');
                context.find('textarea').css('height', 'auto');
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            
            context.find('button[type=submit]').removeClass('disabled').find('i.mdi').removeClass('mdi-loading mdi-spin').addClass('mdi-check');
        });
    });
    /**
     * On submit form method get, such a search form
     */
    $('body:not(.no-ajax)').on('submit', 'form[method=GET]:not(.no-ajax)', function(e) {
        e.preventDefault();
        let formData = new FormData(this);
        formData.append('_request', 'token');
        formData.append('_token', (sessionStorage.getItem('_token') ? sessionStorage.getItem('_token') : $('meta[name=_token]').attr('content')));
        $('input[role=datepicker]', $(this)).each(function() {
            // Format date as yyyy-mm-dd
            let date = new Date(
                $(this).val().toLocaleString(config.language ?? 'id', {
                    timeZone: config.timezone
                })
            );
            let year = date.getFullYear();
            let month = ('0' + (date.getMonth() + 1)).slice(-2);
            let day = ('0' + date.getDate()).slice(-2);
            formData.append($(this).attr('name'), [year, month, day].join('-'));
        });
        if ($(this).find('button[type=submit]:focus').attr('name') && $(this).find('button[type=submit]:focus').attr('value')) {
            formData.append($(this).find('button[type=submit]:focus').attr('name'), $(this).find('button[type=submit]:focus').attr('value'));
        }
        let sandbox = $.now();
        if ('_blank' === $(this).attr('target') && UA !== 'mobile') {
            // Prepare the new window if it have a _blank target
            window.open(config.base_url + 'pages/blank', 'placeholder_' + sandbox);
        }
        // Otherwise, request new token
        xhr = $.ajax({
            url: $(this).attr('action'),
            method: 'POST',
            data: formData,
            context: this,
            contentType: false,
            processData: false,
            beforeSend: function(progress) {
                if (xhr) {
                    xhr.abort();
                }
                if ($.fn.tooltip !== 'undefined') {
                    $('body').tooltip('dispose');
                }
                
                if (typeof $.fn.popover !== 'undefined') {
                    $('body').popover('dispose');
                }
            }
        })
        .done(function(response) {
            if ('_blank' === $(this).attr('target')) {
                // Reopen the new window placeholder with current url
                if (UA !== 'mobile') {
                    window.open(response.callback, 'placeholder_' + sandbox);
                } else {
                    window.location.href = response.callback;
                }
            } else {
                $('<a href="' + response.callback + '" class="--xhr"></a>').appendTo('body').trigger('click').remove();
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            // Play sound
            if (config.action_sound && $.inArray(response.code, [500]) !== -1) {
                failBuzzer.play();
            }
            // Throw messages
            return throw_exception(response.code, response?.message, response?.target, response?.redirect);
        });
    });
    /**
     * Initial of hyperlink that use XHR Request
     */
    $('body:not(.no-ajax)').on('click touch', 'a.--xhr, a.--modal', function(e) {
        if (! $(this).attr('href') || $(this).attr('href') === '#' || $(this).attr('href') === 'javascript:void(0)' || $(this).attr('href').indexOf(config.base_url) === -1 || $(this).attr('target')) {
            return;
        }
        e.preventDefault();
        let context = $(this);
        let oldTitle = $('[role=title]:first').text();
        let oldIcon = $('[role=icon]:first').attr('class');
        let backupIcon = 'mdi mdi-check';
        let identifier = $.now();
        if (context.hasClass('--modal') && (UA !== 'mobile' || context.hasClass('--force-xs'))) {
            context.data('identifier', identifier);
            context.data('prefer', 'modal');
            let output = parser.parse('core/modal.twig', {
                identifier: identifier
            });
            $(output).appendTo('body').modal('show', {
                backdrop: 'static',
                keyboard: false
            });
        }
        xhr = $.ajax({
            url: context.attr('href'),
            method: 'POST',
            data: context.data(),
            context: this,
            beforeSend: function(progress) {
                if (xhr) {
                    xhr.abort();
                }
                $('body').css({
                    cursor: 'wait'
                });
                // Check if progress need to be shown in the clicked element
                if (context.hasClass('show-progress') && ! context.hasClass('--modal')) {
                    // Prevent click active link
                    context.addClass('disabled').prop('disabled', true);
                    // Trigger icon to spin
                    if (context.find('i.mdi').length) {
                        // Backup button icon
                        backupIcon = context.find('i.mdi').attr('class');
                        context.find('i.mdi').removeAttr('class').addClass('mdi mdi-loading mdi-spin');
                    }
                } else if (! context.hasClass('--prevent-modify') && ! context.hasClass('--modal')) {
                    // Otherwise
                    $('[role=title]').text(phrase('Loading...'));
                    $('[role=icon]').removeAttr('class').addClass('mdi mdi-loading mdi-spin');
                }
                if (typeof $.fn.tooltip !== 'undefined') {
                    $('[data-bs-toggle=tooltip]').tooltip('hide');
                }
                if (typeof $.fn.popover !== 'undefined') {
                    $('[data-bs-toggle=popover]').popover('hide');
                }
            },
            statusCode: {
                301: function(response) {
                    if ($('.modal#dynamic-modal-' + identifier).length) {
                        // Close modal
                        $('.modal#dynamic-modal-' + identifier).modal('hide');
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                403: function(response) {
                    if ($('.modal#dynamic-modal-' + identifier).length) {
                        // Close modal
                        $('.modal#dynamic-modal-' + identifier).modal('hide');
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                404: function(response) {
                    if ($('.modal#dynamic-modal-' + identifier).length) {
                        // Close modal
                        $('.modal#dynamic-modal-' + identifier).modal('hide');
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                },
                500: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        failBuzzer.play();
                    }
                    if ($('.modal#dynamic-modal-' + identifier).length) {
                        // Close modal
                        $('.modal#dynamic-modal-' + identifier).modal('hide');
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        // Get response JSON
                        response = response.responseJSON;
                    }
                    
                    return throw_exception(response.code, response?.message, response?.target, response?.redirect);
                }
            }
        })
        .done(function(response) {
            $('body').css({
                cursor: 'default'
            });
            if (context.hasClass('show-progress') && ! context.hasClass('--modal')) {
                // Prevent click active link
                context.removeClass('disabled').prop('disabled', false);
                // Trigger icon to spin
                if (context.find('i.mdi').length) {
                    context.find('i.mdi').removeAttr('class').addClass(backupIcon);
                }
            }
            if (typeof response.target !== 'undefined') {
                // Redirect into target
                if (typeof response.popup !== 'undefined' && response.popup) {
                    $('.modal').modal('hide'),
                    $('<a href="' + response.target + '" class="--modal"></a>').appendTo('body').trigger('click').remove();
                } else {
                    $('<a href="' + response.target + '" class="--xhr"></a>').appendTo('body').trigger('click').remove();
                }
                return;
            } else if (typeof response.toggle_html !== 'undefined') {
                // Append HTML response
                return context.parent().html(response.toggle_html);
            } else {
                const oldStuff = {
                    identifier: identifier,
                    title: oldTitle,
                    icon: oldIcon
                }
                
                // Parse response
                return parser.render(context, response, false, oldStuff);
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            $('body').css({
                cursor: 'default'
            });
            if (context.hasClass('show-progress') && ! context.hasClass('--modal')) {
                // Prevent click active link
                context.removeClass('disabled').prop('disabled', false);
                // Trigger icon to spin
                if (context.find('i.mdi').length) {
                    context.find('i.mdi').removeAttr('class').addClass(backupIcon);
                }
            }
            $('[role=title]').text(oldTitle);
            $('[role=icon]').removeAttr('class').addClass(oldIcon);
        });
    });
    /**
     * Simple request and modify
     */
    $('body').on('click', '.--modify', function(e)
    {
        e.preventDefault();
        
        xhr = $.ajax({
            url: ($(this).data('href') ? $(this).data('href') : $(this).attr('href')),
            method: 'POST',
            context: this,
            data: $(this).data(),
            beforeSend: function() {
                $(this).prop('disabled', true);
                $('[data-bs-toggle=tooltip]').tooltip('dispose')
            },
            complete: function() {
                $(this).prop('disabled', false)
            },
            statusCode: {
                403: function(response, status, error) {
                    if (config.action_sound) {
                        warningBuzzer.play()
                    }
                    if (typeof response.responseJSON !== 'undefined') {
                        response = response.responseJSON;
                        
                        throw_exception(response.code, response.message)
                    }
                }
            }
        })
        .done(function(response) {
            if (typeof response.element !== 'undefined' && response.content !== 'undefined') {
                $(response.element).html(response.content);
                if (typeof response.class_add !== 'undefined' && typeof response.class_remove !== 'undefined') {
                    $(this).removeClass(response.class_remove).addClass(response.class_add)
                }
            }
        })
        .fail(function(response, status, error) {
            if (response.statusText == 'abort') {
                return;
            }
        })
    });
    /**
     * Delete item after confirmed
     */
    $('body').on('click.delete touch.delete', '.--delete-anyway', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
        // Generate the response data
        xhr = $.ajax({
            url: $(this).attr('href'),
            method: 'POST',
            context: this,
            data: {
                batch: ($(this).data('bulk-delete') ? 1 : 0),
                items: $('input[name^=bulk_delete]:checked').map(function() {
                    return $(this).val();
                }).get()
            },
            beforeSend: function(progress) {
                if (xhr) {
                    xhr.abort();
                }
                $(this).find('i.mdi').removeAttr('class').addClass('mdi mdi-refresh mdi-spin');
                if ($.fn.tooltip !== 'undefined') {
                    $('body').tooltip('dispose');
                }
                
                if (typeof $.fn.popover !== 'undefined') {
                    $('body').popover('dispose');
                }
            },
            statusCode: {
                301: function(response) {
                    // Play sound
                    if (config.action_sound) {
                        successBuzzer.play();
                    }
                    // Delete success, hide confirmation modal and redirect
                    $(this).closest('.modal').modal('hide');
                    // Throw messages
                    return throw_exception(response.code, response.message, response.target, response.redirect);
                },
                403: function(response) {
                    // Permission denied, hide confirmation modal and redirect
                    $(this).closest('.modal').modal('hide');
                    // Throw messages
                    return throw_exception(response.code, response.message, response.target, response.redirect);
                }
            }
        })
        .done(function(response) {
            // Play sound
            if (config.action_sound) {
                successBuzzer.play();
            }
            $(this).find('i.mdi').removeAttr('class').addClass('mdi mdi-check');
            // Delete success, hide confirmation modal and follow response
            $(this).closest('.modal').modal('hide');
            // Throw messages
            return throw_exception(response.code, response.message, response.target, response.redirect);
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            // Play sound
            if (config.action_sound && $.inArray(response.code, [500]) !== -1) {
                failBuzzer.play();
            }
            // Throw messages
            return throw_exception(response.code, response?.message, response?.target, response?.redirect);
        });
    });
    /**
     * On click expandable
     */
    $('body').on('click touch', '[role=expand]', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
        if ($(this).find('.mdi').hasClass('mdi-arrow-expand')) {
            $(this).attr('data-bs-original-title', phrase('Collapse'));
            $(this).find('.mdi').removeClass('mdi-arrow-expand').addClass('mdi-arrow-collapse');
        } else {
            $(this).attr('data-bs-original-title', phrase('Expand'));
            $(this).find('.mdi').removeClass('mdi-arrow-collapse').addClass('mdi-arrow-expand');
        }
        $('body').toggleClass('content-expanded');
        $('[role=table], [role=form]').height(function(index, height) {
            return $(window).outerHeight(true) - (($('[role=header]:visible').outerHeight(true) ?? 0) + ($('[role=breadcrumb]:visible').outerHeight(true) ?? 0) + ($('[role=meta]:visible').outerHeight(true) ?? 0) + ($('[role=toolbar]:visible').outerHeight(true) ?? 0) + ($('[role=pagination]:visible').outerHeight(true) ?? 0) + ($('[role=submit]:visible').outerHeight(true) ?? 0));
        });
        if (typeof $.fn.tooltip !== 'undefined') {
            $('[data-bs-toggle=tooltip]').tooltip('dispose');
            $('[data-bs-toggle=tooltip]').tooltip();
        }
    });
    /**
     * On click removable
     */
    $('body').on('click touch', '[role=close]', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
    });
    /**
     * Move element up / down
     */
    $('body').on('click touch', '.--move-up', function(e) {
        // Move element down
        e.preventDefault();
        $(this).closest($(this).attr('data-element')).insertBefore($(this).closest($(this).attr('data-element')).prev($(this).attr('data-element')));
    })
    .on('click touch', '.--move-down', function(e) {
        // Move element up
        e.preventDefault();
        $(this).closest($(this).attr('data-element')).insertAfter($(this).closest($(this).attr('data-element')).next($(this).attr('data-element')));
    });
    /**
     * On bootstrap toast hidden
     */
    $('body').on('hidden.bs.toast', '.toast', function(e) {
        $(this).closest('.toast-container').remove();
    });
    /**
     * On bootstrap modal hidden
     */
    $('body').on('shown.bs.modal', '.modal', function(e) {
        // Fix the scrollbar for creating duplicate object
        $('html').addClass('fix-scrollbar');
    })
    .on('shown.bs.modal', '.modal:not(.dragless)', function(e) {
        let context = $(this).find('.modal-dialog');
        require.js([
            config.base_url + 'assets/draggable/jquery-ui.draggable.min.js'
        ], function() {
            // Apply jquery-ui draggable
            context.draggable({
                cursor: 'move',
                handle: '.modal-header'
            });
            $('.ui-draggable-handle').css('cursor', 'move');
        });
    });
    /**
     * On bootstrap modal hidden
     */
    $('body').on('hidden.bs.modal', '.modal', function(e) {
        $('html').removeClass('fix-scrollbar');
        $('.dcalendarpicker, #sortableListsBase, .note-popover').remove();
        if ($('.modal:visible').length) {
            $('html').addClass('fix-scrollbar');
            $('body').addClass('modal-open');
        }
        if (! $(this).hasClass('--prevent-remove')) {
            $(this).remove();
        }
        $('.modal.note-modal').remove();
    });
    /**
     * On navbar collapse
     */
    $('body').on('shown.bs.collapse', '.navbar-collapse', function(e) {
        ($('html').addClass('overflow-hidden'), $('body').addClass('sidebar-expanded').removeClass('sidebar-collapsing sidebar-collapsed'), $('.navbar').first().removeClass('bg-transparent'));
    })
    .on('show.bs.collapse', '.navbar-collapse', function(e) {
        $('body').addClass('sidebar-collapsing');
    })
    .on('hidden.bs.collapse', '.navbar-collapse', function(e) {
        ($('html').removeClass('overflow-hidden'), $('body').removeClass('sidebar-collapsing sidebar-expanded').addClass('sidebar-collapsed'));
        ($('.leading').visible(true) ? $('.navbar').first().addClass('bg-transparent') : '');
    })
    .on('hide.bs.collapse', '.navbar-collapse', function(e) {
        $('body').addClass('sidebar-collapsing');
    });
    /**
     * On bootstrap collapse shown / collapsed
     */
    $('body').on('hide.bs.collapse show.bs.collapse', '[role=description]', function(e) {
        ($('[role=table]').length ? $('body').css('overflow-y', 'hidden') : null);
    });
    $('body').on('hidden.bs.collapse', '[role=description]', function(e) {
        $('[role=table]').animate({
            height: $(window).outerHeight(true) - (($('[role=header]:visible').outerHeight(true) ?? 0) + ($('[role=breadcrumb]:visible').outerHeight(true) ?? 0) + ($('[role=meta]:visible').outerHeight(true) ?? 0) + ($('[role=toolbar]:visible').outerHeight(true) ?? 0) + ($('[role=pagination]:visible').outerHeight(true) ?? 0))
        }, {
            complete: function() {
                $('body').css('overflow-y', 'auto');
            }
        });
    })
    .on('shown.bs.collapse', '[role=description]', function(e) {
        $('[role=table]').animate({
            height: $(window).outerHeight(true) - (($('[role=header]:visible').outerHeight(true) ?? 0) + ($('[role=breadcrumb]:visible').outerHeight(true) ?? 0) + ($('[role=meta]:visible').outerHeight(true) ?? 0) + ($('[role=toolbar]:visible').outerHeight(true) ?? 0) + ($('[role=pagination]:visible').outerHeight(true) ?? 0))
        }, {
            complete: function() {
                $('body').css('overflow-y', 'auto');
            }
        });
    });
    /**
     * Sidebar for mobile device
     */
    $('body').on('click touch', '[data-toggle=sidebar]', function(e) {
        if ($('body').hasClass('sidebar-collapsed')) {
            $(this).find('i').removeClass('mdi-arrow-right').addClass('mdi-arrow-left');
        } else {
            $(this).find('i').removeClass('mdi-arrow-left').addClass('mdi-arrow-right');
        }
        $('.navbar-collapse').collapse('hide');
        ($('body').hasClass('sidebar-collapsed') ? ($('body').addClass('sidebar-expanded').removeClass('sidebar-collapsed'), (UA === 'mobile' ? $('html').addClass('overflow-hidden') : null)) : ($('body').hasClass('sidebar-expanded') ? ($('body').removeClass('sidebar-expanded').addClass('sidebar-collapsed'), $('html').removeClass('overflow-hidden')) : (UA === 'mobile' ? ($('body').addClass('sidebar-expanded'), $('html').addClass('overflow-hidden')) : ($('body').addClass('sidebar-collapsed')))));
        
        // Wait event to complete 
        setTimeout(function() {
            // Trigger window resize to resize the table
            $(window).trigger('resize');
        }, 300);
    });
    /**
     * Expand and collapse for sidebar menu
     */
    $('body').on('click touch', '[data-toggle=expand-collapse]', function(e) {
        e.preventDefault();
        $(this).closest('li').siblings().find('[data-toggle=expand-collapse]').removeClass('is-expanded');
        $(this).closest('li').siblings().find('ul').collapse('hide');
        if ($(this).next('ul').hasClass('show')) {
            $(this).removeClass('is-expanded');
            $(this).next('ul').collapse('hide');
        } else {
            $(this).addClass('is-expanded');
            $(this).next('ul').collapse('show');
        }
    });
    /**
     * Dependent dropdown
     */
    $('body').on('change', 'select[data-to-change]', function(e) {
        e.preventDefault();
        xhr = $.ajax({
            url: $(this).closest('form').attr('action'),
            method: 'POST',
            data: {
                trigger: 'dropdown',
                value: $(this).val(),
                selector: $(this).attr('data-to-change')
            },
            context: this,
            beforeSend: function() {
                if (xhr) {
                    xhr.abort();
                }
                $(this).closest('form').find('select[name=' + $(this).attr('data-to-change') + ']').empty();
                $(this).closest('form').find('select[name=' + $(this).attr('data-to-change') + ']').html('<option> </option>').attr('disabled', true).trigger('change');
            }
        })
        .done(function(response) {
            if (typeof response.suggestions !== 'undefined') {
                $(this).closest('form').find('select[name=' + response.selector + ']').empty().select2({
                    data: response.suggestions,
                    dropdownCssClass: ($(this).hasClass('form-control-sm') ? 'select2-dropdown-sm' : ''),
                    dropdownParent: $(this).parent()
                });
                $(this).closest('form').find('select[name=' + response.selector + ']').attr('disabled', false);
            } else if (typeof response.content !== 'undefined') {
                $(this).closest('form').find('select[name=' + response.selector + ']').html(response.content).attr('disabled', false).select2();
                $(this).closest('form').find('select[name=' + response.selector + ']').trigger('change')
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            return throw_exception(response.code, response?.message);
        });
    });
    /**
     * Get dropdown content
     */
    $('body').on('change', 'select.get-dropdown-content', function(e) {
        e.preventDefault();
        xhr = $.ajax({
            url: $(this).closest('form').attr('action'),
            method: 'POST',
            data: {
                trigger: 'get-dropdown-content',
                value: $(this).val()
            },
            context: this,
            beforeSend: function() {
                if (xhr) {
                    xhr.abort();
                }
                $(this).closest('.form-group').find('.dropdown-content').remove();
            }
        })
        .done(function(response) {
            if (typeof response.content !== 'undefined') {
                $(response.content).addClass('dropdown-content mt-3').appendTo($(this).closest('.form-group'));
            } else if (typeof response.fields !== 'undefined' && typeof response.fields === 'object') {
                $.each(response.fields, function(key, val) {
                    if ($('input[name=' + key + ']').length) {
                        $('input[name=' + key + ']').val(val)
                    } else if ($('textarea[name=' + key + ']').length) {
                        $('textarea[name=' + key + ']').val(val)
                    }
                });
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            return throw_exception(response.code, response?.message);
        });
    });
    /**
     * Preview image before upload
     */
    $('body').on('change', '[role=image-upload]', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
        var context = $(this);
        if (this.files && this.files[0]) {
            var reader = new FileReader;
            reader.onload = function(e) {
                if (context.hasClass('multiple')) {
                    context.parents('.fileupload').clone().appendTo(t.parents('.multiple-upload'));
                }
                context.parents('.btn-file').find('.upload_preview').attr('src', e.target.result);
            };
            reader.readAsDataURL(this.files[0]);
        }
    });
    /**
     * Check all rows
     */
    $('body').on('change', '[role=checker]', function(e) {
        var _parent = $(this).data('parent');
        if ($(this).is(':checked')) {
            $(this).closest(_parent).find(':checkbox.form-check-input').prop('checked', true);
            if ($(this).hasClass('bulk-delete')) {
                $('[data-bulk-delete=true]').prev('.btn').removeClass('rounded-end');
                $('[data-bulk-delete=true]').removeClass('d-none disabled');
            }
        } else {
            $(this).closest(_parent).find(':checkbox.form-check-input').prop('checked', false);
            if ($(this).hasClass('bulk-delete')) {
                $('[data-bulk-delete=true]').prev('.btn').addClass('rounded-end');
                $('[data-bulk-delete=true]').addClass('d-none disabled');
            }
        }
    })
    .on('change', '.form-check-input', function(e) {
        var _parent = $(this).parents('div').find('[role=checker]').attr('data-parent');
        if ($(this).closest(_parent).find('input.form-check-input').is(':checked') > 0) {
            $(this).closest(_parent).find('[role=checker]').prop('checked', true);
            if (typeof e.target.name !== 'undefined' && e.target.name === 'bulk_delete[]') {
                $('[data-bulk-delete=true]').prev('.btn').removeClass('rounded-end');
                $('[data-bulk-delete=true]').removeClass('d-none disabled');
            }
        } else {
            $(this).closest(_parent).find('[role=checker]').prop('checked', false);
            if (typeof e.target.name !== 'undefined' && e.target.name === 'bulk_delete[]') {
                $('[data-bulk-delete=true]').prev('.btn').addClass('rounded-end');
                $('[data-bulk-delete=true]').addClass('d-none disabled');
            }
        }
    });
    /**
     * Attributions type field, add attribute
     */
    $('body').on('click touch', '[role=add-attribution]', function(e) {
        e.preventDefault();
        $(`
            <div class="row mb-1">
                <div class="col-4 pe-0">
                    <input type="text" name="${$(this).attr('data-label')}" class="form-control form-control-sm" placeholder="${$(this).attr('data-label-placeholder')}" autocomplete="off" />
                </div>
                <div class="col-5 pe-0">
                    <input type="text" name="${$(this).attr('data-value')}" class="form-control form-control-sm" placeholder="${$(this).attr('data-value-placeholder')}" autocomplete="off" />
                </div>
                <div class="col-3">
                    <div class="btn-group btn-group-sm float-end">
                        <a href="javascript:void(0)" class="btn btn-secondary --move-up" data-element=".row" data-bs-toggle="tooltip" title="${phrase('Move Up')}">
                            <i class="mdi mdi-arrow-collapse-up"></i>
                        </a>
                        <a href="javascript:void(0)" class="btn btn-secondary --move-down" data-element=".row" data-bs-toggle="tooltip" title="${phrase('Move Down')}">
                            <i class="mdi mdi-arrow-collapse-down"></i>
                        </a>
                        <a href="javascript:void(0)" class="btn btn-secondary float-end" role="remove-attribution" data-element=".row" data-bs-toggle="tooltip" title="${phrase('Remove')}">
                            <i class="mdi mdi-delete"></i>
                        </a>
                    </div>
                </div>
            </div>
        `)
        .appendTo($(this).closest('.attribution-input').find('.attribution-input-body'));
        $('[data-bs-toggle=tooltip]').tooltip();
    });
    /**
     * Attributions type field, remove existing
     */
    $('body').on('click touch', '[role=remove-attribution]', function(e) {
        e.preventDefault();
        $(this).closest($(this).attr('data-element')).fadeOut(200, function() {
            $('[data-bs-toggle=tooltip]').tooltip('dispose');
            $(this).remove();
        });
    });
    /**
     * Accordion field type field add
     */
    $('body').on('click touch', '[role=add-accordion]', function(e) {
        e.preventDefault();
        $(`
            <div class="card mb-3">
                <div class="card-header p-2">
                    <div class="input-group input-group-sm">
                        <input type="text" name="${$(this).attr('data-field')}[title][]" class="form-control" placeholder="${phrase('Accordion Title')}" id="${$(this).attr('data-field')}_input" />
                        <a href="javascript:void(0)" class="btn btn-secondary --move-up" data-element=".card" data-bs-toggle="tooltip" title="${phrase('Move Up')}">
                            <i class="mdi mdi-arrow-collapse-up"></i>
                        </a>
                        <a href="javascript:void(0)" class="btn btn-secondary --move-down" data-element=".card" data-bs-toggle="tooltip" title="${phrase('Move Down')}">
                            <i class="mdi mdi-arrow-collapse-down"></i>
                        </a>
                        <a href="javascript:void(0)" class="btn btn-secondary" role="remove-accordion" data-element=".card" data-bs-toggle="tooltip" title="${phrase('Remove')}">
                            <i class="mdi mdi-delete"></i>
                        </a>
                    </div>
                </div>
                <div class="card-body p-2">
                    <textarea name="${$(this).attr('data-field')}[body][]" class="form-control" role="wysiwyg" placeholder="${phrase('Accordion Body')}" id="${$(this).attr('data-field')}_input" rows="1"></textarea>
                </div>
            </div>
        `)
        .insertBefore($(this));
        $('[data-bs-toggle=tooltip]').tooltip();
        reactivate('wysiwyg')
    });
    /**
     * Accordion field type, remove existing
     */
    $('body').on('click touch', '[role=remove-accordion]', function(e) {
        e.preventDefault();
        $(this).closest($(this).attr('data-element')).fadeOut(200, function() {
            $('[data-bs-toggle=tooltip]').tooltip('dispose');
            $(this).remove();
        });
    });
    /**
     * Carousel type field
     */
    $('body').on('click touch', '[role=add-carousel]', function(e) {
        e.preventDefault();
        const length = $(this).closest('.mb-3').find('.card').length;
        $(`
            <div class="card mb-3">
                <div class="card-body">
                    <div class="form-group mb-3">
                        <label class="text-muted">${phrase('Background')}</label>
                        <div data-provides="fileupload" class="fileupload fileupload-new">
                            <span class="btn btn-file d-block">
                                <input type="file" name="${$(this).attr('data-field')}[background][${length}]" accept="images/*" role="image-upload" id="${$(this).attr('data-field')}_input" />
                                <div class="fileupload-new text-center">
                                    <img class="img-fluid upload_preview rounded" src="${$(this).attr('data-image-placeholder')}" alt="" />
                                </div>
                                <button type="button" class="btn btn-sm btn-danger rounded-circle position-absolute top-0 end-0" onclick="jExec($(this).closest('.fileupload').find('input[type=file]').val(''), $(this).closest('.fileupload').find('img').attr('src', '${$(this).attr('data-image-placeholder')}'))">
                                    <i class="mdi mdi-delete"></i>
                                </button>
                            </span>
                        </div>
                    </div>
                    <div class="form-group mb-3">
                        <input type="text" name="${$(this).attr('data-field')}[title][${length}]" class="form-control" placeholder="${phrase('Title')}" id="${$(this).attr('data-field')}_input" />
                    </div>
                    <div class="form-group mb-3">
                        <textarea name="${$(this).attr('data-field')}[description][${length}]" class="form-control" placeholder="${phrase('Description')}" id="${$(this).attr('data-field')}_input" rows="1"></textarea>
                    </div>
                    <div class="row">
                        <div class="col-md-6">
                            <div class="form-group mb-3">
                                <input type="text" name="${$(this).attr('data-field')}[link][${length}]" class="form-control" placeholder="${phrase('Target URL')}" id="${$(this).attr('data-field')}_input" />
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-group mb-3">
                                <input type="text" name="${$(this).attr('data-field')}[label][${length}]" class="form-control" placeholder="${phrase('Button Label')}" id="${$(this).attr('data-field')}_input" />
                            </div>
                        </div>
                    </div>
                </div>
                <div class="card-footer pt-1 pb-1">
                    <div class="btn-group btn-group-sm">
                        <a href="javascript:void(0)" class="btn btn-secondary --move-up" data-element=".card" data-bs-toggle="tooltip" title="${phrase('Move Up')}">
                            <i class="mdi mdi-arrow-collapse-up"></i>
                        </a>
                        <a href="javascript:void(0)" class="btn btn-secondary --move-down" data-element=".card" data-bs-toggle="tooltip" title="${phrase('Move Down')}">
                            <i class="mdi mdi-arrow-collapse-down"></i>
                        </a>
                    </div>
                    <a href="javascript:void(0)" class="btn btn-outline-danger btn-sm float-end" role="remove-carousel" data-element=".card">
                        <i class="mdi mdi-delete" data-bs-toggle="tooltip" title="${phrase('Remove')}"></i>
                    </a>
                </div>
            </div>
        `)
        .insertBefore($(this));
        $('[data-bs-toggle=tooltip]').tooltip();
    });
    /**
     * Carousel type field, remove existing
     */
    $('body').on('click touch', '[role=remove-carousel]', function(e) {
        e.preventDefault();
        $(this).closest($(this).attr('data-element')).fadeOut(200, function() {
            $('[data-bs-toggle=tooltip]').tooltip('dispose');
            $(this).remove();
        });
    });
    /**
     * Open option
     */
    $('body').on('click touch', '.--open-item-option', function(e) {
        e.preventDefault();
        $(this).blur();
        let options = $(this).data('options');
        if (options) {
            let list_items = '';
            $.each(options, function(key, val) {
                list_items += `<a href="${val.url}" class="list-group-item list-group-item-action ${val.class}" data-prefer="${val.prefer}" target="${val.target}"><i class="mdi ${(val.icon ?? 'mdi-blank')}"></i> ${val.label}</a>`;
            });
            $(`
                <div class="modal modal-alert" id="options-modal" role="dialog" aria-labelledby="options-modal-title" aria-hidden="true">
                    <div class="modal-dialog modal-dialog-centered" role="document" style="max-width:360px">
                        <div class="modal-content border">
                            <div class="modal-body">
                                <div class="list-group list-group-flush">
                                    ${list_items}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `)
            .appendTo('body')
            .modal('show');
        }
    });
    /**
     * Open delete confirmation
     */
    $('body').on('click touch', '.--open-delete-confirm', function(e) {
        e.preventDefault();
        $(this).blur();
        $(`
            <div class="modal modal-alert" id="delete-modal" role="dialog" aria-labelledby="delete-modal-title" aria-hidden="true">
                <div class="modal-dialog modal-dialog-centered" role="document" style="max-width:360px">
                    <div class="modal-content border">
                        <div class="modal-body text-center">
                            <h5>${phrase('Delete Data')}</h5>
                            <div>
                                ${($(this).data('bulk-delete') ? $('input[name^=bulk_delete]:checked').length + ' ' + phrase('data will be deleted') : phrase('Are you sure want to delete this data?'))}
                            </div>
                        </div>
                        <div class="modal-footer flex-nowrap p-0">
                            <button type="button" class="btn btn-lg btn-link fs-6 text-decoration-none col-6 m-0 rounded-0 border-end" data-bs-dismiss="modal" data-dismiss="modal">
                                ${phrase('Cancel')}
                            </button>
                            <a href="${$(this).attr('href')}" class="btn btn-lg btn-link text-danger fs-6 text-decoration-none col-6 m-0 rounded-0 --delete-anyway"${($(this).data('bulk-delete') ? ' data-bulk-delete="true"' : '')}>
                                <i class="mdi mdi-check"></i> ${phrase('Delete')}
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        `)
        .appendTo('body')
        .modal('show');
    });
    /**
     * Remove previous dom when user start typing in the field
     */
    $('body').on('change keyup', 'input, textarea, select', function(e) {
        // Remove highlighter class in the field
        $(this).removeClass('is-invalid');
        $(this).prev('.input-group-text').removeClass('border-danger');
        $(this).next('.input-group-text').removeClass('border-danger');
    });
    /**
     * Instant form submit if textarea entered
     */
    $('body').on('keydown keyup', 'textarea.--instant-submit', function(e) {
        if (UA != 'mobile') {
            if (e.which == 13 && ! e.shiftKey) {
                e.preventDefault();
                $(this).blur();
                $(this).closest('form').submit();
            }
        } else {
            if ($(this).val()) {
                $(this).next('button').removeClass('d-none');
            } else {
                $(this).next('button').addClass('d-none');
            }
        }
    });
    /**
     * On click remove exception
     */
    $('body').on('click touch', '.alert.exception', function(e) {
        // Prevent browser to take place as well
        e.preventDefault();
        $(this).fadeOut(200, function() {
            $(this).remove();
        });
    });
    /**
     * Override bootstrap dropdown-menu
     */
    $('body').on('click', '.dropdown-menu a.dropdown-toggle', function(e) {
        e.preventDefault();
        if (! $(this).hasClass('show')) {
            $(this).closest('.dropdown').find('ul.dropdown-menu').first().removeClass('show');
        } else {
            $(this).closest('.dropdown').find('ul.dropdown-menu').first().addClass('show');
        }
    });
    /**
     * Colorpicker
     */
    $('body').on('change', 'input[type=color]', function(e) {
        if ($(this).closest('.card-body').length) {
            if ($(this).hasClass('background-color')) {
                $(this).closest('.card-body').css('background-color', $(this).val());
            } else if ($(this).hasClass('foreground-color')) {
                $(this).closest('.card-body').css('color', $(this).val());
            }
        }
    });
    /**
     * Language picker
     */
    $('body').on('click touch', '[role=language]', function(e) {
        if (! $(this).closest('li').find('ul').find('li').length) {
            let context = $(this);
            $.ajax({
                url: $(this).attr('href'),
                method: 'POST',
                context: this,
                data: {
                    prefer: 'dropdown'
                }
            })
            .done(function(response) {
                if (typeof response === 'object') {
                    $.each(response, function(key, val) {
                        $(`
                            <li class="nav-item">
                                <a href="${config.base_url}xhr/language/${val.code}" class="nav-link --xhr">
                                    <i class="mdi mdi-translate"></i> <span>${val.language}</span>
                                </a>
                            </li>
                        `)
                        .appendTo(context.closest('li').find('ul'))
                    });
                }
            })
            .fail(function(response, textStatus, errorThrown) {
                if (textStatus === 'abort') {
                    return;
                }
    
                if (typeof response.responseJSON !== 'undefined') {
                    // Get response JSON
                    response = response.responseJSON;
                }
    
                return throw_exception(response.code, response?.message);
            })
        }
    });
    /**
     * Notification picker
     */
    $('body').on('click touch', '[role=notifications]', function(e) {
        let context = $(this);
        $.ajax({
            url: $(this).attr('href'),
            method: 'POST',
            context: this,
            data: {
                prefer: 'dropdown'
            },
            beforeSend: function() {
                context.closest('li').find('ul').html('')
            }
        })
        .done(function(response) {
            if (typeof response === 'object') {
                context.closest('li').find('ul').css('minWidth', 340);
                context.closest('li').find('ul').html(`
                    <li class="nav-item px-3 mb-2 d-none d-md-block">
                        <h4>${phrase('Notifications')}</h4>
                    </li>
                `);
                if (response.length) {
                    $.each(response, function(key, val) {
                        $(`
                            <li class="nav-item px-2 mb-2">
                                <a href="${val.url}" class="nav-link rounded --xhr" target="${val.target}">
                                    <div class="row g-0">
                                        <div class="col-2">
                                            <div class="position-relative">
                                                <i class="mdi ${('comment' === val.type ? 'mdi-comment-processing bg-success' : ('reply' === val.type ? 'mdi-reply bg-dark' : ('like' === val.type ? 'mdi-thumb-up bg-primary' : ('upvote' === val.type ? 'mdi-arrow-up-circle bg-info' : 'mdi-heart bg-danger'))))} text-light px-1 rounded-circle gradient position-absolute end-0 bottom-0"></i>
                                                <img src="${val.avatar}" class="rounded-circle img-fluid" />
                                            </div>
                                        </div>
                                        <div class="col-10 ps-2">
                                            <p class="mb-0">
                                                <b>${val.user}</b> ${val.text}
                                            </p>
                                            <p class="mb-0 text-muted">
                                                ${val.timestamp}
                                            </p>
                                        </div>
                                    </div>
                                </a>
                            </li>
                        `)
                        .appendTo(context.closest('li').find('ul'))
                    });
                } else {
                    $(`
                        <li class="nav-item px-3 mb-2">
                            <div class="alert alert-warning callout">
                                ${phrase('You have no notification at the moment.')}
                            </div>
                        </li>
                    `)
                    .appendTo(context.closest('li').find('ul'))
                }
            }
        })
        .fail(function(response, textStatus, errorThrown) {
            if (textStatus === 'abort') {
                return;
            }
            if (typeof response.responseJSON !== 'undefined') {
                // Get response JSON
                response = response.responseJSON;
            }
            return throw_exception(response.code, response?.message);
        })
    });
    /**
     * Share button
     */
    $('body').on('click touch', '[role=share]', function(e) {
        const context = $(this);
        if (! context.data('href')) return;
        xhr = $.ajax({
            url: config.base_url + context.data('href'),
            method: 'POST',
            data: context.data(),
            context: this
        })
        .done(function(response) {
            if (navigator.share && typeof response.meta !== 'undefined' && typeof response.links !== 'undefined') {
                navigator.share({
                    text: response.meta.title,
                    url: response.links.current_page
                }).then(() => {
                    console.log('Thanks for sharing!');
                })
                .catch(console.error);
            } else {
                alert('This browser doesn\'t support the Web Share API')
            }
        })
    });
    /**
     * Password peek
     */
    $('body').on('click touch', '.password-peek', function(e) {
        e.preventDefault();
        $(this).toggleClass('peek');
        if ($(this).hasClass('peek')) {
            $(this).removeClass('mdi-eye-outline').addClass('mdi-eye-off-outline').closest($(this).attr('data-parent')).find($(this).attr('data-peek')).attr('type', 'text');
        } else {
            $(this).removeClass('mdi-eye-off-outline').addClass('mdi-eye-outline').closest($(this).attr('data-parent')).find($(this).attr('data-peek')).attr('type', 'password');
        }
    });
});
 |