/***************************************************************************************************
LoadingOverlay - A flexible loading overlay jQuery plugin
    Author          : Gaspare Sganga
    Version         : 2.1.6
    License         : MIT
    Documentation   : https://gasparesganga.com/labs/jquery-loading-overlay/
***************************************************************************************************/
;(function(factory){
    if (typeof define === "function" && define.amd) {
        // AMD. Register as an anonymous module
        define(["jquery"], factory);
    } else if (typeof module === "object" && module.exports) {
        // Node/CommonJS
        factory(require("jquery"));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function($, undefined){
    "use strict";
    
    // Default Settings
    var _defaults = {
        // Background
        background              : "rgba(255, 255, 255, 0.8)",
        backgroundClass         : "",
        // Image
        image                   : "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 1000'><circle r='80' cx='500' cy='90'/><circle r='80' cx='500' cy='910'/><circle r='80' cx='90' cy='500'/><circle r='80' cx='910' cy='500'/><circle r='80' cx='212' cy='212'/><circle r='80' cx='788' cy='212'/><circle r='80' cx='212' cy='788'/><circle r='80' cx='788' cy='788'/></svg>",
        imageAnimation          : "2000ms rotate_right",
        imageAutoResize         : true,
        imageResizeFactor       : 1,
        imageColor              : "#202020",
        imageClass              : "",
        imageOrder              : 1,
        // Font Awesome
        fontawesome             : "",
        fontawesomeAnimation    : "",
        fontawesomeAutoResize   : true,
        fontawesomeResizeFactor : 1,
        fontawesomeColor        : "#202020",
        fontawesomeOrder        : 2,
        // Custom
        custom                  : "",
        customAnimation         : "",
        customAutoResize        : true,
        customResizeFactor      : 1,
        customOrder             : 3,
        // Text
        text                    : "",
        textAnimation           : "",
        textAutoResize          : true,
        textResizeFactor        : 0.5,
        textColor               : "#202020",
        textClass               : "",
        textOrder               : 4,
        // Progress
        progress                : false,
        progressAutoResize      : true,
        progressResizeFactor    : 0.25,
        progressColor           : "#a0a0a0",
        progressClass           : "",
        progressOrder           : 5,
        progressFixedPosition   : "",
        progressSpeed           : 200,
        progressMin             : 0,
        progressMax             : 100,
        // Sizing
        size                    : 50,
        maxSize                 : 120,
        minSize                 : 20,
        // Misc
        direction               : "column",
        fade                    : true,
        resizeInterval          : 50,
        zIndex                  : 2147483647
    };
    
    // Required CSS
    var _css = {
        overlay : {
            "box-sizing"        : "border-box",
            "position"          : "relative",
            "display"           : "flex",
            "flex-wrap"         : "nowrap",
            "align-items"       : "center",
            "justify-content"   : "space-around"
        },
        element : {
            "box-sizing"        : "border-box",
            "overflow"          : "visible",
            "flex"              : "0 0 auto",
            "display"           : "flex",
            "justify-content"   : "center",
            "align-items"       : "center"
        },
        element_svg : {
            "width"             : "100%",
            "height"            : "100%"
        },
        progress_fixed : {
            "position"          : "absolute",
            "left"              : "0",
            "width"             : "100%"
        },
        progress_wrapper : {
            "position"          : "absolute",
            "top"               : "0",
            "left"              : "0",
            "width"             : "100%",
            "height"            : "100%"
        },
        progress_bar : {
            "position"          : "absolute",
            "left"              : "0"
        }
    };
    
    // Data Template
    var _dataTemplate = {
        "count"             : 0,
        "container"         : undefined,
        "settings"          : undefined,
        "wholePage"         : undefined,
        "resizeIntervalId"  : undefined,
        "text"              : undefined,
        "progress"          : undefined
    };
    
    // Whitelists
    var _whitelists = {
        animations : [
            "rotate_right",
            "rotate_left",
            "fadein",
            "pulse"
        ],
        progressPosition : [
            "top",
            "bottom"
        ]
    };
    
    // Default Values
    var _defaultValues = {
        animations : {
            name    : "rotate_right",
            time    : "2000ms"
        },
        fade : [400, 200]
    };
    
    
    $.LoadingOverlaySetup = function(settings){
        $.extend(true, _defaults, settings);
    };
    
    $.LoadingOverlay = function(action, options){
        switch (action.toLowerCase()) {
            case "show":
                var settings = $.extend(true, {}, _defaults, options);
                Show("body", settings);
                break;
                
            case "hide":
                Hide("body", options);
                break;
                
            case "resize":
                Resize("body", options);
                break;  
                
            case "text":
                Text("body", options);
                break;
                
            case "progress":
                Progress("body", options);
                break;
        }
    };
    
    $.fn.LoadingOverlay = function(action, options){
        switch (action.toLowerCase()) {
            case "show":
                var settings = $.extend(true, {}, _defaults, options);
                return this.each(function(){
                    Show(this, settings);
                });
                
            case "hide":
                return this.each(function(){
                    Hide(this, options);
                });
                
             case "resize":
                return this.each(function(){
                    Resize(this, options);
                });
                
            case "text":
                return this.each(function(){
                    Text(this, options);
                });
                
            case "progress":
                return this.each(function(){
                    Progress(this, options);
                });
        }
    };
    
    
    function Show(container, settings){
        container               = $(container);
        settings.size           = _ParseSize(settings.size);
        settings.maxSize        = parseInt(settings.maxSize, 10) || 0;
        settings.minSize        = parseInt(settings.minSize, 10) || 0;
        settings.resizeInterval = parseInt(settings.resizeInterval, 10) || 0;
        
        var overlay = _GetOverlay(container);
        var data    = _GetData(container);
        if (data === false) {
            // Init data
            data = $.extend({}, _dataTemplate);
            data.container = container;
            data.wholePage = container.is("body");
            
            // Overlay
            overlay = $("<div>", {
                "class" : "loadingoverlay"
            })
            .css(_css.overlay)
            .css("flex-direction", settings.direction.toLowerCase() === "row" ? "row" : "column");
            if (settings.backgroundClass) {
                overlay.addClass(settings.backgroundClass);
            } else {
                overlay.css("background", settings.background);
            }
            if (data.wholePage) {
                overlay.css({
                    "position"  : "fixed",
                    "top"       : 0,
                    "left"      : 0,
                    "width"     : "100%",
                    "height"    : "100%"
                });
            }
            if (typeof settings.zIndex !== "undefined") overlay.css("z-index", settings.zIndex);
            
            // Image
            if (settings.image) {
                if ($.isArray(settings.imageColor)) {
                    if (settings.imageColor.length === 0) {
                        settings.imageColor = false;
                    } else if (settings.imageColor.length  === 1) {
                        settings.imageColor = {
                            "fill"  : settings.imageColor[0]
                        };
                    } else {
                        settings.imageColor = {
                            "fill"      : settings.imageColor[0],
                            "stroke"    : settings.imageColor[1]
                        };
                    }
                } else if (settings.imageColor) {
                    settings.imageColor = {
                        "fill"  : settings.imageColor
                    };
                }
                var element = _CreateElement(overlay, settings.imageOrder, settings.imageAutoResize, settings.imageResizeFactor, settings.imageAnimation);
                if (settings.image.slice(0, 4).toLowerCase() === "<svg" && settings.image.slice(-6).toLowerCase() === "</svg>") {
                    // Inline SVG
                    element.append(settings.image);
                    element.children().css(_css.element_svg);
                    if (!settings.imageClass && settings.imageColor) element.find("*").css(settings.imageColor);
                } else if (settings.image.slice(-4).toLowerCase() === ".svg" || settings.image.slice(0, 14).toLowerCase() === "data:image/svg") {
                    // SVG file or base64-encoded SVG
                    $.ajax({
                        url         : settings.image,
                        type        : "GET",
                        dataType    : "html",
                        global      : false
                    }).done(function(data){
                        element.html(data);
                        element.children().css(_css.element_svg);
                        if (!settings.imageClass && settings.imageColor) element.find("*").css(settings.imageColor);
                    });
                } else {
                    // Raster
                    element.css({
                        "background-image"      : "url(" + settings.image + ")",
                        "background-position"   : "center",
                        "background-repeat"     : "no-repeat",
                        "background-size"       : "cover"
                    });
                }
                if (settings.imageClass) element.addClass(settings.imageClass);
            }
            
            // Font Awesome
            if (settings.fontawesome) {
                var element = _CreateElement(overlay, settings.fontawesomeOrder, settings.fontawesomeAutoResize, settings.fontawesomeResizeFactor, settings.fontawesomeAnimation)
                    .addClass("loadingoverlay_fa");
                $("<div>", {
                    "class" : settings.fontawesome
                }).appendTo(element);
                if (settings.fontawesomeColor) element.css("color", settings.fontawesomeColor);
            }
            
            // Custom
            if (settings.custom) {
                var element = _CreateElement(overlay, settings.customOrder, settings.customAutoResize, settings.customResizeFactor, settings.customAnimation)
                    .append(settings.custom);
            }
            
            // Text
            if (settings.text) {
                data.text = _CreateElement(overlay, settings.textOrder, settings.textAutoResize, settings.textResizeFactor, settings.textAnimation)
                        .addClass("loadingoverlay_text")
                        .text(settings.text);
                if (settings.textClass) {
                    data.text.addClass(settings.textClass);
                } else if (settings.textColor) {
                    data.text.css("color", settings.textColor);
                }
            }
            
            // Progress
            if (settings.progress) {
                var element = _CreateElement(overlay, settings.progressOrder, settings.progressAutoResize, settings.progressResizeFactor, false)
                    .addClass("loadingoverlay_progress");
                var wrapper = $("<div>")
                    .css(_css.progress_wrapper)
                    .appendTo(element);
                data.progress = {
                    bar     : $("<div>").css(_css.progress_bar).appendTo(wrapper),
                    fixed   : false,
                    margin  : 0,
                    min     : parseFloat(settings.progressMin),
                    max     : parseFloat(settings.progressMax),
                    speed   : parseInt(settings.progressSpeed, 10)
                };
                var progressPositionParts = (settings.progressFixedPosition + "").replace(/\s\s+/g, " ").toLowerCase().split(" ");
                if (progressPositionParts.length === 2 && _ValidateProgressPosition(progressPositionParts[0])) {
                    data.progress.fixed     = progressPositionParts[0];
                    data.progress.margin    = _ParseSize(progressPositionParts[1]);
                } else if (progressPositionParts.length === 2 && _ValidateProgressPosition(progressPositionParts[1])) {
                    data.progress.fixed     = progressPositionParts[1];
                    data.progress.margin    = _ParseSize(progressPositionParts[0]);
                } else if (progressPositionParts.length === 1 && _ValidateProgressPosition(progressPositionParts[0])) {
                    data.progress.fixed     = progressPositionParts[0];
                    data.progress.margin    = 0;
                }
                if (data.progress.fixed === "top") {
                    element
                        .css(_css.progress_fixed)
                        .css("top", data.progress.margin ? data.progress.margin.value + (data.progress.margin.fixed ? data.progress.margin.units : "%") : 0);
                } else if (data.progress.fixed === "bottom") {
                    element
                        .css(_css.progress_fixed)
                        .css("top", "auto");
                }
                if (settings.progressClass) {
                    data.progress.bar.addClass(settings.progressClass);
                } else if (settings.progressColor) {
                    data.progress.bar.css("background", settings.progressColor);
                } 
            }
            
            // Fade
            if (!settings.fade) {
                settings.fade = [0, 0];
            } else if (settings.fade === true) {
                settings.fade = _defaultValues.fade;
            } else if (typeof settings.fade === "string" || typeof settings.fade === "number") {
                settings.fade = [settings.fade, settings.fade];
            } else if ($.isArray(settings.fade) && settings.fade.length < 2) {
                settings.fade = [settings.fade[0], settings.fade[0]];
            }
            settings.fade = [parseInt(settings.fade[0], 10), parseInt(settings.fade[1], 10)]
            
            
            // Save settings
            data.settings = settings;
            // Save data
            overlay.data("loadingoverlay_data", data);
            // Save reference to overlay
            container.data("loadingoverlay", overlay);
            
            
            // Resize
            overlay
                .fadeTo(0, 0.01)
                .appendTo("body");
            _IntervalResize(container, true);
            if (settings.resizeInterval > 0) {
                data.resizeIntervalId = setInterval(function(){
                    _IntervalResize(container, false);
                }, settings.resizeInterval);
            }
            
            // Show LoadingOverlay
            overlay.fadeTo(settings.fade[0], 1);
        }
        data.count++;
    }
    
    function Hide(container, force){
        container   = $(container);
        var overlay = _GetOverlay(container);
        var data    = _GetData(container);
        if (data === false) return;
        
        data.count--;
        if (force || data.count <= 0) {
            overlay.animate({
                "opacity"   : 0
            }, data.settings.fade[1], function(){
                if (data.resizeIntervalId) clearInterval(data.resizeIntervalId);
                $(this).remove();
                container.removeData("loadingoverlay");
            });
        }
    }
    
    function Resize(container){
        _IntervalResize($(container), true);
    }
    
    function Text(container, value){
        container   = $(container);
        var data    = _GetData(container);
        if (data === false || !data.text) return;
        
        if (value === false) {
            data.text.hide();
        } else {
            data.text
                    .show()
                    .text(value);
        }
    }
    
    function Progress(container, value){
        container   = $(container);
        var data    = _GetData(container);
        if (data === false || !data.progress) return;
        
        if (value === false) {
            data.progress.bar.hide();
        } else {
            var v = ((parseFloat(value) || 0) - data.progress.min) * 100 / (data.progress.max - data.progress.min);
            if (v < 0)   v = 0;
            if (v > 100) v = 100;
            data.progress.bar
                    .show()
                    .animate({
                        "width" : v + "%"
                    }, data.progress.speed);
        }
    }
    
    
    function _IntervalResize(container, force){
        var overlay = _GetOverlay(container);
        var data    = _GetData(container);
        if (data === false) return;
        
        // Overlay
        if (!data.wholePage) {
            var isFixed = container.css("position") === "fixed";
            var pos     = isFixed ? container[0].getBoundingClientRect() : container.offset();            
            overlay.css({
                "position"  : isFixed ? "fixed" : "absolute",
                "top"       : pos.top + parseInt(container.css("border-top-width"), 10),
                "left"      : pos.left + parseInt(container.css("border-left-width"), 10),
                "width"     : container.innerWidth(),
                "height"    : container.innerHeight()
            });
        }
        
        // Elements
        if (data.settings.size) {
            var c    = data.wholePage ? $(window) : container;
            var size = data.settings.size.value;
            if (!data.settings.size.fixed) {
                size = Math.min(c.innerWidth(), c.innerHeight()) * size / 100;
                if (data.settings.maxSize && size > data.settings.maxSize) size = data.settings.maxSize;
                if (data.settings.minSize && size < data.settings.minSize) size = data.settings.minSize;
            }
            overlay.children(".loadingoverlay_element").each(function(){
                var $this = $(this);
                if (force || $this.data("loadingoverlay_autoresize")) {
                    var resizeFactor = $this.data("loadingoverlay_resizefactor");
                    if ($this.hasClass("loadingoverlay_fa") || $this.hasClass("loadingoverlay_text")) {
                        $this.css("font-size", (size * resizeFactor) + data.settings.size.units);
                    } else if ($this.hasClass("loadingoverlay_progress")) {
                        data.progress.bar.css("height", (size * resizeFactor) + data.settings.size.units);
                        if (!data.progress.fixed) {
                            data.progress.bar
                                .css("top", $this.position().top)
                                .css("top", "-=" + (size * resizeFactor * 0.5) + data.settings.size.units);
                        } else if (data.progress.fixed === "bottom") {
                            $this
                                .css("bottom", data.progress.margin ? data.progress.margin.value + (data.progress.margin.fixed ? data.progress.margin.units : "%") : 0)
                                .css("bottom", "+=" + (size * resizeFactor) + data.settings.size.units);
                        }
                    } else {
                        $this.css({
                            "width"  : (size * resizeFactor) + data.settings.size.units,
                            "height" : (size * resizeFactor) + data.settings.size.units
                        });
                    }
                }
            });
        }
    }
    
    
    function _GetOverlay(container){
        return container.data("loadingoverlay");
    }
    
    function _GetData(container){
        var overlay = _GetOverlay(container);
        var data    = (typeof overlay === "undefined") ? undefined : overlay.data("loadingoverlay_data");
        if (typeof data === "undefined") {
            // Clean DOM
            $(".loadingoverlay").each(function(){
                var $this   = $(this);
                var data    = $this.data("loadingoverlay_data");
                if (!document.body.contains(data.container[0])) {
                    if (data.resizeIntervalId) clearInterval(data.resizeIntervalId);
                    $this.remove();
                }
            });
            return false;
        } else {
            overlay.toggle(container.is(":visible"));
            return data;
        }
    }
    
    
    function _CreateElement(overlay, order, autoResize, resizeFactor, animation){
        var element = $("<div>", {
            "class" : "loadingoverlay_element",
            "css"   : {
                "order" : order
            }
        })
        .css(_css.element)
        .data({
            "loadingoverlay_autoresize"     : autoResize,
            "loadingoverlay_resizefactor"   : resizeFactor
        })
        .appendTo(overlay);
        
        // Parse animation
        if (animation === true) animation = _defaultValues.animations.time + " " + _defaultValues.animations.name;
        if (typeof animation === "string") {
            var animationName;
            var animationTime;
            var parts = animation.replace(/\s\s+/g, " ").toLowerCase().split(" ");
            if (parts.length === 2 && _ValidateCssTime(parts[0]) && _ValidateAnimation(parts[1])) {
                animationName = parts[1];
                animationTime = parts[0];
            } else if (parts.length === 2 && _ValidateCssTime(parts[1]) && _ValidateAnimation(parts[0])) {
                animationName = parts[0];
                animationTime = parts[1];
            } else if (parts.length === 1 && _ValidateCssTime(parts[0])) {
                animationName = _defaultValues.animations.name;
                animationTime = parts[0];
            } else if (parts.length === 1 && _ValidateAnimation(parts[0])) {
                animationName = parts[0];
                animationTime = _defaultValues.animations.time;
            }
            element.css({
                "animation-name"            : "loadingoverlay_animation__" + animationName,
                "animation-duration"        : animationTime,
                "animation-timing-function" : "linear",
                "animation-iteration-count" : "infinite"
            });
        }
        
        return element;
    }
    
    function _ValidateCssTime(value){
        return !isNaN(parseFloat(value)) && (value.slice(-1) === "s" || value.slice(-2) === "ms");
    }
    
    function _ValidateAnimation(value){
        return _whitelists.animations.indexOf(value) > -1;
    }
    
    function _ValidateProgressPosition(value){
        return _whitelists.progressPosition.indexOf(value) > -1;
    }
    
    
    function _ParseSize(value){
        if (!value || value < 0) {
            return false;
        } else if (typeof value === "string" && ["vmin", "vmax"].indexOf(value.slice(-4)) > -1) {
            return {
                fixed   : true,
                units   : value.slice(-4),
                value   : value.slice(0, -4)
            };
        } else if (typeof value === "string" && ["rem"].indexOf(value.slice(-3)) > -1) {
            return {
                fixed   : true,
                units   : value.slice(-3),
                value   : value.slice(0, -3)
            };
        } else if (typeof value === "string" && ["px", "em", "cm", "mm", "in", "pt", "pc", "vh", "vw"].indexOf(value.slice(-2)) > -1) {
            return {
                fixed   : true,
                units   : value.slice(-2),
                value   : value.slice(0, -2)
            };
        } else {
            return {
                fixed   : false,
                units   : "px",
                value   : parseFloat(value)
            }; 
        }
    }
    
    
    $(function(){
        $("head").append([
            "<style>",
                "@-webkit-keyframes loadingoverlay_animation__rotate_right {",
                  "to {",
                    "-webkit-transform : rotate(360deg);",
                            "transform : rotate(360deg);",
                  "}",
                "}",
                "@keyframes loadingoverlay_animation__rotate_right {",
                  "to {",
                    "-webkit-transform : rotate(360deg);",
                            "transform : rotate(360deg);",
                  "}",
                "}",
                
                "@-webkit-keyframes loadingoverlay_animation__rotate_left {",
                  "to {",
                    "-webkit-transform : rotate(-360deg);",
                            "transform : rotate(-360deg);",
                  "}",
                "}",
                "@keyframes loadingoverlay_animation__rotate_left {",
                  "to {",
                    "-webkit-transform : rotate(-360deg);",
                            "transform : rotate(-360deg);",
                  "}",
                "}",
                
                "@-webkit-keyframes loadingoverlay_animation__fadein {",
                  "0% {",
                            "opacity   : 0;",
                    "-webkit-transform : scale(0.1, 0.1);",
                            "transform : scale(0.1, 0.1);",
                  "}",
                  "50% {",
                            "opacity   : 1;",
                  "}",
                  "100% {",
                            "opacity   : 0;",
                    "-webkit-transform : scale(1, 1);",
                            "transform : scale(1, 1);",
                  "}",
                "}",
                "@keyframes loadingoverlay_animation__fadein {",
                  "0% {",
                            "opacity   : 0;",
                    "-webkit-transform : scale(0.1, 0.1);",
                            "transform : scale(0.1, 0.1);",
                  "}",
                  "50% {",
                            "opacity   : 1;",
                  "}",
                  "100% {",
                            "opacity   : 0;",
                    "-webkit-transform : scale(1, 1);",
                            "transform : scale(1, 1);",
                  "}",
                "}",
                
                "@-webkit-keyframes loadingoverlay_animation__pulse {",
                  "0% {",
                    "-webkit-transform : scale(0, 0);",
                            "transform : scale(0, 0);",
                  "}",
                  "50% {",
                    "-webkit-transform : scale(1, 1);",
                            "transform : scale(1, 1);",
                  "}",
                  "100% {",
                    "-webkit-transform : scale(0, 0);",
                            "transform : scale(0, 0);",
                  "}",
                "}",
                "@keyframes loadingoverlay_animation__pulse {",
                  "0% {",
                    "-webkit-transform : scale(0, 0);",
                            "transform : scale(0, 0);",
                  "}",
                  "50% {",
                    "-webkit-transform : scale(1, 1);",
                            "transform : scale(1, 1);",
                  "}",
                  "100% {",
                    "-webkit-transform : scale(0, 0);",
                            "transform : scale(0, 0);",
                  "}",
                "}",
            "</style>"
        ].join(" "));
    });
    
}));