modal = function (options) {

        var modal = $("<div/>")
            .addClass("modal")
            .addClass("fade")
            .attr("role", "dialog");
        var dialog = $("<div/>")
            .addClass("modal-dialog")
            .appendTo(modal);
        if (options.width) {
            dialog.css("width", options.width);
        }
        if (options.height) {
            dialog.css("height", options.height);
        }
        var dialog_content = $("<div/>")
            .addClass("modal-content")
            .appendTo(dialog);
        if(typeof(options.body) === 'string' && options.sanitize !== false){
            options.body = $("<p/>").text(options.body)
        }
        dialog_content.append(
            $("<div/>")
                .addClass("modal-header")
                .append($("<button>")
                    .attr("type", "button")
                    .addClass("close")
                    .attr("data-dismiss", "modal")
                    .attr("aria-hidden", "true")
                    .html("&times;")
                ).append(
                    $("<h4/>")
                        .addClass('modal-title')
                        .text(options.title || "")
                )
        ).append(
            $("<div/>").addClass("modal-body").append(
                options.body || $("<p/>")
            )
        );

        var footer = $("<div/>").addClass("modal-footer");

        for (var label in options.buttons) {
            var btn_opts = options.buttons[label];
            var button = $("<button/>")
                .addClass("btn btn-default btn-sm")
                .attr("data-dismiss", "modal")
                .text(label);
            if (btn_opts.click) {
                button.click($.proxy(btn_opts.click, dialog_content));
            }
            if (btn_opts.class) {
                button.addClass(btn_opts.class);
            }
            footer.append(button);
        }
        dialog_content.append(footer);
        // hook up on-open event
        modal.on("shown.bs.modal", function() {
            setTimeout(function() {
                footer.find("button").last().focus();
                if (options.open) {
                    $.proxy(options.open, modal)();
                }
            }, 0);
        });

        // destroy modal on hide, unless explicitly asked not to
        if (options.destroy === undefined || options.destroy) {
            modal.on("hidden.bs.modal", function () {
                modal.remove();
            });
        }
        modal.on("hidden.bs.modal", function () {
            if (options.notebook) {
                var cell = options.notebook.get_selected_cell();
                if (cell) cell.select();
            }
            if (options.keyboard_manager) {
                options.keyboard_manager.enable();
                options.keyboard_manager.command_mode();
            }
        });

        if (options.keyboard_manager) {
            options.keyboard_manager.disable();
        }

        options.backdrop = options.backdrop || 'static';

        return modal.modal(options);
    }
A wrapper around bootstrap modal for easier use
Pass it an option dictionary with the following properties:

   - body : <string> or <DOM node>, main content of the dialog
           if pass a <string> it will be wrapped in a p tag and
           html element escaped, unless you specify sanitize=false
           option.
   - title : Dialog title, default to empty string.
   - buttons : dict of btn_options who keys are button label.
           see btn_options below for description
   - open : callback to trigger on dialog open.
   - destroy:
   - notebook : notebook instance
   - keyboard_manager: keyboard manager instance.

 Unlike bootstrap modals, the backdrop options is set by default
 to 'static'.

 The rest of the options are passed as is to bootstrap modals.

 btn_options: dict with the following property:

   - click : callback to trigger on click
   - class : css classes to add to button.