Merge pull request #6783 from takluyver/contentsmanager-js

contents.js wrapper for contents service
This commit is contained in:
Min RK 2014-11-03 17:09:49 -08:00
commit ef49e32ef0
9 changed files with 502 additions and 394 deletions

View File

@ -136,6 +136,12 @@ class IPythonHandler(AuthenticatedHandler):
@property
def ws_url(self):
return self.settings.get('websocket_url', '')
@property
def contents_js_source(self):
self.log.debug("Using contents: %s", self.settings.get('contents_js_source',
'services/contents'))
return self.settings.get('contents_js_source', 'services/contents')
#---------------------------------------------------------------
# Manager objects
@ -224,7 +230,8 @@ class IPythonHandler(AuthenticatedHandler):
logged_in=self.logged_in,
login_available=self.login_available,
static_url=self.static_url,
sys_info=sys_info
sys_info=sys_info,
contents_js_source=self.contents_js_source,
)
def get_json_body(self):

View File

@ -563,6 +563,22 @@ define([
);
};
/** Error type for wrapped XHR errors. */
var XHR_ERROR = 'XhrError';
/**
* Wraps an AJAX error as an Error object.
*/
var wrap_ajax_error = function (jqXHR, status, error) {
var wrapped_error = new Error(ajax_error_msg(jqXHR));
wrapped_error.name = XHR_ERROR;
// provide xhr response
wrapped_error.xhr = jqXHR;
wrapped_error.xhr_status = status;
wrapped_error.xhr_error = error;
return wrapped_error;
}
var utils = {
regex_split : regex_split,
uuid : uuid,
@ -588,6 +604,8 @@ define([
ajax_error_msg : ajax_error_msg,
log_ajax_error : log_ajax_error,
requireCodeMirrorMode : requireCodeMirrorMode,
XHR_ERROR : XHR_ERROR,
wrap_ajax_error : wrap_ajax_error
};
// Backwards compatability.

View File

@ -5,6 +5,7 @@ require([
'base/js/namespace',
'jquery',
'notebook/js/notebook',
'contents',
'base/js/utils',
'base/js/page',
'notebook/js/layoutmanager',
@ -27,6 +28,7 @@ require([
IPython,
$,
notebook,
contents,
utils,
page,
layoutmanager,
@ -70,10 +72,14 @@ require([
var save_widget = new savewidget.SaveWidget('span#save_widget', {
events: events,
keyboard_manager: keyboard_manager});
var contents = new contents.Contents($.extend({
events: events},
common_options));
var notebook = new notebook.Notebook('div#notebook', $.extend({
events: events,
keyboard_manager: keyboard_manager,
save_widget: save_widget,
contents: contents,
config: user_config},
common_options));
var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
@ -86,6 +92,7 @@ require([
notebook: notebook});
var menubar = new menubar.MenuBar('#menubar', $.extend({
notebook: notebook,
contents: contents,
layout_manager: layout_manager,
events: events,
save_widget: save_widget,
@ -131,6 +138,7 @@ require([
IPython.page = page;
IPython.layout_manager = layout_manager;
IPython.notebook = notebook;
IPython.contents = contents;
IPython.pager = pager;
IPython.quick_help = quick_help;
IPython.login_widget = login_widget;

View File

@ -21,6 +21,7 @@ define([
// options: dictionary
// Dictionary of keyword arguments.
// notebook: Notebook instance
// contents: ContentManager instance
// layout_manager: LayoutManager instance
// events: $(Events) instance
// save_widget: SaveWidget instance
@ -32,6 +33,7 @@ define([
this.base_url = options.base_url || utils.get_body_data("baseUrl");
this.selector = selector;
this.notebook = options.notebook;
this.contents = options.contents;
this.layout_manager = options.layout_manager;
this.events = options.events;
this.save_widget = options.save_widget;
@ -85,7 +87,26 @@ define([
// File
var that = this;
this.element.find('#new_notebook').click(function () {
that.notebook.new_notebook();
// Create a new notebook in the same path as the current
// notebook's path.
that.contents.new(that.notebook.notebook_path, null, {
ext: ".ipynb",
extra_settings: {async: false}, // So we can open a new window afterwards
success: function (data) {
window.open(
utils.url_join_encode(
that.base_url, 'notebooks',
data.path, data.name
), '_blank');
},
error: function(error) {
dialog.modal({
title : 'Creating Notebook Failed',
body : "The error was: " + error.message,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
});
});
this.element.find('#open_notebook').click(function () {
window.open(utils.url_join_encode(

View File

@ -50,6 +50,7 @@ define([
// Dictionary of keyword arguments.
// events: $(Events) instance
// keyboard_manager: KeyboardManager instance
// contents: Contents instance
// save_widget: SaveWidget instance
// config: dictionary
// base_url : string
@ -61,6 +62,7 @@ define([
this.notebook_name = options.notebook_name;
this.events = options.events;
this.keyboard_manager = options.keyboard_manager;
this.contents = options.contents;
this.save_widget = options.save_widget;
this.tooltip = new tooltip.Tooltip(this.events);
this.ws_url = options.ws_url;
@ -1860,7 +1862,9 @@ define([
}
var data = {
cells: cell_array,
metadata : this.metadata
metadata: this.metadata,
nbformat: this.nbformat,
nbformat_minor: this.nbformat_minor
};
if (trusted != this.trusted) {
this.trusted = trusted;
@ -1904,52 +1908,33 @@ define([
*/
Notebook.prototype.save_notebook = function (extra_settings) {
// Create a JSON model to be sent to the server.
var model = {};
model.name = this.notebook_name;
model.path = this.notebook_path;
model.type = 'notebook';
model.format = 'json';
model.content = this.toJSON();
model.content.nbformat = this.nbformat;
model.content.nbformat_minor = this.nbformat_minor;
var model = {
name : this.notebook_name,
path : this.notebook_path,
type : "notebook",
content : this.toJSON()
};
// time the ajax call for autosave tuning purposes.
var start = new Date().getTime();
// We do the call with settings so we can set cache to false.
var settings = {
processData : false,
cache : false,
type : "PUT",
data : JSON.stringify(model),
contentType: 'application/json',
dataType : "json",
success : $.proxy(this.save_notebook_success, this, start),
error : $.proxy(this.save_notebook_error, this)
};
if (extra_settings) {
for (var key in extra_settings) {
settings[key] = extra_settings[key];
}
}
this.events.trigger('notebook_saving.Notebook');
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name
);
$.ajax(url, settings);
var that = this;
this.contents.save(this.notebook_path, this.notebook_name, model, {
extra_settings: extra_settings,
success: $.proxy(this.save_notebook_success, this, start),
error: function (error) {
that.events.trigger('notebook_save_failed.Notebook');
}
});
};
/**
* Success callback for saving a notebook.
*
* @method save_notebook_success
* @param {Integer} start the time when the save request started
* @param {Integer} start Time when the save request start
* @param {Object} data JSON representation of a notebook
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
Notebook.prototype.save_notebook_success = function (start, data) {
this.set_dirty(false);
if (data.message) {
// save succeeded, but validation failed.
@ -2002,18 +1987,6 @@ define([
}
}
};
/**
* Failure callback for saving a notebook.
*
* @method save_notebook_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error HTTP error message
*/
Notebook.prototype.save_notebook_error = function (xhr, status, error) {
this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
};
/**
* Explicitly trust the output of this notebook.
@ -2065,124 +2038,47 @@ define([
});
};
Notebook.prototype.new_notebook = function(){
var path = this.notebook_path;
var base_url = this.base_url;
var settings = {
processData : false,
cache : false,
type : "POST",
dataType : "json",
async : false,
success : function (data, status, xhr){
var notebook_name = data.name;
window.open(
utils.url_join_encode(
base_url,
'notebooks',
path,
notebook_name
),
'_blank'
);
},
error : utils.log_ajax_error,
};
var url = utils.url_join_encode(
base_url,
'api/contents',
path
);
$.ajax(url,settings);
};
Notebook.prototype.copy_notebook = function(){
var path = this.notebook_path;
var base_url = this.base_url;
var settings = {
processData : false,
cache : false,
type : "POST",
dataType : "json",
data : JSON.stringify({copy_from : this.notebook_name}),
async : false,
success : function (data, status, xhr) {
this.contents.copy(this.notebook_path, null, this.notebook_name, {
// synchronous so we can open a new window on success
extra_settings: {async: false},
success: function (data) {
window.open(utils.url_join_encode(
base_url,
'notebooks',
data.path,
data.name
base_url, 'notebooks', data.path, data.name
), '_blank');
},
error : utils.log_ajax_error,
};
var url = utils.url_join_encode(
base_url,
'api/contents',
path
);
$.ajax(url,settings);
error : utils.log_ajax_error
});
};
Notebook.prototype.rename = function (nbname) {
var that = this;
if (!nbname.match(/\.ipynb$/)) {
nbname = nbname + ".ipynb";
Notebook.prototype.rename = function (new_name) {
if (!new_name.match(/\.ipynb$/)) {
new_name = new_name + ".ipynb";
}
var data = {name: nbname};
var settings = {
processData : false,
cache : false,
type : "PATCH",
data : JSON.stringify(data),
dataType: "json",
contentType: 'application/json',
success : $.proxy(that.rename_success, this),
error : $.proxy(that.rename_error, this)
};
this.events.trigger('rename_notebook.Notebook', data);
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name
);
$.ajax(url, settings);
var that = this;
this.contents.rename(this.notebook_path, this.notebook_name,
this.notebook_path, new_name, {
success: function (json) {
var name = that.notebook_name = json.name;
that.session.rename_notebook(name, json.path);
that.events.trigger('notebook_renamed.Notebook', json);
},
error: $.proxy(this.rename_error, this)
});
};
Notebook.prototype.delete = function () {
var that = this;
var settings = {
processData : false,
cache : false,
type : "DELETE",
dataType: "json",
error : utils.log_ajax_error,
};
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name
);
$.ajax(url, settings);
this.contents.delete(this.notebook_name, this.notebook_path);
};
Notebook.prototype.rename_success = function (json, status, xhr) {
var name = this.notebook_name = json.name;
var path = json.path;
this.session.rename_notebook(name, path);
this.events.trigger('notebook_renamed.Notebook', json);
};
Notebook.prototype.rename_error = function (xhr, status, error) {
Notebook.prototype.rename_error = function (error) {
var that = this;
var dialog_body = $('<div/>').append(
$("<p/>").text('This notebook name already exists.')
);
this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
this.events.trigger('notebook_rename_failed.Notebook', error);
dialog.modal({
notebook: this,
keyboard_manager: this.keyboard_manager,
@ -2193,7 +2089,7 @@ define([
"OK": {
class: "btn-primary",
click: function () {
this.save_widget.rename_notebook({notebook:that});
that.save_widget.rename_notebook({notebook:that});
}}
},
open : function (event, ui) {
@ -2216,26 +2112,13 @@ define([
* @param {String} notebook_name and path A notebook to load
*/
Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
var that = this;
this.notebook_name = notebook_name;
this.notebook_path = notebook_path;
// We do the call with settings so we can set cache to false.
var settings = {
processData : false,
cache : false,
type : "GET",
dataType : "json",
success : $.proxy(this.load_notebook_success,this),
error : $.proxy(this.load_notebook_error,this),
};
this.events.trigger('notebook_loading.Notebook');
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name
);
$.ajax(url, settings);
this.contents.load(notebook_path, notebook_name, {
success: $.proxy(this.load_notebook_success, this),
error: $.proxy(this.load_notebook_error, this)
});
};
/**
@ -2245,10 +2128,8 @@ define([
*
* @method load_notebook_success
* @param {Object} data JSON representation of a notebook
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.load_notebook_success = function (data, status, xhr) {
Notebook.prototype.load_notebook_success = function (data) {
var failed;
try {
this.fromJSON(data);
@ -2393,20 +2274,18 @@ define([
* Failure callback for loading a notebook from the server.
*
* @method load_notebook_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error HTTP error message
* @param {Error} error
*/
Notebook.prototype.load_notebook_error = function (xhr, status, error) {
this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
utils.log_ajax_error(xhr, status, error);
var msg = $("<div>");
if (xhr.status === 400) {
msg.text(utils.ajax_error_msg(xhr));
} else if (xhr.status === 500) {
msg.text("An unknown error occurred while loading this notebook. " +
Notebook.prototype.load_notebook_error = function (error) {
this.events.trigger('notebook_load_failed.Notebook', error);
var msg;
if (error.name = utils.XHR_ERROR && error.xhr.status === 500) {
utils.log_ajax_error(error.xhr, error.xhr_status, error.xhr_error);
msg = "An unknown error occurred while loading this notebook. " +
"This version can load notebook formats " +
"v" + this.nbformat + " or earlier. See the server log for details.");
"v" + this.nbformat + " or earlier. See the server log for details.";
} else {
msg = error.message;
}
dialog.modal({
notebook: this,
@ -2459,18 +2338,13 @@ define([
* @method list_checkpoints
*/
Notebook.prototype.list_checkpoints = function () {
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name,
'checkpoints'
);
$.get(url).done(
$.proxy(this.list_checkpoints_success, this)
).fail(
$.proxy(this.list_checkpoints_error, this)
);
var that = this;
this.contents.list_checkpoints(this.notebook_path, this.notebook_name, {
success: $.proxy(this.list_checkpoints_success, this),
error: function(error) {
that.events.trigger('list_checkpoints_failed.Notebook');
}
});
};
/**
@ -2478,10 +2352,8 @@ define([
*
* @method list_checkpoint_success
* @param {Object} data JSON representation of a checkpoint
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
Notebook.prototype.list_checkpoints_success = function (data) {
data = $.parseJSON(data);
this.checkpoints = data;
if (data.length) {
@ -2492,36 +2364,19 @@ define([
this.events.trigger('checkpoints_listed.Notebook', [data]);
};
/**
* Failure callback for listing a checkpoint.
*
* @method list_checkpoint_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error_msg HTTP error message
*/
Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
this.events.trigger('list_checkpoints_failed.Notebook');
};
/**
* Create a checkpoint of this notebook on the server from the most recent save.
*
* @method create_checkpoint
*/
Notebook.prototype.create_checkpoint = function () {
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name,
'checkpoints'
);
$.post(url).done(
$.proxy(this.create_checkpoint_success, this)
).fail(
$.proxy(this.create_checkpoint_error, this)
);
var that = this;
this.contents.create_checkpoint(this.notebook_path, this.notebook_name, {
success: $.proxy(this.create_checkpoint_success, this),
error: function (error) {
that.events.trigger('checkpoint_failed.Notebook');
}
});
};
/**
@ -2529,27 +2384,13 @@ define([
*
* @method create_checkpoint_success
* @param {Object} data JSON representation of a checkpoint
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
Notebook.prototype.create_checkpoint_success = function (data) {
data = $.parseJSON(data);
this.add_checkpoint(data);
this.events.trigger('checkpoint_created.Notebook', data);
};
/**
* Failure callback for creating a checkpoint.
*
* @method create_checkpoint_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error_msg HTTP error message
*/
Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
this.events.trigger('checkpoint_failed.Notebook');
};
Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
var that = this;
checkpoint = checkpoint || this.last_checkpoint;
@ -2599,46 +2440,26 @@ define([
*/
Notebook.prototype.restore_checkpoint = function (checkpoint) {
this.events.trigger('notebook_restoring.Notebook', checkpoint);
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name,
'checkpoints',
checkpoint
);
$.post(url).done(
$.proxy(this.restore_checkpoint_success, this)
).fail(
$.proxy(this.restore_checkpoint_error, this)
);
var that = this;
this.contents.restore_checkpoint(this.notebook_path, this.notebook_name,
checkpoint, {
success: $.proxy(this.restore_checkpoint_success, this),
error: function (error) {
that.events.trigger('checkpoint_restore_failed.Notebook');
}
});
};
/**
* Success callback for restoring a notebook to a checkpoint.
*
* @method restore_checkpoint_success
* @param {Object} data (ignored, should be empty)
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
Notebook.prototype.restore_checkpoint_success = function () {
this.events.trigger('checkpoint_restored.Notebook');
this.load_notebook(this.notebook_name, this.notebook_path);
};
/**
* Failure callback for restoring a notebook to a checkpoint.
*
* @method restore_checkpoint_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error_msg HTTP error message
*/
Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
this.events.trigger('checkpoint_restore_failed.Notebook');
};
/**
* Delete a notebook checkpoint.
*
@ -2647,18 +2468,13 @@ define([
*/
Notebook.prototype.delete_checkpoint = function (checkpoint) {
this.events.trigger('notebook_restoring.Notebook', checkpoint);
var url = utils.url_join_encode(
this.base_url,
'api/contents',
this.notebook_path,
this.notebook_name,
'checkpoints',
checkpoint
);
$.ajax(url, {
type: 'DELETE',
var that = this;
this.contents.delete_checkpoint(this.notebook_path, this.notebook_name,
checkpoint, {
success: $.proxy(this.delete_checkpoint_success, this),
error: $.proxy(this.delete_checkpoint_error, this)
error: function (error) {
that.events.trigger('checkpoint_delete_failed.Notebook', error);
}
});
};
@ -2666,27 +2482,12 @@ define([
* Success callback for deleting a notebook checkpoint
*
* @method delete_checkpoint_success
* @param {Object} data (ignored, should be empty)
* @param {String} status Description of response status
* @param {jqXHR} xhr jQuery Ajax object
*/
Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
this.events.trigger('checkpoint_deleted.Notebook', data);
Notebook.prototype.delete_checkpoint_success = function () {
this.events.trigger('checkpoint_deleted.Notebook');
this.load_notebook(this.notebook_name, this.notebook_path);
};
/**
* Failure callback for deleting a notebook checkpoint.
*
* @method delete_checkpoint_error
* @param {jqXHR} xhr jQuery Ajax object
* @param {String} status Description of response status
* @param {String} error HTTP error message
*/
Notebook.prototype.delete_checkpoint_error = function (xhr, status, error) {
this.events.trigger('checkpoint_delete_failed.Notebook', [xhr, status, error]);
};
// For backwards compatability.
IPython.Notebook = Notebook;

View File

@ -0,0 +1,285 @@
// Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'base/js/namespace',
'jquery',
'base/js/utils',
'base/js/dialog',
], function(IPython, $, utils, dialog) {
var Contents = function(options) {
// Constructor
//
// A contents handles passing file operations
// to the back-end. This includes checkpointing
// with the normal file operations.
//
// Parameters:
// options: dictionary
// Dictionary of keyword arguments.
// base_url: string
this.base_url = options.base_url;
};
/** Error type */
Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';
Contents.DirectoryNotEmptyError = function() {
// Constructor
//
// An error representing the result of attempting to delete a non-empty
// directory.
this.message = 'A directory must be empty before being deleted.';
}
Contents.DirectoryNotEmptyError.prototype = new Error;
Contents.DirectoryNotEmptyError.prototype.name =
Contents.DIRECTORY_NOT_EMPTY_ERROR;
Contents.prototype.api_url = function() {
var url_parts = [this.base_url, 'api/contents'].concat(
Array.prototype.slice.apply(arguments));
return utils.url_join_encode.apply(null, url_parts);
};
/**
* Creates a basic error handler that wraps a jqXHR error as an Error.
*
* Takes a callback that accepts an Error, and returns a callback that can
* be passed directly to $.ajax, which will wrap the error from jQuery
* as an Error, and pass that to the original callback.
*
* @method create_basic_error_handler
* @param{Function} callback
* @return{Function}
*/
Contents.prototype.create_basic_error_handler = function(callback) {
if (!callback) {
return function(xhr, status, error) { };
}
return function(xhr, status, error) {
callback(utils.wrap_ajax_error(xhr, status, error));
};
}
/**
* File Functions (including notebook operations)
*/
/**
* Load a file.
*
* Calls success with file JSON model, or error with error.
*
* @method load_notebook
* @param {String} path
* @param {String} name
* @param {Function} success
* @param {Function} error
*/
Contents.prototype.load = function (path, name, options) {
// We do the call with settings so we can set cache to false.
var settings = {
processData : false,
cache : false,
type : "GET",
dataType : "json",
success : options.success,
error : this.create_basic_error_handler(options.error)
};
var url = this.api_url(path, name);
$.ajax(url, settings);
};
/**
* Creates a new notebook file at the specified directory path.
*
* @method scroll_to_cell
* @param {String} path The path to create the new notebook at
* @param {String} name Name for new file. Chosen by server if unspecified.
* @param {Object} options:
* ext: file extension to use if name unspecified
*/
Contents.prototype.new = function(path, name, options) {
var method, data;
if (name) {
method = "PUT";
} else {
method = "POST";
data = JSON.stringify({ext: options.ext || ".ipynb"});
}
var settings = {
processData : false,
type : method,
data: data,
dataType : "json",
success : options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
if (options.extra_settings) {
$.extend(settings, options.extra_settings);
}
$.ajax(this.api_url(path), settings);
};
Contents.prototype.delete = function(name, path, options) {
var error_callback = options.error || function() {};
var that = this;
var settings = {
processData : false,
type : "DELETE",
dataType : "json",
success : options.success || function() {},
error : function(xhr, status, error) {
// TODO: update IPEP27 to specify errors more precisely, so
// that error types can be detected here with certainty.
if (xhr.status === 400) {
error_callback(new Contents.DirectoryNotEmptyError());
}
error_callback(utils.wrap_ajax_error(xhr, status, error));
}
};
var url = this.api_url(path, name);
$.ajax(url, settings);
};
Contents.prototype.rename = function(path, name, new_path, new_name, options) {
var data = {name: new_name, path: new_path};
var settings = {
processData : false,
type : "PATCH",
data : JSON.stringify(data),
dataType: "json",
contentType: 'application/json',
success : options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
var url = this.api_url(path, name);
$.ajax(url, settings);
};
Contents.prototype.save = function(path, name, model, options) {
// We do the call with settings so we can set cache to false.
var settings = {
processData : false,
type : "PUT",
data : JSON.stringify(model),
contentType: 'application/json',
success : options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
if (options.extra_settings) {
$.extend(settings, options.extra_settings);
}
var url = this.api_url(path, name);
$.ajax(url, settings);
};
Contents.prototype.copy = function(to_path, to_name, from, options) {
var url, method;
if (to_name) {
url = this.api_url(to_path, to_name);
method = "PUT";
} else {
url = this.api_url(to_path);
method = "POST";
}
var settings = {
processData : false,
type: method,
data: JSON.stringify({copy_from: from}),
dataType : "json",
success: options.success || function() {},
error: this.create_basic_error_handler(options.error)
};
if (options.extra_settings) {
$.extend(settings, options.extra_settings);
}
$.ajax(url, settings);
};
/**
* Checkpointing Functions
*/
Contents.prototype.create_checkpoint = function(path, name, options) {
var url = this.api_url(path, name, 'checkpoints');
var settings = {
type : "POST",
success: options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
$.ajax(url, settings);
};
Contents.prototype.list_checkpoints = function(path, name, options) {
var url = this.api_url(path, name, 'checkpoints');
var settings = {
type : "GET",
success: options.success,
error : this.create_basic_error_handler(options.error)
};
$.ajax(url, settings);
};
Contents.prototype.restore_checkpoint = function(path, name, checkpoint_id, options) {
var url = this.api_url(path, name, 'checkpoints', checkpoint_id);
var settings = {
type : "POST",
success: options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
$.ajax(url, settings);
};
Contents.prototype.delete_checkpoint = function(path, name, checkpoint_id, options) {
var url = this.api_url(path, name, 'checkpoints', checkpoint_id);
var settings = {
type : "DELETE",
success: options.success || function() {},
error : this.create_basic_error_handler(options.error)
};
$.ajax(url, settings);
};
/**
* File management functions
*/
/**
* List notebooks and directories at a given path
*
* On success, load_callback is called with an array of dictionaries
* representing individual files or directories. Each dictionary has
* the keys:
* type: "notebook" or "directory"
* name: the name of the file or directory
* created: created date
* last_modified: last modified dat
* path: the path
* @method list_notebooks
* @param {String} path The path to list notebooks in
* @param {Function} load_callback called with list of notebooks on success
* @param {Function} error called with ajax results on error
*/
Contents.prototype.list_contents = function(path, options) {
var settings = {
processData : false,
cache : false,
type : "GET",
dataType : "json",
success : options.success,
error : this.create_basic_error_handler(options.error)
};
$.ajax(this.api_url(path), settings);
};
IPython.Contents = Contents;
return {'Contents': Contents};
});

View File

@ -7,6 +7,7 @@ require([
'base/js/events',
'base/js/page',
'base/js/utils',
'contents',
'tree/js/notebooklist',
'tree/js/clusterlist',
'tree/js/sessionlist',
@ -23,6 +24,7 @@ require([
events,
page,
utils,
contents,
notebooklist,
clusterlist,
sesssionlist,
@ -39,7 +41,11 @@ require([
session_list = new sesssionlist.SesssionList($.extend({
events: events},
common_options));
contents = new contents.Contents($.extend({
events: events},
common_options));
notebook_list = new notebooklist.NotebookList('#notebook_list', $.extend({
contents: contents,
session_list: session_list},
common_options));
cluster_list = new clusterlist.ClusterList('#cluster_list', common_options);
@ -54,7 +60,24 @@ require([
login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
$('#new_notebook').click(function (e) {
notebook_list.new_notebook();
contents.new(common_options.notebook_path, null, {
ext: ".ipynb",
extra_settings: {async: false}, // So we can open a new window afterwards
success: function (data) {
window.open(
utils.url_join_encode(
common_options.base_url, 'notebooks',
data.path, data.name
), '_blank');
},
error: function(error) {
dialog.modal({
title : 'Creating Notebook Failed',
body : "The error was: " + error.message,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
});
});
var interval_id=0;
@ -118,5 +141,4 @@ require([
if (window.location.hash) {
$("#tabs").find("a[href=" + window.location.hash + "]").click();
}
});

View File

@ -20,6 +20,7 @@ define([
// element_name: string
// base_url: string
// notebook_path: string
// contents: Contents instance
var that = this;
this.session_list = options.session_list;
// allow code re-use by just changing element_name in kernellist.js
@ -34,6 +35,7 @@ define([
this.sessions = {};
this.base_url = options.base_url || utils.get_body_data("baseUrl");
this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath");
this.contents = options.contents;
if (this.session_list && this.session_list.events) {
this.session_list.events.on('sessions_loaded.Dashboard',
function(e, d) { that.sessions_loaded(d); });
@ -139,38 +141,27 @@ define([
};
NotebookList.prototype.load_list = function () {
var that = this;
var settings = {
processData : false,
cache : false,
type : "GET",
dataType : "json",
success : $.proxy(this.list_loaded, this),
error : $.proxy( function(xhr, status, error){
utils.log_ajax_error(xhr, status, error);
that.list_loaded([], null, null, {msg:"Error connecting to server."});
},this)
};
var url = utils.url_join_encode(
this.base_url,
'api',
'contents',
this.notebook_path
);
$.ajax(url, settings);
var that = this
this.contents.list_contents(that.notebook_path, {
success: $.proxy(this.draw_notebook_list, this),
error: function(error) {
that.draw_notebook_list({content: []}, "Server error: " + error.message);
}
});
};
NotebookList.prototype.list_loaded = function (data, status, xhr, param) {
var message = 'Notebook list empty.';
if (param !== undefined && param.msg) {
message = param.msg;
}
/**
* Draw the list of notebooks
* @method draw_notebook_list
* @param {Array} list An array of dictionaries representing files or
* directories.
* @param {String} error_msg An error message
*/
NotebookList.prototype.draw_notebook_list = function (list, error_msg) {
var message = error_msg || 'Notebook list empty.';
var item = null;
var model = null;
var list = data.content;
var len = list.length;
var len = list.content.length;
this.clear_list();
var n_uploads = this.element.children('.list_item').length;
if (len === 0) {
@ -192,7 +183,7 @@ define([
offset += 1;
}
for (var i=0; i<len; i++) {
model = list[i];
model = list.content[i];
item = this.new_item(i+offset);
this.add_link(model, item);
}
@ -329,8 +320,9 @@ define([
// We use the filename from the parent list_item element's
// data because the outer scope's values change as we iterate through the loop.
var parent_item = that.parents('div.list_item');
var name = parent_item.data('name');
var message = 'Are you sure you want to permanently delete the file: ' + name + '?';
var name = parent_item.data('nbname');
var path = parent_item.data('path');
var message = 'Are you sure you want to permanently delete the file: ' + nbname + '?';
dialog.modal({
title : "Delete file",
body : message,
@ -338,23 +330,11 @@ define([
Delete : {
class: "btn-danger",
click: function() {
var settings = {
processData : false,
cache : false,
type : "DELETE",
dataType : "json",
success : function (data, status, xhr) {
parent_item.remove();
},
error : utils.log_ajax_error,
};
var url = utils.url_join_encode(
notebooklist.base_url,
'api/contents',
notebooklist.notebook_path,
name
);
$.ajax(url, settings);
notebooklist.contents.delete(name, path, {
success: function() {
notebooklist.notebook_deleted(path, name);
}
});
}
},
Cancel : {}
@ -365,6 +345,17 @@ define([
item.find(".item_buttons").text("").append(delete_button);
};
NotebookList.prototype.notebook_deleted = function(path, name) {
// Remove the deleted notebook.
$( ":data(nbname)" ).each(function() {
var element = $( this );
if (element.data( "nbname" ) == d.name &&
element.data( "path" ) == d.path) {
element.remove();
}
});
}
NotebookList.prototype.add_upload_button = function (item, type) {
var that = this;
@ -487,53 +478,7 @@ define([
};
NotebookList.prototype.new_notebook = function(){
var path = this.notebook_path;
var base_url = this.base_url;
var settings = {
processData : false,
cache : false,
type : "POST",
dataType : "json",
async : false,
success : function (data, status, xhr) {
var notebook_name = data.name;
window.open(
utils.url_join_encode(
base_url,
'notebooks',
path,
notebook_name),
'_blank'
);
},
error : $.proxy(this.new_notebook_failed, this),
};
var url = utils.url_join_encode(
base_url,
'api/contents',
path
);
$.ajax(url, settings);
};
NotebookList.prototype.new_notebook_failed = function (xhr, status, error) {
utils.log_ajax_error(xhr, status, error);
var msg;
if (xhr.responseJSON && xhr.responseJSON.message) {
msg = xhr.responseJSON.message;
} else {
msg = xhr.statusText;
}
dialog.modal({
title : 'Creating Notebook Failed',
body : "The error was: " + msg,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
};
// Backwards compatability.
// Backwards compatability.
IPython.NotebookList = NotebookList;
return {'NotebookList': NotebookList};

View File

@ -29,7 +29,8 @@
highlight: 'components/highlight.js/build/highlight.pack',
moment: "components/moment/moment",
codemirror: 'components/codemirror',
termjs: "components/term.js/src/term"
termjs: "components/term.js/src/term",
contents: '{{ contents_js_source }}',
},
shim: {
underscore: {