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