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