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