// SweetAlert
// 2014 (c) - Tristan Edwards
// github.com/t4t5/sweetalert
(function (window, document) {

    var modalClass = '.sweet-alert',
        overlayClass = '.sweet-overlay',
        alertTypes = ['error', 'warning', 'info', 'success'],
        defaultParams = {
            title: '',
            text: '',
            type: null,
            allowOutsideClick: false,
            showCancelButton: false,
            showConfirmButton: true,
            closeOnConfirm: true,
            closeOnCancel: true,
            confirmButtonText: 'OK',
            confirmButtonClass: 'btn-primary',
            cancelButtonText: 'Cancel',
            cancelButtonClass: 'btn-default',
            containerClass: '',
            titleClass: '',
            textClass: '',
            imageUrl: null,
            imageSize: null,
            timer: null
        };


    /*
     * Manipulate DOM
     */

    var getModal = function () {
            return document.querySelector(modalClass);
        },
        getOverlay = function () {
            return document.querySelector(overlayClass);
        },
        hasClass = function (elem, className) {
            return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
        },
        addClass = function (elem, className) {
            if (className && !hasClass(elem, className)) {
                elem.className += ' ' + className;
            }
        },
        removeClass = function (elem, className) {
            var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' ';
            if (hasClass(elem, className)) {
                while (newClass.indexOf(' ' + className + ' ') >= 0) {
                    newClass = newClass.replace(' ' + className + ' ', ' ');
                }
                elem.className = newClass.replace(/^\s+|\s+$/g, '');
            }
        },
        escapeHtml = function (str) {
            var div = document.createElement('div');
            div.appendChild(document.createTextNode(str));
            return div.innerHTML;
        },
        _show = function (elem) {
            elem.style.opacity = '';
            elem.style.display = 'block';
        },
        show = function (elems) {
            if (elems && !elems.length) {
                return _show(elems);
            }
            for (var i = 0; i < elems.length; ++i) {
                _show(elems[i]);
            }
        },
        _hide = function (elem) {
            elem.style.opacity = '';
            elem.style.display = 'none';
        },
        hide = function (elems) {
            if (elems && !elems.length) {
                return _hide(elems);
            }
            for (var i = 0; i < elems.length; ++i) {
                _hide(elems[i]);
            }
        },
        isDescendant = function (parent, child) {
            var node = child.parentNode;
            while (node !== null) {
                if (node === parent) {
                    return true;
                }
                node = node.parentNode;
            }
            return false;
        },
        getTopMargin = function (elem) {
            elem.style.left = '-9999px';
            elem.style.display = 'block';

            var height = elem.clientHeight;
            var padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10);

            elem.style.left = '';
            elem.style.display = 'none';
            return ('-' + parseInt(height / 2 + padding) + 'px');
        },
        fadeIn = function (elem, interval) {
            if (+elem.style.opacity < 1) {
                interval = interval || 16;
                elem.style.opacity = 0;
                elem.style.display = 'block';
                var last = +new Date();
                var tick = function () {
                    elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100;
                    last = +new Date();

                    if (+elem.style.opacity < 1) {
                        setTimeout(tick, interval);
                    }
                };
                tick();
            }
        },
        fadeOut = function (elem, interval) {
            interval = interval || 16;
            elem.style.opacity = 1;
            var last = +new Date();
            var tick = function () {
                elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100;
                last = +new Date();

                if (+elem.style.opacity > 0) {
                    setTimeout(tick, interval);
                } else {
                    elem.style.display = 'none';
                }
            };
            tick();
        },
        fireClick = function (node) {
            // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
            // Then fixed for today's Chrome browser.
            if (MouseEvent) {
                // Up-to-date approach
                var mevt = new MouseEvent('click', {
                    view: window,
                    bubbles: false,
                    cancelable: true
                });
                node.dispatchEvent(mevt);
            } else if (document.createEvent) {
                // Fallback
                var evt = document.createEvent('MouseEvents');
                evt.initEvent('click', false, false);
                node.dispatchEvent(evt);
            } else if (document.createEventObject) {
                node.fireEvent('onclick');
            } else if (typeof node.onclick === 'function') {
                node.onclick();
            }
        },
        stopEventPropagation = function (e) {
            // In particular, make sure the space bar doesn't scroll the main window.
            if (typeof e.stopPropagation === 'function') {
                e.stopPropagation();
                e.preventDefault();
            } else if (window.event && window.event.hasOwnProperty('cancelBubble')) {
                window.event.cancelBubble = true;
            }
        };

    // Remember state in cases where opening and handling a modal will fiddle with it.
    var previousActiveElement,
        previousDocumentClick,
        previousWindowKeyDown,
        lastFocusedButton;

    /*
     * Add modal + overlay to DOM
     */

    window.sweetAlertInitialize = function () {
        var sweetHTML = '<div class="sweet-overlay" tabIndex="-1"></div><div class="sweet-alert" tabIndex="-1"><div class="icon error"><span class="x-mark"><span class="line left"></span><span class="line right"></span></span></div><div class="icon warning"> <span class="body"></span> <span class="dot"></span> </div> <div class="icon info"></div> <div class="icon success"> <span class="line tip"></span> <span class="line long"></span> <div class="placeholder"></div> <div class="fix"></div> </div> <div class="icon custom"></div> <h2>Title</h2><p class="lead text-muted">Text</p><p><button class="cancel btn btn-lg" tabIndex="2">Cancel</button> <button class="confirm btn btn-lg" tabIndex="1">OK</button></p></div>',
            sweetWrap = document.createElement('div');

        sweetWrap.innerHTML = sweetHTML;

        // For readability: check sweet-alert.html
        document.body.appendChild(sweetWrap);

        // For development use only!
        /*jQuery.ajax({
         url: '../lib/sweet-alert.html', // Change path depending on file location
         dataType: 'html'
         })
         .done(function(html) {
         jQuery('body').append(html);
         });*/
    }

    /*
     * Global sweetAlert function
     */

    window.sweetAlert = window.swal = function () {
        if (arguments[0] === undefined) {
            window.console.error('sweetAlert expects at least 1 attribute!');
            return false;
        }

        var params = extend({}, defaultParams);

        switch (typeof arguments[0]) {

            case 'string':
                params.title = arguments[0];
                params.text = arguments[1] || '';
                params.type = arguments[2] || '';
                params.confirmButtonClass = params.type ? 'btn-' + params.type : defaultParams.confirmButtonClass;

                break;

            case 'object':
                if (arguments[0].title === undefined) {
                    window.console.error('Missing "title" argument!');
                    return false;
                }

                params.title = arguments[0].title;
                params.text = arguments[0].text || defaultParams.text;
                params.type = arguments[0].type || defaultParams.type;
                params.allowOutsideClick = arguments[0].allowOutsideClick || defaultParams.allowOutsideClick;
                params.showCancelButton = arguments[0].showCancelButton !== undefined ? arguments[0].showCancelButton : defaultParams.showCancelButton;
                params.showConfirmButton = arguments[0].showConfirmButton !== undefined ? arguments[0].showConfirmButton : defaultParams.showConfirmButton;
                params.closeOnConfirm = arguments[0].closeOnConfirm !== undefined ? arguments[0].closeOnConfirm : defaultParams.closeOnConfirm;
                params.closeOnCancel = arguments[0].closeOnCancel !== undefined ? arguments[0].closeOnCancel : defaultParams.closeOnCancel;
                params.timer = arguments[0].timer || defaultParams.timer;

                // Show "Confirm" instead of "OK" if cancel button is visible
                params.confirmButtonText = (defaultParams.showCancelButton) ? 'Confirm' : defaultParams.confirmButtonText;
                params.confirmButtonText = arguments[0].confirmButtonText || defaultParams.confirmButtonText;
                params.confirmButtonClass = arguments[0].confirmButtonClass || (arguments[0].type ? 'btn-' + arguments[0].type : null) || defaultParams.confirmButtonClass;
                params.cancelButtonText = arguments[0].cancelButtonText || defaultParams.cancelButtonText;
                params.cancelButtonClass = arguments[0].cancelButtonClass || defaultParams.cancelButtonClass;
                params.containerClass = arguments[0].containerClass || defaultParams.containerClass;
                params.titleClass = arguments[0].titleClass || defaultParams.titleClass;
                params.textClass = arguments[0].textClass || defaultParams.textClass;
                params.imageUrl = arguments[0].imageUrl || defaultParams.imageUrl;
                params.imageSize = arguments[0].imageSize || defaultParams.imageSize;
                params.doneFunction = arguments[1] || null;

                break;

            default:
                window.console.error('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]);
                return false;

        }

        setParameters(params);
        fixVerticalPosition();
        openModal();


        // Modal interactions
        var modal = getModal();

        // Mouse interactions
        var onButtonEvent = function (e) {

            var target = e.target || e.srcElement,
                targetedConfirm = (target.className.indexOf('confirm') > -1),
                modalIsVisible = hasClass(modal, 'visible'),
                doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true');

            switch (e.type) {
                case ("click"):
                    if (targetedConfirm && doneFunctionExists && modalIsVisible) { // Clicked "confirm"

                        params.doneFunction(true);

                        if (params.closeOnConfirm) {
                            closeModal();
                        }
                    } else if (doneFunctionExists && modalIsVisible) { // Clicked "cancel"

                        // Check if callback function expects a parameter (to track cancel actions)
                        var functionAsStr = String(params.doneFunction).replace(/\s/g, '');
                        var functionHandlesCancel = functionAsStr.substring(0, 9) === "function(" && functionAsStr.substring(9, 10) !== ")";

                        if (functionHandlesCancel) {
                            params.doneFunction(false);
                        }

                        if (params.closeOnCancel) {
                            closeModal();
                        }
                    } else {
                        closeModal();
                    }

                    break;
            }
        };

        var $buttons = modal.querySelectorAll('button');
        for (var i = 0; i < $buttons.length; i++) {
            $buttons[i].onclick = onButtonEvent;
        }

        // Remember the current document.onclick event.
        previousDocumentClick = document.onclick;
        document.onclick = function (e) {
            var target = e.target || e.srcElement;

            var clickedOnModal = (modal === target),
                clickedOnModalChild = isDescendant(modal, e.target),
                modalIsVisible = hasClass(modal, 'visible'),
                outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true';

            if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) {
                closeModal();
            }
        };


        // Keyboard interactions
        var $okButton = modal.querySelector('button.confirm'),
            $cancelButton = modal.querySelector('button.cancel'),
            $modalButtons = modal.querySelectorAll('button:not([type=hidden])');


        function handleKeyDown(e) {
            var keyCode = e.keyCode || e.which;

            if ([9, 13, 32, 27].indexOf(keyCode) === -1) {
                // Don't do work on keys we don't care about.
                return;
            }

            var $targetElement = e.target || e.srcElement;

            var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.
            for (var i = 0; i < $modalButtons.length; i++) {
                if ($targetElement === $modalButtons[i]) {
                    btnIndex = i;
                    break;
                }
            }

            if (keyCode === 9) {
                // TAB
                if (btnIndex === -1) {
                    // No button focused. Jump to the confirm button.
                    $targetElement = $okButton;
                } else {
                    // Cycle to the next button
                    if (btnIndex === $modalButtons.length - 1) {
                        $targetElement = $modalButtons[0];
                    } else {
                        $targetElement = $modalButtons[btnIndex + 1];
                    }
                }

                stopEventPropagation(e);
                $targetElement.focus();

            } else {
                if (keyCode === 13 || keyCode === 32) {
                    if (btnIndex === -1) {
                        // ENTER/SPACE clicked outside of a button.
                        $targetElement = $okButton;
                    } else {
                        // Do nothing - let the browser handle it.
                        $targetElement = undefined;
                    }
                } else if (keyCode === 27 && !($cancelButton.hidden || $cancelButton.style.display === 'none')) {
                    // ESC to cancel only if there's a cancel button displayed (like the alert() window).
                    $targetElement = $cancelButton;
                } else {
                    // Fallback - let the browser handle it.
                    $targetElement = undefined;
                }

                if ($targetElement !== undefined) {
                    fireClick($targetElement, e);
                }
            }
        }

        previousWindowKeyDown = window.onkeydown;
        window.onkeydown = handleKeyDown;

        function handleOnBlur(e) {
            var $targetElement = e.target || e.srcElement,
                $focusElement = e.relatedTarget,
                modalIsVisible = hasClass(modal, 'visible');

            if (modalIsVisible) {
                var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.

                if ($focusElement !== null) {
                    // If we picked something in the DOM to focus to, let's see if it was a button.
                    for (var i = 0; i < $modalButtons.length; i++) {
                        if ($focusElement === $modalButtons[i]) {
                            btnIndex = i;
                            break;
                        }
                    }

                    if (btnIndex === -1) {
                        // Something in the dom, but not a visible button. Focus back on the button.
                        $targetElement.focus();
                    }
                } else {
                    // Exiting the DOM (e.g. clicked in the URL bar);
                    lastFocusedButton = $targetElement;
                }
            }
        }

        $okButton.onblur = handleOnBlur;
        $cancelButton.onblur = handleOnBlur;

        window.onfocus = function () {
            // When the user has focused away and focused back from the whole window.
            window.setTimeout(function () {
                // Put in a timeout to jump out of the event sequence. Calling focus() in the event
                // sequence confuses things.
                if (lastFocusedButton !== undefined) {
                    lastFocusedButton.focus();
                    lastFocusedButton = undefined;
                }
            }, 0);
        };
    };

    /**
     * Set default params for each popup
     * @param {Object} userParams
     */
    window.swal.setDefaults = function (userParams) {
        if (!userParams) {
            throw new Error('userParams is required');
        }
        if (typeof userParams !== 'object') {
            throw new Error('userParams has to be a object');
        }

        extend(defaultParams, userParams);
    };

    /**
     * Closes the current modal
     */
    window.swal.close = function () {
        closeModal();
    }

    /*
     * Set type, text and actions on modal
     */

    function setParameters(params) {
        var modal = getModal();

        var $title = modal.querySelector('h2'),
            $text = modal.querySelector('p'),
            $cancelBtn = modal.querySelector('button.cancel'),
            $confirmBtn = modal.querySelector('button.confirm');

        // Title
        $title.innerHTML = escapeHtml(params.title).split("\n").join("<br>");

        // Text
        $text.innerHTML = escapeHtml(params.text || '').split("\n").join("<br>");
        if (params.text) {
            show($text);
        }

        // Icon
        hide(modal.querySelectorAll('.icon'));
        if (params.type) {
            var validType = false;
            for (var i = 0; i < alertTypes.length; i++) {
                if (params.type === alertTypes[i]) {
                    validType = true;
                    break;
                }
            }
            if (!validType) {
                window.console.error('Unknown alert type: ' + params.type);
                return false;
            }
            var $icon = modal.querySelector('.icon.' + params.type);
            show($icon);

            // Animate icon
            switch (params.type) {
                case "success":
                    addClass($icon, 'animate');
                    addClass($icon.querySelector('.tip'), 'animateSuccessTip');
                    addClass($icon.querySelector('.long'), 'animateSuccessLong');
                    break;
                case "error":
                    addClass($icon, 'animateErrorIcon');
                    addClass($icon.querySelector('.x-mark'), 'animateXMark');
                    break;
                case "warning":
                    addClass($icon, 'pulseWarning');
                    addClass($icon.querySelector('.body'), 'pulseWarningIns');
                    addClass($icon.querySelector('.dot'), 'pulseWarningIns');
                    break;
            }

        }

        // Custom image
        if (params.imageUrl) {
            var $customIcon = modal.querySelector('.icon.custom');

            $customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')';
            show($customIcon);

            var _imgWidth = 80,
                _imgHeight = 80;

            if (params.imageSize) {
                var imgWidth = params.imageSize.split('x')[0];
                var imgHeight = params.imageSize.split('x')[1];

                if (!imgWidth || !imgHeight) {
                    window.console.error("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize);
                } else {
                    _imgWidth = imgWidth;
                    _imgHeight = imgHeight;

                    $customIcon.css({
                        'width': imgWidth + 'px',
                        'height': imgHeight + 'px'
                    });
                }
            }
            $customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px');
        }

        // Cancel button
        modal.setAttribute('data-has-cancel-button', params.showCancelButton);
        if (params.showCancelButton) {
            $cancelBtn.style.display = 'inline-block';
        } else {
            hide($cancelBtn);
        }

        //Confirm button
        modal.setAttribute('data-has-confirm-button', params.showConfirmButton);
        if (params.showConfirmButton) {
            $confirmBtn.style.display = 'inline-block';
        } else {
            hide($confirmBtn);
        }


        // Edit text on cancel and confirm buttons
        if (params.cancelButtonText) {
            $cancelBtn.innerHTML = escapeHtml(params.cancelButtonText);
        }
        if (params.confirmButtonText) {
            $confirmBtn.innerHTML = escapeHtml(params.confirmButtonText);
        }

        // Reset confirm buttons to default class (Ugly fix)
        $confirmBtn.className = 'confirm btn btn-lg';

        // Attach selected class to the sweet alert modal
        addClass(modal, params.containerClass);

        // Set confirm button to selected class
        addClass($confirmBtn, params.confirmButtonClass);

        // Set cancel button to selected class
        addClass($cancelBtn, params.cancelButtonClass);

        // Set title to selected class
        addClass($title, params.titleClass);

        // Set text to selected class
        addClass($text, params.textClass);

        // Allow outside click?
        modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick);

        // Done-function
        var hasDoneFunction = (params.doneFunction) ? true : false;
        modal.setAttribute('data-has-done-function', hasDoneFunction);

        // Close timer
        modal.setAttribute('data-timer', params.timer);
    }


    /*
     * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color)
     */

    function colorLuminance(hex, lum) {
        // Validate hex string
        hex = String(hex).replace(/[^0-9a-f]/gi, '');
        if (hex.length < 6) {
            hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
        }
        lum = lum || 0;

        // Convert to decimal and change luminosity
        var rgb = "#", c, i;
        for (i = 0; i < 3; i++) {
            c = parseInt(hex.substr(i * 2, 2), 16);
            c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
            rgb += ("00" + c).substr(c.length);
        }

        return rgb;
    }

    function extend(a, b) {
        for (var key in b) {
            if (b.hasOwnProperty(key)) {
                a[key] = b[key];
            }
        }

        return a;
    }

    function hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null;
    }

    // Add box-shadow style to button (depending on its chosen bg-color)
    function setFocusStyle($button, bgColor) {
        var rgbColor = hexToRgb(bgColor);
        $button.style.boxShadow = '0 0 2px rgba(' + rgbColor + ', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)';
    }


    /*
     * Animations
     */

    function openModal() {
        var modal = getModal();
        fadeIn(getOverlay(), 10);
        show(modal);
        addClass(modal, 'showSweetAlert');
        removeClass(modal, 'hideSweetAlert');

        previousActiveElement = document.activeElement;
        var $okButton = modal.querySelector('button.confirm');
        $okButton.focus();

        setTimeout(function () {
            addClass(modal, 'visible');
        }, 500);

        var timer = modal.getAttribute('data-timer');
        if (timer !== "null" && timer !== "") {
            setTimeout(function () {
                closeModal();
            }, timer);
        }
    }

    function closeModal() {
        var modal = getModal();
        fadeOut(getOverlay(), 5);
        fadeOut(modal, 5);
        removeClass(modal, 'showSweetAlert');
        addClass(modal, 'hideSweetAlert');
        removeClass(modal, 'visible');


        // Reset icon animations

        var $successIcon = modal.querySelector('.icon.success');
        removeClass($successIcon, 'animate');
        removeClass($successIcon.querySelector('.tip'), 'animateSuccessTip');
        removeClass($successIcon.querySelector('.long'), 'animateSuccessLong');

        var $errorIcon = modal.querySelector('.icon.error');
        removeClass($errorIcon, 'animateErrorIcon');
        removeClass($errorIcon.querySelector('.x-mark'), 'animateXMark');

        var $warningIcon = modal.querySelector('.icon.warning');
        removeClass($warningIcon, 'pulseWarning');
        removeClass($warningIcon.querySelector('.body'), 'pulseWarningIns');
        removeClass($warningIcon.querySelector('.dot'), 'pulseWarningIns');


        // Reset the page to its previous state
        window.onkeydown = previousWindowKeyDown;
        document.onclick = previousDocumentClick;
        if (previousActiveElement) {
            previousActiveElement.focus();
        }
        lastFocusedButton = undefined;
    }


    /*
     * Set "margin-top"-property on modal based on its computed height
     */

    function fixVerticalPosition() {
        var modal = getModal();
        modal.style.marginTop = getTopMargin(getModal());
    }


    /*
     * If library is injected after page has loaded
     */

    (function () {
        if (document.readyState === "complete" || document.readyState === "interactive" && document.body) {
            sweetAlertInitialize();
        } else {
            if (document.addEventListener) {
                document.addEventListener('DOMContentLoaded', function handler() {
                    document.removeEventListener('DOMContentLoaded', handler, false);
                    sweetAlertInitialize();
                }, false);
            } else if (document.attachEvent) {
                document.attachEvent('onreadystatechange', function handler() {
                    if (document.readyState === 'complete') {
                        document.detachEvent('onreadystatechange', handler);
                        sweetAlertInitialize();
                    }
                });
            }
        }
    })();

})(window, document);
