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