##// END OF EJS Templates
add missing error dialogs to tree actions...
Min RK -
Show More
@@ -1,101 +1,105 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'jquery',
6 6 'base/js/namespace',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 ], function ($, IPython, utils, dialog) {
10 10 "use strict";
11 11
12 12 var NewNotebookWidget = function (selector, options) {
13 13 this.selector = selector;
14 14 this.base_url = options.base_url;
15 15 this.notebook_path = options.notebook_path;
16 16 this.contents = options.contents;
17 17 this.default_kernel = null;
18 18 this.kernelspecs = {};
19 19 if (this.selector !== undefined) {
20 20 this.element = $(selector);
21 21 this.request_kernelspecs();
22 22 }
23 23 this.bind_events();
24 24 };
25 25
26 26 NewNotebookWidget.prototype.bind_events = function () {
27 27 var that = this;
28 28 this.element.find('#new_notebook').click(function () {
29 29 that.new_notebook();
30 30 });
31 31 };
32 32
33 33 NewNotebookWidget.prototype.request_kernelspecs = function () {
34 34 /** request and then load kernel specs */
35 35 var url = utils.url_join_encode(this.base_url, 'api/kernelspecs');
36 36 utils.promising_ajax(url).then($.proxy(this._load_kernelspecs, this));
37 37 };
38 38
39 39 NewNotebookWidget.prototype._load_kernelspecs = function (data) {
40 40 /** load kernelspec list */
41 41 var that = this;
42 42 this.kernelspecs = data.kernelspecs;
43 43 var menu = this.element.find("#notebook-kernels");
44 44 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
45 45 var da = data.kernelspecs[a].spec.display_name;
46 46 var db = data.kernelspecs[b].spec.display_name;
47 47 if (da === db) {
48 48 return 0;
49 49 } else if (da > db) {
50 50 return 1;
51 51 } else {
52 52 return -1;
53 53 }
54 54 });
55 55
56 56 // Create the kernel list in reverse order because
57 57 // the .after insertion causes each item to be added
58 58 // to the top of the list.
59 59 for (var i = keys.length - 1; i >= 0; i--) {
60 60 var ks = this.kernelspecs[keys[i]];
61 61 var li = $("<li>")
62 62 .attr("id", "kernel-" +ks.name)
63 63 .data('kernelspec', ks).append(
64 64 $('<a>')
65 65 .attr('href', '#')
66 66 .click($.proxy(this.new_notebook, this, ks.name))
67 67 .text(ks.spec.display_name)
68 68 .attr('title', 'Create a new notebook with ' + ks.spec.display_name)
69 69 );
70 70 menu.after(li);
71 71 }
72 72 };
73 73
74 74 NewNotebookWidget.prototype.new_notebook = function (kernel_name) {
75 75 /** create and open a new notebook */
76 76 var that = this;
77 77 kernel_name = kernel_name || this.default_kernel;
78 78 var w = window.open();
79 79 this.contents.new_untitled(that.notebook_path, {type: "notebook"}).then(
80 80 function (data) {
81 81 var url = utils.url_join_encode(
82 82 that.base_url, 'notebooks', data.path
83 83 );
84 84 if (kernel_name) {
85 85 url += "?kernel_name=" + kernel_name;
86 86 }
87 87 w.location = url;
88 },
89 function (error) {
90 w.close();
91 dialog.modal({
92 title : 'Creating Notebook Failed',
93 body : "The error was: " + error.message,
94 buttons : {'OK' : {'class' : 'btn-primary'}}
95 });
96 }
97 );
88 }).catch(function (e) {
89 w.close();
90 dialog.modal({
91 title : 'Creating Notebook Failed',
92 body : $('<div/>')
93 .text("An error occurred while creating a new notebook.")
94 .append($('<div/>')
95 .addClass('alert alert-danger')
96 .text(e.message || e)),
97 buttons: {
98 OK: {'class' : 'btn-primary'}
99 }
100 });
101 });
98 102 };
99 103
100 104 return {'NewNotebookWidget': NewNotebookWidget};
101 105 });
@@ -1,722 +1,754 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 'base/js/events',
10 10 'base/js/keyboard',
11 11 ], function(IPython, $, utils, dialog, events, keyboard) {
12 12 "use strict";
13 13
14 14 var NotebookList = function (selector, options) {
15 15 /**
16 16 * Constructor
17 17 *
18 18 * Parameters:
19 19 * selector: string
20 20 * options: dictionary
21 21 * Dictionary of keyword arguments.
22 22 * session_list: SessionList instance
23 23 * element_name: string
24 24 * base_url: string
25 25 * notebook_path: string
26 26 * contents: Contents instance
27 27 */
28 28 var that = this;
29 29 this.session_list = options.session_list;
30 30 // allow code re-use by just changing element_name in kernellist.js
31 31 this.element_name = options.element_name || 'notebook';
32 32 this.selector = selector;
33 33 if (this.selector !== undefined) {
34 34 this.element = $(selector);
35 35 this.style();
36 36 this.bind_events();
37 37 }
38 38 this.notebooks_list = [];
39 39 this.sessions = {};
40 40 this.base_url = options.base_url || utils.get_body_data("baseUrl");
41 41 this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath");
42 42 this.contents = options.contents;
43 43 if (this.session_list && this.session_list.events) {
44 44 this.session_list.events.on('sessions_loaded.Dashboard',
45 45 function(e, d) { that.sessions_loaded(d); });
46 46 }
47 47 };
48 48
49 49 NotebookList.prototype.style = function () {
50 50 var prefix = '#' + this.element_name;
51 51 $(prefix + '_toolbar').addClass('list_toolbar');
52 52 $(prefix + '_list_info').addClass('toolbar_info');
53 53 $(prefix + '_buttons').addClass('toolbar_buttons');
54 54 $(prefix + '_list_header').addClass('list_header');
55 55 this.element.addClass("list_container");
56 56 };
57 57
58 58 NotebookList.prototype.bind_events = function () {
59 59 var that = this;
60 60 $('#refresh_' + this.element_name + '_list').click(function () {
61 61 that.load_sessions();
62 62 });
63 63 this.element.bind('dragover', function () {
64 64 return false;
65 65 });
66 66 this.element.bind('drop', function(event){
67 67 that.handleFilesUpload(event,'drop');
68 68 return false;
69 69 });
70 70
71 71 // Bind events for singleton controls.
72 72 if (!NotebookList._bound_singletons) {
73 73 NotebookList._bound_singletons = true;
74 74 $('#new-file').click(function(e) {
75 75 var w = window.open();
76 76 that.contents.new_untitled(that.notebook_path || '', {type: 'file', ext: '.txt'}).then(function(data) {
77 77 var url = utils.url_join_encode(
78 78 that.base_url, 'edit', data.path
79 79 );
80 80 w.location = url;
81 }).catch(function (e) {
82 w.close();
83 dialog.modal({
84 title: 'Creating File Failed',
85 body: $('<div/>')
86 .text("An error occurred while creating a new file.")
87 .append($('<div/>')
88 .addClass('alert alert-danger')
89 .text(e.message || e)),
90 buttons: {
91 OK: {'class': 'btn-primary'}
92 }
93 });
81 94 });
82 95 that.load_sessions();
83 96 });
84 97 $('#new-folder').click(function(e) {
85 98 that.contents.new_untitled(that.notebook_path || '', {type: 'directory'})
86 99 .then(function(){
87 100 that.load_list();
101 }).catch(function (e) {
102 dialog.modal({
103 title: 'Creating Folder Failed',
104 body: $('<div/>')
105 .text("An error occurred while creating a new folder.")
106 .append($('<div/>')
107 .addClass('alert alert-danger')
108 .text(e.message || e)),
109 buttons: {
110 OK: {'class': 'btn-primary'}
111 }
112 });
88 113 });
114 that.load_sessions();
89 115 });
90 116
91 117 $('.rename-button').click($.proxy(this.rename_selected, this));
92 118 $('.shutdown-button').click($.proxy(this.shutdown_selected, this));
93 119 $('.duplicate-button').click($.proxy(this.duplicate_selected, this));
94 120 $('.delete-button').click($.proxy(this.delete_selected, this));
95 121 }
96 122 };
97 123
98 124 NotebookList.prototype.handleFilesUpload = function(event, dropOrForm) {
99 125 var that = this;
100 126 var files;
101 127 if(dropOrForm =='drop'){
102 128 files = event.originalEvent.dataTransfer.files;
103 129 } else
104 130 {
105 131 files = event.originalEvent.target.files;
106 132 }
107 133 for (var i = 0; i < files.length; i++) {
108 134 var f = files[i];
109 135 var name_and_ext = utils.splitext(f.name);
110 136 var file_ext = name_and_ext[1];
111 137
112 138 var reader = new FileReader();
113 139 if (file_ext === '.ipynb') {
114 140 reader.readAsText(f);
115 141 } else {
116 142 // read non-notebook files as binary
117 143 reader.readAsArrayBuffer(f);
118 144 }
119 145 var item = that.new_item(0, true);
120 146 item.addClass('new-file');
121 147 that.add_name_input(f.name, item, file_ext == '.ipynb' ? 'notebook' : 'file');
122 148 // Store the list item in the reader so we can use it later
123 149 // to know which item it belongs to.
124 150 $(reader).data('item', item);
125 151 reader.onload = function (event) {
126 152 var item = $(event.target).data('item');
127 153 that.add_file_data(event.target.result, item);
128 154 that.add_upload_button(item);
129 155 };
130 156 reader.onerror = function (event) {
131 157 var item = $(event.target).data('item');
132 158 var name = item.data('name');
133 159 item.remove();
134 160 dialog.modal({
135 161 title : 'Failed to read file',
136 162 body : "Failed to read file '" + name + "'",
137 163 buttons : {'OK' : { 'class' : 'btn-primary' }}
138 164 });
139 165 };
140 166 }
141 167 // Replace the file input form wth a clone of itself. This is required to
142 168 // reset the form. Otherwise, if you upload a file, delete it and try to
143 169 // upload it again, the changed event won't fire.
144 170 var form = $('input.fileinput');
145 171 form.replaceWith(form.clone(true));
146 172 return false;
147 173 };
148 174
149 175 NotebookList.prototype.clear_list = function (remove_uploads) {
150 176 /**
151 177 * Clears the navigation tree.
152 178 *
153 179 * Parameters
154 180 * remove_uploads: bool=False
155 181 * Should upload prompts also be removed from the tree.
156 182 */
157 183 if (remove_uploads) {
158 184 this.element.children('.list_item').remove();
159 185 } else {
160 186 this.element.children('.list_item:not(.new-file)').remove();
161 187 }
162 188 };
163 189
164 190 NotebookList.prototype.load_sessions = function(){
165 191 this.session_list.load_sessions();
166 192 };
167 193
168 194
169 195 NotebookList.prototype.sessions_loaded = function(data){
170 196 this.sessions = data;
171 197 this.load_list();
172 198 };
173 199
174 200 NotebookList.prototype.load_list = function () {
175 201 var that = this;
176 202 this.contents.list_contents(that.notebook_path).then(
177 203 $.proxy(this.draw_notebook_list, this),
178 204 function(error) {
179 205 that.draw_notebook_list({content: []}, "Server error: " + error.message);
180 206 }
181 207 );
182 208 };
183 209
184 210 /**
185 211 * Draw the list of notebooks
186 212 * @method draw_notebook_list
187 213 * @param {Array} list An array of dictionaries representing files or
188 214 * directories.
189 215 * @param {String} error_msg An error message
190 216 */
191 217
192 218
193 219 var type_order = {'directory':0,'notebook':1,'file':2};
194 220
195 221 NotebookList.prototype.draw_notebook_list = function (list, error_msg) {
196 222 list.content.sort(function(a, b) {
197 223 if (type_order[a['type']] < type_order[b['type']]) {
198 224 return -1;
199 225 }
200 226 if (type_order[a['type']] > type_order[b['type']]) {
201 227 return 1;
202 228 }
203 229 if (a['name'] < b['name']) {
204 230 return -1;
205 231 }
206 232 if (a['name'] > b['name']) {
207 233 return 1;
208 234 }
209 235 return 0;
210 236 });
211 237 var message = error_msg || 'Notebook list empty.';
212 238 var item = null;
213 239 var model = null;
214 240 var len = list.content.length;
215 241 this.clear_list();
216 242 var n_uploads = this.element.children('.list_item').length;
217 243 if (len === 0) {
218 244 item = this.new_item(0);
219 245 var span12 = item.children().first();
220 246 span12.empty();
221 247 span12.append($('<div style="margin:auto;text-align:center;color:grey"/>').text(message));
222 248 }
223 249 var path = this.notebook_path;
224 250 var offset = n_uploads;
225 251 if (path !== '') {
226 252 item = this.new_item(offset, false);
227 253 model = {
228 254 type: 'directory',
229 255 name: '..',
230 256 path: utils.url_path_split(path)[0],
231 257 };
232 258 this.add_link(model, item);
233 259 offset += 1;
234 260 }
235 261 for (var i=0; i<len; i++) {
236 262 model = list.content[i];
237 263 item = this.new_item(i+offset, true);
238 264 this.add_link(model, item);
239 265 }
240 266 // Trigger an event when we've finished drawing the notebook list.
241 267 events.trigger('draw_notebook_list.NotebookList');
242 268 this._selection_changed();
243 269 };
244 270
245 271
246 272 /**
247 273 * Creates a new item.
248 274 * @param {integer} index
249 275 * @param {boolean} [selectable] - tristate, undefined: don't draw checkbox,
250 276 * false: don't draw checkbox but pad
251 277 * where it should be, true: draw checkbox.
252 278 * @return {JQuery} row
253 279 */
254 280 NotebookList.prototype.new_item = function (index, selectable) {
255 281 var row = $('<div/>')
256 282 .addClass("list_item")
257 283 .addClass("row");
258 284
259 285 var item = $("<div/>")
260 286 .addClass("col-md-12")
261 287 .appendTo(row);
262 288
263 289 var checkbox;
264 290 if (selectable !== undefined) {
265 291 checkbox = $('<input/>')
266 292 .attr('type', 'checkbox')
267 293 .attr('title', 'Click here to rename, delete, etc.')
268 294 .appendTo(item);
269 295 }
270 296
271 297 $('<i/>')
272 298 .addClass('item_icon')
273 299 .appendTo(item);
274 300
275 301 var link = $("<a/>")
276 302 .addClass("item_link")
277 303 .appendTo(item);
278 304
279 305 $("<span/>")
280 306 .addClass("item_name")
281 307 .appendTo(link);
282 308
283 309 if (selectable === false) {
284 310 checkbox.css('visibility', 'hidden');
285 311 } else if (selectable === true) {
286 312 var that = this;
287 313 link.click(function(e) {
288 314 e.stopPropagation();
289 315 });
290 316 checkbox.click(function(e) {
291 317 e.stopPropagation();
292 318 that._selection_changed();
293 319 });
294 320 row.click(function(e) {
295 321 e.stopPropagation();
296 322 checkbox.prop('checked', !checkbox.prop('checked'));
297 323 that._selection_changed();
298 324 });
299 325 }
300 326
301 327 var buttons = $('<div/>')
302 328 .addClass("item_buttons pull-right")
303 329 .appendTo(item);
304 330
305 331 $('<div/>')
306 332 .addClass('running-indicator')
307 333 .text('Running')
308 334 .css('visibility', 'hidden')
309 335 .appendTo(buttons);
310 336
311 337 if (index === -1) {
312 338 this.element.append(row);
313 339 } else {
314 340 this.element.children().eq(index).after(row);
315 341 }
316 342 return row;
317 343 };
318 344
319 345
320 346 NotebookList.icons = {
321 347 directory: 'folder_icon',
322 348 notebook: 'notebook_icon',
323 349 file: 'file_icon',
324 350 };
325 351
326 352 NotebookList.uri_prefixes = {
327 353 directory: 'tree',
328 354 notebook: 'notebooks',
329 355 file: 'edit',
330 356 };
331 357
332 358 NotebookList.prototype._selection_changed = function() {
333 359 var selected = [];
334 360 var has_running_notebook = false;
335 361 var has_directory = false;
336 362 var has_file = false;
337 363 var that = this;
338 364 $('.list_item :checked').each(function(index, item) {
339 365 var parent = $(item).parent().parent();
340 366 selected.push({
341 367 name: parent.data('name'),
342 368 path: parent.data('path'),
343 369 type: parent.data('type')
344 370 });
345 371
346 372 has_running_notebook = has_running_notebook ||
347 373 (parent.data('type') == 'notebook' && that.sessions[parent.data('path')] !== undefined);
348 374 has_file = has_file || parent.data('type') == 'file';
349 375 has_directory = has_directory || parent.data('type') == 'directory';
350 376 });
351 377 this.selected = selected;
352 378
353 379 // Rename is only visible when one item is selected.
354 380 if (selected.length==1) {
355 381 $('.rename-button').css('display', 'inline-block');
356 382 } else {
357 383 $('.rename-button').css('display', 'none');
358 384 }
359 385
360 386 // Shutdown is only visible when one or more notebooks are visible.
361 387 if (has_running_notebook && !(has_file || has_directory)) {
362 388 $('.shutdown-button').css('display', 'inline-block');
363 389 } else {
364 390 $('.shutdown-button').css('display', 'none');
365 391 }
366 392
367 393 // Duplicate isn't visible if a directory is selected.
368 394 if (selected.length > 0 && !has_directory) {
369 395 $('.duplicate-button').css('display', 'inline-block');
370 396 } else {
371 397 $('.duplicate-button').css('display', 'none');
372 398 }
373 399
374 400 // Delete is visible if one or more items are selected.
375 401 if (selected.length > 0) {
376 402 $('.delete-button').css('display', 'inline-block');
377 403 } else {
378 404 $('.delete-button').css('display', 'none');
379 405 }
380 406 };
381 407
382 408 NotebookList.prototype.add_link = function (model, item) {
383 409 var path = model.path,
384 410 name = model.name;
385 411 item.data('name', name);
386 412 item.data('path', path);
387 413 item.data('type', model.type);
388 414 item.find(".item_name").text(name);
389 415 var icon = NotebookList.icons[model.type];
390 416 var uri_prefix = NotebookList.uri_prefixes[model.type];
391 417 item.find(".item_icon").addClass(icon).addClass('icon-fixed-width');
392 418 var link = item.find("a.item_link")
393 419 .attr('href',
394 420 utils.url_join_encode(
395 421 this.base_url,
396 422 uri_prefix,
397 423 path
398 424 )
399 425 );
400 426
401 427 var running = (model.type == 'notebook' && this.sessions[path] !== undefined);
402 428 item.find(".item_buttons .running-indicator").css('visibility', running ? '' : 'hidden');
403 429
404 430 // directory nav doesn't open new tabs
405 431 // files, notebooks do
406 432 if (model.type !== "directory") {
407 433 link.attr('target','_blank');
408 434 }
409 435 };
410 436
411 437
412 438 NotebookList.prototype.add_name_input = function (name, item, icon_type) {
413 439 item.data('name', name);
414 440 item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width');
415 441 item.find(".item_name").empty().append(
416 442 $('<input/>')
417 443 .addClass("filename_input")
418 444 .attr('value', name)
419 445 .attr('size', '30')
420 446 .attr('type', 'text')
421 447 .keyup(function(event){
422 448 if(event.keyCode == 13){item.find('.upload_button').click();}
423 449 else if(event.keyCode == 27){item.remove();}
424 450 })
425 451 );
426 452 };
427 453
428 454
429 455 NotebookList.prototype.add_file_data = function (data, item) {
430 456 item.data('filedata', data);
431 457 };
432 458
433 459
434 460 NotebookList.prototype.shutdown_selected = function() {
435 461 var that = this;
436 462 this.selected.forEach(function(item) {
437 463 if (item.type == 'notebook') {
438 464 that.shutdown_notebook(item.path);
439 465 }
440 466 });
441 467 };
442 468
443 469 NotebookList.prototype.shutdown_notebook = function(path) {
444 470 var that = this;
445 471 var settings = {
446 472 processData : false,
447 473 cache : false,
448 474 type : "DELETE",
449 475 dataType : "json",
450 476 success : function () {
451 477 that.load_sessions();
452 478 },
453 479 error : utils.log_ajax_error,
454 480 };
455 481
456 482 var session = this.sessions[path];
457 483 if (session) {
458 484 var url = utils.url_join_encode(
459 485 this.base_url,
460 486 'api/sessions',
461 487 session
462 488 );
463 489 $.ajax(url, settings);
464 490 }
465 }
491 };
466 492
467 493 NotebookList.prototype.rename_selected = function() {
468 494 if (this.selected.length != 1) return;
469 495
470 496 var that = this;
471 497 var path = this.selected[0].path;
472 498 var input = $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
473 499 .val(path);
474 500 var dialog_body = $('<div/>').append(
475 501 $("<p/>").addClass("rename-message")
476 502 .text('Enter a new directory name:')
477 503 ).append(
478 504 $("<br/>")
479 505 ).append(input);
480 506 var d = dialog.modal({
481 507 title : "Rename directory",
482 508 body : dialog_body,
483 509 buttons : {
484 510 OK : {
485 511 class: "btn-primary",
486 512 click: function() {
487 513 that.contents.rename(path, input.val()).then(function() {
488 514 that.load_list();
489 515 }).catch(function(e) {
490 516 dialog.modal({
491 title : "Error",
492 body : $('<div/>')
517 title: "Rename Failed",
518 body: $('<div/>')
493 519 .text("An error occurred while renaming \"" + path + "\" to \"" + input.val() + "\".")
494 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
495 buttons : {
496 OK : {}
520 .append($('<div/>')
521 .addClass('alert alert-danger')
522 .text(e.message || e)),
523 buttons: {
524 OK: {'class': 'btn-primary'}
497 525 }
498 526 });
499 527 });
500 528 }
501 529 },
502 530 Cancel : {}
503 531 },
504 532 open : function () {
505 533 // Upon ENTER, click the OK button.
506 534 input.keydown(function (event) {
507 535 if (event.which === keyboard.keycodes.enter) {
508 536 d.find('.btn-primary').first().click();
509 537 return false;
510 538 }
511 539 });
512 540 input.focus().select();
513 541 }
514 542 });
515 543 };
516 544
517 545 NotebookList.prototype.delete_selected = function() {
518 546 var message;
519 547 if (this.selected.length == 1) {
520 548 message = 'Are you sure you want to permanently delete: ' + this.selected[0].name + '?';
521 549 } else {
522 550 message = 'Are you sure you want to permanently delete the ' + this.selected.length + ' files/folders selected?';
523 551 }
524 552 var that = this;
525 553 dialog.modal({
526 554 title : "Delete",
527 555 body : message,
528 556 buttons : {
529 557 Delete : {
530 558 class: "btn-danger",
531 559 click: function() {
532 560 // Shutdown any/all selected notebooks before deleting
533 561 // the files.
534 562 that.shutdown_selected();
535 563
536 564 // Delete selected.
537 565 that.selected.forEach(function(item) {
538 566 that.contents.delete(item.path).then(function() {
539 567 that.notebook_deleted(item.path);
540 568 }).catch(function(e) {
541 569 dialog.modal({
542 title : "Error",
543 body : $('<div/>')
570 title: "Delete Failed",
571 body: $('<div/>')
544 572 .text("An error occurred while deleting \"" + item.path + "\".")
545 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
546 buttons : {
547 OK : {}
573 .append($('<div/>')
574 .addClass('alert alert-danger')
575 .text(e.message || e)),
576 buttons: {
577 OK: {'class': 'btn-primary'}
548 578 }
549 579 });
550 580 });
551 581 });
552 582 }
553 583 },
554 584 Cancel : {}
555 585 }
556 586 });
557 587 };
558 588
559 589 NotebookList.prototype.duplicate_selected = function() {
560 590 var message;
561 591 if (this.selected.length == 1) {
562 592 message = 'Are you sure you want to duplicate: ' + this.selected[0].name + '?';
563 593 } else {
564 594 message = 'Are you sure you want to duplicate the ' + this.selected.length + ' files selected?';
565 595 }
566 596 var that = this;
567 597 dialog.modal({
568 598 title : "Delete",
569 599 body : message,
570 600 buttons : {
571 601 Duplicate : {
572 602 class: "btn-primary",
573 603 click: function() {
574 604 that.selected.forEach(function(item) {
575 605 that.contents.copy(item.path, that.notebook_path).then(function () {
576 606 that.load_list();
577 607 }).catch(function(e) {
578 608 dialog.modal({
579 title : "Error",
580 body : $('<div/>')
581 .text("An error occurred while copying \"" + item.path + "\".")
582 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
583 buttons : {
584 OK : {}
609 title: "Delete Failed",
610 body: $('<div/>')
611 .text("An error occurred while deleting \"" + item.path + "\".")
612 .append($('<div/>')
613 .addClass('alert alert-danger')
614 .text(e.message || e)),
615 buttons: {
616 OK: {'class': 'btn-primary'}
585 617 }
586 618 });
587 619 });
588 620 });
589 621 }
590 622 },
591 623 Cancel : {}
592 624 }
593 625 });
594 626 };
595 627
596 628 NotebookList.prototype.notebook_deleted = function(path) {
597 629 /**
598 630 * Remove the deleted notebook.
599 631 */
600 632 var that = this;
601 633 $( ":data(path)" ).each(function() {
602 634 var element = $(this);
603 635 if (element.data("path") === path) {
604 636 element.remove();
605 637 events.trigger('notebook_deleted.NotebookList');
606 638 that._selection_changed();
607 639 }
608 640 });
609 641 };
610 642
611 643
612 644 NotebookList.prototype.add_upload_button = function (item) {
613 645 var that = this;
614 646 var upload_button = $('<button/>').text("Upload")
615 647 .addClass('btn btn-primary btn-xs upload_button')
616 648 .click(function (e) {
617 649 var filename = item.find('.item_name > input').val();
618 650 var path = utils.url_path_join(that.notebook_path, filename);
619 651 var filedata = item.data('filedata');
620 652 var format = 'text';
621 653 if (filename.length === 0 || filename[0] === '.') {
622 654 dialog.modal({
623 655 title : 'Invalid file name',
624 656 body : "File names must be at least one character and not start with a dot",
625 657 buttons : {'OK' : { 'class' : 'btn-primary' }}
626 658 });
627 659 return false;
628 660 }
629 661 if (filedata instanceof ArrayBuffer) {
630 662 // base64-encode binary file data
631 663 var bytes = '';
632 664 var buf = new Uint8Array(filedata);
633 665 var nbytes = buf.byteLength;
634 666 for (var i=0; i<nbytes; i++) {
635 667 bytes += String.fromCharCode(buf[i]);
636 668 }
637 669 filedata = btoa(bytes);
638 670 format = 'base64';
639 671 }
640 672 var model = {};
641 673
642 674 var name_and_ext = utils.splitext(filename);
643 675 var file_ext = name_and_ext[1];
644 676 var content_type;
645 677 if (file_ext === '.ipynb') {
646 678 model.type = 'notebook';
647 679 model.format = 'json';
648 680 try {
649 681 model.content = JSON.parse(filedata);
650 682 } catch (e) {
651 683 dialog.modal({
652 684 title : 'Cannot upload invalid Notebook',
653 685 body : "The error was: " + e,
654 686 buttons : {'OK' : {
655 687 'class' : 'btn-primary',
656 688 click: function () {
657 689 item.remove();
658 690 }
659 691 }}
660 692 });
661 693 return false;
662 694 }
663 695 content_type = 'application/json';
664 696 } else {
665 697 model.type = 'file';
666 698 model.format = format;
667 699 model.content = filedata;
668 700 content_type = 'application/octet-stream';
669 701 }
670 702 filedata = item.data('filedata');
671 703
672 704 var on_success = function () {
673 705 item.removeClass('new-file');
674 706 that.add_link(model, item);
675 707 that.add_delete_button(item);
676 708 that.session_list.load_sessions();
677 709 };
678 710
679 711 var exists = false;
680 712 $.each(that.element.find('.list_item:not(.new-file)'), function(k,v){
681 713 if ($(v).data('name') === filename) { exists = true; return false; }
682 714 });
683 715
684 716 if (exists) {
685 717 dialog.modal({
686 718 title : "Replace file",
687 719 body : 'There is already a file named ' + filename + ', do you want to replace it?',
688 720 buttons : {
689 721 Overwrite : {
690 722 class: "btn-danger",
691 723 click: function () {
692 724 that.contents.save(path, model).then(on_success);
693 725 }
694 726 },
695 727 Cancel : {
696 728 click: function() { item.remove(); }
697 729 }
698 730 }
699 731 });
700 732 } else {
701 733 that.contents.save(path, model).then(on_success);
702 734 }
703 735
704 736 return false;
705 737 });
706 738 var cancel_button = $('<button/>').text("Cancel")
707 739 .addClass("btn btn-default btn-xs")
708 740 .click(function (e) {
709 741 item.remove();
710 742 return false;
711 743 });
712 744 item.find(".item_buttons").empty()
713 745 .append(upload_button)
714 746 .append(cancel_button);
715 747 };
716 748
717 749
718 750 // Backwards compatability.
719 751 IPython.NotebookList = NotebookList;
720 752
721 753 return {'NotebookList': NotebookList};
722 754 });
General Comments 0
You need to be logged in to leave comments. Login now