/*
 * The loading indicator.
 *
 * Displays the animated loading indicator at the top of the page.
 *
 * JavaScript API:
 * $.ti.loadingIndicator.show()
 * $.ti.loadingIndicator.hide()
 *
 * By default if the show() method has been called several times, the hide() method should be
 * called the same number of times in order to hide the card. Use hide(true) to hide the
 * indicator forcibly.
 */
+function ($) {
    "use strict"

    if ($.ti === undefined)
        $.ti = {}

    const LOADER_CLASS = 'ti-loading',
        LOADER_MARGIN = 12.5,
        LOADER_LEFT_MARGIN = LOADER_MARGIN / 100,
        LOADER_RIGHT_MARGIN = 1-LOADER_LEFT_MARGIN;


    var LoadingIndicator = function () {
        var self = this
        this.timeout = undefined
        this.counter = 0
        this.progress = 0
        this.indicator = $('<div/>').addClass('bar-loading-indicator loaded')
            .append($('<div />').addClass('bar'))
            .append($('<div />').addClass('bar-loaded'))
        this.bar = this.indicator.find('.bar')
        this.bar.html('<div class="peg"></div>')

        $(document).ready(function () {
            $(document.body).append(self.indicator)
        })
    }

    LoadingIndicator.barTemplate = [
        '<div class="bar" role="bar">',
        '<div class="peg"></div>',
        '</div>',
    ].join('\n')

    LoadingIndicator.prototype.show = function () {
        this.counter++

        // Restart the animation
        this.bar.after(this.bar = this.bar.clone()).remove()

        if (this.counter > 1)
            return

        this.progress = LOADER_LEFT_MARGIN
        this.indicator.removeClass('loaded')
        $(document.body).addClass('ti-loading')

        this.bar.animate({translateX: '0%'}, 0)

        var self = this
        setTimeout(function () {
            self.animate()
            self.trickle()
        }, 0)
    }

    LoadingIndicator.prototype.hide = function (force) {
        this.counter--
        if (force !== undefined && force)
            this.counter = 0

        if (this.counter <= 0) {
            this.indicator.addClass('loaded')
            $(document.body).removeClass('ti-loading')
        }
    }

    LoadingIndicator.prototype.animate = function () {
        this.indicator.animate({translateX: this.progress * 100+'%'}, 200)
    }

    LoadingIndicator.prototype.clear = function () {
        if (this.timeout) clearTimeout(this.timeout)
        this.timeout = undefined
    }

    LoadingIndicator.prototype.trickle = function () {
        var self = this
        this.timeout = setTimeout(function () {
            self.increment((LOADER_RIGHT_MARGIN-self.progress) * .035 * Math.random())
            self.trickle()
        }, 350+(400 * Math.random()))
    }

    LoadingIndicator.prototype.increment = function (amount) {
        if (this.progress < LOADER_RIGHT_MARGIN) this.progress += amount || .05
        this.animate()
    }

    $.ti.loadingIndicator = new LoadingIndicator()

    // BAR LOAD INDICATOR DATA-API
    // ==============

    $(document)
        .on('ajaxPromise', '[data-request]', function (event) {
            // Prevent this event from bubbling up to a non-related data-request
            // element, for example a <form> tag wrapping a <button> tag
            event.stopPropagation()

            $.ti.loadingIndicator.show()

            // This code will cover instances where the element has been removed
            // from the DOM, making the resolution event below an orphan.
            var $el = $(this)
            $(window).one('ajaxUpdateComplete', function () {
                if ($el.closest('html').length === 0)
                    $.ti.loadingIndicator.hide()
            })
        })
        .on('ajaxFail ajaxDone', '[data-request]', function (event) {
            event.stopPropagation()
            $.ti.loadingIndicator.hide()
        })

    // BUTTON LOAD INDICATOR DATA-API
    // ==============

    $(document)
        .on('ajaxPromise', '[data-request]', function () {
            var $target = $(this)

            if ($target.data('attach-loading') !== undefined) {
                attachLoadingToggleClass($target, true)
            }

            if ($target.is('form')) {
                attachLoadingToggleClass($('[data-attach-loading]', $target), true)
                replaceLoadingToggleClass($('[data-replace-loading]', $target), true)
            }

            if ($target.data('replace-loading') !== undefined) {
                replaceLoadingToggleClass($target, true)
            }
        })
        .on('ajaxFail ajaxDone', '[data-request]', function () {
            var $target = $(this)

            if ($target.data('attach-loading') !== undefined) {
                attachLoadingToggleClass($target, false)
            }

            if ($target.is('form')) {
                attachLoadingToggleClass($('[data-attach-loading]', $target), false)
                replaceLoadingToggleClass($('[data-replace-loading]', $target), false)
            }

            if ($target.data('replace-loading') !== undefined) {
                replaceLoadingToggleClass($target, false)
            }
        })

    function attachLoadingToggleClass($el, show) {
        if (!$el || !$el.length)
            return;

        var loaderClass = $el.data('attach-loading').length ? $el.data('attach-loading') : LOADER_CLASS

        if (show === true) {
            $el.addClass(loaderClass)
                .prop('disabled', true)
        } else {
            $el.removeClass(loaderClass)
                .prop('disabled', false)
        }
    }

    function replaceLoadingToggleClass($el, show) {
        if (!$el || !$el.length)
            return;

        var loaderClass = $el.data('replace-loading').length ? $el.data('replace-loading') : LOADER_CLASS

        if (show === true) {
            $el.children().wrapAll('<div class="replace-loading-bk d-none"></div>')
            $el.find('.replace-loading-bk').before('<i class="replace-loading '+loaderClass+'"></i>')
            $el.prop('disabled', true)
        } else {
            $el.find('.replace-loading').remove()
            $el.find('.replace-loading-bk').children().unwrap()
            $el.prop('disabled', false)
        }
    }
}(window.jQuery);
