123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- define(['jquery',
- 'codemirror/lib/codemirror',
- 'bootstrap',
- 'base/js/i18n'],
- function($, CodeMirror, bs, i18n) {
- "use strict";
- /**
- * 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.
- *
- *
- *
- **/
- var modal = function (options) {
- var modal = $("<div/>")
- .addClass("modal")
- .addClass("fade")
- .attr("role", "dialog");
- var dialog = $("<div/>")
- .addClass("modal-dialog")
- .appendTo(modal);
- 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")
- .mousedown(function() {
- $(".modal").draggable({handle: '.modal-header'});
- })
- .append($("<button>")
- .attr("type", "button")
- .addClass("close")
- .attr("data-dismiss", "modal")
- .attr("aria-hidden", "true")
- .html("×")
- ).append(
- $("<h4/>")
- .addClass('modal-title')
- .text(options.title || "")
- )
- ).append(
- $("<div/>")
- .addClass("modal-body")
- .append(
- options.body || $("<p/>")
- )
- );
-
- var footer = $("<div/>").addClass("modal-footer");
-
- var default_button;
-
- 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(i18n.msg.translate(label).fetch());
- if (btn_opts.id) {
- button.attr('id', btn_opts.id);
- }
- if (btn_opts.click) {
- button.click($.proxy(btn_opts.click, dialog_content));
- }
- if (btn_opts.class) {
- button.addClass(btn_opts.class);
- }
- footer.append(button);
- if (options.default_button && label === options.default_button) {
- default_button = button;
- }
- }
- if (!options.default_button) {
- default_button = footer.find("button").last();
- }
- dialog_content.append(footer);
- // hook up on-open event
- modal.on("shown.bs.modal", function () {
- setTimeout(function () {
- default_button.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();
- }
-
- if(options.backdrop === undefined){
- options.backdrop = 'static';
- }
-
- return modal.modal(options);
- };
- var kernel_modal = function (options) {
- /**
- * only one kernel dialog should be open at a time -- but
- * other modal dialogs can still be open
- */
- $('.kernel-modal').modal('hide');
- var dialog = modal(options);
- dialog.addClass('kernel-modal');
- return dialog;
- };
- var edit_metadata = function (options) {
- options.name = options.name || "Cell";
- var error_div = $('<div/>').css('color', 'red');
- var message_cell =
- i18n.msg._("Manually edit the JSON below to manipulate the metadata for this cell.");
- var message_notebook =
- i18n.msg._("Manually edit the JSON below to manipulate the metadata for this notebook.");
- var message_end =
- i18n.msg._(" We recommend putting custom metadata attributes in an appropriately named substructure," +
- " so they don't conflict with those of others.");
- var message;
- if (options.name === 'Notebook') {
- message = message_notebook + message_end;
- } else {
- message = message_cell + message_end;
- }
- var textarea = $('<textarea/>')
- .attr('rows', '13')
- .attr('cols', '80')
- .attr('name', 'metadata')
- .text(JSON.stringify(options.md || {}, null, 2));
-
- var dialogform = $('<div/>').attr('title', i18n.msg._('Edit the metadata'))
- .append(
- $('<form/>').append(
- $('<fieldset/>').append(
- $('<label/>')
- .attr('for','metadata')
- .text(message)
- )
- .append(error_div)
- .append($('<br/>'))
- .append(textarea)
- )
- );
- var editor = CodeMirror.fromTextArea(textarea[0], {
- lineNumbers: true,
- matchBrackets: true,
- indentUnit: 2,
- autoIndent: true,
- mode: 'application/json',
- });
- var title_msg;
- if (options.name === "Notebook") {
- title_msg = i18n.msg._("Edit Notebook Metadata");
- } else {
- title_msg = i18n.msg._("Edit Cell Metadata");
- }
- // This statement is used simply so that message extraction
- // will pick up the strings.
- var button_labels = [ i18n.msg._("Cancel"), i18n.msg._("Edit"), i18n.msg._("OK"), i18n.msg._("Apply")];
- var modal_obj = modal({
- title: title_msg,
- body: dialogform,
- default_button: "Cancel",
- buttons: {
- Cancel: {},
- Edit: { class : "btn-primary",
- click: function() {
- /**
- * validate json and set it
- */
- var new_md;
- try {
- new_md = JSON.parse(editor.getValue());
- } catch(e) {
- console.log(e);
- error_div.text(i18n.msg._('WARNING: Could not save invalid JSON.'));
- return false;
- }
- options.callback(new_md);
- }
- }
- },
- notebook: options.notebook,
- keyboard_manager: options.keyboard_manager,
- });
- modal_obj.on('shown.bs.modal', function(){ editor.refresh(); });
- };
- var edit_attachments = function (options) {
- // This shows the Edit Attachments dialog. This dialog allows the
- // user to delete attachments. We show a list of attachments to
- // the user and he can mark some of them for deletion. The deletion
- // is applied when the 'Apply' button of this dialog is pressed.
- var message;
- var attachments_list;
- if (Object.keys(options.attachments).length == 0) {
- message = i18n.msg._("There are no attachments for this cell.");
- attachments_list = $('<div>');
- } else {
- message = i18n.msg._("Current cell attachments");
- attachments_list = $('<div>')
- .addClass('list_container')
- .append(
- $('<div>')
- .addClass('row list_header')
- .append(
- $('<div>')
- .text(i18n.msg._('Attachments'))
- )
- );
- // This is a set containing keys of attachments to be deleted when
- // the Apply button is clicked
- var to_delete = {};
- var refresh_attachments_list = function() {
- $(attachments_list).find('.row').remove();
- for (var key in options.attachments) {
- var mime = Object.keys(options.attachments[key])[0];
- var deleted = key in to_delete;
- // This ensures the current value of key is captured since
- // javascript only has function scope
- var btn;
- // Trash/restore button
- (function(){
- var _key = key;
- btn = $('<button>')
- .addClass('btn btn-default btn-xs')
- .css('display', 'inline-block');
- if (deleted) {
- btn.attr('title', i18n.msg._('Restore'))
- .append(
- $('<i>')
- .addClass('fa fa-plus')
- );
- btn.click(function() {
- delete to_delete[_key];
- refresh_attachments_list();
- });
- } else {
- btn.attr('title', i18n.msg._('Delete'))
- .addClass('btn-danger')
- .append(
- $('<i>')
- .addClass('fa fa-trash')
- );
- btn.click(function() {
- to_delete[_key] = true;
- refresh_attachments_list();
- });
- }
- return btn;
- })();
- var row = $('<div>')
- .addClass('col-md-12 att_row')
- .append(
- $('<div>')
- .addClass('row')
- .append(
- $('<div>')
- .addClass('att-name col-xs-4')
- .text(key)
- )
- .append(
- $('<div>')
- .addClass('col-xs-4 text-muted')
- .text(mime)
- )
- .append(
- $('<div>')
- .addClass('item-buttons pull-right')
- .append(btn)
- )
- );
- if (deleted) {
- row.find('.att-name')
- .css('text-decoration', 'line-through');
- }
- attachments_list.append($('<div>')
- .addClass('list_item row')
- .append(row)
- );
- }
- };
- refresh_attachments_list();
- }
- var dialogform = $('<div/>')
- .attr('title', i18n.msg._('Edit attachments'))
- .append(message)
- .append('<br />')
- .append(attachments_list);
- var title_msg;
- if ( options.name === "Notebook" ) {
- title_msg = i18n.msg._("Edit Notebook Attachments");
- } else {
- title_msg = i18n.msg._("Edit Cell Attachments");
- }
- var modal_obj = modal({
- title: title_msg,
- body: dialogform,
- buttons: {
- Apply: { class : "btn-primary",
- click: function() {
- for (var key in to_delete) {
- delete options.attachments[key];
- }
- options.callback(options.attachments);
- }
- },
- Cancel: {}
- },
- notebook: options.notebook,
- keyboard_manager: options.keyboard_manager,
- });
- };
- var insert_image = function (options) {
- var message =
- i18n.msg._("Select a file to insert.");
- var file_input = $('<input/>')
- .attr('type', 'file')
- .attr('accept', 'image/*')
- .attr('name', 'file')
- .on('change', function(file) {
- var $btn = $(modal_obj).find('#btn_ok');
- if (this.files.length > 0) {
- $btn.removeClass('disabled');
- } else {
- $btn.addClass('disabled');
- }
- });
- var dialogform = $('<div/>').attr('title', i18n.msg._('Edit attachments'))
- .append(
- $('<form id="insert-image-form" />').append(
- $('<fieldset/>').append(
- $('<label/>')
- .attr('for','file')
- .text(message)
- )
- .append($('<br/>'))
- .append(file_input)
- )
- );
- var modal_obj = modal({
- title: i18n.msg._("Select a file"),
- body: dialogform,
- buttons: {
- OK: {
- id : 'btn_ok',
- class : "btn-primary disabled",
- click: function() {
- options.callback(file_input[0].files[0]);
- }
- },
- Cancel: {}
- },
- notebook: options.notebook,
- keyboard_manager: options.keyboard_manager,
- });
- };
-
- var dialog = {
- modal : modal,
- kernel_modal : kernel_modal,
- edit_metadata : edit_metadata,
- edit_attachments : edit_attachments,
- insert_image : insert_image
- };
- return dialog;
- });
|