Skip to content

Commit

Permalink
Add save as menu option
Browse files Browse the repository at this point in the history
  • Loading branch information
Madhu94 committed Mar 17, 2018
1 parent e321c80 commit 5fdf8c2
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 53 deletions.
1 change: 1 addition & 0 deletions notebook/notebook/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def get(self, path):
self.write(self.render_template('notebook.html',
notebook_path=path,
notebook_name=name,
server_root=self.settings['server_root_dir'],
kill_kernel=False,
mathjax_url=self.mathjax_url,
mathjax_config=self.mathjax_config,
Expand Down
5 changes: 5 additions & 0 deletions notebook/static/notebook/js/menubar.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ define([
that.notebook.copy_notebook();
return false;
});
this.element.find('#save_notebook_as').click(function() {
that.notebook.save_notebook_as();
return false;

});
this.element.find('#download_ipynb').click(function () {
var base_url = that.notebook.base_url;
var notebook_path = utils.encode_uri_components(that.notebook.notebook_path);
Expand Down
53 changes: 52 additions & 1 deletion notebook/static/notebook/js/notebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2755,7 +2755,7 @@ define([
// In some cases the filesystem reports an inconsistent time,
// so we allow 0.5 seconds difference before complaining.
// This is configurable in nbconfig/notebook.json as `last_modified_check_margin`.
if ((last_modified.getTime() - that.last_modified.getTime()) > last_modified_check_margin) {
if ((last_modified.getTime() - that.last_modified.getTime()) > last_modified_check_margin) {
console.warn("Last saving was done on `"+that.last_modified+"`("+that._last_modified+"), "+
"while the current file seem to have been saved on `"+data.last_modified+"`");
if (that._changed_on_disk_dialog !== null) {
Expand Down Expand Up @@ -2845,6 +2845,57 @@ define([
}
};


Notebook.prototype.save_notebook_as = function() {
// If current notebook has some changes, save them
if (this.writable && this.dirty) {
this.save_notebook();
}
var that = this;
var d = that.save_widget._nb_name_dialog({
dialog_title: i18n.msg._('Save As'),
message: i18n.msg._('Enter a notebook path relative to notebook dir'),
message_classname: 'save-message',
button_name: 'Save',
click_handler: function(d) {
var nb_path = d.find('input').val();
var nb_name = nb_path.split('/').slice(-1).pop();
var ext = utils.splitext(nb_name)[1];
// If notebook name does not contain extension '.ipynb' add it
if (ext === '') {
nb_name = nb_name + '.ipynb';
nb_path = nb_path + '.ipynb';
}
var save_thunk = function() {
var model = {'type': 'notebook','content': that.toJSON(),
'name': nb_name};
return that.contents.save(nb_path, model)
.then(function(data) {
var dir_path = utils.get_body_data("baseUrl");
window.open(utils.url_path_join(dir_path, "notebooks", data.path), '_self');
},function(error) {
console.error(i18n.msg._(error.message || 'Unknown error saving notebook'));
});
}
that.contents.get(nb_path, {type: 'notebook'}).then(function(data) {
var warning_body = $('<div/>').append(
$("<p/>").text(i18n.msg._('Notebook with that name exists.')));
dialog.modal({
title: i18n.msg._('Notebook exists'),
body: warning_body,
buttons: {Cancel: {},
Overwrite: {
class: 'btn-warning',
click: save_thunk
}
}
});
}, save_thunk)
return false;
}
});
}

/**
* Update the autosave interval based on the duration of the last save.
*
Expand Down
123 changes: 71 additions & 52 deletions notebook/static/notebook/js/savewidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,61 @@ define([
that.set_autosaved(data.value);
});
};

/** A modal dialog that is used for the specific purpose of
* getting a notebook name, validating it, and passing a reference
* of it to the button click handler.
* @param options Expected to have dialog message, dialog title,
* a primary button and a click handler associated with them.
*/
SaveWidget.prototype._nb_name_dialog = function(options) {
var that = this;
var dialog_template = _.template(`<p class="<%= classname %>"><%= msg %></p>
<br>
<label> <%= server_root %>/
<input class="form-control" size=25 type="text">
</label>`)
var {message, message_classname, dialog_title,
button_name, click_handler, input_box} = options;
var dialog_body = $(dialog_template({classname: message_classname,
msg: message,
server_root: utils.get_body_data("serverRoot")
}));
var dialog_buttons = {"Cancel": {}};
dialog_buttons[button_name] = {
class: "btn-primary",
click: function() {
var nb_name = d.find('input').val();
if (!that.notebook.test_notebook_name(nb_name)) {
d.find("." + message_classname).text(i18n.msg._(
"Invalid notebook name. Notebook names must have 1 or more characters and can contain any characters except :/\\. Please enter a new notebook name:")
);
return false;
}
click_handler(d);
}
};
var d = dialog.modal({
title: dialog_title,
body: dialog_body,
notebook: options.notebook,
keyboard_manager: this.keyboard_manager,
default_button: "Cancel",
buttons : dialog_buttons,
open : function () {
/**
* Upon ENTER, click the OK button.
*/
d.find('input[type="text"]').keydown(function (event) {
if (event.which === keyboard.keycodes.enter) {
d.find('.btn-primary').first().click();
return false;
}
});
d.find('input[type="text"]').focus().select();
}
});
}

// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
Expand All @@ -74,59 +129,23 @@ define([
SaveWidget.prototype.rename_notebook = function (options) {
options = options || {};
var that = this;
var dialog_body = $('<div/>').append(
$("<p/>").addClass("rename-message")
.text(i18n.msg._('Enter a new notebook name:'))
).append(
$("<br/>")
).append(
$('<input/>').attr('type','text').attr('size','25').addClass('form-control')
.val(options.notebook.get_notebook_name())
);
var d = dialog.modal({
title: i18n.msg._("Rename Notebook"),
body: dialog_body,
notebook: options.notebook,
keyboard_manager: this.keyboard_manager,
default_button: "Cancel",
buttons : {
"Cancel": {},
"Rename": {
class: "btn-primary",
click: function () {
var new_name = d.find('input').val();
if (!options.notebook.test_notebook_name(new_name)) {
d.find('.rename-message').text(i18n.msg._(
"Invalid notebook name. Notebook names must have 1 or more characters and can contain any characters except :/\\. Please enter a new notebook name:")
);
return false;
} else {
d.find('.rename-message').text(i18n.msg._("Renaming..."));
d.find('input[type="text"]').prop('disabled', true);
that.notebook.rename(new_name).then(
function () {
d.modal('hide');
}, function (error) {
d.find('.rename-message').text(error.message || i18n.msg._('Unknown error'));
d.find('input[type="text"]').prop('disabled', false).focus().select();
}
);
return false;
}
}
}
},
open : function () {
/**
* Upon ENTER, click the OK button.
*/
d.find('input[type="text"]').keydown(function (event) {
if (event.which === keyboard.keycodes.enter) {
d.find('.btn-primary').first().click();
return false;
that._nb_name_dialog({
dialog_title: i18n.msg._("Rename Notebook"),
message: i18n.msg._('Enter a new notebook name:'),
message_classname: "rename-message",
button_name: "Rename",
click_handler: function (d) {
var new_name = d.find('input').val();
d.find('.rename-message').text(i18n.msg._("Renaming..."));
d.find('input[type="text"]').prop('disabled', true);
that.notebook.rename(new_name).then(function() {
d.modal('hide');
}, function(error) {
d.find('.rename-message').text(error.message || i18n.msg._('Unknown error'))
.find('input[type="text"]').prop('disabled', false).focus().select();
}
});
d.find('input[type="text"]').focus().select();
);
return false;
}
});
};
Expand Down
4 changes: 4 additions & 0 deletions notebook/templates/notebook.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
data-ws-url="{{ws_url | urlencode}}"
data-notebook-name="{{notebook_name | urlencode}}"
data-notebook-path="{{notebook_path | urlencode}}"
data-server-root="{{server_root}}"
{% endblock %}


Expand Down Expand Up @@ -89,6 +90,9 @@
<li id="copy_notebook"
title="{% trans %}Open a copy of this notebook's contents and start a new kernel{% endtrans %}">
<a href="#">{% trans %}Make a Copy...{% endtrans %}</a></li>
<li id="save_notebook_as"
title="{% trans %}Save a copy of the notebook's contents and start a new kernel{% endtrans %}">
<a href="#">{% trans %}Save as{% endtrans %}</a></li>
<li id="rename_notebook"><a href="#">{% trans %}Rename...{% endtrans %}</a></li>
<li id="save_checkpoint"><a href="#">{% trans %}Save and Checkpoint{% endtrans %}</a></li>
<!-- <hr/> -->
Expand Down

0 comments on commit 5fdf8c2

Please sign in to comment.