##// END OF EJS Templates
Added ESC keep to upload textbox to cancel.
Jeffrey Bush -
Show More
@@ -1,495 +1,498 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 ], 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 }
100 100 // Replace the file input form wth a clone of itself. This is required to
101 101 // reset the form. Otherwise, if you upload a file, delete it and try to
102 102 // upload it again, the changed event won't fire.
103 103 var form = $('input.fileinput');
104 104 form.replaceWith(form.clone(true));
105 105 return false;
106 106 };
107 107
108 108 NotebookList.prototype.clear_list = function (remove_uploads) {
109 109 // Clears the navigation tree.
110 110 //
111 111 // Parameters
112 112 // remove_uploads: bool=False
113 113 // Should upload prompts also be removed from the tree.
114 114 if (remove_uploads) {
115 115 this.element.children('.list_item').remove();
116 116 } else {
117 117 this.element.children('.list_item:not(.new-file)').remove();
118 118 }
119 119 };
120 120
121 121 NotebookList.prototype.load_sessions = function(){
122 122 this.session_list.load_sessions();
123 123 };
124 124
125 125
126 126 NotebookList.prototype.sessions_loaded = function(data){
127 127 this.sessions = data;
128 128 this.load_list();
129 129 };
130 130
131 131 NotebookList.prototype.load_list = function () {
132 132 var that = this;
133 133 var settings = {
134 134 processData : false,
135 135 cache : false,
136 136 type : "GET",
137 137 dataType : "json",
138 138 success : $.proxy(this.list_loaded, this),
139 139 error : $.proxy( function(xhr, status, error){
140 140 utils.log_ajax_error(xhr, status, error);
141 141 that.list_loaded([], null, null, {msg:"Error connecting to server."});
142 142 },this)
143 143 };
144 144
145 145 var url = utils.url_join_encode(
146 146 this.base_url,
147 147 'api',
148 148 'contents',
149 149 this.notebook_path
150 150 );
151 151 $.ajax(url, settings);
152 152 };
153 153
154 154
155 155 NotebookList.prototype.list_loaded = function (data, status, xhr, param) {
156 156 var message = 'Notebook list empty.';
157 157 if (param !== undefined && param.msg) {
158 158 message = param.msg;
159 159 }
160 160 var item = null;
161 161 var model = null;
162 162 var list = data.content;
163 163 var len = list.length;
164 164 this.clear_list();
165 165 if (len === 0) {
166 166 item = this.new_item(0);
167 167 var span12 = item.children().first();
168 168 span12.empty();
169 169 span12.append($('<div style="margin:auto;text-align:center;color:grey"/>').text(message));
170 170 }
171 171 var path = this.notebook_path;
172 172 var offset = 0;
173 173 if (path !== '') {
174 174 item = this.new_item(0);
175 175 model = {
176 176 type: 'directory',
177 177 name: '..',
178 178 path: path,
179 179 };
180 180 this.add_link(model, item);
181 181 offset = 1;
182 182 }
183 183 for (var i=0; i<len; i++) {
184 184 model = list[i];
185 185 item = this.new_item(i+offset);
186 186 this.add_link(model, item);
187 187 }
188 188 };
189 189
190 190
191 191 NotebookList.prototype.new_item = function (index) {
192 192 var item = $('<div/>').addClass("list_item").addClass("row");
193 193 // item.addClass('list_item ui-widget ui-widget-content ui-helper-clearfix');
194 194 // item.css('border-top-style','none');
195 195 item.append($("<div/>").addClass("col-md-12").append(
196 196 $('<i/>').addClass('item_icon')
197 197 ).append(
198 198 $("<a/>").addClass("item_link").append(
199 199 $("<span/>").addClass("item_name")
200 200 )
201 201 ).append(
202 202 $('<div/>').addClass("item_buttons btn-group pull-right")
203 203 ));
204 204
205 205 if (index === -1) {
206 206 this.element.append(item);
207 207 } else {
208 208 this.element.children().eq(index).after(item);
209 209 }
210 210 return item;
211 211 };
212 212
213 213
214 214 NotebookList.icons = {
215 215 directory: 'folder_icon',
216 216 notebook: 'notebook_icon',
217 217 file: 'file_icon',
218 218 };
219 219
220 220 NotebookList.uri_prefixes = {
221 221 directory: 'tree',
222 222 notebook: 'notebooks',
223 223 file: 'files',
224 224 };
225 225
226 226
227 227 NotebookList.prototype.add_link = function (model, item) {
228 228 var path = model.path,
229 229 name = model.name;
230 230 item.data('name', name);
231 231 item.data('path', path);
232 232 item.find(".item_name").text(name);
233 233 var icon = NotebookList.icons[model.type];
234 234 var uri_prefix = NotebookList.uri_prefixes[model.type];
235 235 item.find(".item_icon").addClass(icon).addClass('icon-fixed-width');
236 236 var link = item.find("a.item_link")
237 237 .attr('href',
238 238 utils.url_join_encode(
239 239 this.base_url,
240 240 uri_prefix,
241 241 path,
242 242 name
243 243 )
244 244 );
245 245 // directory nav doesn't open new tabs
246 246 // files, notebooks do
247 247 if (model.type !== "directory") {
248 248 link.attr('target','_blank');
249 249 }
250 250 var path_name = utils.url_path_join(path, name);
251 251 if (model.type == 'file') {
252 252 this.add_delete_button(item);
253 253 } else if (model.type == 'notebook') {
254 254 if(this.sessions[path_name] === undefined){
255 255 this.add_delete_button(item);
256 256 } else {
257 257 this.add_shutdown_button(item, this.sessions[path_name]);
258 258 }
259 259 }
260 260 };
261 261
262 262
263 263 NotebookList.prototype.add_name_input = function (name, item, icon_type) {
264 264 item.data('name', name);
265 265 item.find(".item_icon").addClass(NotebookList.icons[icon_type]).addClass('icon-fixed-width');
266 266 item.find(".item_name").empty().append(
267 267 $('<input/>')
268 268 .addClass("filename_input")
269 269 .attr('value', name)
270 270 .attr('size', '30')
271 271 .attr('type', 'text')
272 .keyup(function(event){if(event.keyCode == 13){item.find('.upload_button').click();}})
272 .keyup(function(event){
273 if(event.keyCode == 13){item.find('.upload_button').click();}
274 else if(event.keyCode == 27){item.remove();}
275 })
273 276 );
274 277 };
275 278
276 279
277 280 NotebookList.prototype.add_file_data = function (data, item) {
278 281 item.data('filedata', data);
279 282 };
280 283
281 284
282 285 NotebookList.prototype.add_shutdown_button = function (item, session) {
283 286 var that = this;
284 287 var shutdown_button = $("<button/>").text("Shutdown").addClass("btn btn-xs btn-danger").
285 288 click(function (e) {
286 289 var settings = {
287 290 processData : false,
288 291 cache : false,
289 292 type : "DELETE",
290 293 dataType : "json",
291 294 success : function () {
292 295 that.load_sessions();
293 296 },
294 297 error : utils.log_ajax_error,
295 298 };
296 299 var url = utils.url_join_encode(
297 300 that.base_url,
298 301 'api/sessions',
299 302 session
300 303 );
301 304 $.ajax(url, settings);
302 305 return false;
303 306 });
304 307 // var new_buttons = item.find('a'); // shutdown_button;
305 308 item.find(".item_buttons").text("").append(shutdown_button);
306 309 };
307 310
308 311 NotebookList.prototype.add_delete_button = function (item) {
309 312 var new_buttons = $('<span/>').addClass("btn-group pull-right");
310 313 var notebooklist = this;
311 314 var delete_button = $("<button/>").text("Delete").addClass("btn btn-default btn-xs").
312 315 click(function (e) {
313 316 // $(this) is the button that was clicked.
314 317 var that = $(this);
315 318 // We use the filename from the parent list_item element's
316 319 // data because the outer scope's values change as we iterate through the loop.
317 320 var parent_item = that.parents('div.list_item');
318 321 var name = parent_item.data('name');
319 322 var message = 'Are you sure you want to permanently delete the file: ' + name + '?';
320 323 dialog.modal({
321 324 title : "Delete file",
322 325 body : message,
323 326 buttons : {
324 327 Delete : {
325 328 class: "btn-danger",
326 329 click: function() {
327 330 var settings = {
328 331 processData : false,
329 332 cache : false,
330 333 type : "DELETE",
331 334 dataType : "json",
332 335 success : function (data, status, xhr) {
333 336 parent_item.remove();
334 337 },
335 338 error : utils.log_ajax_error,
336 339 };
337 340 var url = utils.url_join_encode(
338 341 notebooklist.base_url,
339 342 'api/contents',
340 343 notebooklist.notebook_path,
341 344 name
342 345 );
343 346 $.ajax(url, settings);
344 347 }
345 348 },
346 349 Cancel : {}
347 350 }
348 351 });
349 352 return false;
350 353 });
351 354 item.find(".item_buttons").text("").append(delete_button);
352 355 };
353 356
354 357
355 358 NotebookList.prototype.add_upload_button = function (item, type) {
356 359 var that = this;
357 360 var upload_button = $('<button/>').text("Upload")
358 361 .addClass('btn btn-primary btn-xs upload_button')
359 362 .click(function (e) {
360 363 var path = that.notebook_path;
361 364 var filename = item.find('.item_name > input').val();
362 365 var filedata = item.data('filedata');
363 366 var format = 'text';
364 367 if (filedata instanceof ArrayBuffer) {
365 368 // base64-encode binary file data
366 369 var bytes = '';
367 370 var buf = new Uint8Array(filedata);
368 371 var nbytes = buf.byteLength;
369 372 for (var i=0; i<nbytes; i++) {
370 373 bytes += String.fromCharCode(buf[i]);
371 374 }
372 375 filedata = btoa(bytes);
373 376 format = 'base64';
374 377 }
375 378 var model = {
376 379 path: path,
377 380 name: filename
378 381 };
379 382
380 383 var name_and_ext = utils.splitext(filename);
381 384 var file_ext = name_and_ext[1];
382 385 var content_type;
383 386 if (file_ext === '.ipynb') {
384 387 model.type = 'notebook';
385 388 model.format = 'json';
386 389 try {
387 390 model.content = JSON.parse(filedata);
388 391 } catch (e) {
389 392 dialog.modal({
390 393 title : 'Cannot upload invalid Notebook',
391 394 body : "The error was: " + e,
392 395 buttons : {'OK' : {
393 396 'class' : 'btn-primary',
394 397 click: function () {
395 398 item.remove();
396 399 }
397 400 }}
398 401 });
399 402 }
400 403 content_type = 'application/json';
401 404 } else {
402 405 model.type = 'file';
403 406 model.format = format;
404 407 model.content = filedata;
405 408 content_type = 'application/octet-stream';
406 409 }
407 410 var filedata = item.data('filedata');
408 411
409 412 var settings = {
410 413 processData : false,
411 414 cache : false,
412 415 type : 'PUT',
413 416 data : JSON.stringify(model),
414 417 headers : {'Content-Type': content_type},
415 418 success : function (data, status, xhr) {
416 419 item.removeClass('new-file');
417 420 that.add_link(model, item);
418 421 that.add_delete_button(item);
419 422 that.session_list.load_sessions();
420 423 },
421 424 error : utils.log_ajax_error,
422 425 };
423 426
424 427 var url = utils.url_join_encode(
425 428 that.base_url,
426 429 'api/contents',
427 430 that.notebook_path,
428 431 filename
429 432 );
430 433 $.ajax(url, settings);
431 434 return false;
432 435 });
433 436 var cancel_button = $('<button/>').text("Cancel")
434 437 .addClass("btn btn-default btn-xs")
435 438 .click(function (e) {
436 439 item.remove();
437 440 return false;
438 441 });
439 442 item.find(".item_buttons").empty()
440 443 .append(upload_button)
441 444 .append(cancel_button);
442 445 };
443 446
444 447
445 448 NotebookList.prototype.new_notebook = function(){
446 449 var path = this.notebook_path;
447 450 var base_url = this.base_url;
448 451 var settings = {
449 452 processData : false,
450 453 cache : false,
451 454 type : "POST",
452 455 dataType : "json",
453 456 async : false,
454 457 success : function (data, status, xhr) {
455 458 var notebook_name = data.name;
456 459 window.open(
457 460 utils.url_join_encode(
458 461 base_url,
459 462 'notebooks',
460 463 path,
461 464 notebook_name),
462 465 '_blank'
463 466 );
464 467 },
465 468 error : $.proxy(this.new_notebook_failed, this),
466 469 };
467 470 var url = utils.url_join_encode(
468 471 base_url,
469 472 'api/contents',
470 473 path
471 474 );
472 475 $.ajax(url, settings);
473 476 };
474 477
475 478
476 479 NotebookList.prototype.new_notebook_failed = function (xhr, status, error) {
477 480 utils.log_ajax_error(xhr, status, error);
478 481 var msg;
479 482 if (xhr.responseJSON && xhr.responseJSON.message) {
480 483 msg = xhr.responseJSON.message;
481 484 } else {
482 485 msg = xhr.statusText;
483 486 }
484 487 dialog.modal({
485 488 title : 'Creating Notebook Failed',
486 489 body : "The error was: " + msg,
487 490 buttons : {'OK' : {'class' : 'btn-primary'}}
488 491 });
489 492 };
490 493
491 494 // Backwards compatability.
492 495 IPython.NotebookList = NotebookList;
493 496
494 497 return {'NotebookList': NotebookList};
495 498 });
General Comments 0
You need to be logged in to leave comments. Login now