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