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