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