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