text: /**
   * bootbox.js [master branch]
   *
   * http://bootboxjs.com/license.txt
   */
  
  // @see https://github.com/makeusabrew/bootbox/issues/180
  // @see https://github.com/makeusabrew/bootbox/issues/186
  (function (root, factory) {
  
    "use strict";
    if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define(["jquery"], factory);
    } else if (typeof exports === "object") {
      // Node. Does not work with strict CommonJS, but
      // only CommonJS-like environments that support module.exports,
      // like Node.
      module.exports = factory(require("jquery"));
    } else {
      // Browser globals (root is window)
      root.bootbox = factory(root.jQuery);
    }
  
  }(this, function init($, undefined) {
  
    "use strict";
  
    // the base DOM structure needed to create a modal
    var templates = {
      dialog:
        "<div class='bootbox modal' tabindex='-1' role='dialog'>" +
          "<div class='modal-dialog'>" +
            "<div class='modal-content'>" +
              "<div class='modal-body'><div class='bootbox-body'></div></div>" +
            "</div>" +
          "</div>" +
        "</div>",
      header:
        "<div class='modal-header'>" +
          "<h4 class='modal-title'></h4>" +
        "</div>",
      footer:
        "<div class='modal-footer'></div>",
      closeButton:
        "<button type='button' class='bootbox-close-button close' data-dismiss='modal' aria-hidden='true'>&times;</button>",
      form:
        "<form class='bootbox-form'></form>",
      inputs: {
        text:
          "<input class='bootbox-input bootbox-input-text form-control' autocomplete=off type=text />",
        textarea:
          "<textarea class='bootbox-input bootbox-input-textarea form-control'></textarea>",
        email:
          "<input class='bootbox-input bootbox-input-email form-control' autocomplete='off' type='email' />",
        select:
          "<select class='bootbox-input bootbox-input-select form-control'></select>",
        checkbox:
          "<div class='checkbox'><label><input class='bootbox-input bootbox-input-checkbox' type='checkbox' /></label></div>",
        date:
          "<input class='bootbox-input bootbox-input-date form-control' autocomplete=off type='date' />",
        time:
          "<input class='bootbox-input bootbox-input-time form-control' autocomplete=off type='time' />",
        number:
          "<input class='bootbox-input bootbox-input-number form-control' autocomplete=off type='number' />",
        password:
          "<input class='bootbox-input bootbox-input-password form-control' autocomplete='off' type='password' />"
      }
    };
  
    var defaults = {
      // default language
      locale: "en",
      // show backdrop or not. Default to static so user has to interact with dialog
      backdrop: "static",
      // animate the modal in/out
      animate: true,
      // additional class string applied to the top level dialog
      className: null,
      // whether or not to include a close button
      closeButton: true,
      // show the dialog immediately by default
      show: true,
      // dialog container
      container: "body"
    };
  
    // our public object; augmented after our private API
    var exports = {};
  
    /**
     * @private
     */
    function _t(key) {
      var locale = locales[defaults.locale];
      return locale ? locale[key] : locales.en[key];
    }
  
    function processCallback(e, dialog, callback) {
      e.stopPropagation();
      e.preventDefault();
  
      // by default we assume a callback will get rid of the dialog,
      // although it is given the opportunity to override this
  
      // so, if the callback can be invoked and it *explicitly returns false*
      // then we'll set a flag to keep the dialog active...
      var preserveDialog = $.isFunction(callback) && callback.call(dialog, e) === false;
  
      // ... otherwise we'll bin it
      if (!preserveDialog) {
        dialog.modal("hide");
      }
    }
  
    function getKeyLength(obj) {
      // @TODO defer to Object.keys(x).length if available?
      var k, t = 0;
      for (k in obj) {
        t ++;
      }
      return t;
    }
  
    function each(collection, iterator) {
      var index = 0;
      $.each(collection, function(key, value) {
        iterator(key, value, index++);
      });
    }
  
    function sanitize(options) {
      var buttons;
      var total;
  
      if (typeof options !== "object") {
        throw new Error("Please supply an object of options");
      }
  
      if (!options.message) {
        throw new Error("Please specify a message");
      }
  
      // make sure any supplied options take precedence over defaults
      options = $.extend({}, defaults, options);
  
      if (!options.buttons) {
        options.buttons = {};
      }
  
      buttons = options.buttons;
  
      total = getKeyLength(buttons);
  
      each(buttons, function(key, button, index) {
  
        if ($.isFunction(button)) {
          // short form, assume value is our callback. Since button
          // isn't an object it isn't a reference either so re-assign it
          button = buttons[key] = {
            callback: button
          };
        }
  
        // before any further checks make sure by now button is the correct type
        if ($.type(button) !== "object") {
          throw new Error("button with key " + key + " must be an object");
        }
  
        if (!button.label) {
          // the lack of an explicit label means we'll assume the key is good enough
          button.label = key;
        }
  
        if (!button.className) {
          if (total <= 2 && index === total-1) {
            // always add a primary to the main option in a two-button dialog
            button.className = "btn-primary";
          } else {
            button.className = "btn-default";
          }
        }
      });
  
      return options;
    }
  
    /**
     * map a flexible set of arguments into a single returned object
     * if args.length is already one just return it, otherwise
     * use the properties argument to map the unnamed args to
     * object properties
     * so in the latter case:
     * mapArguments(["foo", $.noop], ["message", "callback"])
     * -> { message: "foo", callback: $.noop }
     */
    function mapArguments(args, properties) {
      var argn = args.length;
      var options = {};
  
      if (argn < 1 || argn > 2) {
        throw new Error("Invalid argument length");
      }
  
      if (argn === 2 || typeof args[0] === "string") {
        options[properties[0]] = args[0];
        options[properties[1]] = args[1];
      } else {
        options = args[0];
      }
  
      return options;
    }
  
    /**
     * merge a set of default dialog options with user supplied arguments
     */
    function mergeArguments(defaults, args, properties) {
      return $.extend(
        // deep merge
        true,
        // ensure the target is an empty, unreferenced object
        {},
        // the base options object for this type of dialog (often just buttons)
        defaults,
        // args could be an object or array; if it's an array properties will
        // map it to a proper options object
        mapArguments(
          args,
          properties
        )
      );
    }
  
    /**
     * this entry-level method makes heavy use of composition to take a simple
     * range of inputs and return valid options suitable for passing to bootbox.dialog
     */
    function mergeDialogOptions(className, labels, properties, args) {
      //  build up a base set of dialog properties
      var baseOptions = {
        className: "bootbox-" + className,
        buttons: createLabels.apply(null, labels)
      };
  
      // ensure the buttons properties generated, *after* merging
      // with user args are still valid against the supplied labels
      return validateButtons(
        // merge the generated base properties with user supplied arguments
        mergeArguments(
          baseOptions,
          args,
          // if args.length > 1, properties specify how each arg maps to an object key
          properties
        ),
        labels
      );
    }
  
    /**
     * from a given list of arguments return a suitable object of button labels
     * all this does is normalise the given labels and translate them where possible
     * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" }
     */
    function createLabels() {
      var buttons = {};
  
      for (var i = 0, j = arguments.length; i < j; i++) {
        var argument = arguments[i];
        var key = argument.toLowerCase();
        var value = argument.toUpperCase();
  
        buttons[key] = {
          label: _t(value)
        };
      }
  
      return buttons;
    }
  
    function validateButtons(options, buttons) {
      var allowedButtons = {};
      each(buttons, function(key, value) {
        allowedButtons[value] = true;
      });
  
      each(options.buttons, function(key) {
        if (allowedButtons[key] === undefined) {
          throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
        }
      });
  
      return options;
    }
  
    exports.alert = function() {
      var options;
  
      options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments);
  
      if (options.callback && !$.isFunction(options.callback)) {
        throw new Error("alert requires callback property to be a function when provided");
      }
  
      /**
       * overrides
       */
      options.buttons.ok.callback = options.onEscape = function() {
        if ($.isFunction(options.callback)) {
          return options.callback.call(this);
        }
        return true;
      };
  
      return exports.dialog(options);
    };
  
    exports.confirm = function() {
      var options;
  
      options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments);
  
      /**
       * overrides; undo anything the user tried to set they shouldn't have
       */
      options.buttons.cancel.callback = options.onEscape = function() {
        return options.callback.call(this, false);
      };
  
      options.buttons.confirm.callback = function() {
        return options.callback.call(this, true);
      };
  
      // confirm specific validation
      if (!$.isFunction(options.callback)) {
        throw new Error("confirm requires a callback");
      }
  
      return exports.dialog(options);
    };
  
    exports.prompt = function() {
      var options;
      var defaults;
      var dialog;
      var form;
      var input;
      var shouldShow;
      var inputOptions;
  
      // we have to create our form first otherwise
      // its value is undefined when gearing up our options
      // @TODO this could be solved by allowing message to
      // be a function instead...
      form = $(templates.form);
  
      // prompt defaults are more complex than others in that
      // users can override more defaults
      // @TODO I don't like that prompt has to do a lot of heavy
      // lifting which mergeDialogOptions can *almost* support already
      // just because of 'value' and 'inputType' - can we refactor?
      defaults = {
        className: "bootbox-prompt",
        buttons: createLabels("cancel", "confirm"),
        value: "",
        inputType: "text"
      };
  
      options = validateButtons(
        mergeArguments(defaults, arguments, ["title", "callback"]),
        ["cancel", "confirm"]
      );
  
      // capture the user's show value; we always set this to false before
      // spawning the dialog to give us a chance to attach some handlers to
      // it, but we need to make sure we respect a preference not to show it
      shouldShow = (options.show === undefined) ? true : options.show;
  
      /**
       * overrides; undo anything the user tried to set they shouldn't have
       */
      options.message = form;
  
      options.buttons.cancel.callback = options.onEscape = function() {
        return options.callback.call(this, null);
      };
  
      options.buttons.confirm.callback = function() {
        var value;
  
        switch (options.inputType) {
          case "text":
          case "textarea":
          case "email":
          case "select":
          case "date":
          case "time":
          case "number":
          case "password":
            value = input.val();
            break;
  
          case "checkbox":
            var checkedItems = input.find("input:checked");
  
            // we assume that checkboxes are always multiple,
            // hence we default to an empty array
            value = [];
  
            each(checkedItems, function(_, item) {
              value.push($(item).val());
            });
            break;
        }
  
        return options.callback.call(this, value);
      };
  
      options.show = false;
  
      // prompt specific validation
      if (!options.title) {
        throw new Error("prompt requires a title");
      }
  
      if (!$.isFunction(options.callback)) {
        throw new Error("prompt requires a callback");
      }
  
      if (!templates.inputs[options.inputType]) {
        throw new Error("invalid prompt type");
      }
  
      // create the input based on the supplied type
      input = $(templates.inputs[options.inputType]);
  
      switch (options.inputType) {
        case "text":
        case "textarea":
        case "email":
        case "date":
        case "time":
        case "number":
        case "password":
          input.val(options.value);
          break;
  
        case "select":
          var groups = {};
          inputOptions = options.inputOptions || [];
  
          if (!$.isArray(inputOptions)) {
            throw new Error("Please pass an array of input options");
          }
  
          if (!inputOptions.length) {
            throw new Error("prompt with select requires options");
          }
  
          each(inputOptions, function(_, option) {
  
            // assume the element to attach to is the input...
            var elem = input;
  
            if (option.value === undefined || option.text === undefined) {
              throw new Error("given options in wrong format");
            }
  
            // ... but override that element if this option sits in a group
  
            if (option.group) {
              // initialise group if necessary
              if (!groups[option.group]) {
                groups[option.group] = $("<optgroup/>").attr("label", option.group);
              }
  
              elem = groups[option.group];
            }
  
            elem.append("<option value='" + option.value + "'>" + option.text + "</option>");
          });
  
          each(groups, function(_, group) {
            input.append(group);
          });
  
          // safe to set a select's value as per a normal input
          input.val(options.value);
          break;
  
        case "checkbox":
          var values   = $.isArray(options.value) ? options.value : [options.value];
          inputOptions = options.inputOptions || [];
  
          if (!inputOptions.length) {
            throw new Error("prompt with checkbox requires options");
          }
  
          if (!inputOptions[0].value || !inputOptions[0].text) {
            throw new Error("given options in wrong format");
          }
  
          // checkboxes have to nest within a containing element, so
          // they break the rules a bit and we end up re-assigning
          // our 'input' element to this container instead
          input = $("<div/>");
  
          each(inputOptions, function(_, option) {
            var checkbox = $(templates.inputs[options.inputType]);
  
            checkbox.find("input").attr("value", option.value);
            checkbox.find("label").append(option.text);
  
            // we've ensured values is an array so we can always iterate over it
            each(values, function(_, value) {
              if (value === option.value) {
                checkbox.find("input").prop("checked", true);
              }
            });
  
            input.append(checkbox);
          });
          break;
      }
  
      // @TODO provide an attributes option instead
      // and simply map that as keys: vals
      if (options.placeholder) {
        input.attr("placeholder", options.placeholder);
      }
  
      if (options.pattern) {
        input.attr("pattern", options.pattern);
      }
  
      if (options.maxlength) {
        input.attr("maxlength", options.maxlength);
      }
  
      // now place it in our form
      form.append(input);
  
      form.on("submit", function(e) {
        e.preventDefault();
        // Fix for SammyJS (or similar JS routing library) hijacking the form post.
        e.stopPropagation();
        // @TODO can we actually click *the* button object instead?
        // e.g. buttons.confirm.click() or similar
        dialog.find(".btn-primary").click();
      });
  
      dialog = exports.dialog(options);
  
      // clear the existing handler focusing the submit button...
      dialog.off("shown.bs.modal");
  
      // ...and replace it with one focusing our input, if possible
      dialog.on("shown.bs.modal", function() {
        // need the closure here since input isn't
        // an object otherwise
        input.focus();
      });
  
      if (shouldShow === true) {
        dialog.modal("show");
      }
  
      return dialog;
    };
  
    exports.dialog = function(options) {
      options = sanitize(options);
  
      var dialog = $(templates.dialog);
      var innerDialog = dialog.find(".modal-dialog");
      var body = dialog.find(".modal-body");
      var buttons = options.buttons;
      var buttonStr = "";
      var callbacks = {
        onEscape: options.onEscape
      };
  
      if ($.fn.modal === undefined) {
        throw new Error(
          "$.fn.modal is not defined; please double check you have included " +
          "the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ " +
          "for more details."
        );
      }
  
      each(buttons, function(key, button) {
  
        // @TODO I don't like this string appending to itself; bit dirty. Needs reworking
        // can we just build up button elements instead? slower but neater. Then button
        // can just become a template too
        buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
        callbacks[key] = button.callback;
      });
  
      body.find(".bootbox-body").html(options.message);
  
      if (options.animate === true) {
        dialog.addClass("fade");
      }
  
      if (options.className) {
        dialog.addClass(options.className);
      }
  
      if (options.size === "large") {
        innerDialog.addClass("modal-lg");
      } else if (options.size === "small") {
        innerDialog.addClass("modal-sm");
      }
  
      if (options.title) {
        body.before(templates.header);
      }
  
      if (options.closeButton) {
        var closeButton = $(templates.closeButton);
  
        if (options.title) {
          dialog.find(".modal-header").prepend(closeButton);
        } else {
          closeButton.css("margin-top", "-10px").prependTo(body);
        }
      }
  
      if (options.title) {
        dialog.find(".modal-title").html(options.title);
      }
  
      if (buttonStr.length) {
        body.after(templates.footer);
        dialog.find(".modal-footer").html(buttonStr);
      }
  
  
      /**
       * Bootstrap event listeners; used handle extra
       * setup & teardown required after the underlying
       * modal has performed certain actions
       */
  
      dialog.on("hidden.bs.modal", function(e) {
        // ensure we don't accidentally intercept hidden events triggered
        // by children of the current dialog. We shouldn't anymore now BS
        // namespaces its events; but still worth doing
        if (e.target === this) {
          dialog.remove();
        }
      });
  
      /*
      dialog.on("show.bs.modal", function() {
        // sadly this doesn't work; show is called *just* before
        // the backdrop is added so we'd need a setTimeout hack or
        // otherwise... leaving in as would be nice
        if (options.backdrop) {
          dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
        }
      });
      */
  
      dialog.on("shown.bs.modal", function() {
        dialog.find(".btn-primary:first").focus();
      });
  
      /**
       * Bootbox event listeners; experimental and may not last
       * just an attempt to decouple some behaviours from their
       * respective triggers
       */
  
      if (options.backdrop !== "static") {
        // A boolean true/false according to the Bootstrap docs
        // should show a dialog the user can dismiss by clicking on
        // the background.
        // We always only ever pass static/false to the actual
        // $.modal function because with `true` we can't trap
        // this event (the .modal-backdrop swallows it)
        // However, we still want to sort of respect true
        // and invoke the escape mechanism instead
        dialog.on("click.dismiss.bs.modal", function(e) {
          // @NOTE: the target varies in >= 3.3.x releases since the modal backdrop
          // moved *inside* the outer dialog rather than *alongside* it
          if (dialog.children(".modal-backdrop").length) {
            e.currentTarget = dialog.children(".modal-backdrop").get(0);
          }
  
          if (e.target !== e.currentTarget) {
            return;
          }
  
          dialog.trigger("escape.close.bb");
        });
      }
  
      dialog.on("escape.close.bb", function(e) {
        if (callbacks.onEscape) {
          processCallback(e, dialog, callbacks.onEscape);
        }
      });
  
      /**
       * Standard jQuery event listeners; used to handle user
       * interaction with our dialog
       */
  
      dialog.on("click", ".modal-footer button", function(e) {
        var callbackKey = $(this).data("bb-handler");
  
        processCallback(e, dialog, callbacks[callbackKey]);
      });
  
      dialog.on("click", ".bootbox-close-button", function(e) {
        // onEscape might be falsy but that's fine; the fact is
        // if the user has managed to click the close button we
        // have to close the dialog, callback or not
        processCallback(e, dialog, callbacks.onEscape);
      });
  
      dialog.on("keyup", function(e) {
        if (e.which === 27) {
          dialog.trigger("escape.close.bb");
        }
      });
  
      // the remainder of this method simply deals with adding our
      // dialogent to the DOM, augmenting it with Bootstrap's modal
      // functionality and then giving the resulting object back
      // to our caller
  
      $(options.container).append(dialog);
  
      dialog.modal({
        backdrop: options.backdrop ? "static": false,
        keyboard: false,
        show: false
      });
  
      if (options.show) {
        dialog.modal("show");
      }
  
      // @TODO should we return the raw element here or should
      // we wrap it in an object on which we can expose some neater
      // methods, e.g. var d = bootbox.alert(); d.hide(); instead
      // of d.modal("hide");
  
     /*
      function BBDialog(elem) {
        this.elem = elem;
      }
  
      BBDialog.prototype = {
        hide: function() {
          return this.elem.modal("hide");
        },
        show: function() {
          return this.elem.modal("show");
        }
      };
      */
  
      return dialog;
  
    };
  
    exports.setDefaults = function() {
      var values = {};
  
      if (arguments.length === 2) {
        // allow passing of single key/value...
        values[arguments[0]] = arguments[1];
      } else {
        // ... and as an object too
        values = arguments[0];
      }
  
      $.extend(defaults, values);
    };
  
    exports.hideAll = function() {
      $(".bootbox").modal("hide");
  
      return exports;
    };
  
  
    /**
     * 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 = {
      ar : {
        OK      : "موافق",
        CANCEL  : "الغاء",
        CONFIRM : "تأكيد"
      },
      bg_BG : {
        OK      : "Ок",
        CANCEL  : "Отказ",
        CONFIRM : "Потвърждавам"
      },
      br : {
        OK      : "OK",
        CANCEL  : "Cancelar",
        CONFIRM : "Sim"
      },
      cs : {
        OK      : "OK",
        CANCEL  : "Zrušit",
        CONFIRM : "Potvrdit"
      },
      da : {
        OK      : "OK",
        CANCEL  : "Annuller",
        CONFIRM : "Accepter"
      },
      de : {
        OK      : "OK",
        CANCEL  : "Abbrechen",
        CONFIRM : "Akzeptieren"
      },
      el : {
        OK      : "Εντάξει",
        CANCEL  : "Ακύρωση",
        CONFIRM : "Επιβεβαίωση"
      },
      en : {
        OK      : "OK",
        CANCEL  : "Cancel",
        CONFIRM : "OK"
      },
      es : {
        OK      : "OK",
        CANCEL  : "Cancelar",
        CONFIRM : "Aceptar"
      },
      et : {
        OK      : "OK",
        CANCEL  : "Katkesta",
        CONFIRM : "OK"
      },
      fa : {
        OK      : "قبول",
        CANCEL  : "لغو",
        CONFIRM : "تایید"
      },
      fi : {
        OK      : "OK",
        CANCEL  : "Peruuta",
        CONFIRM : "OK"
      },
      fr : {
        OK      : "OK",
        CANCEL  : "Annuler",
        CONFIRM : "D'accord"
      },
      he : {
        OK      : "אישור",
        CANCEL  : "ביטול",
        CONFIRM : "אישור"
      },
      hu : {
        OK      : "OK",
        CANCEL  : "Mégsem",
        CONFIRM : "Megerősít"
      },
      hr : {
        OK      : "OK",
        CANCEL  : "Odustani",
        CONFIRM : "Potvrdi"
      },
      id : {
        OK      : "OK",
        CANCEL  : "Batal",
        CONFIRM : "OK"
      },
      it : {
        OK      : "OK",
        CANCEL  : "Annulla",
        CONFIRM : "Conferma"
      },
      ja : {
        OK      : "OK",
        CANCEL  : "キャンセル",
        CONFIRM : "確認"
      },
      lt : {
        OK      : "Gerai",
        CANCEL  : "Atšaukti",
        CONFIRM : "Patvirtinti"
      },
      lv : {
        OK      : "Labi",
        CANCEL  : "Atcelt",
        CONFIRM : "Apstiprināt"
      },
      nl : {
        OK      : "OK",
        CANCEL  : "Annuleren",
        CONFIRM : "Accepteren"
      },
      no : {
        OK      : "OK",
        CANCEL  : "Avbryt",
        CONFIRM : "OK"
      },
      pl : {
        OK      : "OK",
        CANCEL  : "Anuluj",
        CONFIRM : "Potwierdź"
      },
      pt : {
        OK      : "OK",
        CANCEL  : "Cancelar",
        CONFIRM : "Confirmar"
      },
      ru : {
        OK      : "OK",
        CANCEL  : "Отмена",
        CONFIRM : "Применить"
      },
      sq : {
        OK : "OK",
        CANCEL : "Anulo",
        CONFIRM : "Prano"
      },
      sv : {
        OK      : "OK",
        CANCEL  : "Avbryt",
        CONFIRM : "OK"
      },
      th : {
        OK      : "ตกลง",
        CANCEL  : "ยกเลิก",
        CONFIRM : "ยืนยัน"
      },
      tr : {
        OK      : "Tamam",
        CANCEL  : "İptal",
        CONFIRM : "Onayla"
      },
      zh_CN : {
        OK      : "OK",
        CANCEL  : "取消",
        CONFIRM : "确认"
      },
      zh_TW : {
        OK      : "OK",
        CANCEL  : "取消",
        CONFIRM : "確認"
      }
    };
  
    exports.addLocale = function(name, values) {
      $.each(["OK", "CANCEL", "CONFIRM"], function(_, v) {
        if (!values[v]) {
          throw new Error("Please supply a translation for '" + v + "'");
        }
      });
  
      locales[name] = {
        OK: values.OK,
        CANCEL: values.CANCEL,
        CONFIRM: values.CONFIRM
      };
  
      return exports;
    };
  
    exports.removeLocale = function(name) {
      delete locales[name];
  
      return exports;
    };
  
    exports.setLocale = function(name) {
      return exports.setDefaults("locale", name);
    };
  
    exports.init = function(_$) {
      return init(_$ || $);
    };
  
    return exports;
  }));
  