##// END OF EJS Templates
File list refreshes no longer move the upload filename boxes....
Jeffrey Bush -
Show More
@@ -1,121 +1,115
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 require([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/events',
8 8 'base/js/page',
9 9 'base/js/utils',
10 10 'tree/js/notebooklist',
11 11 'tree/js/clusterlist',
12 12 'tree/js/sessionlist',
13 13 'tree/js/kernellist',
14 14 'auth/js/loginwidget',
15 15 // only loaded, not used:
16 16 'jqueryui',
17 17 'bootstrap',
18 18 'custom/custom',
19 19 ], function(
20 20 IPython,
21 21 $,
22 22 events,
23 23 page,
24 24 utils,
25 25 notebooklist,
26 26 clusterlist,
27 27 sesssionlist,
28 28 kernellist,
29 29 loginwidget){
30 30
31 31 page = new page.Page();
32 32
33 33 var common_options = {
34 34 base_url: utils.get_body_data("baseUrl"),
35 35 notebook_path: utils.get_body_data("notebookPath"),
36 36 };
37 37 session_list = new sesssionlist.SesssionList($.extend({
38 38 events: events},
39 39 common_options));
40 40 notebook_list = new notebooklist.NotebookList('#notebook_list', $.extend({
41 41 session_list: session_list},
42 42 common_options));
43 43 cluster_list = new clusterlist.ClusterList('#cluster_list', common_options);
44 44 kernel_list = new kernellist.KernelList('#running_list', $.extend({
45 45 session_list: session_list},
46 46 common_options));
47 47 login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
48 48
49 49 $('#new_notebook').click(function (e) {
50 50 notebook_list.new_notebook();
51 51 });
52 52
53 53 var interval_id=0;
54 54 // auto refresh every xx secondes, no need to be fast,
55 55 // update is done at least when page get focus
56 56 var time_refresh = 60; // in sec
57 57
58 58 var enable_autorefresh = function(){
59 59 //refresh immediately , then start interval
60 if($('.upload_button').length === 0)
61 {
62 session_list.load_sessions();
63 cluster_list.load_list();
64 }
60 session_list.load_sessions();
61 cluster_list.load_list();
65 62 if (!interval_id){
66 63 interval_id = setInterval(function(){
67 if($('.upload_button').length === 0)
68 {
69 session_list.load_sessions();
70 cluster_list.load_list();
71 }
64 session_list.load_sessions();
65 cluster_list.load_list();
72 66 }, time_refresh*1000);
73 67 }
74 68 };
75 69
76 70 var disable_autorefresh = function(){
77 71 clearInterval(interval_id);
78 72 interval_id = 0;
79 73 };
80 74
81 75 // stop autorefresh when page lose focus
82 76 $(window).blur(function() {
83 77 disable_autorefresh();
84 78 });
85 79
86 80 //re-enable when page get focus back
87 81 $(window).focus(function() {
88 82 enable_autorefresh();
89 83 });
90 84
91 85 // finally start it, it will refresh immediately
92 86 enable_autorefresh();
93 87
94 88 page.show();
95 89
96 90 // For backwards compatability.
97 91 IPython.page = page;
98 92 IPython.notebook_list = notebook_list;
99 93 IPython.cluster_list = cluster_list;
100 94 IPython.session_list = session_list;
101 95 IPython.kernel_list = kernel_list;
102 96 IPython.login_widget = login_widget;
103 97
104 98 events.trigger('app_initialized.DashboardApp');
105 99
106 100 // bound the upload method to the on change of the file select list
107 101 $("#alternate_upload").change(function (event){
108 102 notebook_list.handleFilesUpload(event,'form');
109 103 });
110 104
111 105 // set hash on tab click
112 106 $("#tabs").find("a").click(function() {
113 107 window.location.hash = $(this).attr("href");
114 108 });
115 109
116 110 // load tab if url hash
117 111 if (window.location.hash) {
118 112 $("#tabs").find("a[href=" + window.location.hash + "]").click();
119 113 }
120 114
121 115 });
@@ -1,539 +1,540
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 ], function(IPython, $, utils, dialog) {
10 10 "use strict";
11 11
12 12 var NotebookList = function (selector, options) {
13 13 // Constructor
14 14 //
15 15 // Parameters:
16 16 // selector: string
17 17 // options: dictionary
18 18 // Dictionary of keyword arguments.
19 19 // session_list: SessionList instance
20 20 // element_name: string
21 21 // base_url: string
22 22 // notebook_path: string
23 23 var that = this;
24 24 this.session_list = options.session_list;
25 25 // allow code re-use by just changing element_name in kernellist.js
26 26 this.element_name = options.element_name || 'notebook';
27 27 this.selector = selector;
28 28 if (this.selector !== undefined) {
29 29 this.element = $(selector);
30 30 this.style();
31 31 this.bind_events();
32 32 }
33 33 this.notebooks_list = [];
34 34 this.sessions = {};
35 35 this.base_url = options.base_url || utils.get_body_data("baseUrl");
36 36 this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath");
37 37 if (this.session_list && this.session_list.events) {
38 38 this.session_list.events.on('sessions_loaded.Dashboard',
39 39 function(e, d) { that.sessions_loaded(d); });
40 40 }
41 41 };
42 42
43 43 NotebookList.prototype.style = function () {
44 44 var prefix = '#' + this.element_name;
45 45 $(prefix + '_toolbar').addClass('list_toolbar');
46 46 $(prefix + '_list_info').addClass('toolbar_info');
47 47 $(prefix + '_buttons').addClass('toolbar_buttons');
48 48 $(prefix + '_list_header').addClass('list_header');
49 49 this.element.addClass("list_container");
50 50 };
51 51
52 52
53 53 NotebookList.prototype.bind_events = function () {
54 54 var that = this;
55 55 $('#refresh_' + this.element_name + '_list').click(function () {
56 56 that.load_sessions();
57 57 });
58 58 this.element.bind('dragover', function () {
59 59 return false;
60 60 });
61 61 this.element.bind('drop', function(event){
62 62 that.handleFilesUpload(event,'drop');
63 63 return false;
64 64 });
65 65 };
66 66
67 67 NotebookList.prototype.handleFilesUpload = function(event, dropOrForm) {
68 68 var that = this;
69 69 var files;
70 70 if(dropOrForm =='drop'){
71 71 files = event.originalEvent.dataTransfer.files;
72 72 } else
73 73 {
74 74 files = event.originalEvent.target.files;
75 75 }
76 76 for (var i = 0; i < files.length; i++) {
77 77 var f = files[i];
78 78 var name_and_ext = utils.splitext(f.name);
79 79 var file_ext = name_and_ext[1];
80 80
81 81 var reader = new FileReader();
82 82 if (file_ext === '.ipynb') {
83 83 reader.readAsText(f);
84 84 } else {
85 85 // read non-notebook files as binary
86 86 reader.readAsArrayBuffer(f);
87 87 }
88 88 var item = that.new_item(0);
89 89 item.addClass('new-file');
90 90 that.add_name_input(f.name, item, file_ext == '.ipynb' ? 'notebook' : 'file');
91 91 // Store the list item in the reader so we can use it later
92 92 // to know which item it belongs to.
93 93 $(reader).data('item', item);
94 94 reader.onload = function (event) {
95 95 var item = $(event.target).data('item');
96 96 that.add_file_data(event.target.result, item);
97 97 that.add_upload_button(item);
98 98 };
99 99 reader.onerror = function (event) {
100 100 var item = $(event.target).data('item');
101 101 var name = item.data('name')
102 102 item.remove();
103 103 dialog.modal({
104 104 title : 'Failed to read file',
105 105 body : "Failed to read file '" + name + "'",
106 106 buttons : {'OK' : { 'class' : 'btn-primary' }}
107 107 });
108 108 };
109 109 }
110 110 // Replace the file input form wth a clone of itself. This is required to
111 111 // reset the form. Otherwise, if you upload a file, delete it and try to
112 112 // upload it again, the changed event won't fire.
113 113 var form = $('input.fileinput');
114 114 form.replaceWith(form.clone(true));
115 115 return false;
116 116 };
117 117
118 118 NotebookList.prototype.clear_list = function (remove_uploads) {
119 119 // Clears the navigation tree.
120 120 //
121 121 // Parameters
122 122 // remove_uploads: bool=False
123 123 // Should upload prompts also be removed from the tree.
124 124 if (remove_uploads) {
125 125 this.element.children('.list_item').remove();
126 126 } else {
127 127 this.element.children('.list_item:not(.new-file)').remove();
128 128 }
129 129 };
130 130
131 131 NotebookList.prototype.load_sessions = function(){
132 132 this.session_list.load_sessions();
133 133 };
134 134
135 135
136 136 NotebookList.prototype.sessions_loaded = function(data){
137 137 this.sessions = data;
138 138 this.load_list();
139 139 };
140 140
141 141 NotebookList.prototype.load_list = function () {
142 142 var that = this;
143 143 var settings = {
144 144 processData : false,
145 145 cache : false,
146 146 type : "GET",
147 147 dataType : "json",
148 148 success : $.proxy(this.list_loaded, this),
149 149 error : $.proxy( function(xhr, status, error){
150 150 utils.log_ajax_error(xhr, status, error);
151 151 that.list_loaded([], null, null, {msg:"Error connecting to server."});
152 152 },this)
153 153 };
154 154
155 155 var url = utils.url_join_encode(
156 156 this.base_url,
157 157 'api',
158 158 'contents',
159 159 this.notebook_path
160 160 );
161 161 $.ajax(url, settings);
162 162 };
163 163
164 164
165 165 NotebookList.prototype.list_loaded = function (data, status, xhr, param) {
166 166 var message = 'Notebook list empty.';
167 167 if (param !== undefined && param.msg) {
168 168 message = param.msg;
169 169 }
170 170 var item = null;
171 171 var model = null;
172 172 var list = data.content;
173 173 var len = list.length;
174 174 this.clear_list();
175 var n_uploads = this.element.children('.list_item').length;
175 176 if (len === 0) {
176 177 item = this.new_item(0);
177 178 var span12 = item.children().first();
178 179 span12.empty();
179 180 span12.append($('<div style="margin:auto;text-align:center;color:grey"/>').text(message));
180 181 }
181 182 var path = this.notebook_path;
182 var offset = 0;
183 var offset = n_uploads;
183 184 if (path !== '') {
184 item = this.new_item(0);
185 item = this.new_item(offset);
185 186 model = {
186 187 type: 'directory',
187 188 name: '..',
188 189 path: path,
189 190 };
190 191 this.add_link(model, item);
191 offset = 1;
192 offset += 1;
192 193 }
193 194 for (var i=0; i<len; i++) {
194 195 model = list[i];
195 196 item = this.new_item(i+offset);
196 197 this.add_link(model, item);
197 198 }
198 199 };
199 200
200 201
201 202 NotebookList.prototype.new_item = function (index) {
202 203 var item = $('<div/>').addClass("list_item").addClass("row");
203 204 // item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
204 205 // item.css('border-top-style','none');
205 206 item.append($("<div/>").addClass("col-md-12").append(
206 207 $('<i/>').addClass('item_icon')
207 208 ).append(
208 209 $("<a/>").addClass("item_link").append(
209 210 $("<span/>").addClass("item_name")
210 211 )
211 212 ).append(
212 213 $('<div/>').addClass("item_buttons btn-group pull-right")
213 214 ));
214 215
215 216 if (index === -1) {
216 217 this.element.append(item);
217 218 } else {
218 219 this.element.children().eq(index).after(item);
219 220 }
220 221 return item;
221 222 };
222 223
223 224
224 225 NotebookList.icons = {
225 226 directory: 'folder_icon',
226 227 notebook: 'notebook_icon',
227 228 file: 'file_icon',
228 229 };
229 230
230 231 NotebookList.uri_prefixes = {
231 232 directory: 'tree',
232 233 notebook: 'notebooks',
233 234 file: 'files',
234 235 };
235 236
236 237
237 238 NotebookList.prototype.add_link = function (model, item) {
238 239 var path = model.path,
239 240 name = model.name;
240 241 item.data('name', name);
241 242 item.data('path', path);
242 243 item.find(".item_name").text(name);
243 244 var icon = NotebookList.icons[model.type];
244 245 var uri_prefix = NotebookList.uri_prefixes[model.type];
245 246 item.find(".item_icon").addClass(icon).addClass('icon-fixed-width');
246 247 var link = item.find("a.item_link")
247 248 .attr('href',
248 249 utils.url_join_encode(
249 250 this.base_url,
250 251 uri_prefix,
251 252 path,
252 253 name
253 254 )
254 255 );
255 256 // directory nav doesn't open new tabs
256 257 // files, notebooks do
257 258 if (model.type !== "directory") {
258 259 link.attr('target','_blank');
259 260 }
260 261 var path_name = utils.url_path_join(path, name);
261 262 if (model.type == 'file') {
262 263 this.add_delete_button(item);
263 264 } else if (model.type == 'notebook') {
264 265 if(this.sessions[path_name] === undefined){
265 266 this.add_delete_button(item);
266 267 } else {
267 268 this.add_shutdown_button(item, this.sessions[path_name]);
268 269 }
269 270 }
270 271 };
271 272
272 273
273 274 NotebookList.prototype.add_name_input = function (name, item, icon_type) {
274 275 item.data('name', name);
275 276 item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width');
276 277 item.find(".item_name").empty().append(
277 278 $('<input/>')
278 279 .addClass("filename_input")
279 280 .attr('value', name)
280 281 .attr('size', '30')
281 282 .attr('type', 'text')
282 283 .keyup(function(event){
283 284 if(event.keyCode == 13){item.find('.upload_button').click();}
284 285 else if(event.keyCode == 27){item.remove();}
285 286 })
286 287 );
287 288 };
288 289
289 290
290 291 NotebookList.prototype.add_file_data = function (data, item) {
291 292 item.data('filedata', data);
292 293 };
293 294
294 295
295 296 NotebookList.prototype.add_shutdown_button = function (item, session) {
296 297 var that = this;
297 298 var shutdown_button = $("<button/>").text("Shutdown").addClass("btn btn-xs btn-danger").
298 299 click(function (e) {
299 300 var settings = {
300 301 processData : false,
301 302 cache : false,
302 303 type : "DELETE",
303 304 dataType : "json",
304 305 success : function () {
305 306 that.load_sessions();
306 307 },
307 308 error : utils.log_ajax_error,
308 309 };
309 310 var url = utils.url_join_encode(
310 311 that.base_url,
311 312 'api/sessions',
312 313 session
313 314 );
314 315 $.ajax(url, settings);
315 316 return false;
316 317 });
317 318 // var new_buttons = item.find('a'); // shutdown_button;
318 319 item.find(".item_buttons").text("").append(shutdown_button);
319 320 };
320 321
321 322 NotebookList.prototype.add_delete_button = function (item) {
322 323 var new_buttons = $('<span/>').addClass("btn-group pull-right");
323 324 var notebooklist = this;
324 325 var delete_button = $("<button/>").text("Delete").addClass("btn btn-default btn-xs").
325 326 click(function (e) {
326 327 // $(this) is the button that was clicked.
327 328 var that = $(this);
328 329 // We use the filename from the parent list_item element's
329 330 // data because the outer scope's values change as we iterate through the loop.
330 331 var parent_item = that.parents('div.list_item');
331 332 var name = parent_item.data('name');
332 333 var message = 'Are you sure you want to permanently delete the file: ' + name + '?';
333 334 dialog.modal({
334 335 title : "Delete file",
335 336 body : message,
336 337 buttons : {
337 338 Delete : {
338 339 class: "btn-danger",
339 340 click: function() {
340 341 var settings = {
341 342 processData : false,
342 343 cache : false,
343 344 type : "DELETE",
344 345 dataType : "json",
345 346 success : function (data, status, xhr) {
346 347 parent_item.remove();
347 348 },
348 349 error : utils.log_ajax_error,
349 350 };
350 351 var url = utils.url_join_encode(
351 352 notebooklist.base_url,
352 353 'api/contents',
353 354 notebooklist.notebook_path,
354 355 name
355 356 );
356 357 $.ajax(url, settings);
357 358 }
358 359 },
359 360 Cancel : {}
360 361 }
361 362 });
362 363 return false;
363 364 });
364 365 item.find(".item_buttons").text("").append(delete_button);
365 366 };
366 367
367 368
368 369 NotebookList.prototype.add_upload_button = function (item, type) {
369 370 var that = this;
370 371 var upload_button = $('<button/>').text("Upload")
371 372 .addClass('btn btn-primary btn-xs upload_button')
372 373 .click(function (e) {
373 374 var path = that.notebook_path;
374 375 var filename = item.find('.item_name > input').val();
375 376 var filedata = item.data('filedata');
376 377 var format = 'text';
377 378 if (filename.length === 0 || filename[0] === '.') {
378 379 dialog.modal({
379 380 title : 'Invalid file name',
380 381 body : "File names must be at least one character and not start with a dot",
381 382 buttons : {'OK' : { 'class' : 'btn-primary' }}
382 383 });
383 384 return false;
384 385 }
385 386 if (filedata instanceof ArrayBuffer) {
386 387 // base64-encode binary file data
387 388 var bytes = '';
388 389 var buf = new Uint8Array(filedata);
389 390 var nbytes = buf.byteLength;
390 391 for (var i=0; i<nbytes; i++) {
391 392 bytes += String.fromCharCode(buf[i]);
392 393 }
393 394 filedata = btoa(bytes);
394 395 format = 'base64';
395 396 }
396 397 var model = {
397 398 path: path,
398 399 name: filename
399 400 };
400 401
401 402 var name_and_ext = utils.splitext(filename);
402 403 var file_ext = name_and_ext[1];
403 404 var content_type;
404 405 if (file_ext === '.ipynb') {
405 406 model.type = 'notebook';
406 407 model.format = 'json';
407 408 try {
408 409 model.content = JSON.parse(filedata);
409 410 } catch (e) {
410 411 dialog.modal({
411 412 title : 'Cannot upload invalid Notebook',
412 413 body : "The error was: " + e,
413 414 buttons : {'OK' : {
414 415 'class' : 'btn-primary',
415 416 click: function () {
416 417 item.remove();
417 418 }
418 419 }}
419 420 });
420 421 return false;
421 422 }
422 423 content_type = 'application/json';
423 424 } else {
424 425 model.type = 'file';
425 426 model.format = format;
426 427 model.content = filedata;
427 428 content_type = 'application/octet-stream';
428 429 }
429 430 var filedata = item.data('filedata');
430 431
431 432 var settings = {
432 433 processData : false,
433 434 cache : false,
434 435 type : 'PUT',
435 436 data : JSON.stringify(model),
436 437 headers : {'Content-Type': content_type},
437 438 success : function (data, status, xhr) {
438 439 item.removeClass('new-file');
439 440 that.add_link(model, item);
440 441 that.add_delete_button(item);
441 442 that.session_list.load_sessions();
442 443 },
443 444 error : utils.log_ajax_error,
444 445 };
445 446
446 447 var url = utils.url_join_encode(
447 448 that.base_url,
448 449 'api/contents',
449 450 that.notebook_path,
450 451 filename
451 452 );
452 453
453 454 var exists = false;
454 455 $.each(that.element.find('.list_item:not(.new-file)'), function(k,v){
455 456 if ($(v).data('name') === filename) { exists = true; return false; }
456 457 });
457 458 if (exists) {
458 459 dialog.modal({
459 460 title : "Replace file",
460 461 body : 'There is already a file named ' + filename + ', do you want to replace it?',
461 462 buttons : {
462 463 Overwrite : {
463 464 class: "btn-danger",
464 465 click: function() { $.ajax(url, settings); }
465 466 },
466 467 Cancel : {
467 468 click: function() { item.remove(); }
468 469 }
469 470 }
470 471 });
471 472 } else {
472 473 $.ajax(url, settings);
473 474 }
474 475
475 476 return false;
476 477 });
477 478 var cancel_button = $('<button/>').text("Cancel")
478 479 .addClass("btn btn-default btn-xs")
479 480 .click(function (e) {
480 481 item.remove();
481 482 return false;
482 483 });
483 484 item.find(".item_buttons").empty()
484 485 .append(upload_button)
485 486 .append(cancel_button);
486 487 };
487 488
488 489
489 490 NotebookList.prototype.new_notebook = function(){
490 491 var path = this.notebook_path;
491 492 var base_url = this.base_url;
492 493 var settings = {
493 494 processData : false,
494 495 cache : false,
495 496 type : "POST",
496 497 dataType : "json",
497 498 async : false,
498 499 success : function (data, status, xhr) {
499 500 var notebook_name = data.name;
500 501 window.open(
501 502 utils.url_join_encode(
502 503 base_url,
503 504 'notebooks',
504 505 path,
505 506 notebook_name),
506 507 '_blank'
507 508 );
508 509 },
509 510 error : $.proxy(this.new_notebook_failed, this),
510 511 };
511 512 var url = utils.url_join_encode(
512 513 base_url,
513 514 'api/contents',
514 515 path
515 516 );
516 517 $.ajax(url, settings);
517 518 };
518 519
519 520
520 521 NotebookList.prototype.new_notebook_failed = function (xhr, status, error) {
521 522 utils.log_ajax_error(xhr, status, error);
522 523 var msg;
523 524 if (xhr.responseJSON && xhr.responseJSON.message) {
524 525 msg = xhr.responseJSON.message;
525 526 } else {
526 527 msg = xhr.statusText;
527 528 }
528 529 dialog.modal({
529 530 title : 'Creating Notebook Failed',
530 531 body : "The error was: " + msg,
531 532 buttons : {'OK' : {'class' : 'btn-primary'}}
532 533 });
533 534 };
534 535
535 536 // Backwards compatability.
536 537 IPython.NotebookList = NotebookList;
537 538
538 539 return {'NotebookList': NotebookList};
539 540 });
General Comments 0
You need to be logged in to leave comments. Login now