##// END OF EJS Templates
Re-add shutdown button to running tab
Jonathan Frederic -
Show More
@@ -1,59 +1,76 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 'tree/js/notebooklist',
7 'tree/js/notebooklist',
8 ], function(IPython, $, notebooklist) {
8 ], function(IPython, $, notebooklist) {
9 "use strict";
9 "use strict";
10
10
11 var KernelList = function (selector, options) {
11 var KernelList = function (selector, options) {
12 /**
12 /**
13 * Constructor
13 * Constructor
14 *
14 *
15 * Parameters:
15 * Parameters:
16 * selector: string
16 * selector: string
17 * options: dictionary
17 * options: dictionary
18 * Dictionary of keyword arguments.
18 * Dictionary of keyword arguments.
19 * session_list: SessionList instance
19 * session_list: SessionList instance
20 * base_url: string
20 * base_url: string
21 * notebook_path: string
21 * notebook_path: string
22 */
22 */
23 notebooklist.NotebookList.call(this, selector, $.extend({
23 notebooklist.NotebookList.call(this, selector, $.extend({
24 element_name: 'running'},
24 element_name: 'running'},
25 options));
25 options));
26 };
26 };
27
27
28 KernelList.prototype = Object.create(notebooklist.NotebookList.prototype);
28 KernelList.prototype = Object.create(notebooklist.NotebookList.prototype);
29
29
30 KernelList.prototype.add_duplicate_button = function () {
30 KernelList.prototype.add_duplicate_button = function () {
31 /**
31 /**
32 * do nothing
32 * do nothing
33 */
33 */
34 };
34 };
35
35
36 KernelList.prototype.sessions_loaded = function (d) {
36 KernelList.prototype.sessions_loaded = function (d) {
37 this.sessions = d;
37 this.sessions = d;
38 this.clear_list();
38 this.clear_list();
39 var item, path;
39 var item, path;
40 for (path in d) {
40 for (path in d) {
41 if (!d.hasOwnProperty(path)) {
41 if (!d.hasOwnProperty(path)) {
42 // nothing is safe in javascript
42 // nothing is safe in javascript
43 continue;
43 continue;
44 }
44 }
45 item = this.new_item(-1);
45 item = this.new_item(-1);
46 this.add_link({
46 this.add_link({
47 name: path,
47 name: path,
48 path: path,
48 path: path,
49 type: 'notebook',
49 type: 'notebook',
50 }, item);
50 }, item);
51 }
51 }
52 $('#running_list_header').toggle($.isEmptyObject(d));
52 $('#running_list_header').toggle($.isEmptyObject(d));
53 };
53 };
54
55 KernelList.prototype.add_link = function (model, item) {
56 notebooklist.NotebookList.prototype.add_link.apply(this, [model, item])
57
58 var running_indicator = item.find(".item_buttons")
59 .text('');
60
61 var that = this;
62 var shutdown_button = $('<button/>')
63 .addClass('btn btn-warning btn-xs')
64 .text('Shutdown')
65 .click(function() {
66 var path = $(this).parent().parent().parent().data('path');
67 that.shutdown_notebook(path);
68 })
69 .appendTo(running_indicator);
70 };
54
71
55 // Backwards compatability.
72 // Backwards compatability.
56 IPython.KernelList = KernelList;
73 IPython.KernelList = KernelList;
57
74
58 return {'KernelList': KernelList};
75 return {'KernelList': KernelList};
59 });
76 });
@@ -1,714 +1,719 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 };
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 .attr('title', 'Click here to rename, delete, etc.')
267 .attr('title', 'Click here to rename, delete, etc.')
268 .appendTo(item);
268 .appendTo(item);
269 }
269 }
270
270
271 $('<i/>')
271 $('<i/>')
272 .addClass('item_icon')
272 .addClass('item_icon')
273 .appendTo(item);
273 .appendTo(item);
274
274
275 var link = $("<a/>")
275 var link = $("<a/>")
276 .addClass("item_link")
276 .addClass("item_link")
277 .appendTo(item);
277 .appendTo(item);
278
278
279 $("<span/>")
279 $("<span/>")
280 .addClass("item_name")
280 .addClass("item_name")
281 .appendTo(link);
281 .appendTo(link);
282
282
283 if (selectable === false) {
283 if (selectable === false) {
284 checkbox.css('visibility', 'hidden');
284 checkbox.css('visibility', 'hidden');
285 } else if (selectable === true) {
285 } else if (selectable === true) {
286 var that = this;
286 var that = this;
287 link.click(function(e) {
287 link.click(function(e) {
288 e.stopPropagation();
288 e.stopPropagation();
289 });
289 });
290 checkbox.click(function(e) {
290 checkbox.click(function(e) {
291 e.stopPropagation();
291 e.stopPropagation();
292 that._selection_changed();
292 that._selection_changed();
293 });
293 });
294 row.click(function(e) {
294 row.click(function(e) {
295 e.stopPropagation();
295 e.stopPropagation();
296 checkbox.prop('checked', !checkbox.prop('checked'));
296 checkbox.prop('checked', !checkbox.prop('checked'));
297 that._selection_changed();
297 that._selection_changed();
298 });
298 });
299 }
299 }
300
300
301 var buttons = $('<div/>')
301 var buttons = $('<div/>')
302 .addClass("item_buttons pull-right")
302 .addClass("item_buttons pull-right")
303 .appendTo(item);
303 .appendTo(item);
304
304
305 $('<div/>')
305 $('<div/>')
306 .addClass('running-indicator')
306 .addClass('running-indicator')
307 .text('Running')
307 .text('Running')
308 .css('visibility', 'hidden')
308 .css('visibility', 'hidden')
309 .appendTo(buttons);
309 .appendTo(buttons);
310
310
311 if (index === -1) {
311 if (index === -1) {
312 this.element.append(row);
312 this.element.append(row);
313 } else {
313 } else {
314 this.element.children().eq(index).after(row);
314 this.element.children().eq(index).after(row);
315 }
315 }
316 return row;
316 return row;
317 };
317 };
318
318
319
319
320 NotebookList.icons = {
320 NotebookList.icons = {
321 directory: 'folder_icon',
321 directory: 'folder_icon',
322 notebook: 'notebook_icon',
322 notebook: 'notebook_icon',
323 file: 'file_icon',
323 file: 'file_icon',
324 };
324 };
325
325
326 NotebookList.uri_prefixes = {
326 NotebookList.uri_prefixes = {
327 directory: 'tree',
327 directory: 'tree',
328 notebook: 'notebooks',
328 notebook: 'notebooks',
329 file: 'edit',
329 file: 'edit',
330 };
330 };
331
331
332 NotebookList.prototype._selection_changed = function() {
332 NotebookList.prototype._selection_changed = function() {
333 var selected = [];
333 var selected = [];
334 var has_running_notebook = false;
334 var has_running_notebook = false;
335 var has_directory = false;
335 var has_directory = false;
336 var that = this;
336 var that = this;
337 $('.list_item :checked').each(function(index, item) {
337 $('.list_item :checked').each(function(index, item) {
338 var parent = $(item).parent().parent();
338 var parent = $(item).parent().parent();
339 selected.push({
339 selected.push({
340 name: parent.data('name'),
340 name: parent.data('name'),
341 path: parent.data('path'),
341 path: parent.data('path'),
342 type: parent.data('type')
342 type: parent.data('type')
343 });
343 });
344
344
345 has_running_notebook = has_running_notebook ||
345 has_running_notebook = has_running_notebook ||
346 (parent.data('type') == 'notebook' && that.sessions[parent.data('path')] !== undefined);
346 (parent.data('type') == 'notebook' && that.sessions[parent.data('path')] !== undefined);
347 has_directory = has_directory || parent.data('type') == 'directory';
347 has_directory = has_directory || parent.data('type') == 'directory';
348 });
348 });
349 this.selected = selected;
349 this.selected = selected;
350
350
351 // Rename is only visible when one item is selected.
351 // Rename is only visible when one item is selected.
352 if (selected.length==1) {
352 if (selected.length==1) {
353 $('.rename-button').css('display', 'inline-block');
353 $('.rename-button').css('display', 'inline-block');
354 } else {
354 } else {
355 $('.rename-button').css('display', 'none');
355 $('.rename-button').css('display', 'none');
356 }
356 }
357
357
358 // Shutdown is only visible when one or more notebooks are visible.
358 // Shutdown is only visible when one or more notebooks are visible.
359 if (has_running_notebook) {
359 if (has_running_notebook) {
360 $('.shutdown-button').css('display', 'inline-block');
360 $('.shutdown-button').css('display', 'inline-block');
361 } else {
361 } else {
362 $('.shutdown-button').css('display', 'none');
362 $('.shutdown-button').css('display', 'none');
363 }
363 }
364
364
365 // Duplicate isn't visible if a directory is selected.
365 // Duplicate isn't visible if a directory is selected.
366 if (selected.length > 0 && !has_directory) {
366 if (selected.length > 0 && !has_directory) {
367 $('.duplicate-button').css('display', 'inline-block');
367 $('.duplicate-button').css('display', 'inline-block');
368 } else {
368 } else {
369 $('.duplicate-button').css('display', 'none');
369 $('.duplicate-button').css('display', 'none');
370 }
370 }
371
371
372 // Delete is visible if one or more items are selected.
372 // Delete is visible if one or more items are selected.
373 if (selected.length > 0) {
373 if (selected.length > 0) {
374 $('.delete-button').css('display', 'inline-block');
374 $('.delete-button').css('display', 'inline-block');
375 } else {
375 } else {
376 $('.delete-button').css('display', 'none');
376 $('.delete-button').css('display', 'none');
377 }
377 }
378 };
378 };
379
379
380 NotebookList.prototype.add_link = function (model, item) {
380 NotebookList.prototype.add_link = function (model, item) {
381 var path = model.path,
381 var path = model.path,
382 name = model.name;
382 name = model.name;
383 item.data('name', name);
383 item.data('name', name);
384 item.data('path', path);
384 item.data('path', path);
385 item.data('type', model.type);
385 item.data('type', model.type);
386 item.find(".item_name").text(name);
386 item.find(".item_name").text(name);
387 var icon = NotebookList.icons[model.type];
387 var icon = NotebookList.icons[model.type];
388 var uri_prefix = NotebookList.uri_prefixes[model.type];
388 var uri_prefix = NotebookList.uri_prefixes[model.type];
389 item.find(".item_icon").addClass(icon).addClass('icon-fixed-width');
389 item.find(".item_icon").addClass(icon).addClass('icon-fixed-width');
390 var link = item.find("a.item_link")
390 var link = item.find("a.item_link")
391 .attr('href',
391 .attr('href',
392 utils.url_join_encode(
392 utils.url_join_encode(
393 this.base_url,
393 this.base_url,
394 uri_prefix,
394 uri_prefix,
395 path
395 path
396 )
396 )
397 );
397 );
398
398
399 var running = (model.type == 'notebook' && this.sessions[path] !== undefined);
399 var running = (model.type == 'notebook' && this.sessions[path] !== undefined);
400 item.find(".item_buttons .running-indicator").css('visibility', running ? '' : 'hidden');
400 item.find(".item_buttons .running-indicator").css('visibility', running ? '' : 'hidden');
401
401
402 // directory nav doesn't open new tabs
402 // directory nav doesn't open new tabs
403 // files, notebooks do
403 // files, notebooks do
404 if (model.type !== "directory") {
404 if (model.type !== "directory") {
405 link.attr('target','_blank');
405 link.attr('target','_blank');
406 }
406 }
407 };
407 };
408
408
409
409
410 NotebookList.prototype.add_name_input = function (name, item, icon_type) {
410 NotebookList.prototype.add_name_input = function (name, item, icon_type) {
411 item.data('name', name);
411 item.data('name', name);
412 item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width');
412 item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width');
413 item.find(".item_name").empty().append(
413 item.find(".item_name").empty().append(
414 $('<input/>')
414 $('<input/>')
415 .addClass("filename_input")
415 .addClass("filename_input")
416 .attr('value', name)
416 .attr('value', name)
417 .attr('size', '30')
417 .attr('size', '30')
418 .attr('type', 'text')
418 .attr('type', 'text')
419 .keyup(function(event){
419 .keyup(function(event){
420 if(event.keyCode == 13){item.find('.upload_button').click();}
420 if(event.keyCode == 13){item.find('.upload_button').click();}
421 else if(event.keyCode == 27){item.remove();}
421 else if(event.keyCode == 27){item.remove();}
422 })
422 })
423 );
423 );
424 };
424 };
425
425
426
426
427 NotebookList.prototype.add_file_data = function (data, item) {
427 NotebookList.prototype.add_file_data = function (data, item) {
428 item.data('filedata', data);
428 item.data('filedata', data);
429 };
429 };
430
430
431
431
432 NotebookList.prototype.shutdown_selected = function() {
432 NotebookList.prototype.shutdown_selected = function() {
433 var that = this;
433 var that = this;
434 this.selected.forEach(function(item) {
435 if (item.type == 'notebook') {
436 that.shutdown_notebook(item.path);
437 }
438 });
439 };
440
441 NotebookList.prototype.shutdown_notebook = function(path) {
442 var that = this;
434 var settings = {
443 var settings = {
435 processData : false,
444 processData : false,
436 cache : false,
445 cache : false,
437 type : "DELETE",
446 type : "DELETE",
438 dataType : "json",
447 dataType : "json",
439 success : function () {
448 success : function () {
440 that.load_sessions();
449 that.load_sessions();
441 },
450 },
442 error : utils.log_ajax_error,
451 error : utils.log_ajax_error,
443 };
452 };
444
453
445 this.selected.forEach(function(item) {
454 var session = this.sessions[path];
446 if (item.type == 'notebook') {
455 if (session) {
447 var session = that.sessions[item.path];
456 var url = utils.url_join_encode(
448 if (session) {
457 this.base_url,
449 var url = utils.url_join_encode(
458 'api/sessions',
450 that.base_url,
459 session
451 'api/sessions',
460 );
452 session
461 $.ajax(url, settings);
453 );
462 }
454 $.ajax(url, settings);
463 }
455 }
456 }
457 });
458 };
459
464
460 NotebookList.prototype.rename_selected = function() {
465 NotebookList.prototype.rename_selected = function() {
461 if (this.selected.length != 1) return;
466 if (this.selected.length != 1) return;
462
467
463 var that = this;
468 var that = this;
464 var path = this.selected[0].path;
469 var path = this.selected[0].path;
465 var input = $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
470 var input = $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
466 .val(path);
471 .val(path);
467 var dialog_body = $('<div/>').append(
472 var dialog_body = $('<div/>').append(
468 $("<p/>").addClass("rename-message")
473 $("<p/>").addClass("rename-message")
469 .text('Enter a new directory name:')
474 .text('Enter a new directory name:')
470 ).append(
475 ).append(
471 $("<br/>")
476 $("<br/>")
472 ).append(input);
477 ).append(input);
473 var d = dialog.modal({
478 var d = dialog.modal({
474 title : "Rename directory",
479 title : "Rename directory",
475 body : dialog_body,
480 body : dialog_body,
476 buttons : {
481 buttons : {
477 OK : {
482 OK : {
478 class: "btn-primary",
483 class: "btn-primary",
479 click: function() {
484 click: function() {
480 that.contents.rename(path, input.val()).then(function() {
485 that.contents.rename(path, input.val()).then(function() {
481 that.load_list();
486 that.load_list();
482 }).catch(function(e) {
487 }).catch(function(e) {
483 dialog.modal({
488 dialog.modal({
484 title : "Error",
489 title : "Error",
485 body : $('<div/>')
490 body : $('<div/>')
486 .text("An error occurred while renaming \"" + path + "\" to \"" + input.val() + "\".")
491 .text("An error occurred while renaming \"" + path + "\" to \"" + input.val() + "\".")
487 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
492 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
488 buttons : {
493 buttons : {
489 OK : {}
494 OK : {}
490 }
495 }
491 });
496 });
492 });
497 });
493 }
498 }
494 },
499 },
495 Cancel : {}
500 Cancel : {}
496 },
501 },
497 open : function () {
502 open : function () {
498 // Upon ENTER, click the OK button.
503 // Upon ENTER, click the OK button.
499 input.keydown(function (event) {
504 input.keydown(function (event) {
500 if (event.which === keyboard.keycodes.enter) {
505 if (event.which === keyboard.keycodes.enter) {
501 d.find('.btn-primary').first().click();
506 d.find('.btn-primary').first().click();
502 return false;
507 return false;
503 }
508 }
504 });
509 });
505 input.focus().select();
510 input.focus().select();
506 }
511 }
507 });
512 });
508 };
513 };
509
514
510 NotebookList.prototype.delete_selected = function() {
515 NotebookList.prototype.delete_selected = function() {
511 var message;
516 var message;
512 if (this.selected.length == 1) {
517 if (this.selected.length == 1) {
513 message = 'Are you sure you want to permanently delete: ' + this.selected[0].name + '?';
518 message = 'Are you sure you want to permanently delete: ' + this.selected[0].name + '?';
514 } else {
519 } else {
515 message = 'Are you sure you want to permanently delete the ' + this.selected.length + ' files selected?';
520 message = 'Are you sure you want to permanently delete the ' + this.selected.length + ' files selected?';
516 }
521 }
517 var that = this;
522 var that = this;
518 dialog.modal({
523 dialog.modal({
519 title : "Delete",
524 title : "Delete",
520 body : message,
525 body : message,
521 buttons : {
526 buttons : {
522 Delete : {
527 Delete : {
523 class: "btn-danger",
528 class: "btn-danger",
524 click: function() {
529 click: function() {
525 // Shutdown any/all selected notebooks before deleting
530 // Shutdown any/all selected notebooks before deleting
526 // the files.
531 // the files.
527 that.shutdown_selected();
532 that.shutdown_selected();
528
533
529 // Delete selected.
534 // Delete selected.
530 that.selected.forEach(function(item) {
535 that.selected.forEach(function(item) {
531 that.contents.delete(item.path).then(function() {
536 that.contents.delete(item.path).then(function() {
532 that.notebook_deleted(item.path);
537 that.notebook_deleted(item.path);
533 }).catch(function(e) {
538 }).catch(function(e) {
534 dialog.modal({
539 dialog.modal({
535 title : "Error",
540 title : "Error",
536 body : $('<div/>')
541 body : $('<div/>')
537 .text("An error occurred while deleting \"" + item.path + "\".")
542 .text("An error occurred while deleting \"" + item.path + "\".")
538 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
543 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
539 buttons : {
544 buttons : {
540 OK : {}
545 OK : {}
541 }
546 }
542 });
547 });
543 });
548 });
544 });
549 });
545 }
550 }
546 },
551 },
547 Cancel : {}
552 Cancel : {}
548 }
553 }
549 });
554 });
550 };
555 };
551
556
552 NotebookList.prototype.duplicate_selected = function() {
557 NotebookList.prototype.duplicate_selected = function() {
553 var message;
558 var message;
554 if (this.selected.length == 1) {
559 if (this.selected.length == 1) {
555 message = 'Are you sure you want to duplicate: ' + this.selected[0].name + '?';
560 message = 'Are you sure you want to duplicate: ' + this.selected[0].name + '?';
556 } else {
561 } else {
557 message = 'Are you sure you want to duplicate the ' + this.selected.length + ' files selected?';
562 message = 'Are you sure you want to duplicate the ' + this.selected.length + ' files selected?';
558 }
563 }
559 var that = this;
564 var that = this;
560 dialog.modal({
565 dialog.modal({
561 title : "Delete",
566 title : "Delete",
562 body : message,
567 body : message,
563 buttons : {
568 buttons : {
564 Duplicate : {
569 Duplicate : {
565 class: "btn-primary",
570 class: "btn-primary",
566 click: function() {
571 click: function() {
567 that.selected.forEach(function(item) {
572 that.selected.forEach(function(item) {
568 that.contents.copy(item.path, that.notebook_path).then(function () {
573 that.contents.copy(item.path, that.notebook_path).then(function () {
569 that.load_list();
574 that.load_list();
570 }).catch(function(e) {
575 }).catch(function(e) {
571 dialog.modal({
576 dialog.modal({
572 title : "Error",
577 title : "Error",
573 body : $('<div/>')
578 body : $('<div/>')
574 .text("An error occurred while copying \"" + item.path + "\".")
579 .text("An error occurred while copying \"" + item.path + "\".")
575 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
580 .append($('<div/>').addClass('alert alert-danger').text(String(e))),
576 buttons : {
581 buttons : {
577 OK : {}
582 OK : {}
578 }
583 }
579 });
584 });
580 });
585 });
581 });
586 });
582 }
587 }
583 },
588 },
584 Cancel : {}
589 Cancel : {}
585 }
590 }
586 });
591 });
587 };
592 };
588
593
589 NotebookList.prototype.notebook_deleted = function(path) {
594 NotebookList.prototype.notebook_deleted = function(path) {
590 /**
595 /**
591 * Remove the deleted notebook.
596 * Remove the deleted notebook.
592 */
597 */
593 $( ":data(path)" ).each(function() {
598 $( ":data(path)" ).each(function() {
594 var element = $(this);
599 var element = $(this);
595 if (element.data("path") == path) {
600 if (element.data("path") == path) {
596 element.remove();
601 element.remove();
597 events.trigger('notebook_deleted.NotebookList');
602 events.trigger('notebook_deleted.NotebookList');
598 this._selection_changed();
603 this._selection_changed();
599 }
604 }
600 });
605 });
601 };
606 };
602
607
603
608
604 NotebookList.prototype.add_upload_button = function (item) {
609 NotebookList.prototype.add_upload_button = function (item) {
605 var that = this;
610 var that = this;
606 var upload_button = $('<button/>').text("Upload")
611 var upload_button = $('<button/>').text("Upload")
607 .addClass('btn btn-primary btn-xs upload_button')
612 .addClass('btn btn-primary btn-xs upload_button')
608 .click(function (e) {
613 .click(function (e) {
609 var filename = item.find('.item_name > input').val();
614 var filename = item.find('.item_name > input').val();
610 var path = utils.url_path_join(that.notebook_path, filename);
615 var path = utils.url_path_join(that.notebook_path, filename);
611 var filedata = item.data('filedata');
616 var filedata = item.data('filedata');
612 var format = 'text';
617 var format = 'text';
613 if (filename.length === 0 || filename[0] === '.') {
618 if (filename.length === 0 || filename[0] === '.') {
614 dialog.modal({
619 dialog.modal({
615 title : 'Invalid file name',
620 title : 'Invalid file name',
616 body : "File names must be at least one character and not start with a dot",
621 body : "File names must be at least one character and not start with a dot",
617 buttons : {'OK' : { 'class' : 'btn-primary' }}
622 buttons : {'OK' : { 'class' : 'btn-primary' }}
618 });
623 });
619 return false;
624 return false;
620 }
625 }
621 if (filedata instanceof ArrayBuffer) {
626 if (filedata instanceof ArrayBuffer) {
622 // base64-encode binary file data
627 // base64-encode binary file data
623 var bytes = '';
628 var bytes = '';
624 var buf = new Uint8Array(filedata);
629 var buf = new Uint8Array(filedata);
625 var nbytes = buf.byteLength;
630 var nbytes = buf.byteLength;
626 for (var i=0; i<nbytes; i++) {
631 for (var i=0; i<nbytes; i++) {
627 bytes += String.fromCharCode(buf[i]);
632 bytes += String.fromCharCode(buf[i]);
628 }
633 }
629 filedata = btoa(bytes);
634 filedata = btoa(bytes);
630 format = 'base64';
635 format = 'base64';
631 }
636 }
632 var model = {};
637 var model = {};
633
638
634 var name_and_ext = utils.splitext(filename);
639 var name_and_ext = utils.splitext(filename);
635 var file_ext = name_and_ext[1];
640 var file_ext = name_and_ext[1];
636 var content_type;
641 var content_type;
637 if (file_ext === '.ipynb') {
642 if (file_ext === '.ipynb') {
638 model.type = 'notebook';
643 model.type = 'notebook';
639 model.format = 'json';
644 model.format = 'json';
640 try {
645 try {
641 model.content = JSON.parse(filedata);
646 model.content = JSON.parse(filedata);
642 } catch (e) {
647 } catch (e) {
643 dialog.modal({
648 dialog.modal({
644 title : 'Cannot upload invalid Notebook',
649 title : 'Cannot upload invalid Notebook',
645 body : "The error was: " + e,
650 body : "The error was: " + e,
646 buttons : {'OK' : {
651 buttons : {'OK' : {
647 'class' : 'btn-primary',
652 'class' : 'btn-primary',
648 click: function () {
653 click: function () {
649 item.remove();
654 item.remove();
650 }
655 }
651 }}
656 }}
652 });
657 });
653 return false;
658 return false;
654 }
659 }
655 content_type = 'application/json';
660 content_type = 'application/json';
656 } else {
661 } else {
657 model.type = 'file';
662 model.type = 'file';
658 model.format = format;
663 model.format = format;
659 model.content = filedata;
664 model.content = filedata;
660 content_type = 'application/octet-stream';
665 content_type = 'application/octet-stream';
661 }
666 }
662 filedata = item.data('filedata');
667 filedata = item.data('filedata');
663
668
664 var on_success = function () {
669 var on_success = function () {
665 item.removeClass('new-file');
670 item.removeClass('new-file');
666 that.add_link(model, item);
671 that.add_link(model, item);
667 that.add_delete_button(item);
672 that.add_delete_button(item);
668 that.session_list.load_sessions();
673 that.session_list.load_sessions();
669 };
674 };
670
675
671 var exists = false;
676 var exists = false;
672 $.each(that.element.find('.list_item:not(.new-file)'), function(k,v){
677 $.each(that.element.find('.list_item:not(.new-file)'), function(k,v){
673 if ($(v).data('name') === filename) { exists = true; return false; }
678 if ($(v).data('name') === filename) { exists = true; return false; }
674 });
679 });
675
680
676 if (exists) {
681 if (exists) {
677 dialog.modal({
682 dialog.modal({
678 title : "Replace file",
683 title : "Replace file",
679 body : 'There is already a file named ' + filename + ', do you want to replace it?',
684 body : 'There is already a file named ' + filename + ', do you want to replace it?',
680 buttons : {
685 buttons : {
681 Overwrite : {
686 Overwrite : {
682 class: "btn-danger",
687 class: "btn-danger",
683 click: function () {
688 click: function () {
684 that.contents.save(path, model).then(on_success);
689 that.contents.save(path, model).then(on_success);
685 }
690 }
686 },
691 },
687 Cancel : {
692 Cancel : {
688 click: function() { item.remove(); }
693 click: function() { item.remove(); }
689 }
694 }
690 }
695 }
691 });
696 });
692 } else {
697 } else {
693 that.contents.save(path, model).then(on_success);
698 that.contents.save(path, model).then(on_success);
694 }
699 }
695
700
696 return false;
701 return false;
697 });
702 });
698 var cancel_button = $('<button/>').text("Cancel")
703 var cancel_button = $('<button/>').text("Cancel")
699 .addClass("btn btn-default btn-xs")
704 .addClass("btn btn-default btn-xs")
700 .click(function (e) {
705 .click(function (e) {
701 item.remove();
706 item.remove();
702 return false;
707 return false;
703 });
708 });
704 item.find(".item_buttons").empty()
709 item.find(".item_buttons").empty()
705 .append(upload_button)
710 .append(upload_button)
706 .append(cancel_button);
711 .append(cancel_button);
707 };
712 };
708
713
709
714
710 // Backwards compatability.
715 // Backwards compatability.
711 IPython.NotebookList = NotebookList;
716 IPython.NotebookList = NotebookList;
712
717
713 return {'NotebookList': NotebookList};
718 return {'NotebookList': NotebookList};
714 });
719 });
General Comments 0
You need to be logged in to leave comments. Login now