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