/**
 * bootbox.js v3.3.0
 *
 * http://bootboxjs.com/license.txt
 */
var bootbox = window.bootbox || (function(document, $) {
    /*jshint scripturl:true sub:true */

    var _locale        = 'en',
        _defaultLocale = 'en',
        _animate       = true,
        _backdrop      = 'static',
        _defaultHref   = 'javascript:;',
        _classes       = '',
        _btnClasses    = {},
        _icons         = {},
        /* last var should always be the public object we'll return */
        that           = {};


    /**
     * public API
     */
    that.setLocale = function(locale) {
        for (var i in _locales) {
            if (i == locale) {
                _locale = locale;
                return;
            }
        }
        throw new Error('Invalid locale: '+locale);
    };

    that.addLocale = function(locale, translations) {
        if (typeof _locales[locale] === 'undefined') {
            _locales[locale] = {};
        }
        for (var str in translations) {
            _locales[locale][str] = translations[str];
        }
    };

    that.setIcons = function(icons) {
        _icons = icons;
        if (typeof _icons !== 'object' || _icons === null) {
            _icons = {};
        }
    };

    that.setBtnClasses = function(btnClasses) {
        _btnClasses = btnClasses;
        if (typeof _btnClasses !== 'object' || _btnClasses === null) {
            _btnClasses = {};
        }
    };

    that.alert = function(/*str, label, cb*/) {
        var str   = "",
            label = _translate('OK'),
            cb    = null;

        switch (arguments.length) {
            case 1:
                // no callback, default button label
                str = arguments[0];
                break;
            case 2:
                // callback *or* custom button label dependent on type
                str = arguments[0];
                if (typeof arguments[1] == 'function') {
                    cb = arguments[1];
                } else {
                    label = arguments[1];
                }
                break;
            case 3:
                // callback and custom button label
                str   = arguments[0];
                label = arguments[1];
                cb    = arguments[2];
                break;
            default:
                throw new Error("Incorrect number of arguments: expected 1-3");
        }

        return that.dialog(str, {
            // only button (ok)
            "label"   : label,
            "icon"    : _icons.OK,
            "class"   : _btnClasses.OK,
            "callback": cb
        }, {
            // ensure that the escape key works; either invoking the user's
            // callback or true to just close the dialog
            "onEscape": cb || true
        });
    };

    that.confirm = function(/*str, labelCancel, labelOk, cb*/) {
        var str         = "",
            labelCancel = _translate('CANCEL'),
            labelOk     = _translate('CONFIRM'),
            cb          = null;

        switch (arguments.length) {
            case 1:
                str = arguments[0];
                break;
            case 2:
                str = arguments[0];
                if (typeof arguments[1] == 'function') {
                    cb = arguments[1];
                } else {
                    labelCancel = arguments[1];
                }
                break;
            case 3:
                str         = arguments[0];
                labelCancel = arguments[1];
                if (typeof arguments[2] == 'function') {
                    cb = arguments[2];
                } else {
                    labelOk = arguments[2];
                }
                break;
            case 4:
                str         = arguments[0];
                labelCancel = arguments[1];
                labelOk     = arguments[2];
                cb          = arguments[3];
                break;
            default:
                throw new Error("Incorrect number of arguments: expected 1-4");
        }

        var cancelCallback = function() {
            if (typeof cb === 'function') {
                return cb(false);
            }
        };

        var confirmCallback = function() {
            if (typeof cb === 'function') {
                return cb(true);
            }
        };

        return that.dialog(str, [{
            // first button (cancel)
            "label"   : labelCancel,
            "icon"    : _icons.CANCEL,
            "class"   : _btnClasses.CANCEL,
            "callback": cancelCallback
        }, {
            // second button (confirm)
            "label"   : labelOk,
            "icon"    : _icons.CONFIRM,
            "class"   : _btnClasses.CONFIRM,
            "callback": confirmCallback
        }], {
            // escape key bindings
            "onEscape": cancelCallback
        });
    };

    that.prompt = function(/*str, labelCancel, labelOk, cb, defaultVal*/) {
        var str         = "",
            labelCancel = _translate('CANCEL'),
            labelOk     = _translate('CONFIRM'),
            cb          = null,
            defaultVal  = "";

        switch (arguments.length) {
            case 1:
                str = arguments[0];
                break;
            case 2:
                str = arguments[0];
                if (typeof arguments[1] == 'function') {
                    cb = arguments[1];
                } else {
                    labelCancel = arguments[1];
                }
                break;
            case 3:
                str         = arguments[0];
                labelCancel = arguments[1];
                if (typeof arguments[2] == 'function') {
                    cb = arguments[2];
                } else {
                    labelOk = arguments[2];
                }
                break;
            case 4:
                str         = arguments[0];
                labelCancel = arguments[1];
                labelOk     = arguments[2];
                cb          = arguments[3];
                break;
            case 5:
                str         = arguments[0];
                labelCancel = arguments[1];
                labelOk     = arguments[2];
                cb          = arguments[3];
                defaultVal  = arguments[4];
                break;
            default:
                throw new Error("Incorrect number of arguments: expected 1-5");
        }

        var header = str;

        // let's keep a reference to the form object for later
        var form = $("<form></form>");
        form.append("<input class='input-block-level' autocomplete=off type=text value='" + defaultVal + "' />");

        var cancelCallback = function() {
            if (typeof cb === 'function') {
                // yep, native prompts dismiss with null, whereas native
                // confirms dismiss with false...
                return cb(null);
            }
        };

        var confirmCallback = function() {
            if (typeof cb === 'function') {
                return cb(form.find("input[type=text]").val());
            }
        };

        var div = that.dialog(form, [{
            // first button (cancel)
            "label"   : labelCancel,
            "icon"    : _icons.CANCEL,
            "class"   : _btnClasses.CANCEL,
            "callback":  cancelCallback
        }, {
            // second button (confirm)
            "label"   : labelOk,
            "icon"    : _icons.CONFIRM,
            "class"   : _btnClasses.CONFIRM,
            "callback": confirmCallback
        }], {
            // prompts need a few extra options
            "header"  : header,
            // explicitly tell dialog NOT to show the dialog...
            "show"    : false,
            "onEscape": cancelCallback
        });

        // ... the reason the prompt needs to be hidden is because we need
        // to bind our own "shown" handler, after creating the modal but
        // before any show(n) events are triggered
        // @see https://github.com/makeusabrew/bootbox/issues/69

        div.on("shown", function() {
            form.find("input[type=text]").focus();

            // ensure that submitting the form (e.g. with the enter key)
            // replicates the behaviour of a normal prompt()
            form.on("submit", function(e) {
                e.preventDefault();
                div.find(".btn-primary").click();
            });
        });

        div.modal("show");

        return div;
    };

    that.dialog = function(str, handlers, options) {
        var buttons    = "",
            callbacks  = [];

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

        // check for single object and convert to array if necessary
        if (typeof handlers === 'undefined') {
            handlers = [];
        } else if (typeof handlers.length == 'undefined') {
            handlers = [handlers];
        }

        var i = handlers.length;
        while (i--) {
            var label    = null,
                href     = null,
                _class   = null,
                icon     = '',
                callback = null;

            if (typeof handlers[i]['label']    == 'undefined' &&
                typeof handlers[i]['class']    == 'undefined' &&
                typeof handlers[i]['callback'] == 'undefined') {
                // if we've got nothing we expect, check for condensed format

                var propCount = 0,      // condensed will only match if this == 1
                    property  = null;   // save the last property we found

                // be nicer to count the properties without this, but don't think it's possible...
                for (var j in handlers[i]) {
                    property = j;
                    if (++propCount > 1) {
                        // forget it, too many properties
                        break;
                    }
                }

                if (propCount == 1 && typeof handlers[i][j] == 'function') {
                    // matches condensed format of label -> function
                    handlers[i]['label']    = property;
                    handlers[i]['callback'] = handlers[i][j];
                }
            }

            if (typeof handlers[i]['callback']== 'function') {
                callback = handlers[i]['callback'];
            }

            if (handlers[i]['class']) {
                _class = handlers[i]['class'];
            } else if (i == handlers.length -1 && handlers.length <= 2) {
                // always add a primary to the main option in a two-button dialog
                _class = 'btn-primary';
            }

            if (handlers[i]['link'] !== true) {
                _class = 'btn ' + _class;
            }

            if (handlers[i]['label']) {
                label = handlers[i]['label'];
            } else {
                label = "Option "+(i+1);
            }

            if (handlers[i]['icon']) {
                icon = "<i class='"+handlers[i]['icon']+"'></i> ";
            }

            if (handlers[i]['href']) {
                href = handlers[i]['href'];
            }
            else {
                href = _defaultHref;
            }

            buttons = "<a data-handler='"+i+"' class='"+_class+"' href='" + href + "'>"+icon+""+label+"</a>" + buttons;

            callbacks[i] = callback;
        }

        // @see https://github.com/makeusabrew/bootbox/issues/46#issuecomment-8235302
        // and https://github.com/twitter/bootstrap/issues/4474
        // for an explanation of the inline overflow: hidden
        // @see https://github.com/twitter/bootstrap/issues/4854
        // for an explanation of tabIndex=-1

        var parts = ["<div class='bootbox modal' tabindex='-1' style='overflow:hidden;'>"];

        if (options['header']) {
            var closeButton = '';
            if (typeof options['headerCloseButton'] == 'undefined' || options['headerCloseButton']) {
                closeButton = "<a href='"+_defaultHref+"' class='close'>&times;</a>";
            }

            parts.push("<div class='modal-header'>"+closeButton+"<h3>"+options['header']+"</h3></div>");
        }

        // push an empty body into which we'll inject the proper content later
        parts.push("<div class='modal-body'></div>");

        if (buttons) {
            parts.push("<div class='modal-footer'>"+buttons+"</div>");
        }

        parts.push("</div>");

        var div = $(parts.join("\n"));

        // check whether we should fade in/out
        var shouldFade = (typeof options.animate === 'undefined') ? _animate : options.animate;

        if (shouldFade) {
            div.addClass("fade");
        }

        var optionalClasses = (typeof options.classes === 'undefined') ? _classes : options.classes;
        if (optionalClasses) {
            div.addClass(optionalClasses);
        }

        // now we've built up the div properly we can inject the content whether it was a string or a jQuery object
        div.find(".modal-body").html(str);

        function onCancel(source) {
            // for now source is unused, but it will be in future
            var hideModal = null;
            if (typeof options.onEscape === 'function') {
                // @see https://github.com/makeusabrew/bootbox/issues/91
                hideModal = options.onEscape();
            }

            if (hideModal !== false) {
                div.modal('hide');
            }
        }

        // hook into the modal's keyup trigger to check for the escape key
        div.on('keyup.dismiss.modal', function(e) {
            // any truthy value passed to onEscape will dismiss the dialog
            // as long as the onEscape function (if defined) doesn't prevent it
            if (e.which === 27 && options.onEscape) {
                onCancel('escape');
            }
        });

        // handle close buttons too
        div.on('click', 'a.close', function(e) {
            e.preventDefault();
            onCancel('close');
        });

        // well, *if* we have a primary - give the first dom element focus
        div.on('shown', function() {
            div.find("a.btn-primary:first").focus();
        });

        div.on('hidden', function(e) {
            // @see https://github.com/makeusabrew/bootbox/issues/115
            // allow for the fact hidden events can propagate up from
            // child elements like tooltips
            if (e.target === this) {
                div.remove();
            }
        });

        // wire up button handlers
        div.on('click', '.modal-footer a', function(e) {

            var handler   = $(this).data("handler"),
                cb        = callbacks[handler],
                hideModal = null;

            // sort of @see https://github.com/makeusabrew/bootbox/pull/68 - heavily adapted
            // if we've got a custom href attribute, all bets are off
            if (typeof handler                   !== 'undefined' &&
                typeof handlers[handler]['href'] !== 'undefined') {

                return;
            }

            e.preventDefault();

            if (typeof cb === 'function') {
                hideModal = cb(e);
            }

            // the only way hideModal *will* be false is if a callback exists and
            // returns it as a value. in those situations, don't hide the dialog
            // @see https://github.com/makeusabrew/bootbox/pull/25
            if (hideModal !== false) {
                div.modal("hide");
            }
        });

        // stick the modal right at the bottom of the main body out of the way
        $("body").append(div);

        div.modal({
            // unless explicitly overridden take whatever our default backdrop value is
            backdrop : (typeof options.backdrop  === 'undefined') ? _backdrop : options.backdrop,
            // ignore bootstrap's keyboard options; we'll handle this ourselves (more fine-grained control)
            keyboard : false,
            // @ see https://github.com/makeusabrew/bootbox/issues/69
            // we *never* want the modal to be shown before we can bind stuff to it
            // this method can also take a 'show' option, but we'll only use that
            // later if we need to
            show     : false
        });

        // @see https://github.com/makeusabrew/bootbox/issues/64
        // @see https://github.com/makeusabrew/bootbox/issues/60
        // ...caused by...
        // @see https://github.com/twitter/bootstrap/issues/4781
        div.on("show", function(e) {
            $(document).off("focusin.modal");
        });

        if (typeof options.show === 'undefined' || options.show === true) {
            div.modal("show");
        }

        return div;
    };

    /**
     * #modal is deprecated in v3; it can still be used but no guarantees are
     * made - have never been truly convinced of its merit but perhaps just
     * needs a tidyup and some TLC
     */
    that.modal = function(/*str, label, options*/) {
        var str;
        var label;
        var options;

        var defaultOptions = {
            "onEscape": null,
            "keyboard": true,
            "backdrop": _backdrop
        };

        switch (arguments.length) {
            case 1:
                str = arguments[0];
                break;
            case 2:
                str = arguments[0];
                if (typeof arguments[1] == 'object') {
                    options = arguments[1];
                } else {
                    label = arguments[1];
                }
                break;
            case 3:
                str     = arguments[0];
                label   = arguments[1];
                options = arguments[2];
                break;
            default:
                throw new Error("Incorrect number of arguments: expected 1-3");
        }

        defaultOptions['header'] = label;

        if (typeof options == 'object') {
            options = $.extend(defaultOptions, options);
        } else {
            options = defaultOptions;
        }

        return that.dialog(str, [], options);
    };


    that.hideAll = function() {
        $(".bootbox").modal("hide");
    };

    that.animate = function(animate) {
        _animate = animate;
    };

    that.backdrop = function(backdrop) {
        _backdrop = backdrop;
    };

    that.classes = function(classes) {
        _classes = classes;
    };

    /**
     * private API
     */

    /**
     * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
     * unlikely to be required. If this gets too large it can be split out into separate JS files.
     */
    var _locales = {
        'br' : {
            OK      : 'OK',
            CANCEL  : 'Cancelar',
            CONFIRM : 'Sim'
        },
        'da' : {
            OK      : 'OK',
            CANCEL  : 'Annuller',
            CONFIRM : 'Accepter'
        },
        'de' : {
            OK      : 'OK',
            CANCEL  : 'Abbrechen',
            CONFIRM : 'Akzeptieren'
        },
        'en' : {
            OK      : 'OK',
            CANCEL  : 'Cancel',
            CONFIRM : 'OK'
        },
        'es' : {
            OK      : 'OK',
            CANCEL  : 'Cancelar',
            CONFIRM : 'Aceptar'
        },
        'fr' : {
            OK      : 'OK',
            CANCEL  : 'Annuler',
            CONFIRM : 'D\'accord'
        },
        'it' : {
            OK      : 'OK',
            CANCEL  : 'Annulla',
            CONFIRM : 'Conferma'
        },
        'nl' : {
            OK      : 'OK',
            CANCEL  : 'Annuleren',
            CONFIRM : 'Accepteren'
        },
        'pl' : {
            OK      : 'OK',
            CANCEL  : 'Anuluj',
            CONFIRM : 'Potwierdź'
        },
        'ru' : {
            OK      : 'OK',
            CANCEL  : 'Отмена',
            CONFIRM : 'Применить'
        },
        'zh_CN' : {
            OK      : 'OK',
            CANCEL  : '取消',
            CONFIRM : '确认'
        },
        'zh_TW' : {
            OK      : 'OK',
            CANCEL  : '取消',
            CONFIRM : '確認'
        }
    };

    function _translate(str, locale) {
        // we assume if no target locale is probided then we should take it from current setting
        if (typeof locale === 'undefined') {
            locale = _locale;
        }
        if (typeof _locales[locale][str] === 'string') {
            return _locales[locale][str];
        }

        // if we couldn't find a lookup then try and fallback to a default translation

        if (locale != _defaultLocale) {
            return _translate(str, _defaultLocale);
        }

        // if we can't do anything then bail out with whatever string was passed in - last resort
        return str;
    }

    return that;

}(document, window.jQuery));

// @see https://github.com/makeusabrew/bootbox/issues/71
window.bootbox = bootbox;
